BlocBuilder的bloc参数何时需传入Bloc/Cubit?附Flutter计数器应用报错问题
报错原因
报错的核心是两处CounterCubit实例不统一,且未将Cubit存入当前Widget树上下文:
- 你在BlocBuilder的bloc参数里手动新建了一个
CounterCubit()实例,这个实例仅供给BlocBuilder自身使用,没有挂载到Widget树的Provider链中 - 浮动按钮的点击事件中使用的
context.read<CounterCubit>(),是从当前上下文的Provider链里查找对应Cubit实例,你没有在CounterPage的父级或者自身通过BlocProvider挂载过CounterCubit,自然无法找到实例,因此抛出该错误。
除此之外你当前的写法还隐含状态丢失问题:每次CounterPage重绘时,BlocBuilder的bloc参数都会新建一个CounterCubit,计数器状态会一直重置为初始值0。
BlocBuilder的bloc参数使用规则
- 规则1:如果你的Bloc/Cubit已经通过BlocProvider挂载到了当前上下文的父级节点,不要传bloc参数,BlocBuilder会自动从上下文查找对应实例,这也是官方最推荐的用法,这种写法后续用
context.read/context.watch也能正常获取到同一个实例。
对应你的场景正确写法如下:// 路由跳转处的代码 Navigator.of(context).push( MaterialPageRoute( builder: (context) => BlocProvider( create: (context) => CounterCubit(), child: const CounterPage(title: "计数器"), ), ) ); // CounterPage内的BlocBuilder去掉bloc参数即可 body: BlocBuilder<CounterCubit, int>( builder: (_, count) => Center(child: Text('$count')), ) - 规则2:只有当你要使用的Bloc/Cubit没有挂载到当前上下文树,且你只需要在当前BlocBuilder里使用这个实例,不需要其他子组件访问它的时候,才需要手动传入bloc参数。这种场景下不能用
context.read获取该实例,需要自行持有实例的引用:
如果你不想修改父级代码,也可以直接调整CounterPage的代码:class CounterPage extends StatelessWidget { const CounterPage({Key? key, required this.title}) : super(key: key); final String title; // 自行持有Cubit实例 final CounterCubit _counterCubit = CounterCubit(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(title), centerTitle: true, ), body: BlocBuilder<CounterCubit, int>( // 传入自行持有得实例 bloc: _counterCubit, builder: (_, count) => Center(child: Text('$count')), ), floatingActionButton: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Padding( padding: const EdgeInsets.all(8.0), child: FloatingActionButton( // 直接调用持有的实例方法,不要用context.read onPressed: () => _counterCubit.increment(), tooltip: 'Increment', child: Icon(Icons.add), ), ), Padding( padding: const EdgeInsets.all(8.0), child: FloatingActionButton( onPressed: () => _counterCubit.decrement(), tooltip: 'Decrement', child: Icon(Icons.remove), ), ), ], ), ); } } - 规则3:手动传入bloc参数时,不要直接在bloc参数的位置新建实例,也就是不要写
bloc: CounterCubit(),否则每次Widget重绘都会新建实例,导致状态丢失,还会存在内存泄漏风险。 - 规则4:如果手动传入了bloc参数,BlocBuilder就只会监听你传入的这个实例的状态变化,不会再从上下文查找同类型的Bloc/Cubit。
内容的提问来源于stack exchange,提问作者Amaan Zafar
相关产品推荐
相关产品推荐

