Flutter自定义明暗主题用SharedPreferences存储后重启失效问题
问题根因
- 主题状态加载后未通知UI刷新
ThemeColorData的loadTheme异步方法修改_isLight取值后,没有调用notifyListeners(),状态变更无法通知到UI层,导致重启后读取到的存储主题值不能生效。 - 异步加载时序问题
loadTheme逻辑在build阶段触发,首次渲染时异步加载未完成,直接使用了默认的浅色主题值,且无后续刷新触发UI更新。 - BottomNavigationBar硬编码样式覆盖主题配置
底部导航栏组件手动指定了unselectedItemColor: Colors.grey.shade500,覆盖了主题中bottomNavigationBarTheme的配置,导致主题色不生效。 - 异常页面布局配置错误
手动给Container设置了和屏幕等高的高度,NestedScrollView的body可用高度小于屏幕高度,导致内容溢出、文本错位;额外设置的Container背景色也可能和主题Scaffold背景冲突。
调试/模拟器环境下异步执行时序和真机release环境不同,加载逻辑可能刚好在首次渲染前完成,所以问题不会复现。
修复方案
1. 修复ThemeColorData状态通知逻辑
修改loadTheme方法,加载完成后通知UI刷新:
void loadTheme() async { await createSharedPrefObject(); _isLight = _sharedPreferences!.getBool('themeData') ?? true; // 新增状态通知 notifyListeners(); }
2. 优化主入口初始化逻辑
提前初始化SharedPreferences并传入Provider实例,用Consumer监听主题变化,保证状态更新能同步到MaterialApp:
Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); // 提前初始化SharedPreferences避免重复实例化 final sharedPref = await SharedPreferences.getInstance(); runApp( ChangeNotifierProvider<ThemeColorData>( create: (context) => ThemeColorData() .._sharedPreferences = sharedPref ..loadTheme(), child: const MyApp(), ), ); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return Consumer<ThemeColorData>( builder: (context, themeProvider, child) { return MaterialApp( debugShowCheckedModeBanner: false, theme: themeProvider.themeColor, home: const SplashScreen(), ); }, ); } }
3. 移除底部导航栏硬编码样式
删除BottomNavigationBar中手动指定的颜色配置,优先使用主题配置:
bottomNavigationBar: BottomNavigationBar( items: [ BottomNavigationBarItem(), BottomNavigationBarItem(), BottomNavigationBarItem() ], currentIndex: _selectedIndex, // 删除硬编码的unselectedItemColor配置 onTap: _onItemTapped, )
4. 修复异常页面布局
移除多余的背景色和高度设置,适配主题样式:
Widget build(BuildContext context) { return SafeArea( child: Scaffold( body: NestedScrollView( headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { return <Widget>[ const SliverAppBar(), ]; }, // 直接用Padding作为body,移除多余的Container层 body: Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5.0), child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 7), const Align(), const SizedBox(height: 10), SelectableText( widget.dataModel.title, style: Theme.of(context).textTheme.headline6!.copyWith( fontFamily: 'Muli', fontWeight: FontWeight.w600), ), const SizedBox(height: 10), SelectableText( widget.dataModel.longDescription, style: TextStyle( height: 1.3, fontSize: 18, fontFamily: 'Muli', fontWeight: FontWeight.w300, // 可选指定文本颜色适配当前主题 color: Theme.of(context).textTheme.bodyLarge?.color ), ), const SizedBox(height: 10.0), ], ), ), ), ), ), ); }
内容的提问来源于stack exchange,提问作者Semih Yilmaz
相关产品推荐
相关产品推荐

