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

iOS设备OTP验证退格失效及焦点跳转异常问题求助

解决Flutter OTP验证码iOS端退格与焦点跳转异常问题

问题根源

  1. iOS与Android的键盘事件机制存在差异,RawKeyDownEvent对iOS退格键的响应不及时,无法触发跨输入框的焦点跳转。
  2. onChanged中手动赋值controller.text = value破坏了TextField的默认输入逻辑,导致输入状态混乱。
  3. 焦点跳转的条件判断冗余且逻辑错误,比如输入字符时的跳转时机不准确,退格场景的处理逻辑冲突。

修复方案

以下是针对问题的具体修复步骤,直接替换原有代码即可:

1. 修正OTP输入框的布局代码

调整第一个和最后一个输入框的焦点节点参数,避免无效的焦点请求:

Row(
  mainAxisAlignment: MainAxisAlignment.center,
  crossAxisAlignment: CrossAxisAlignment.center,
  children: [
    CodeInput(
        controller: _controllers[0],
        focusNode: _focusNodes[0],
        nextFocusNode: _focusNodes[1],
        previousFocusNode: null),
    SizedBox(width: 12.63.w),
    CodeInput(
        controller: _controllers[1],
        focusNode: _focusNodes[1],
        nextFocusNode: _focusNodes[2],
        previousFocusNode: _focusNodes[0]),
    SizedBox(width: 12.63.w),
    CodeInput(
        controller: _controllers[2],
        focusNode: _focusNodes[2],
        nextFocusNode: _focusNodes[3],
        previousFocusNode: _focusNodes[1]),
    SizedBox(width: 12.63.w),
    CodeInput(
        controller: _controllers[3],
        focusNode: _focusNodes[3],
        nextFocusNode: null,
        previousFocusNode: _focusNodes[2]),
  ],
),

2. 重构CodeInput组件

优化输入与退格的逻辑处理,适配iOS和Android平台:

class CodeInput extends StatelessWidget {
  final TextEditingController controller;
  final FocusNode focusNode;
  final FocusNode? nextFocusNode;
  final FocusNode? previousFocusNode;

  const CodeInput({
    required this.controller,
    required this.focusNode,
    this.nextFocusNode,
    this.previousFocusNode,
  });

  @override
  Widget build(BuildContext context) {
    return Focus(
      onKey: (node, event) {
        // Android端单独处理退格键的跨输入框跳转
        if (Theme.of(context).platform == TargetPlatform.android) {
          if (event is RawKeyDownEvent &&
              event.logicalKey == LogicalKeyboardKey.backspace) {
            if (controller.text.isEmpty && previousFocusNode != null) {
              previousFocusNode!.requestFocus();
              return KeyEventResult.handled;
            }
          }
        }
        return KeyEventResult.ignored;
      },
      child: SizedBox(
        width: 24,
        child: TextField(
          onTapOutside: (event) => focusNode.unfocus(),
          controller: controller,
          focusNode: focusNode,
          textAlignVertical: TextAlignVertical.center,
          maxLength: 1,
          keyboardType: TextInputType.number,
          showCursor: false,
          style: const TextStyle(fontSize: 23.3),
          // 仅第一个输入框自动聚焦,避免多输入框争抢焦点
          autofocus: focusNode == _focusNodes[0],
          decoration: const InputDecoration(
            border: InputBorder.none,
            hintText: "\u25cb",
            hintStyle: TextStyle(
              color: Colors.black,
              fontSize: 18,
              fontWeight: FontWeight.w500,
            ),
            counterText: "",
          ),
          onChanged: (value) {
            // 输入字符后,延迟跳转焦点确保输入状态稳定
            if (value.isNotEmpty && nextFocusNode != null) {
              WidgetsBinding.instance.addPostFrameCallback((_) {
                nextFocusNode!.requestFocus();
              });
            }
            // iOS端通过内容变化判断退格,触发上一个输入框焦点跳转
            else if (value.isEmpty && previousFocusNode != null) {
              WidgetsBinding.instance.addPostFrameCallback((_) {
                previousFocusNode!.requestFocus();
              });
            }
          },
        ),
      ),
    );
  }
}

关键修复点说明

  • 移除onChanged中controller.text = value的手动赋值,恢复TextField的默认输入逻辑。
  • 针对平台差异拆分退格处理:Android用onKey捕获退格事件,iOS依赖onChanged的内容变化触发焦点跳转。
  • 使用WidgetsBinding.instance.addPostFrameCallback延迟焦点跳转,确保输入状态同步完成后再执行。
  • 修正首尾输入框的焦点节点参数,避免无效的焦点请求。
  • 仅第一个输入框设置自动聚焦,解决多输入框焦点冲突问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 03:06:01