Building theme switcher using Provider and Shared Preferences

Building theme switcher using Provider and Shared Preferences


In this tutorial, we’ll learn how to create dark and light theme switcher in our Flutter application using Provider and we’ll persist the chosen theme using Shared Preferences so that when the users open the app for the next time, that theme that they chose would be permanent.

Contents

Before we kick off

Before we start we make sure:

  • You have a text editor ( VsCode).
  • You have Flutter running on your computer.
  • You have basic knowledge of Flutter and Dart language.

Introduction

This article shows dynamically switching between themes in flutter during runtime using Provider as well as making the chosen theme permanent between app sessions using shared preferences.

Flutter Theme Switcher

For illustration, consider the following simple app.

Theme Switcher

Shared Preferences in Flutter

Wraps platform-specific persistent storage for simple data (NSUserDefaults on iOS and macOS, SharedPreferences on Android, etc.). In case you want to go deeper, click here to get it done, using the official documentation.

Provider

You can use it to provide a value (usually a data model object) to anywhere in the widget tree. In case you want to go more deep click here to get it done, using the official documentation.

Building theme switcher using provider

Create a Flutter project named theme_swicher and open it to your text editor.

flutter create theme_swicher
cd theme_swicher

On lib path create two folders: 

lib folder

Now on services path create theme_provider.dart and on view path create settings.dart.

files

Let’s import some packages. Open pubspec.yaml and add these dependecies:

dependencies:
  provider: ^4.3.1
  shared_preferences: ^0.5.8

It should looks  like this:

pubspec.yaml

You can find all the packages here. Save it or run this command:

 $ flutter pub get

Now that we have set up the project we are ready to move forward.

Let’s create our themes. Open theme_provider.dart:

theme_provider.dart

import 'package:flutter/material.dart';

ThemeData light = ThemeData(
  brightness: Brightness.light,
  primarySwatch: Colors.indigo,
  accentColor: Colors.pink,
  scaffoldBackgroundColor: Color(0xfff1f1f1)
);

ThemeData dark = ThemeData(
  brightness: Brightness.dark,
  primarySwatch: Colors.indigo,
  accentColor: Colors.pink,
);

Let’s create a simple UI with a switch button so that we can choose the light or dark mode.

Open your settings.dart file and past this:

import 'package:flutter/material.dart';

class SettingsPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Theme Switcher"),
      ),
      body: SingleChildScrollView(
        padding: EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
                  SwitchListTile(
                    title: Text("Dark Mode"),
                    onChanged:(value){ 
                  } ,
                  value:false ,
              ),
            Card(
              child: ListTile(
                title: Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."),
              ),
            ),
          ]
        ),
      )
    );
  }
}

On main.dart

import 'package:flutter/material.dart';
import 'package:theme_switcher/view/settings.dart';
import 'package:theme_switcher/services/theme_provider.dart';
void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Theme Provider',
      theme: light,
      home: SettingsPage(),
    );
  }
}

Run the app.

$ flutter run
Flutter theme switcher
light theme

So our job is to make this switch button work so that when users turns on the dark mode, the dark mode theme is applied and when they run off the dark mode the light theme is applied.

On theme_provider.dart we’ll create a class that extends ChangeNotifier that will allow us to change the dark/light theme.

Add this class on theme_provider.dart

class ThemeNotifier extends ChangeNotifier {
  final String key = "theme";
  SharedPreferences _pref;
  bool _darkTheme;
  bool get darkTheme => _darkTheme;
  ThemeNotifier() {
    _darkTheme = true;
  }
  toggleTheme(){
    _darkTheme = !_darkTheme;
    notifyListeners();
  }
}

Above we have

  1.  key to save in the Shared Preferences (we’ll see in the next section). 
  2. _darkTheme: this variable says whether the light or dark theme misses.
  3. get darkTheme to get the value on _darkTheme.
  4. The construct that makes the dark theme the default.
  5. toggleTheme() function that toggles the theme.
  6. Into toggleTheme() we call notifyListeners() method to tell the “listeners” to rebuild and get the new value of _darkTheme.

On main.dart wrap the MaterialApp with ChangeNotifierProvider and add a Consumer. You can find here the official documentation of the Provider.

It should look like this

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:theme_switcher/view/settings.dart';
import 'package:theme_switcher/services/theme_provider.dart';
void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => ThemeNotifier(),
      child: Consumer<ThemeNotifier>(
        builder: (context, ThemeNotifier notifier, child) {
          return MaterialApp(
            title: "Flutter Provider",
            theme: notifier.darkTheme ? dark : light,
            home: SettingsPage(),
          );
        },
      ),
      );
  }
}

Before we run the app. Let’s update our switch button. We need to wrap the SwitchListTile with a Consumer and the value from ThemeProvider.

            Consumer<ThemeNotifier>(
                builder:(context, notifier, child) => 
                  SwitchListTile(
                    title: Text("Dark Mode"),
                    onChanged:(value){
                      notifier.toggleTheme();
                  } ,
                  value: notifier.darkTheme ,
              ),
            ),

Run it.

Congratulations!!!! The theme is changing right now.

But when we Hot Restart the App the theme doesn’t persist.  We can solve this with Shared Preferences.

Persist the chosen theme using Shared Preferences

To persist the theme whenever the users close the app we need to create two functions on theme_provider.dart.  One to load the theme from Shared Preferences and the next one to save the theme to Shared Preferences.

Open theme_provider.dart

 // _initPref() is to iniliaze  the _pref variable
  _initPrefs() async {
    if(_pref == null)
      _pref  = await SharedPreferences.getInstance();
  }
  _loadFromPrefs() async {
      await _initPrefs();
      _darkTheme = _pref.getBool(key) ?? true;
      notifyListeners();
  }
  _saveToPrefs() async {
    await _initPrefs();
    _pref.setBool(key, _darkTheme);
  }

Whenever we instance the ThemeNotifier we would like to load the theme from Shared Preferences.

Update the constructor

  ThemeNotifier() {
    _darkTheme = true;
    _loadFromPrefs(); // add this line
  }

And whenever we toggle the theme we would like to save it to Shared Preferences so that it is persist.

Update the toggleTheme()

toggleTheme(){
    _darkTheme = !_darkTheme;
    _saveToPrefs(); // add this line
    notifyListeners();
  }

Before you run the app you have to rebuilt it. If you are on debug mode, stop the app and run it again.

Conclusion

Now you are able to access a value anywhere on your Widget tree using Provider – Change Notifier and With Shared Preferences you can read and write key-value pairs in a couple of lines easily. But always remember Shared Preferences is not a solution for storing critical data.

You can find the source code here.


Share on social media

//