You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中用MultiProvider启动加载SharedPreferences无需Consumer的实现问题

问题清单

    1. 错误的Provider实例创建:ChangeNotifierProvider的create回调要求返回ChangeNotifier类型的实例,你当前的写法SettingsProvider().loadSettings()返回的是loadSettings方法的执行结果(无返回值),导致Provider容器中存储的不是SettingsProvider实例,后续读取时会触发类型不匹配报错。
    1. 主题加载逻辑错误:loadSavedTheme方法中获取存储主题的默认值写错为£,当SharedPreferences中未存储主题配置时,会返回非法的主题值,导致主题匹配逻辑失效。
    1. 潜在的重建循环风险: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 09:27:03