Flutter中BLoC事件调整解决2FA TOTP应用弹窗不重复弹出的问题
问题根因拆解
当前实现存在三处逻辑漏洞,直接导致二次点击无法弹出错误提示:
- 致命逻辑错误:监听到
ManualInputError状态、弹出弹窗后,代码直接调用BlocProvider.of<ManualInputBloc>(context).close()。close()方法会彻底关停当前Bloc的事件接收管道、销毁内部状态流,后续再点击按钮发送的事件根本无法进入Bloc处理队列,自然不会产生新的状态变化触发弹窗。 - 机制适配漏洞:Bloc底层基于Stream实现,自带状态去重规则——如果新生成的状态和当前留存的旧状态完全相等,Bloc不会向下游推送状态。就算把
close()移除,第二次点击时如果Bloc校验后还是返回和上次完全相同的ManualInputError状态,listener根本不会被触发,弹窗也就不会弹出。 - UI联动错误:按钮渲染逻辑的判断条件存在问题——只有Bloc状态是
ManualInputInitial或ManualInputFinal时,按钮才设置了有效的点击回调;一旦状态切换为ManualInputError,按钮的onPressed被设为空函数,用户根本点不动按钮,更别说触发后续弹窗流程。
分步修复方案
1. 移除错误位置的Bloc关闭逻辑
和页面生命周期绑定的Bloc不需要在收到单次错误时主动关闭,Bloc的资源回收会在页面销毁时由框架自动处理,直接删掉弹窗逻辑里的close()调用即可。
2. 适配Bloc的状态去重规则
通过状态流转设计,保证每次错误触发时都满足Bloc的状态变化推送要求,两种实现方式可选:
- 方式一:Bloc处理提交事件时,先切到和Error状态不同的中间态(比如加载中、初始态),校验不通过后再抛出
ManualInputError,让状态序列变成「中间态 -> Error」,每次触发的状态流都是连续变化的,不会被去重拦截。 - 方式二:在AlertDialog的关闭回调里,给Bloc发送一个重置事件,把状态切回
ManualInputInitial,这样下次点击提交时,状态从Initial -> Error,也能正常触发监听器。
3. 修正按钮的可点击判断逻辑
不要因为Bloc处于Error状态就禁用按钮,调整判断规则:只有Bloc处于“正在处理提交”的加载态时才禁用按钮,其余状态(初始、错误、最终成功态)都保持按钮可点击,保证用户每次关闭弹窗后都能正常触发提交操作。
核心代码修改示例
修改后的BlocConsumer核心片段如下:
BlocConsumer<ManualInputBloc, ManualInputState>( listener: (context, state) { if (state is ManualInputError) { Platform.isAndroid ? CustomAlertDialog.showAndroidAlertDialog( context, state.alertDialogErrorTitle, state.alertDialogErrorContent, // 弹窗关闭时触发Bloc状态重置 onDismiss: () { context.read<ManualInputBloc>().add(ResetManualInputEvent()); }) : CustomAlertDialog.showIosAlertDialog( context, state.alertDialogErrorTitle, state.alertDialogErrorContent, onDismiss: () { context.read<ManualInputBloc>().add(ResetManualInputEvent()); }); // 原代码此处的Bloc close()调用直接删除 } }, builder: (context, state) { // 仅在提交加载时禁用按钮 final isSubmitting = state is ManualInputSubmitting; return TextButton( onPressed: isSubmitting ? null : () { context.read<ManualInputBloc>().add( GetFormTextEvent(secretKey, issuer, accountName, numberOfDigits, timeStep)); }, child: Text('add').tr(), ); }, )
对应的Bloc层补充事件处理逻辑:
// 初始化时注册事件处理 ManualInputBloc() { on<ResetManualInputEvent>(_onResetState); on<GetFormTextEvent>(_onFormSubmit); } // 状态重置逻辑 void _onResetState(ResetManualInputEvent event, Emitter<ManualInputState> emit) { emit(ManualInputInitial()); } // 表单提交校验逻辑 void _onFormSubmit(GetFormTextEvent event, Emitter<ManualInputState> emit) async { // 先切到加载中间态,避免状态重复 emit(ManualInputSubmitting()); // 执行表单校验 final isFormValid = _validateFormFields(event); if (!isFormValid) { // 校验不通过抛出错误状态 emit(ManualInputError( alertDialogErrorTitle: '提示', alertDialogErrorContent: '请完整填写表单信息' )); return; } // 校验通过走正常提交逻辑 emit(ManualInputFinal()); }
修复后无论连续多少次点击未填写完整表单的Add按钮,都能正常弹出错误提示,不会出现二次点击无响应的问题。
内容的提问来源于stack exchange,提问作者szakes1
相关产品推荐
相关产品推荐

