iOS设备OTP验证退格失效及焦点跳转异常问题求助
解决Flutter OTP验证码iOS端退格与焦点跳转异常问题
问题根源
- iOS与Android的键盘事件机制存在差异,
RawKeyDownEvent对iOS退格键的响应不及时,无法触发跨输入框的焦点跳转。 onChanged中手动赋值controller.text = value破坏了TextField的默认输入逻辑,导致输入状态混乱。- 焦点跳转的条件判断冗余且逻辑错误,比如输入字符时的跳转时机不准确,退格场景的处理逻辑冲突。
修复方案
以下是针对问题的具体修复步骤,直接替换原有代码即可:
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
相关产品推荐
相关产品推荐

