使用ThemeProvider切换主题导致Widget状态重置问题求助
问题描述
开发小型测验应用时,添加明暗模式切换开关后遇到问题:点击开关切换主题时,整个Widget会重建(打印哈希值每次都不同),页面的状态和变量也会被重置。由于页面加载了测验题目资源,还会更新题目数量等文本内容,这些数据都会丢失,希望仅切换主题,保留Widget及其变量的状态。
涉及代码
main.dart
void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) => ChangeNotifierProvider( create: (context) => ThemeProvider(), builder: (context, _) { final themeProvider = Provider.of<ThemeProvider>(context); return MaterialApp( title: 'ROquiz', themeMode: themeProvider.themeMode, theme: MyThemes.themeLight, darkTheme: MyThemes.themeDark, home: ViewMenu(), ); }); }
themes.dart
class ThemeProvider extends ChangeNotifier { ThemeMode themeMode = ThemeMode.light; bool get isDarkMode => themeMode == ThemeMode.dark; void toggleTheme(bool isOn) { themeMode = isOn ? ThemeMode.dark : ThemeMode.light; notifyListeners(); } } class MyThemes { static final themeLight = ThemeData( colorSchemeSeed: Colors.blue, brightness: Brightness.light, //iconTheme: IconThemeData(color: Colors.blue[900]), elevatedButtonTheme: ElevatedButtonThemeData( style: ButtonStyle( shape: MaterialStateProperty.all<RoundedRectangleBorder>( RoundedRectangleBorder( borderRadius: BorderRadius.circular(30.0), )))), ); static final themeDark = ThemeData( scaffoldBackgroundColor: Colors.indigo[700], brightness: Brightness.dark, colorScheme: const ColorScheme.dark(), elevatedButtonTheme: ElevatedButtonThemeData( style: ButtonStyle( shape: MaterialStateProperty.all<RoundedRectangleBorder>( RoundedRectangleBorder( borderRadius: BorderRadius.circular(30.0), side: BorderSide(color: Colors.blue)))))); }
change_theme_button_widget.dart
class ChangeThemeButtonWidget extends StatelessWidget { @override Widget build(BuildContext context) { final themeProvider = Provider.of<ThemeProvider>(context); return Switch.adaptive( value: themeProvider.isDarkMode, onChanged: (value) { final provider = Provider.of<ThemeProvider>(context, listen: false); provider.toggleTheme(value); }); } }
ViewMenu.dart
class ViewMenu extends StatefulWidget { // [...] } class ViewMenuState extends State<ViewMenu> { @override Widget build(BuildContext context) { final themeProvider = Provider.of<ThemeProvider>(context); return Scaffold( // [...] ChangeThemeButtonWidget(), // [...] ); } }
解决方案
问题根源
ViewMenuState的build方法中调用了Provider.of<ThemeProvider>(context)(默认listen: true),导致ViewMenu会监听ThemeProvider的所有变化。每次切换主题时,ThemeProvider调用notifyListeners(),ViewMenu会被强制重建,进而重置其内部状态和变量。
修改步骤
移除ViewMenu对ThemeProvider的不必要监听
修改ViewMenuState的build方法,去掉对ThemeProvider的监听(如果不需要在ViewMenu中主动使用主题数据),或者将监听设置为false:class ViewMenuState extends State<ViewMenu> { @override Widget build(BuildContext context) { // 不需要监听主题变化则删除此行,或改为listen: false // final themeProvider = Provider.of<ThemeProvider>(context, listen: false); return Scaffold( // [...] ChangeThemeButtonWidget(), // [...] ); } }确认ChangeThemeButtonWidget的监听逻辑
ChangeThemeButtonWidget需要监听ThemeProvider来更新开关状态,现有代码逻辑正确,无需修改。它只会在主题变化时自身重建,不会影响父Widget的状态。验证效果
修改完成后,点击主题开关时,只有ChangeThemeButtonWidget会因主题变化重建,ViewMenu的状态和内部变量会被完整保留,测验题目、计数等数据不会重置。
内容的提问来源于stack exchange,提问作者mikyll98
相关产品推荐
相关产品推荐

