基于SharedPreference数据设置Flutter主题启动获取空值问题咨询
问题根因
SettingsSharedPreferences.getThemeString() 为同步返回方法,但内部读取SharedPreferences是异步IO操作,then回调执行前方法就已经返回了未赋值的theme变量,所以Provider初始化时拿到的主题值必然为null,异步读取完成后也没有触发UI更新,就导致首次启动主题不生效。
修复步骤
1. 修正SharedPreferences工具类
删掉错误的同步获取方法,统一异步逻辑,补全SharedPreferences实例获取的缺失逻辑:
class SettingsSharedPreferences { static const _keytheme = "theme"; static const String _defaultTheme = "System"; // 新增获取SharedPreferences实例的统一方法 static Future<SharedPreferences> _getPrefs() async => await SharedPreferences.getInstance(); static Future<void> setTheme(String theme) async { final prefs = await _getPrefs(); await prefs.setString(_keytheme, theme); } static Future<String> getTheme() async { final prefs = await _getPrefs(); final savedTheme = prefs.getString(_keytheme); if (savedTheme == null) { await setTheme(_defaultTheme); return _defaultTheme; } return savedTheme; } }
2. 修正PreferencesProvider逻辑
给主题设默认值避免null,新增异步初始化方法读取本地存储,读取完成后通知更新:
class PreferencesProvider extends ChangeNotifier { // 先设置默认值为跟随系统,避免初始化时为null String theme = "System"; PreferencesProvider() { _initTheme(); } Future<void> _initTheme() async { final savedTheme = await SettingsSharedPreferences.getTheme(); theme = savedTheme; notifyListeners(); } // 新增修改主题的方法,修改后同步到本地存储并通知更新 Future<void> updateTheme(String newTheme) async { theme = newTheme; await SettingsSharedPreferences.setTheme(newTheme); notifyListeners(); } }
3. 优化MyApp中的themeMode判断逻辑
减少重复的watch调用,逻辑更简洁:
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { final currentTheme = context.watch<PreferencesProvider>().theme; final ThemeMode themeMode; switch(currentTheme) { case "Dark": themeMode = ThemeMode.dark; break; case "Light": themeMode = ThemeMode.light; break; case "System": default: themeMode = ThemeMode.system; break; } return ChangeNotifierProvider<HabitProvider>( create: (context) => HabitProvider(), child: MaterialApp( darkTheme: ThemeData(brightness: Brightness.dark), themeMode: themeMode, home: MyHomePage(title: 'My App'), ), ); } }
可选优化:启动前预加载主题
如果不想出现启动后闪切主题的情况,可以在main函数中提前读取主题值再初始化Provider:
void main() async { WidgetsFlutterBinding.ensureInitialized(); // 提前读取主题 final initTheme = await SettingsSharedPreferences.getTheme(); runApp(ChangeNotifierProvider<PreferencesProvider>( create: (context) { return PreferencesProvider()..theme = initTheme; }, child: MyApp())); }
内容的提问来源于stack exchange,提问作者J0nathan Lam
相关产品推荐
相关产品推荐

