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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.31 15:15:06