Flutter:修改应用主题时避免全局页面重建
主题切换导致全局重建并返回初始页面的解决方案
问题描述
使用Bloc和GoRouter进行Flutter开发时,切换主题(向ThemeBloc发送ToggleTheme事件)后,整个应用会全局重建并回到初始页面。尝试过用对话框、独立页面修改主题,问题依旧,希望实现无全局重建、不丢失导航栈的主题切换。
问题原因
当前AppView中用BlocBuilder包裹了整个MaterialApp.router,主题状态变化时会重建MaterialApp;同时每次重建都会调用AppRouter.mainRouter()创建新的GoRouter实例,导致导航栈丢失,回到初始路由。
解决方案
- 提前初始化GoRouter实例:避免每次重建
MaterialApp时重新创建路由配置,保留导航栈状态。 - 仅更新themeMode属性:将
BlocBuilder替换为context.watch,只更新MaterialApp的themeMode,不重建整个MaterialApp和GoRouter。
修改后的代码
App及AppView修改
final mainNavigatorKey = GlobalKey<NavigatorState>(); // 提前初始化GoRouter实例,复用同一个路由配置 final _appRouter = AppRouter.mainRouter(mainNavigatorKey); class App extends StatelessWidget { const App({ required this.themeRepository, required this.usersRepository, super.key, }); final UsersRepository usersRepository; final ThemeRepository themeRepository; @override Widget build(BuildContext context) { return MultiRepositoryProvider( providers: [ RepositoryProvider.value(value: usersRepository), RepositoryProvider.value(value: themeRepository), ], child: BlocProvider( create: (context) => ThemeBloc(themeRepository: themeRepository) ..add(ThemeModeRequested()), child: const AppView(), ), ); } } class AppView extends StatelessWidget { const AppView({super.key}); @override Widget build(BuildContext context) { // 监听ThemeBloc状态,仅获取themeMode final currentThemeMode = context.watch<ThemeBloc>().state.themeMode; return MaterialApp.router( theme: LighTheme.theme, darkTheme: DarkTheme.theme, themeMode: currentThemeMode, // 仅更新此属性 localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, routerConfig: _appRouter, // 使用预初始化的路由实例 ); } }
确保ThemeBloc状态更新正确
class ThemeBloc extends Bloc<ThemeEvent, ThemeState> { final ThemeRepository themeRepository; ThemeBloc({required this.themeRepository}) : super(ThemeState.initial()) { on<ThemeModeRequested>(_onThemeModeRequested); on<ToggleThemeMode>(_onToggleThemeMode); } void _onThemeModeRequested(ThemeModeRequested event, Emitter<ThemeState> emit) async { final savedMode = await themeRepository.getThemeMode(); emit(state.copyWith(themeMode: savedMode)); } void _onToggleThemeMode(ToggleThemeMode event, Emitter<ThemeState> emit) async { await themeRepository.saveThemeMode(event.themeMode); emit(state.copyWith(themeMode: event.themeMode)); } }
说明
修改后,主题切换时只会更新MaterialApp的themeMode属性,不会重建整个MaterialApp和GoRouter实例,导航栈得以保留,实现无全局重建的主题切换效果。
内容的提问来源于stack exchange,提问作者jfco_fer
相关产品推荐
相关产品推荐

