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

如何修复Bloc Listener重复触发相同状态导致页面重复跳转的问题?

问题分析与解决方案

核心问题

你的代码存在两个关键问题导致重复导航:

  1. success状态未重置:当_addTransaction执行成功后,addTransactionStatus一直停留在success状态。后续任何对该Bloc的订阅(比如新页面通过BlocProvider.value复用同一实例时),都会立即触发success状态的监听逻辑,导致重复导航。
  2. 页面未被替换:使用Navigator.push只会将新页面压入栈,原第二页仍保留在栈中。如果Bloc后续有状态更新,原页面的BlocConsumer还会继续响应,触发重复导航。

控制台打印两次hehe,正是因为原页面和新页面的BlocConsumer都监听了同一个success状态。

修复步骤

1. 重置Bloc的状态

在触发success状态后,立即重置状态为初始空闲状态(需先在AddTransactionStatus枚举中新增idle值),避免后续订阅重复响应:

Future<void> _addTransaction(
    AddTransaction event,
    Emitter<TransactionState> emit,
  ) async {
    emit(
      state.copyWith(
        addTransactionStatus: AddTransactionStatus.loading,
        errorMessage: null,
      ),
    );
    try {
      
      final result = await repository.addTransaction(model: AddTransactionModel(
        accountNumber: state.accountNumber, type: event.type, transaction: TransactionDataModel(amount: state.amount)));
      
      // 先触发成功状态
      emit(
        state.copyWith(
          addTransactionStatus: AddTransactionStatus.success,
          transactionId: result.transactionId
        ),
      );
      // 立即重置为空闲状态
      emit(
        state.copyWith(
          addTransactionStatus: AddTransactionStatus.idle,
        ),
      );
    } catch (e) {
      emit(
        state.copyWith(addTransactionStatus: AddTransactionStatus.failed, errorMessage: '$e'),
      );
    }
  }

2. 使用pushReplacement替换页面

将Navigator.push改为Navigator.pushReplacement,移除栈中的原第二页,避免它继续监听Bloc状态:

BlocConsumer<TransactionBloc, TransactionState>(
            listener: (context, state) {
              if (state.addTransactionStatus == AddTransactionStatus.success) {
                print('hehe');
                Navigator.pushReplacement(
                    context,
                    MaterialPageRoute(
                        builder: (_) => BlocProvider.value(
                              value: context.read<TransactionBloc>(),
                              child: PayBillsPinPage(),
                            )));
              }
            },
            builder: (context, state) {
              if (state.addTransactionStatus == AddTransactionStatus.loading) {
                return ElevatedButton(
                  child: Text('Lanjut'),
                  onPressed: () {},
                );
              }
              return ElevatedButton(
                onPressed: () {
                  context
                      .read<TransactionBloc>()
                      .add(AddTransaction(type: 'cash-deposit'));
                },
                child: Text('触发操作'),
              );
            },
          ),

3. 可选:避免在新页面复用同一Bloc实例

如果PayBillsPinPage不需要依赖之前的TransactionBloc状态,可以创建新的Bloc实例,彻底避免状态共享导致的重复触发:

Navigator.pushReplacement(
  context,
  MaterialPageRoute(
    builder: (_) => BlocProvider(
          create: (_) => GetIt.I<TransactionBloc>(),
          child: PayBillsPinPage(),
        )));

验证效果

完成修改后:

  • 触发成功状态时,控制台只会打印一次hehe
  • 页面流程将符合预期:第一页 -> 第二页 -> 最后一页,无重复跳转

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 16:40:58