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

异步Bloc事件处理器中多次emit状态的报错及解决方法

问题解决办法

核心问题是你在事件处理器执行完毕后调用了emit,违反了Bloc的规则——状态只能在事件处理的生命周期内发射。下面是具体的解决方案:

原因分析

当你调用repository.addPhoto时,如果没有等待该异步操作完成就让事件处理器函数返回,后续onSendProgress回调触发的emit就会处于事件处理器已结束的状态,从而抛出异常。

解决方案

方案一:await上传异步操作

确保事件处理器在整个上传过程中保持活跃,直接await addPhoto方法(前提是该方法返回的Future会在上传完成后才完成):

class PhotoBloc extends Bloc<PhotoEvent, PhotoState> {
  final PhotoRepository repository;

  PhotoBloc(this.repository) : super(PhotoInitial()) {
    on<PhotoAdded>((event, emit) async {
      // 发射初始进度状态
      emit(PhotoUploadingState(progress: 0.0));
      
      try {
        // await 保证事件处理器直到上传完成才结束
        await repository.addPhoto(
          fileBytes: event.fileBytes,
          onSendProgress: (sent, total) {
            final progress = (sent / total) * 100;
            // 此时事件处理器仍在执行,emit合法
            emit(PhotoUploadingState(progress: progress));
          },
        );
        // 上传完成后发射成功状态
        emit(PhotoUploadSuccess());
      } catch (e) {
        emit(PhotoUploadFailed(error: e.toString()));
      }
    });
  }
}

方案二:将进度回调转为Stream

如果addPhoto的回调逻辑无法通过await覆盖,可以把进度通知转为Stream,在事件处理器中监听流直到上传完成:

改造Repository

class PhotoRepository {
  final Dio _dio = Dio();

  Stream<double> addPhoto(Uint8List fileBytes) {
    final progressController = StreamController<double>();

    _dio.post(
      '/api/photos',
      data: fileBytes,
      options: Options(contentType: 'application/octet-stream'),
      onSendProgress: (sent, total) {
        final progress = (sent / total) * 100;
        progressController.add(progress);
        if (sent == total) progressController.close();
      },
    ).catchError(progressController.addError);

    return progressController.stream;
  }
}

Bloc中监听进度流

class PhotoBloc extends Bloc<PhotoEvent, PhotoState> {
  final PhotoRepository repository;

  PhotoBloc(this.repository) : super(PhotoInitial()) {
    on<PhotoAdded>((event, emit) async {
      emit(PhotoUploadingState(progress: 0.0));
      
      try {
        // 监听进度流,直到上传完成
        await for (final progress in repository.addPhoto(event.fileBytes)) {
          emit(PhotoUploadingState(progress: progress));
        }
        emit(PhotoUploadSuccess());
      } catch (e) {
        emit(PhotoUploadFailed(error: e.toString()));
      }
    });
  }
}

说明

以上两种方案都能保证所有emit操作都在事件处理器的执行周期内完成,BlocConsumer可以正常监听状态变化并更新进度条,完全符合你的需求,不需要替换为BlocListener。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 00:40:15