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

Flutter如何实现验证码TextFormField输入自动跳转、删除时回退焦点

需要修改/补充的内容:

  1. 替换焦点控制变量
    原代码中用isFocus1~isFocus6的布尔值控制自动聚焦逻辑不合理,改为使用Flutter官方提供的FocusNode控制每个输入框的焦点状态,同时需要在组件销毁时释放FocusNode避免内存泄漏。
  2. 为PhoneCodeTextField扩展参数
    增加onChanged回调、FocusNode参数、当前输入框的位置索引,用于监听输入内容变化和控制焦点跳转。
  3. 实现焦点跳转逻辑
    在每个输入框的onChanged回调中做判断:
  • 当输入内容长度为1时,当前输入框失焦,自动请求下一个输入框的焦点(最后一个输入框除外)
  • 当输入内容被清空时,自动请求上一个输入框的焦点(第一个输入框除外)
  1. 修正原代码语法问题
    原代码中Column直接嵌套Padding缺少children数组包裹,属于语法错误需要修正。

完整修改后代码:

import 'package:flutter/material.dart';

class GetPhoneCode extends StatefulWidget {
  const GetPhoneCode({Key? key}) : super(key: key);
  @override
  _GetPhoneCodeState createState() => _GetPhoneCodeState();
}

class _GetPhoneCodeState extends State<GetPhoneCode> {
  final codeP1 = GlobalKey<FormState>();
  final codeP2 = GlobalKey<FormState>();
  final codeP3 = GlobalKey<FormState>();
  final codeP4 = GlobalKey<FormState>();
  final codeP5 = GlobalKey<FormState>();
  final codeP6 = GlobalKey<FormState>();

  String codeP1S = "";
  String codeP2S = "";
  String codeP3S = "";
  String codeP4S = "";
  String codeP5S = "";
  String codeP6S = "";

  // 新增:每个输入框对应的FocusNode
  final FocusNode focus1 = FocusNode();
  final FocusNode focus2 = FocusNode();
  final FocusNode focus3 = FocusNode();
  final FocusNode focus4 = FocusNode();
  final FocusNode focus5 = FocusNode();
  final FocusNode focus6 = FocusNode();

  @override
  void dispose() {
    // 新增:释放FocusNode
    focus1.dispose();
    focus2.dispose();
    focus3.dispose();
    focus4.dispose();
    focus5.dispose();
    focus6.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    Size size = MediaQuery.of(context).size;
    return Scaffold(
      body: Column(
        // 修正:补充children数组
        children: [
          Padding(
            padding: EdgeInsets.only(top: size.width * .07),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                PhoneCodeTextField(size, codeP1S, focus1, codeP1, index: 1),
                SizedBox(width: size.width * .040,),
                PhoneCodeTextField(size, codeP2S, focus2, codeP2, index: 2),
                SizedBox(width: size.width * .040,),
                PhoneCodeTextField(size, codeP3S, focus3, codeP3, index: 3),
                SizedBox(width: size.width * .040,),
                PhoneCodeTextField(size, codeP4S, focus4, codeP4, index: 4),
                SizedBox(width: size.width * .040,),
                PhoneCodeTextField(size, codeP5S, focus5, codeP5, index: 5),
                SizedBox(width: size.width * .040,),
                PhoneCodeTextField(size, codeP6S, focus6, codeP6, index: 6),
              ],
            ),
          ),
        ],
      ),
    );
  }

  // 修改:扩展参数,增加FocusNode、index索引、onChanged逻辑
  Container PhoneCodeTextField(Size size, String variable, FocusNode focusNode, GlobalKey key, {required int index}) {
    return Container(
      width: size.width * .07,
      child: Form(
        key: key,
        child: TextFormField(
          focusNode: focusNode,
          decoration: const InputDecoration(
            counterText: "",
            focusedBorder: UnderlineInputBorder(
              borderSide: BorderSide(
                color: Colors.black,
                width: 2.0,
              ),
            ),
            border: UnderlineInputBorder(
              borderSide: BorderSide(color: Colors.black),
            ),
          ),
          maxLength: 1,
          // 仅第一个输入框默认自动聚焦
          autofocus: index == 1,
          textAlign: TextAlign.center,
          keyboardType: TextInputType.number,
          // 新增:输入变化回调处理焦点跳转
          onChanged: (value) {
            setState(() {
              switch(index) {
                case 1: codeP1S = value; break;
                case 2: codeP2S = value; break;
                case 3: codeP3S = value; break;
                case 4: codeP4S = value; break;
                case 5: codeP5S = value; break;
                case 6: codeP6S = value; break;
              }
            });
            // 输入了1位数字,跳转到下一个输入框
            if (value.length == 1) {
              focusNode.unfocus();
              switch(index) {
                case 1: FocusScope.of(context).requestFocus(focus2); break;
                case 2: FocusScope.of(context).requestFocus(focus3); break;
                case 3: FocusScope.of(context).requestFocus(focus4); break;
                case 4: FocusScope.of(context).requestFocus(focus5); break;
                case 5: FocusScope.of(context).requestFocus(focus6); break;
                // 最后一个输入框输入完成后可以在这里加提交验证码的逻辑
              }
            }
            // 清空了输入内容,跳转到上一个输入框
            if (value.isEmpty) {
              switch(index) {
                case 2: FocusScope.of(context).requestFocus(focus1); break;
                case 3: FocusScope.of(context).requestFocus(focus2); break;
                case 4: FocusScope.of(context).requestFocus(focus3); break;
                case 5: FocusScope.of(context).requestFocus(focus4); break;
                case 6: FocusScope.of(context).requestFocus(focus5); break;
              }
            }
          },
        ),
      ),
    );
  }
}

内容的提问来源于stack exchange,提问作者roee attias

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 02:18:01