使用MultiBlocProvider引入两个Bloc时抛出异常的问题
问题场景
此前单个BlocProvider配置正常,现需开发一个页面访问两个独立Bloc(对应两个不同接口提交需求),采用MultiBlocProvider配置后出现红屏异常。
相关代码如下:
- 跳转至目标页面的代码:
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (context) => IncomeExpenseScreen(title:"Income", property: widget.property, expense: null))),
- 目标页面的MultiBlocProvider配置:
Widget build(BuildContext context) { return MultiBlocProvider( providers: [ BlocProvider<PortfolioBloc>(create: (context) => PortfolioBloc()), BlocProvider<IncomeAndExpenseBloc>(create: (context) => IncomeAndExpenseBloc()), ], child: IncomeExpenseWidget(title, property, expense), ); }
- 子组件IncomeExpenseWidget的init方法:
propertyListBloc = BlocProvider.of<PortfolioBloc>(context); incomeExpenseBloc = BlocProvider.of<IncomeAndExpenseBloc>(context);
终端抛出异常:
======== Exception caught by widgets library =======================================================
The following assertion was thrown building Builder:
BlocProvider.of() called with a context that does not contain a PortfolioBloc.No ancestor could be found starting from the context that was passed to BlocProvider.of<PortfolioBloc>(). This can happen if the context you used comes from a widget above the BlocProvider. The context used was: IncomeExpenseWidget(dirty, state: _IncomeExpenseWidgetState#39035(lifecycle state: created))
原因分析
异常核心是上下文范围不匹配:IncomeExpenseWidget初始化时使用的context,是MultiBlocProvider创建之前的上下文,不属于MultiBlocProvider的子树,因此无法找到上层配置的Bloc实例。
解决方案
提供三种可行的解决方式:
方式一:用Builder包裹子组件,获取子树上下文
修改MultiBlocProvider的child,通过Builder生成新的上下文,确保传入IncomeExpenseWidget的上下文属于MultiBlocProvider的子树:
Widget build(BuildContext context) { return MultiBlocProvider( providers: [ BlocProvider<PortfolioBloc>(create: (context) => PortfolioBloc()), BlocProvider<IncomeAndExpenseBloc>(create: (context) => IncomeAndExpenseBloc()), ], child: Builder( builder: (context) => IncomeExpenseWidget(title, property, expense), ), ); }
方式二:在didChangeDependencies中获取Bloc
initState阶段组件尚未完全挂载到MultiBlocProvider子树,而didChangeDependencies会在组件依赖更新时触发,此时上下文已能访问到上层Bloc:
@override void didChangeDependencies() { super.didChangeDependencies(); propertyListBloc = BlocProvider.of<PortfolioBloc>(context); incomeExpenseBloc = BlocProvider.of<IncomeAndExpenseBloc>(context); }
方式三:使用BlocConsumer/BlocBuilder自动获取(推荐)
若子组件需要监听Bloc状态,直接使用Bloc官方提供的BlocConsumer或BlocBuilder,它们会自动从子树上下文中获取Bloc实例,无需手动调用BlocProvider.of:
BlocConsumer<PortfolioBloc, PortfolioState>( listener: (context, state) { // 处理状态变更逻辑 }, builder: (context, state) { // 根据状态构建UI return Container(); }, )
内容的提问来源于stack exchange,提问作者raiton

