Flutter Bloc yield新状态后内部state未更新问题排查
问题原因
首先明确:正常使用 Bloc 时,只要上一个 yield 输出的状态满足更新条件,两次 yield 操作之间内部 state 会自动同步更新,不需要手动赋值。你遇到的 count 始终为 0 的问题,90% 是以下两个核心原因导致:
- 重写了 Bloc 的
onChange或onTransition方法但没有调用父类实现:Bloc 默认在这两个生命周期方法里完成内部 state 的赋值,如果重写时漏掉super.onTransition(transition)或super.onChange(change)调用,所有 yield 出来的状态都会被丢弃,内部 state 永远停留在初始值。 - Bloc 实例被重复创建:如果没有通过 BlocProvider 缓存 Bloc 实例,而是在 build 方法、按钮点击回调里直接
CameraBloc()生成新实例触发事件,每次事件处理都在全新的初始状态实例上执行,处理完的实例立刻被回收,看起来就像 state 永远不更新。
你贴出的代码里还有三个隐患,虽然不是当前问题的直接原因,但后续会引发其他bug:
- 直接构造新状态时硬编码
error: null、path: null、orientation: null,会把之前状态里的这几个字段值直接覆盖清空,正确做法是用copyWith方法只修改需要变更的字段,避免无关字段被重置。 - 你的
copyWith方法存在逻辑缺陷:对于可空类型字段,用?? this.xxx的写法无法实现将字段主动赋值为 null 的需求,只要你传 null 就会自动回退到旧值,建议用 freezed 生成代码规避这个问题,或者手动改造 copyWith 支持空值覆盖。 - 所有自定义事件类的
props返回空数组,后续使用并发/防抖/丢弃类事件转换器时,同类型事件会被判定为完全相等,导致事件被意外忽略。
修复步骤
- 检查 Bloc 类中是否重写了
onChange、onTransition方法,如果有,必须在方法第一行调用父类实现:
@override void onTransition(Transition<CameraEvent, CameraState> transition) { super.onTransition(transition); // 必须保留父类调用 // 自定义逻辑 } @override void onChange(Change<CameraState> change) { super.onChange(change); // 必须保留父类调用 // 自定义逻辑 }
- 检查 Bloc 实例的创建和获取逻辑:必须在组件树的上层通过
BlocProvider(create: (_) => CameraBloc())创建并缓存实例,触发事件时通过context.read<CameraBloc>().add(CheckCameraOrientation())获取同一个实例,绝对不要在 build 或点击回调里手动实例化 CameraBloc。 - 改造事件处理逻辑,用 copyWith 生成新状态,避免硬编码覆盖无关字段:
Stream<CameraState> _mapCameraOrientationCheckToState( CheckCameraOrientation event) async* { try { print('当前count: ${state.count}'); // 只修改需要更新的count和status字段,其他字段沿用旧值 yield state.copyWith( count: state.count + 1, status: CameraStatus.capturing, ); } catch (e) { print(e); } }
- (可选)修复 copyWith 的空值覆盖问题,或者直接用 freezed 生成状态类,避免手写 Equatable 和 copyWith 的低级错误。
修复后每次触发 CheckCameraOrientation 事件,print 输出的 count 会依次递增,Bloc 内部的 state 属性也会同步更新。
内容的提问来源于stack exchange,提问作者user2561417
相关产品推荐
相关产品推荐

