Flutter BLoC 8.0.0无法正常发射新状态问题排查
问题
在Flutter中使用BLoC 8.0.0版本处理UI事件,通过ToggleButtons让用户切换三个按钮,选中不同按钮时从数据库返回对应实体列表展示。实体包含bool isSale和bool isClosed参数,三个按钮对应布尔组合:
- 按钮1:isSale = true, isClosed = false
- 按钮2:isSale = true, isClosed = true
- 按钮3:isSale = false, isClosed = false
目前发现第二种组合无法更新到新状态,相关代码如下:
状态类定义
abstract class DocViewerState extends Equatable { const DocViewerState(); } class DocViewerLoadingState extends DocViewerState { @override List<Object> get props => []; } class DocViewerLoadedState extends DocViewerState { bool isSale; bool isClosed; int? selectedIndex; DocViewerLoadedState({required this.isSale, required this.isClosed, required this.selectedIndex}); @override List<Object> get props => [isSale, isClosed]; }
事件类定义
abstract class DocViewerEvent extends Equatable { const DocViewerEvent(); } class ModeChangedEvent extends DocViewerEvent { bool isSale; bool isClosed; ModeChangedEvent({required this.isSale, required this.isClosed}); @override List<Object?> get props => [isSale, isClosed]; }
事件处理函数
void _onModeChanged(ModeChangedEvent event, Emitter<DocViewerState> emit) { emit(DocViewerLoadingState()); this.isSale = event.isSale; this.isClosed = event.isClosed; // 用于外部搜索流 try { emit(DocViewerLoadedState( isClosed: event.isClosed, isSale: event.isSale, selectedIndex: null, )); // 仅第一种和第三种组合的emit生效 } catch (e) { print(e); /// 未捕获到异常 } print(event.isSale); print(event.isClosed); print(state); }
打印结果显示参数传递正确,但第二种组合的状态停留在DocViewerLoadingState():
- 第一种组合:
I/flutter ( 5019): true I/flutter ( 5019): false I/flutter ( 5019): DocViewerLoadedState(true, false)
- 第二种组合:
I/flutter ( 5019): true I/flutter ( 5019): true I/flutter ( 5019): DocViewerLoadingState()
- 第三种组合:
I/flutter ( 5019): false I/flutter ( 5019): false I/flutter ( 5019): DocViewerLoadedState(false, false)
问题分析
核心问题出在Equatable的props配置不完整以及BLoC的状态比较逻辑:
DocViewerLoadedState的props只包含了isSale和isClosed,未纳入selectedIndex。如果之前的状态中selectedIndex不为null,当新状态传入selectedIndex: null时,Equatable会认为两个状态的props相同(仅比较isSale和isClosed),导致BLoC跳过状态更新。- 第二种组合的
isSale=true, isClosed=true刚好命中上述场景:历史状态中存在相同的isSale和isClosed组合,且selectedIndex的变化未被纳入比较,导致emit(DocViewerLoadedState)被BLoC忽略,最终状态停留在之前的DocViewerLoadingState。
修复方案
1. 完善DocViewerLoadedState的props配置
将selectedIndex加入props列表,确保状态的所有可变属性都参与Equatable的相等性比较:
class DocViewerLoadedState extends DocViewerState { bool isSale; bool isClosed; int? selectedIndex; DocViewerLoadedState({required this.isSale, required this.isClosed, required this.selectedIndex}); @override List<Object?> get props => [isSale, isClosed, selectedIndex]; }
2. 确保状态类的不可变性(可选但推荐)
BLoC的状态类应保持不可变性,将属性改为final:
class DocViewerLoadedState extends DocViewerState { final bool isSale; final bool isClosed; final int? selectedIndex; const DocViewerLoadedState({required this.isSale, required this.isClosed, required this.selectedIndex}); @override List<Object?> get props => [isSale, isClosed, selectedIndex]; }
3. 验证状态更新逻辑
在事件处理函数中打印状态的相等性,确认新状态与当前状态的差异:
void _onModeChanged(ModeChangedEvent event, Emitter<DocViewerState> emit) { emit(DocViewerLoadingState()); this.isSale = event.isSale; this.isClosed = event.isClosed; try { final newLoadedState = DocViewerLoadedState( isClosed: event.isClosed, isSale: event.isSale, selectedIndex: null, ); print('New state equals current state? ${newLoadedState == state}'); emit(newLoadedState); } catch (e) { print(e); } print(event.isSale); print(event.isClosed); print(state); }
内容的提问来源于stack exchange,提问作者dalibro
相关产品推荐
相关产品推荐

