Flutter中用MultiProvider启动加载SharedPreferences无需Consumer的实现问题
问题清单
- 错误的Provider实例创建:
ChangeNotifierProvider的create回调要求返回ChangeNotifier类型的实例,你当前的写法SettingsProvider().loadSettings()返回的是loadSettings方法的执行结果(无返回值),导致Provider容器中存储的不是SettingsProvider实例,后续读取时会触发类型不匹配报错。
- 错误的Provider实例创建:
- 主题加载逻辑错误:
loadSavedTheme方法中获取存储主题的默认值写错为£,当SharedPreferences中未存储主题配置时,会返回非法的主题值,导致主题匹配逻辑失效。
- 主题加载逻辑错误:
- 潜在的重建循环风险:
loadSavedCurrency和loadSavedTheme方法执行完成后都会调用notifyListeners,如果Provider实例被重复创建、或者loadSettings被重复调用,会触发反复rebuild形成死循环。
- 潜在的重建循环风险:
修复方案
1. 修正providers.dart代码
重点修改loadSavedTheme的默认值:
class SettingsProvider with ChangeNotifier { SharedPreferences? _prefs; String _savedCurrency = '£'; String _currentTheme = 'light'; String get currencySymbol => _savedCurrency; String get currentTheme => _currentTheme; _initPrefs() async { _prefs ??= await SharedPreferences.getInstance(); } loadSettings() { _savedCurrency = '£'; _currentTheme = 'light'; loadSavedTheme(); loadSavedCurrency(); } // Currency Future<void> loadSavedCurrency() async { await _initPrefs(); _savedCurrency = _prefs!.getString('currency') ?? '£'; notifyListeners(); } Future<void> saveNewCurrency(String newCurrency) async { await _initPrefs(); _prefs!.setString('currency', newCurrency); _savedCurrency = newCurrency; notifyListeners(); } //Theme Future<void> loadSavedTheme() async { await _initPrefs(); // 修正默认值为light _currentTheme = _prefs!.getString('theme') ?? 'light'; notifyListeners(); } Future<void> saveNewTheme(String newTheme) async { await _initPrefs(); _prefs!.setString('theme', newTheme); _currentTheme = newTheme; notifyListeners(); } }
2. 修正main.dart的Provider创建逻辑
用级联运算符调用loadSettings,保证create回调返回的是SettingsProvider实例:
Future main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) => MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => GoogleSignInProvider()), // 修正Provider创建逻辑,用级联保证返回实例 ChangeNotifierProvider( create: (_) => SettingsProvider()..loadSettings()), ], builder: (context, child) { String? _theme = Provider.of<SettingsProvider>(context, listen: true) .currentTheme; print('theme is $_theme'); return MaterialApp( home: LandingPage(), theme: ThemeData.light(), darkTheme: ThemeData.dark(), themeMode: applyThemeMode(_theme), ); }); } ThemeMode applyThemeMode(String? theme) { if (theme == null || theme.isEmpty) { return ThemeMode.system; } if (theme == 'light') { return ThemeMode.light; } else if (theme == 'dark') { return ThemeMode.dark; } else { return ThemeMode.system; } }
可选优化(避免主题切换闪烁)
如果需要避免启动时先显示默认主题再切换到存储主题的闪烁问题,可以在main函数中提前初始化SharedPreferences,加载完配置后再启动应用:
Future main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); // 提前加载配置 final prefs = await SharedPreferences.getInstance(); final initialTheme = prefs.getString('theme') ?? 'light'; final initialCurrency = prefs.getString('currency') ?? '£'; runApp(MyApp(initialTheme: initialTheme, initialCurrency: initialCurrency)); } // 后续把初始值传给SettingsProvider构造函数即可,无需启动后异步读取再notify
内容的提问来源于stack exchange,提问作者Trax
相关产品推荐
相关产品推荐

