vtomic85
vtomic85

Reputation: 611

Late initialization of a Theme in Flutter

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?

Upvotes: 1

Views: 836

Answers (1)

Amir_P
Amir_P

Reputation: 9019

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();
    });
  }
...
}

Upvotes: 2

Related Questions