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

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的状态比较逻辑:

  1. DocViewerLoadedState的props只包含了isSale和isClosed,未纳入selectedIndex。如果之前的状态中selectedIndex不为null,当新状态传入selectedIndex: null时,Equatable会认为两个状态的props相同(仅比较isSale和isClosed),导致BLoC跳过状态更新。
  2. 第二种组合的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 12:54:54