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

使用Riverpod持久化ThemeMode时启动出现类型不匹配报错

错误根源

重启App时读取shared_preferences是异步操作,但你直接将未完成的Future赋值给了需要同步ThemeMode的参数(如MaterialApp.themeMode),或是Provider定义错误,返回了Future<dynamic>而非ThemeMode,导致类型不匹配报错。

解决方法

方法1:使用Riverpod 2.x的AsyncNotifier(推荐)

这种方式天然支持异步状态管理,能处理加载、错误、数据三种状态:

  1. 定义AsyncNotifier和对应Provider:
class ThemeModeNotifier extends AsyncNotifier<ThemeMode> {
  @override
  Future<ThemeMode> build() async {
    // 初始化时读取本地存储的主题模式
    final repo = ref.read(settingsRepositoryProvider);
    final savedMode = await repo.getThemeMode();
    // 无保存值时返回系统默认
    return savedMode ?? ThemeMode.system;
  }

  // 更新并持久化主题模式
  Future<void> updateTheme(ThemeMode newMode) async {
    final repo = ref.read(settingsRepositoryProvider);
    await repo.saveThemeMode(newMode);
    state = AsyncData(newMode);
  }
}

final themeModeProvider = AsyncNotifierProvider<ThemeModeNotifier, ThemeMode>(ThemeModeNotifier.new);
  1. 在MaterialApp中处理异步状态:
class MyApp extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final themeState = ref.watch(themeModeProvider);
    
    return themeState.when(
      loading: () => const MaterialApp(
        home: Scaffold(body: Center(child: CircularProgressIndicator()))
      ),
      error: (err, _) => MaterialApp(
        home: Scaffold(body: Center(child: Text('主题加载失败: $err')))
      ),
      data: (themeMode) => MaterialApp(
        theme: ThemeData.light(),
        darkTheme: ThemeData.dark(),
        themeMode: themeMode,
        home: const HomePage(),
      ),
    );
  }
}

方法2:使用StateNotifier+默认初始值

如果坚持用StateNotifier,确保初始状态是合法的ThemeMode,异步加载完成后再更新状态:

  1. 定义StateNotifier:
class ThemeNotifier extends StateNotifier<ThemeMode> {
  final SettingsRepository _repo;

  ThemeNotifier(this._repo) : super(ThemeMode.system) {
    // 初始化时异步加载主题
    _loadSavedTheme();
  }

  Future<void> _loadSavedTheme() async {
    final savedMode = await _repo.getThemeMode();
    if (savedMode != null) state = savedMode;
  }

  Future<void> setThemeMode(ThemeMode mode) async {
    await _repo.saveThemeMode(mode);
    state = mode;
  }
}

final themeNotifierProvider = StateNotifierProvider<ThemeNotifier, ThemeMode>((ref) {
  final repo = ref.read(settingsRepositoryProvider);
  return ThemeNotifier(repo);
});
  1. 确保SettingsRepository方法返回正确类型:
class SettingsRepository {
  final SharedPreferences _prefs;

  SettingsRepository(this._prefs);

  Future<ThemeMode?> getThemeMode() async {
    final modeStr = _prefs.getString('theme_mode');
    if (modeStr == null) return null;
    // 转换为ThemeMode枚举,避免返回dynamic
    return ThemeMode.values.firstWhere((mode) => mode.name == modeStr);
  }

  Future<void> saveThemeMode(ThemeMode mode) async {
    await _prefs.setString('theme_mode', mode.name);
  }
}

// 封装SharedPreferences的Provider
final sharedPreferencesProvider = FutureProvider<SharedPreferences>((ref) async {
  return await SharedPreferences.getInstance();
});

// 确保SettingsRepository依赖的SharedPreferences已初始化
final settingsRepositoryProvider = FutureProvider<SettingsRepository>((ref) async {
  final prefs = await ref.watch(sharedPreferencesProvider.future);
  return SettingsRepository(prefs);
});
  1. 在MaterialApp中使用:
class MyApp extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 等待SettingsRepository初始化完成
    final repoAsync = ref.watch(settingsRepositoryProvider);
    
    return repoAsync.when(
      loading: () => const MaterialApp(home: Scaffold(body: Center(child: CircularProgressIndicator()))),
      error: (err, _) => MaterialApp(home: Scaffold(body: Center(child: Text('初始化失败: $err')))),
      data: (_) {
        final themeMode = ref.watch(themeNotifierProvider);
        return MaterialApp(
          theme: ThemeData.light(),
          darkTheme: ThemeData.dark(),
          themeMode: themeMode,
          home: const HomePage(),
        );
      },
    );
  }
}

关键注意点

  • 禁止直接将Future类型赋值给需要ThemeMode的参数,必须等待异步操作完成后再使用结果。
  • 确保SettingsRepository的getThemeMode方法返回Future<ThemeMode?>而非Future<dynamic>,避免类型转换错误。
  • 使用Riverpod的异步状态组件(如AsyncNotifier、FutureProvider)处理异步依赖的初始化流程。

内容的提问来源于stack exchange,提问作者Ydev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 13:32:18