Themes are switched using RIverpod; ThemeMode is saved using Shared Preferences. They are working fine.But when I set the default values as follows, the default theme is shown for a moment at the beginning. That is ugly.
late ThemeMode _themeMode = ThemeMode.system;.
If I don’t set the initial value, I get the following error, but the application runs fine without crashing.
late ThemeMode _themeMode;
LateInitialisationError: field '_themeMode@47036434' has not been initialised.
The whole code looks like this.
void main() async {
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
await SharedPreferences.getInstance();
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends ConsumerWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Demo App',
theme: myLightTheme,
darkTheme: myDarkTheme,
themeMode: ref.watch(themeModeProvider).mode,
home: const HomeScreen(),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../utils/theme_mode.dart';
final themeModeProvider = ChangeNotifierProvider((ref) => ThemeModeNotifier());
class ThemeModeNotifier extends ChangeNotifier {
late ThemeMode _themeMode = ThemeMode.system;
ThemeModeNotifier() {
_init();
}
ThemeMode get mode => _themeMode;
void _init() async {
_themeMode = await loadThemeMode(); // get ThemeMode from shared preferences
notifyListeners();
}
void update(ThemeMode nextMode) async {
_themeMode = nextMode;
await saveThemeMode(nextMode); // save ThemeMode to shared preferences
notifyListeners();
}
}
I would like to somehow prevent this error from appearing.
Please, I would appreciate it if you could help me.
I tryed to delete "late" and be nullable. But it didn’t work.
2
Answers
You’re using FlutterNativeSplash, then you can remove splash screen after the theme mode loaded.
Completer can be helpful for this situation.
and where removing the SplashScreen,
The point is that you have to somehow wait somewhere for the asynchronous code to load your
ThemeMode
. Along with this, I recommend that you stop usingChangeNotifierProvider
in favor ofNotifier
.0️⃣ This is what your
ThemeModeNotifier
will look like now:In the
build
method, you can safely listen towatch
any of your providers, and this method will be restarted every time your dependencies change.You can also insure yourself with a side effect for the future (when new dependencies appear that may change):
1️⃣ Now, as you can see,
StorageService
has appeared. This is a neat dependency injection to use later on this instance in theupdate
method. And here’s what it looks like:I have used abstract code for brevity of explanation and no bugs in my ide.
2️⃣ Next, your
main
method now looks like this:It is in it that the asynchronous initialization of your state will now take place.
P.s. descendants, when Riverpod >2.x.x is able to override again with
overrideWithValue
forNotifierProvider
, use this method. Follow the situation in this issue.Here is a complete example of a working application, in order to understand how it works. Try running directly in dartpad.dev:
Pay particular attention to the fact that
StorageServiceImpl.loadThemeMode
returnsThemeMode.dark
. AndThemeMode.light
is returned inThemeModeNotifier.build
. But when the application starts, the theme will be exactlydark
.