You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.17 11:24:10