如何在Flutter组件树特定层级使用MultiBlocProvider?
问题描述
我希望在特定层级使用MultiBlocProvider,在MaterialApp顶层使用该组件时无异常,但在如下代码的层级中使用时出现报错。当前使用版本:flutter_bloc 8.1.2,bloc 8.1.1
报错信息
Error: Cannot hit test a render box that has never been laid out.
示例代码
void main() { runApp(MaterialApp( onGenerateRoute: (settings) { switch (settings.name) { case "/": return MaterialPageRoute( builder: (_) => MultiBlocProvider(providers: [ BlocProvider( create: (_) => CounterBloc(), ) ], child: const GroupA()), settings: settings); case "/ScopeA": return MaterialPageRoute( builder: (_) => const ScopeA(), settings: settings); default: return MaterialPageRoute( builder: (_) => const Text("ERROR"), settings: settings); } }, )); } class GroupA extends StatelessWidget { const GroupA({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Group A:')), body: Center( child: MaterialButton( onPressed: () => Navigator.pushNamed(context, '/ScopeA'), child: const Text("Go To Scope A")), ), ); } } class ScopeA extends StatelessWidget { const ScopeA({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Scope A:')), body: Center( child: BlocBuilder<CounterBloc, int>( builder: (context, count) { return Text( '$count', style: Theme.of(context).textTheme.displayLarge, ); }, ), ), ); } } abstract class CounterEvent {} class CounterIncrementPressed extends CounterEvent {} class CounterDecrementPressed extends CounterEvent {} class CounterBloc extends Bloc<CounterEvent, int> { CounterBloc() : super(0) { on<CounterIncrementPressed>((event, emit) => emit(state + 1)); on<CounterDecrementPressed>((event, emit) => emit(state - 1)); } }
问题原因
GroupA和ScopeA分属两个独立的MaterialPageRoute,各自拥有独立的Widget树上下文。CounterBloc仅在/路由的MultiBlocProvider中提供,ScopeA的上下文无法访问到这个Bloc实例,导致BlocBuilder找不到对应Bloc,进而引发布局相关的连锁异常。
解决方法
方法1:将MultiBlocProvider提升到MaterialApp上层
把MultiBlocProvider包在MaterialApp外面,让所有路由共享同一个Bloc上下文:
void main() { runApp(MultiBlocProvider( providers: [ BlocProvider(create: (_) => CounterBloc()), ], child: MaterialApp( onGenerateRoute: (settings) { switch (settings.name) { case "/": return MaterialPageRoute( builder: (_) => const GroupA(), settings: settings); case "/ScopeA": return MaterialPageRoute( builder: (_) => const ScopeA(), settings: settings); default: return MaterialPageRoute( builder: (_) => const Text("ERROR"), settings: settings); } }, ), )); }
方法2:通过BlocProvider.value传递Bloc实例
在跳转到ScopeA时传递已有的Bloc实例,并在目标路由中用BlocProvider.value提供:
- 修改
GroupA中的跳转逻辑:
onPressed: () { final counterBloc = BlocProvider.of<CounterBloc>(context); Navigator.pushNamed(context, '/ScopeA', arguments: counterBloc); },
- 修改
onGenerateRoute中/ScopeA的路由处理:
case "/ScopeA": final counterBloc = settings.arguments as CounterBloc; return MaterialPageRoute( builder: (_) => BlocProvider.value( value: counterBloc, child: const ScopeA(), ), settings: settings, );
内容的提问来源于stack exchange,提问作者Esmaeil Ahmadipour
相关产品推荐
相关产品推荐

