1

I'm trying to implement dark and light themes in my Flutter app. To do this, I'm using a ViewModel approach to notify the whole application when the theme changes. When a user changes the theme, I'm saving it using shared_preferences. When the application starts again, I'm loading the saved theme from shared preferences:

main.dart

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<ThemeViewModel>(
      builder: (context, themeViewModel, _) => MaterialApp(
        theme: themeViewModel.getTheme(),
        ...
  

theme_view_model

class ThemeViewModel extends ChangeNotifier {
  final darkTheme = ThemeData(...;

  final lightTheme = ThemeData(...);

  late ThemeData _themeData;

  ThemeData getTheme() => _themeData;

  ThemeViewModel() {
    StorageManager.readData('themeMode').then((value) {
      var themeMode = value ?? 'light';
      if (themeMode == 'light') {
        _themeData = lightTheme;
      } else {
        _themeData = darkTheme;
      }
      notifyListeners();
    });
  }
...
}

However, when I start the app, I get the error screen for a few seconds (probably before the theme data is loaded from the shared preferences):

enter image description here

How can this be solved? How could I display e.g. a loading spinner until the theme is loaded?

vtomic85
  • 590
  • 1
  • 11
  • 32

1 Answers1

2

There are a few ways to solve it.

1-You can define an async initialize method for your ThemeViewModel and wait for it in your main method.

void main() async {
  final viewModel = ThemeViewModel();
  await viewModel.init();
  ...
}

class ThemeViewModel extends ChangeNotifier {
  final darkTheme = ThemeData(...;

  final lightTheme = ThemeData(...);

  late ThemeData _themeData;

  ThemeData getTheme() => _themeData;
  
  Future init() async {
    themeMode = await StorageManager.readData('themeMode') ?? 'light';
    if (themeMode == 'light') {
      _themeData = lightTheme;
    } else {
      _themeData = darkTheme;
    }
  }
}

2-You can provide a default theme to use when the _themeData is null

class ThemeViewModel extends ChangeNotifier {
  final darkTheme = ThemeData(...;

  final lightTheme = ThemeData(...);

  ThemeData? _themeData;

  ThemeData getTheme() => _themeData ?? lightTheme;

  ThemeViewModel() {
    StorageManager.readData('themeMode').then((value) {
      var themeMode = value ?? 'light';
      if (themeMode == 'light') {
        _themeData = lightTheme;
      } else {
        _themeData = darkTheme;
      }
      notifyListeners();
    });
  }
...
}
Amir_P
  • 8,322
  • 5
  • 43
  • 92
  • 2
    Yeah, I was thinking about providing the default theme, but it would be ugly if the app loads and then the theme switches after a few seconds. I'll try to implement the first approach. – vtomic85 Aug 13 '21 at 08:35