Persisting theme using SharedPreferences (Android, iOS, and Web)

Posted on December 30, 2019 in Flutter

x-platform-themes

Background

In this previous article, we saw how to implement theme switching using Provider. In this article, we'll see how to persist the selected theme using SharedPreferences Flutter plugin. iOS platform uses NSUserDefaults and Android platform uses SharedPreferences implementations to store simple data to disk asynchronously.

The difference between persisting data using SharedPreferences plugin vs local database is that SharedPreferences plugin can not guarantee that writes will be persisted to disk after app restarts. However, saving data to local database is more reliable.


Checkout related articles:


Target Audience: Beginner

Recipe: Persisting selected theme using SharedPreferences plugin.

Focus Widget: SharedPreferences plugin

Goal: Persisting chosen theme in disk. Implement a simple UI with an image, text and a button to switch themes. Page's default theme is light. Clicking on "Switch Theme" button will apply dark theme to page, and vice versa. Switching theme will save selected theme using SharedPreferences plugin for Android, iOS and FlutterWeb. Flutter Web is supported since version: 0.5.6.

Light Theme:

light-theme

Dark Theme:

dark-theme


Checkout the companion video tutorial:


Note: In this article, I'll only focus on persisting data using SharedPreferences plugin. Please refer to previous article for app architecture and other details.

Step #1. pubspec.yaml

Add package dependencies in pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter

  #SharedPreferences plugin for Android, iOS and Web
  shared_preferences: ^0.5.6

Step #2. App's entry point

This code recipe is a part of the code recipes app as shown below:

x-cookbook-flutter


There are two ways to run this code recipe:

  1. StandAlone: Use following code snippet at the top of the themes_db.dart to run it independently.
void main() => runApp(ChangeNotifierProvider<ThemesNotifierSharedPrefs>(
    child: ThemesSharedPrefsCaching(),
    builder: (BuildContext context) {
      return ThemesNotifierSharedPrefs();
    }));
  1. Code Recipe App: Use following code in router.dart to run this code recipe as part of the code recipe app.
case THEMES_DEMO_SHAREDPREFS:
  return MaterialPageRoute(builder: (context) {
    return ChangeNotifierProvider<ThemesNotifierSharedPrefs>(
      child: ThemesSharedPrefsCaching(),
      builder: (BuildContext context) {
        return ThemesNotifierSharedPrefs();
      },
    );
  });
  break;

Step #3. Loading theme from SharedPreferences

Stateful widget ThemesSharedPrefsCaching loads active theme using Provider.of<ThemesNotifierSharedPrefs>(context) .loadActiveThemeData(context);

themes_sharedPrefs.dart:

class _ThemesSharedPrefsCachingState extends State<ThemesSharedPrefsCaching> {
  @override
  Widget build(BuildContext context) {
    Provider.of<ThemesNotifierSharedPrefs>(context)
        .loadActiveThemeData(context);
    return MaterialApp(
        theme: Provider.of<ThemesNotifierSharedPrefs>(context).currentThemeData,
        home: Scaffold(
          appBar: AppBar(
            title: Text("Theme Caching (SharedPreference)"),
          ),
          body: body(),
        ));
  }
  ...
}  

themes_notifier_sp.dart:

Fetching theme_id from database, loading and notifying currentTheme:

class ThemesNotifierSharedPrefs with ChangeNotifier {

  ...

  Future<void> activateTheme(MyThemes theme) async {
    var sharedPrefs = await SharedPreferences.getInstance();
    await sharedPrefs.setInt('theme_id', theme.index);
  }

  void loadActiveThemeData(BuildContext context) async {
    var sharedPrefs = await SharedPreferences.getInstance();
    int themeId = sharedPrefs.getInt('theme_id') ?? MyThemes.light.index;
    currentTheme = MyThemes.values[themeId];
  }

  set currentTheme(MyThemes theme) {
    if (theme != null) {
      _currentTheme = theme;
      _currentThemeData = themeData[_currentTheme.index];
      notifyListeners();
    }
  }

}

Step #4. Switching and Saving theme to database

Switching theme toggles previously selected theme. We don't need to explicitly remove old theme since sharedPrefs key theme_id is updated to new value. Newly updated currentTheme overwrites the theme_id using api activateTheme(...).

class ThemesNotifierSharedPrefs with ChangeNotifier {

  ...

  void switchTheme(BuildContext context) async {
    currentTheme == MyThemes.light
        ? currentTheme = MyThemes.dark
        : currentTheme = MyThemes.light;

    activateTheme(currentTheme);
  }

  ...
}

All Done !

Source Code

  1. Recipe source code is available here

  2. Code recipe project's source code is available here

References:

  1. SharedPreferences plugin
  2. Cross-platform ToDo App template
  3. Previous article: Implement Flutter themes using Provider
  4. Related article: Persisting theme in LocalDatabase (Moor plugin)

Happy cooking with Flutter :)

Liked the article ? Couldn't find a topic of your interest ? Please leave comments or email me about topics you would like me to write ! BTW I love cupcakes and coffee both :)

Follow me at twitter