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
相关产品推荐
相关产品推荐

