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

Flutter OTP输入框粘贴异常:仅首个字符可粘贴

问题:粘贴OTP验证码仅能填入首个字符

我通过邮件获取OTP验证码,但尝试粘贴时仅能成功粘贴首个字符。以下是自定义OtpTextField组件的代码:

class OtpTextField extends StatelessWidget {
  final int lengthOfTextField;
  final List<TextEditingController> textEdittingControllers;
  const OtpTextField(
      {super.key,
      required this.lengthOfTextField,
      required this.textEdittingControllers});

  @override
  Widget build(BuildContext context) {
    double fem = MediaQuery.of(context).size.width / 428;
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: List.generate(
        lengthOfTextField,
        (i) => SizedBox(
          width: 48 * fem,
          height: 48 * fem,
          child: Center(
            child: TextFormField(
              controller: textEdittingControllers[i],
              keyboardType: TextInputType.number,
              inputFormatters: [
                LengthLimitingTextInputFormatter(1),
                FilteringTextInputFormatter.digitsOnly
              ],
              textAlign: TextAlign.center,
              onChanged: (value) {
                if (value.length == 1 && i <= (lengthOfTextField)) {
                  FocusScope.of(context).nextFocus();
                }
              },
              autofocus: true,
              textInputAction:
                  i == 5 ? TextInputAction.done : TextInputAction.next,
              style: const TextStyle(
                  color: ColorConstant.blackColor,
                  fontFamily: AllFontFamilies.robotoRegular),
              decoration: InputDecoration(
                contentPadding: EdgeInsets.fromLTRB(
                    17 * fem, 13 * fem, 14.0 * fem, 15.0 * fem),
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(6 * fem),
                ),
                focusedBorder: OutlineInputBorder(
                  borderSide: const BorderSide(
                    width: 0.7,
                    color: ColorConstant.darkBlue,
                  ),
                  borderRadius: BorderRadius.circular(6 * fem),
                ),
                enabledBorder: OutlineInputBorder(
                  borderSide: const BorderSide(
                    width: 0.7,
                    color: ColorConstant.blueColor,
                  ),
                  borderRadius: BorderRadius.circular(
                    6 * fem,
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

粘贴前状态:6个空白的OTP输入框;粘贴后状态:仅第一个输入框填入了OTP的首个字符,其余输入框仍为空。


解决方案

问题根源在于当前每个输入框独立控制,且仅限制单字符输入,没有处理多字符粘贴的分配逻辑。可以通过以下方式修复:

方法1:利用onPaste回调处理粘贴内容(Flutter 2.8+支持)

在TextFormField中添加onPaste回调,获取完整粘贴文本后拆分分配到对应控制器,并自动调整焦点:

TextFormField(
  // ... 原有代码保留
  onPaste: (value) {
    // 过滤非数字字符,仅保留验证码数字
    final otp = value.replaceAll(RegExp(r'[^0-9]'), '');
    if (otp.isNotEmpty) {
      // 截取与输入框长度匹配的OTP内容
      final validOtp = otp.substring(0, min(otp.length, lengthOfTextField));
      // 将每个字符分配到对应输入框控制器
      for (int index = 0; index < validOtp.length; index++) {
        textEdittingControllers[index].text = validOtp[index];
      }
      // 自动跳转到最后一个填充的输入框或直接失焦
      if (validOtp.length == lengthOfTextField) {
        FocusScope.of(context).unfocus();
      } else {
        FocusScope.of(context).requestFocus(
          FocusScopeNode()..focusedChild = textEdittingControllers[validOtp.length].focusNode
        );
      }
    }
  },
  // ... 原有代码保留
)

方法2:修正onChanged逻辑错误

原代码中i <= (lengthOfTextField)的判断存在问题,会导致最后一个输入框尝试跳转不存在的焦点,修改为:

onChanged: (value) {
  if (value.length == 1 && i < lengthOfTextField - 1) {
    FocusScope.of(context).nextFocus();
  }
},

内容的提问来源于stack exchange,提问作者Kanwarpreet Singh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 17:53:27