2

I am trying to add dark/light/custom theme modes in my Flutter application. I've used this tutorial, but my theme preferences do not save and do not apply after the application is restarted. I am using the shared_preferences library in my project, which you can access here

I've tried importing theme_manager.dart and other files as a packages and my storage_manager file looks like this now:

import 'package:shared_preferences/shared_preferences.dart';
import 'package:calculator/main.dart';
import 'package:calculator/theme_manager.dart';
import 'package:calculator/settings.dart';

class StorageManager {
  static void saveData(String key, dynamic value) async {
    final prefs = await SharedPreferences.getInstance();
    print(value);
    if (value is int) {
      prefs.setInt(key, value);
    } else if (value is String) {
      prefs.setString(key, value);
    } else if (value is bool) {
      prefs.setBool(key, value);
    } else {
      print("Invalid Type");
    }
  }

  static Future<dynamic> readData(String key) async {
    final prefs = await SharedPreferences.getInstance();
    dynamic obj = prefs.get(key);
    return obj;
  }

  static Future<bool> deleteData(String key) async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.remove(key);
  }
}

And function in theme_manager.dart, that should change theme (at the app start), looks like this:

late ThemeData _themeData;
var _buttonsData;

ThemeData getTheme() => _themeData;
getButtons() => _buttonsData;

ThemeNotifier() {
  StorageManager.readData('themeMode').then((value) {
    var themeMode = value ?? 'light';
    if (themeMode == 'light') {
        _themeData = lightTheme;
    } else if (themeMode == 'dark') {
        _themeData = darkTheme;
    } else {
        _themeData = customTheme;
    }
      notifyListeners();
    });
  StorageManager.readData('buttonsMode').then((value) {
    var buttonsMode = value ?? 'circle';
    if (buttonsMode == 'circle') {
        _buttonsData = circledButtons;
    } else if (buttonsMode == 'rounded') {
        _buttonsData = roundedButtons;
    } else if (buttonsMode == 'box') {
        _buttonsData = boxButtons;
    }
      notifyListeners();
  });
}

In the original example - there is no late argument before ThemeData _themeData;, but without it, my whole code does not work. Can this be a problem? Any help would be much appreciated!

  • saveData in your storage_manager is async but you're not awaiting it in theme_manager – TheFunk Jul 16 '22 at 14:58
  • Personally, I'd save a JSON representation of the theme data itself instead of just a string name of the theme. It won't cost more than a few bytes and then you don't need checks for if the theme is light mode or dark mode or any mode when reapplying the theme on boot. Plus you could more easily allow for further customizations by the user and still provide default themes for light and dark mode.. – TheFunk Jul 16 '22 at 15:01
  • I am a newbie basically, so, would appreciate an example of how to use JSON, or a tutorial, if you know any. Thank you! –  Jul 16 '22 at 16:47

1 Answers1

0

Your initialization process isn't working because you are not waiting for it. So you never get the saved theme from shared preferences. You can change your ThemeNotifier to something like that:

  ThemeNotifier();

  Future<void> init() async {
      final themeMode = await StorageManager.readData('themeMode') ?? 'light';
      final buttonsMode =
          await StorageManager.readData('buttonsMode') ?? 'circle';

      if (themeMode == 'light') {
        _themeData = lightTheme;
      } else if (themeMode == 'dark') {
        _themeData = darkTheme;
      } else {
        _themeData = customTheme;
      }

      if (buttonsMode == 'circle') {
        _buttonsData = circledButtons;
      } else if (buttonsMode == 'rounded') {
        _buttonsData = roundedButtons;
      } else if (buttonsMode == 'box') {
        _buttonsData = boxButtons;
      }

      notifyListeners();
  }

Then in your main method you should await the init method

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setPreferredOrientations(
      [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);

  final themeNotifier = ThemeNotifier();
  await themeNotifier.init();
  return runApp(ChangeNotifierProvider<ThemeNotifier>.value(
    value: themeNotifier,
    child: const MyApp(),
  ));
}

This way ThemeNotifier will be initialized with your saved value

Pierre Monier
  • 599
  • 2
  • 9
  • I've copied your answer into my code, created an apk and it still does not work. However, the app does not start with circled buttons and light theme, as stated in `await StorageManager.readData('themeMode') ?? 'light';`. Does `ThemeNotifier();` should be an empty function? After `await themeNotifier.init();` I added `print(themeNotifier);` and it printed `Instance of 'ThemeNotifier'`, should it state which theme to use? –  Jul 16 '22 at 18:23
  • Dear @Pierre Monier, can you, please, post the whole code? –  Jul 17 '22 at 17:06
  • the initialization must be async because you are reading the shared preferences asynchronously. The constructor is empty because you can't write async code in constructor. You can add a breakpoint after the `init` call and check `_themeData` and `_buttonData` properties – Pierre Monier Jul 18 '22 at 07:46
  • At the beginning `_themeData` is `ThemeData#e6367` and `_buttonsData` is `BorderRadius.zero`. But after changing in app these variables change. –  Jul 18 '22 at 10:50
  • So, basically, I was assigning properties to `_themeData` and `_buttonsData`. Now I changed these assignments to functions, so instead of assigning, now it calls a function that changes the theme. But what about `ThemeData getTheme() => _themeData;`? In the `main.dart` file I have `theme: theme.getTheme(),` which is incorrect, because `_themeData` is `ThemeData#[letter][4numbers]` –  Jul 18 '22 at 11:19
  • Would you want to change your answer, so I could mark it as a correct one? –  Jul 18 '22 at 12:30
  • All, I actually changed, after your hint is `if (themeMode == 'light') { setLightMode(); } else if (themeMode == 'dark') { setDarkMode(); } else { setCustomMode(); } if (buttonsMode == 'circle') { setCircleButtons(); } else if (buttonsMode == 'rounded') { setRoundedButtons(); } else if (buttonsMode == 'box') { setBoxButtons(); }`, and you can find all code [here](https://github.com/Alex-Zab/calculator/tree/main/after) –  Jul 18 '22 at 12:39