Flutter集成GoRouter后No ScaffoldMessenger错误的解决求助
解决GoRouter重构后BlocListener调用ScaffoldMessenger报错的问题
错误原因
当前BlocListener<GlobalBloc, GlobalState>的上下文位于MaterialApp.router的上层组件树中,而ScaffoldMessenger是由MaterialApp内部提供的,因此上层上下文无法找到对应的ScaffoldMessenger实例,导致报错。
修复方案
方案一:使用全局ScaffoldMessengerKey
通过定义全局的ScaffoldMessengerKey,直接关联到MaterialApp.router,绕过上下文查找的限制:
修改MyApp类:
class MyApp extends StatelessWidget { final AuthRepository authRepository; // 定义全局ScaffoldMessengerKey final GlobalKey<ScaffoldMessengerState> _scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>(); const MyApp({super.key, required this.authRepository}); @override Widget build(BuildContext context) { final GoRouter router = GoRouter( routes: [ GoRoute( path: '/', builder: (context, state) => const MyHomePage(title: 'Flutter Demo Home Page'), ), GoRoute( path: '/settings', builder: (context, state) => const SettingsScreen(), ), GoRoute( path: '/profile', builder: (context, state) => const ProfileScreen(), ), ], ); return MultiBlocProvider( providers: [ BlocProvider<GlobalBloc>( create: (context) => GlobalBloc(), ), BlocProvider<AuthBloc>( create: (context) => AuthBloc(authRepository: authRepository), ), ], child: Builder( builder: (context) { return BlocListener<GlobalBloc, GlobalState>( listener: (context, state) { if (state is GlobalSuccess) { _scaffoldMessengerKey.currentState?.hideCurrentSnackBar(); _scaffoldMessengerKey.currentState?.showSnackBar( SnackBar( content: Text(state.message), backgroundColor: Colors.green, ), ); } else if (state is GlobalError) { _scaffoldMessengerKey.currentState?.hideCurrentSnackBar(); _scaffoldMessengerKey.currentState?.showSnackBar( SnackBar( content: Text(state.message), backgroundColor: Colors.red, ), ); } }, child: BlocBuilder<GlobalBloc, GlobalState>( builder: (context, state) { bool isLoading = state is GlobalLoading; return LoadingOverlay( isLoading: isLoading, child: MaterialApp.router( title: 'Flutter Demo', theme: AppTheme.lightTheme, routerConfig: router, // 将全局key传入MaterialApp scaffoldMessengerKey: _scaffoldMessengerKey, ), ); }, ), ); }, ), ); } }
方案二:将BlocListener嵌入MaterialApp子树
调整组件层级,让BlocListener处于MaterialApp.router的子树中,确保上下文能正确找到ScaffoldMessenger:
修改MyApp类的build方法:
@override Widget build(BuildContext context) { final GoRouter router = GoRouter( routes: [ GoRoute( path: '/', builder: (context, state) => const MyHomePage(title: 'Flutter Demo Home Page'), ), GoRoute( path: '/settings', builder: (context, state) => const SettingsScreen(), ), GoRoute( path: '/profile', builder: (context, state) => const ProfileScreen(), ), ], ); return MultiBlocProvider( providers: [ BlocProvider<GlobalBloc>( create: (context) => GlobalBloc(), ), BlocProvider<AuthBloc>( create: (context) => AuthBloc(authRepository: authRepository), ), ], child: BlocBuilder<GlobalBloc, GlobalState>( builder: (context, state) { bool isLoading = state is GlobalLoading; return LoadingOverlay( isLoading: isLoading, child: MaterialApp.router( title: 'Flutter Demo', theme: AppTheme.lightTheme, routerConfig: router, // 通过builder将BlocListener嵌入MaterialApp内部 builder: (context, child) { return BlocListener<GlobalBloc, GlobalState>( listener: (context, state) { if (state is GlobalSuccess) { ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(state.message), backgroundColor: Colors.green, ), ); } else if (state is GlobalError) { ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(state.message), backgroundColor: Colors.red, ), ); } }, child: child, ); }, ), ); }, ), ); }
方案对比
- 方案一:无需调整组件层级,实现简单,但依赖全局key,需注意空安全处理。
- 方案二:符合Flutter组件树的上下文查找逻辑,结构更合理,是推荐的标准解决方案。
内容的提问来源于stack exchange,提问作者Dumindu Jayasekara
相关产品推荐
相关产品推荐

