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

Flutter Cubit中SastaWalletBalanceState对应的BlocListener未触发

问题描述

项目采用Cubit/State架构,MultiBlocListener中其他BlocListener均正常工作,但监听SastaWalletBalanceState的Listener未执行预期逻辑——API与Cubit已成功获取余额数据,然而判断state.balance为SastaWalletBalanceSuccess的代码块始终未触发。

相关代码

State类

@immutable
class SastaWalletBalanceState extends Equatable {
  static bool match(SastaWalletState a, SastaWalletState b) =>
      a.balance != b.balance;

  final num? data;
  final String? message;

  SastaWalletBalanceState({
    this.data,
    this.message,
  });

  @override
  List<Object?> get props => [
        this.data,
        this.message,
      ];
}

@immutable
class SastaWalletBalanceDefault extends SastaWalletBalanceState {}

@immutable
class SastaWalletBalanceLoading extends SastaWalletBalanceState {
  SastaWalletBalanceLoading() : super();
}

@immutable
class SastaWalletBalanceSuccess extends SastaWalletBalanceState {
  SastaWalletBalanceSuccess({num? data}) : super(data: data);
}

@immutable
class SastaWalletBalanceFailed extends SastaWalletBalanceState {
  SastaWalletBalanceFailed({String? message}) : super(message: message);
}

Cubit方法

Future<void> getBalance() async {
    emit(state.copyWith(
      balance: SastaWalletBalanceLoading(),
    ));
    try {
      final data = await repo.getBalance();

      emit(state.copyWith(
        balance: SastaWalletBalanceSuccess(data: data),
      ));
    } catch (e) {
      emit(state.copyWith(
        balance: SastaWalletBalanceFailed(message: e.toString()),
      ));
    }
  }

UI代码

问题出在以下MultiBlocListener中,标记处代码未执行:

...其余代码
return MultiBlocListener(
      listeners: [
        // 其他正常工作的BlocListener代码
        BlocListener<SastaWalletCubit, SastaWalletState>(
          listenWhen: SastaWalletBalanceState.match,
          listener: (context, state) {
            print('----- balance listener -----');
            print(state.balance);

            if (state.balance is SastaWalletBalanceSuccess) { // 此处未触发后续打印
              print('--- balance success -----');
              // 后续弹窗逻辑
            }
          },
        )
      ],
  //// .... 其余代码

问题根源

核心问题在listenWhen方法的相等性判断逻辑:

  • 直接使用!=比较两个SastaWalletBalanceState实例,这是引用比较,但你的状态类继承了Equatable,应该用==来判断值相等性。
  • 当通过copyWith创建新状态时,即使balance的类型或数据变化,!=的引用比较可能因某些场景返回false,导致listenWhen不触发Listener。
  • 另外,SastaWalletBalanceLoading的构造函数未传递参数到父类,导致它的props与SastaWalletBalanceDefault完全相同,可能引发不必要的相等性误判。

修复方案

1. 修正listenWhen的判断逻辑

替换为基于Equatable值相等性的判断,或者更精准的状态类型+数据判断:

// 方案1:基于Equatable的相等性反转
static bool match(SastaWalletState a, SastaWalletState b) =>
    !(a.balance == b.balance);

// 方案2:更精准的类型+数据判断(推荐)
static bool match(SastaWalletState a, SastaWalletState b) {
  // 状态类型不同时触发
  if (a.balance.runtimeType != b.balance.runtimeType) return true;
  // 同为Success状态时,数据变化才触发
  if (a.balance is SastaWalletBalanceSuccess && b.balance is SastaWalletBalanceSuccess) {
    return (a.balance as SastaWalletBalanceSuccess).data != (b.balance as SastaWalletBalanceSuccess).data;
  }
  // 其他状态类型(如Loading/Failed)变化时触发
  return false;
}

2. 完善状态类构造函数

确保所有子类构造函数正确传递参数到父类,保持props的一致性:

@immutable
class SastaWalletBalanceLoading extends SastaWalletBalanceState {
  SastaWalletBalanceLoading() : super(data: null, message: null);
}

3. 验证copyWith方法

确认SastaWalletState的copyWith方法正确创建新实例,避免复用旧的balance对象引用:

// 示例正确的copyWith实现
SastaWalletState copyWith({
  SastaWalletBalanceState? balance,
  // 其他字段...
}) {
  return SastaWalletState(
    balance: balance ?? this.balance,
    // 其他字段赋值...
  );
}

4. 调试辅助

在listenWhen中添加打印,验证触发逻辑是否符合预期:

static bool match(SastaWalletState a, SastaWalletState b) {
  bool shouldListen = !(a.balance == b.balance);
  print('listenWhen触发判断: $shouldListen, 旧状态: ${a.balance.runtimeType}, 新状态: ${b.balance.runtimeType}');
  return shouldListen;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 17:41:06