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

Flutter Bloc中StreamController报错:Stream已被监听/添加事件异常

问题描述

我有如下「View Items」组件:

class _ViewItems extends State<ViewItems> {

  Widget build(BuildContext context) { 
    return Scaffold(
      body: BlocBuilder<MyBloc, MyBlocState>(
         builder: (context, state){
            StreamBuilder<List<String>>(
              stream: state.stream,
              builder: (context, snapshot) { }// building some UI
            )
         }
      ) 
    );
  }
}

对应的Bloc实现:

class MyBloc extends Bloc<MyBlocEvent, MyBlocState> {

  List<String> _items = [];
  final StreamController<List<String>> _streamController = StreamController();

  Stream<List<String>> get stream => _streamController.stream;

  MyBloc() : super(const MyBlocStateInitialize()) {
    _streamController.add(_items);

    on<MyBlocEventInitialize>(
      (event, emit) async {
        _items = await objectBox.getItems().expand((element) => element).toList();; // grabbing data from local db
        _streamController.add(_items);
      },
    );
  }
}

使用Navigator.of(context).pushNamed(someRoute)跳转页面后返回「View Items」时,出现以下错误:

StateError (Bad state: Cannot add event while adding a stream)

有时还会出现:

StateError (Bad state: Stream has already been listened to.)

尽管已销毁控制器,仍存在该问题,求解决建议!

解决建议

1. 移除自定义StreamController,复用Bloc原生状态管理

Bloc本身已提供完整的状态发射与监听机制,额外维护StreamController是冲突根源。直接将数据整合到Bloc状态类中:

更新Bloc状态类

abstract class MyBlocState {}

class MyBlocStateInitialize extends MyBlocState {}

class MyBlocStateLoaded extends MyBlocState {
  final List<String> items;

  const MyBlocStateLoaded(this.items);
}

重构Bloc逻辑

class MyBloc extends Bloc<MyBlocEvent, MyBlocState> {

  MyBloc() : super(MyBlocStateInitialize()) {
    on<MyBlocEventInitialize>(
      (event, emit) async {
        final items = await objectBox.getItems().expand((element) => element).toList();
        emit(MyBlocStateLoaded(items));
      },
    );
  }
}

修改View组件

去掉嵌套的StreamBuilder,直接从Bloc状态取数据:

class _ViewItems extends State<ViewItems> {
  @override
  void initState() {
    super.initState();
    // 页面初始化触发数据加载
    context.read<MyBloc>().add(MyBlocEventInitialize());
  }

  Widget build(BuildContext context) { 
    return Scaffold(
      body: BlocBuilder<MyBloc, MyBlocState>(
         builder: (context, state){
            if (state is MyBlocStateLoaded) {
              // 用state.items构建业务UI
              return ListView.builder(
                itemCount: state.items.length,
                itemBuilder: (_, index) => ListTile(title: Text(state.items[index])),
              );
            }
            // 加载状态占位UI
            return const Center(child: CircularProgressIndicator());
         }
      ) 
    );
  }
}

2. 若必须保留自定义流,改用广播流

如果因特殊需求需保留StreamController,将其改为广播流以支持多订阅:

final StreamController<List<String>> _streamController = StreamController.broadcast();

同时在Bloc生命周期中正确销毁控制器:

@override
Future<void> close() {
  _streamController.close();
  return super.close();
}

⚠️ 此方式不推荐,会破坏Bloc状态一致性,仅作为临时兼容方案。

3. 避免重复订阅与实例冲突

  • 确保Bloc通过BlocProvider全局/局部提供单例,避免页面返回时重复初始化
  • 若保留StreamBuilder,可设置initialData减少不必要的订阅触发

内容的提问来源于stack exchange,提问作者Preem Palver2

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 14:07:28