如何修复Bloc Listener重复触发相同状态导致页面重复跳转的问题?
问题分析与解决方案
核心问题
你的代码存在两个关键问题导致重复导航:
success状态未重置:当_addTransaction执行成功后,addTransactionStatus一直停留在success状态。后续任何对该Bloc的订阅(比如新页面通过BlocProvider.value复用同一实例时),都会立即触发success状态的监听逻辑,导致重复导航。- 页面未被替换:使用
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
相关产品推荐
相关产品推荐

