如何在Flutter中结合Hive与Riverpod实现自定义主题系统
实现基于Riverpod+Hive的自定义调色板主题系统
1. 完善自定义调色板抽象类与实现
先定义抽象调色板类,再实现各主题的具体配色:
abstract class CustomTheme { Color get backgroundColor; Color get primaryColor; Color get textColor; // 按需扩展更多配色属性 } class LightCustomTheme implements CustomTheme { @override Color get backgroundColor => Colors.white; @override Color get primaryColor => Colors.blue; @override Color get textColor => Colors.black87; } class DarkCustomTheme implements CustomTheme { @override Color get backgroundColor => Colors.grey[900]!; @override Color get primaryColor => Colors.lightBlueAccent; @override Color get textColor => Colors.white70; } class ChristmasCustomTheme implements CustomTheme { @override Color get backgroundColor => Colors.red[900]!; @override Color get primaryColor => Colors.green[600]!; @override Color get textColor => Colors.white; }
2. 用Riverpod创建主题状态管理
通过StateNotifierProvider统一管理主题状态,同时监听系统亮度变化与Hive存储:
// 定义主题类型枚举,对应Hive存储值 enum AppThemeType { system, // Hive无值时的状态 light, // 对应存储值1 dark, // 对应存储值2 christmas, // 对应存储值3 } final themeProvider = StateNotifierProvider<ThemeNotifier, CustomTheme>((ref) { return ThemeNotifier(ref); }); class ThemeNotifier extends StateNotifier<CustomTheme> { final Ref ref; late final Box _themeBox; late final VoidCallback _systemBrightnessListener; ThemeNotifier(this.ref) : super(LightCustomTheme()) { _themeBox = Hive.box('themeBox'); // 初始化加载保存的主题 _loadSavedTheme(); // 监听系统亮度变化(仅跟随系统模式时生效) _systemBrightnessListener = WidgetsBinding.instance.addPlatformBrightnessListener((brightness) { if (_getCurrentThemeType() == AppThemeType.system) { _updateSystemTheme(brightness); } }); } @override void dispose() { WidgetsBinding.instance.removePlatformBrightnessListener(_systemBrightnessListener); super.dispose(); } // 加载Hive中保存的主题配置 void _loadSavedTheme() { final savedValue = _themeBox.get('theme'); if (savedValue == null) { // 跟随系统,初始化时读取当前系统亮度 final currentBrightness = WidgetsBinding.instance.window.platformBrightness; _updateSystemTheme(currentBrightness); } else { switch (savedValue) { case 1: state = LightCustomTheme(); break; case 2: state = DarkCustomTheme(); break; case 3: state = ChristmasCustomTheme(); break; } } } // 根据系统亮度切换主题 void _updateSystemTheme(Brightness brightness) { state = brightness == Brightness.light ? LightCustomTheme() : DarkCustomTheme(); } // 获取当前主题类型 AppThemeType _getCurrentThemeType() { final savedValue = _themeBox.get('theme'); if (savedValue == null) return AppThemeType.system; switch (savedValue) { case 1: return AppThemeType.light; case 2: return AppThemeType.dark; case 3: return AppThemeType.christmas; default: return AppThemeType.system; } } // 对外暴露的主题切换方法 void switchTheme(AppThemeType type) { switch (type) { case AppThemeType.system: _themeBox.delete('theme'); final currentBrightness = WidgetsBinding.instance.window.platformBrightness; _updateSystemTheme(currentBrightness); break; case AppThemeType.light: _themeBox.put('theme', 1); state = LightCustomTheme(); break; case AppThemeType.dark: _themeBox.put('theme', 2); state = DarkCustomTheme(); break; case AppThemeType.christmas: _themeBox.put('theme', 3); state = ChristmasCustomTheme(); break; } } }
3. 应用入口集成ProviderScope
确保应用外层包裹ProviderScope,且不使用MaterialApp默认的theme参数:
void main() async { WidgetsFlutterBinding.ensureInitialized(); // 已完成Hive初始化与themeBox打开 await Hive.openBox('themeBox'); runApp( const ProviderScope( child: MyApp(), ), ); } class MyApp extends ConsumerWidget { const MyApp({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { return MaterialApp( home: const HomePage(), // 不配置默认theme参数 ); } }
4. UI中直接调用自定义调色板
在任意ConsumerWidget中通过ref.watch获取当前主题,直接调用配色属性:
class HomePage extends ConsumerWidget { const HomePage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final currentTheme = ref.watch(themeProvider); return Scaffold( backgroundColor: currentTheme.backgroundColor, appBar: AppBar( backgroundColor: currentTheme.primaryColor, title: Text( '自定义主题演示', style: TextStyle(color: currentTheme.textColor), ), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( '当前主题配色展示', style: TextStyle(color: currentTheme.textColor, fontSize: 18), ), const SizedBox(height: 20), // 主题切换按钮组 Wrap( spacing: 10, children: [ ElevatedButton( onPressed: () => ref.read(themeProvider.notifier).switchTheme(AppThemeType.system), child: const Text('跟随系统'), ), ElevatedButton( onPressed: () => ref.read(themeProvider.notifier).switchTheme(AppThemeType.light), child: const Text('浅色主题'), ), ElevatedButton( onPressed: () => ref.read(themeProvider.notifier).switchTheme(AppThemeType.dark), child: const Text('深色主题'), ), ElevatedButton( onPressed: () => ref.read(themeProvider.notifier).switchTheme(AppThemeType.christmas), child: const Text('圣诞主题'), ), ], ), ], ), ), ); } }
核心逻辑说明
- 完全脱离Material默认主题:所有配色直接从Riverpod提供的
CustomTheme实例获取,不依赖MaterialApp.theme参数 - 系统亮度自动同步:跟随系统模式下,系统亮度变化会自动切换对应明暗主题
- Hive存储适配:切换到系统模式时删除
theme键,其他模式存储对应数值,完全匹配需求 - 全局便捷访问:通过
ref.watch(themeProvider)可在任意组件中直接调用配色属性
内容的提问来源于stack exchange,提问作者Florian Leeser
相关产品推荐
相关产品推荐

