You need to enable JavaScript to run this app. Flutter开发:如何用单个TextForm为输入框每个字符加下划线实现手机号验证
优惠活动
大模型
产品
解决方案
定价
更多

Flutter开发:如何用单个TextForm为输入框每个字符加下划线实现手机号验证

Flutter 实现单字符独立下划线的手机号输入效果

核心结论

  • 不需要拆分多个独立输入框,完全可以通过单个TextField/TextFormField实现目标交互
  • 拆分多输入框的方案需要额外处理焦点跳转、退格回退、满值自动跳位等边界逻辑,兼容成本高;单输入框的方案可以完全复用原生输入的状态管理能力,稳定性更强

实现思路

将单个原生输入框改造为纯事件接收层:隐藏其自带的文字渲染、默认装饰元素,仅保留接收键盘输入、维护光标位置、处理输入校验的基础能力;在其下方叠放一层自定义视觉组件,负责渲染每个单字符占位、对应位置的字符内容、专属下划线的状态样式,两层通过同一个TextEditingController做实时状态同步。

具体实现步骤

  1. 配置透明化的原生输入框
    对输入框做如下基础配置,去掉所有原生渲染元素:
    final TextEditingController _phoneController = TextEditingController();
    
    TextField(
      controller: _phoneController,
      keyboardType: TextInputType.phone,
      maxLength: 11, // 和手机号位数对齐
      // 隐藏原生输入的文字渲染,保持透明
      style: const TextStyle(color: Colors.transparent),
      // 移除默认装饰、长度计数器、原生下划线
      decoration: const InputDecoration(
        counterText: "",
        border: InputBorder.none,
        enabledBorder: InputBorder.none,
        focusedBorder: InputBorder.none,
        contentPadding: EdgeInsets.zero,
      ),
      // 限制仅可输入数字,符合手机号校验规则
      inputFormatters: [FilteringTextInputFormatter.digitsOnly],
    )
    
  2. 实现自定义单字符占位视觉组件
    横向排列对应位数的等宽占位块,每个块独立承担内容展示、下划线绘制的能力,根据输入状态切换下划线样式:
    class CharPlaceholderRow extends StatelessWidget {
      final String inputContent;
      final int totalCount;
    
      const CharPlaceholderRow({
        super.key,
        required this.inputContent,
        required this.totalCount,
      });
    
      @override
      Widget build(BuildContext context) {
        return Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: List.generate(totalCount, (index) {
            // 取当前位置对应的输入字符,无输入则展示空内容
            final char = index < inputContent.length ? inputContent[index] : "";
            return Container(
              width: 32,
              margin: const EdgeInsets.symmetric(horizontal: 6),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text(
                    char,
                    style: const TextStyle(fontSize: 22, color: Colors.black87),
                  ),
                  const SizedBox(height: 6),
                  // 每个占位独立的下划线,根据位置切换状态
                  Container(
                    height: 2,
                    color: index == inputContent.length
                        ? Theme.of(context).primaryColor // 光标所在位置的下划线高亮
                        : Colors.grey.shade300, // 其余位置默认浅灰色下划线
                  )
                ],
              ),
            );
          }),
        );
      }
    }
    
  3. 叠层组装组件
    使用Stack将自定义占位层放在下层,透明输入框放在上层,让用户的点击、输入操作全部由上层原生输入框承接:
    Stack(
      alignment: Alignment.center,
      children: [
        // 下层:自定义视觉展示层
        ListenableBuilder(
          listenable: _phoneController,
          builder: (context, _) {
            return CharPlaceholderRow(
              inputContent: _phoneController.text,
              totalCount: 11,
            );
          },
        ),
        // 上层:透明输入框,覆盖整个区域承接输入事件
        SizedBox(
          height: 48, // 适配占位组件的整体高度,保证点击热区覆盖完整
          child: TextField(
            controller: _phoneController,
            // 前面步骤配置的透明样式规则
          ),
        )
      ],
    )
    

优化注意点

  • 调整上层透明输入框的尺寸,让点击热区完全覆盖所有占位块的范围,避免点击空隙无法唤起键盘的问题
  • 可根据交互需求调整下划线的状态逻辑:已输入位置的下划线可设置为深灰色或主题色,未输入位置为浅灰色,光标停留位置可做加粗高亮
  • 输入控制器需要添加监听,文本变化时自动触发下层占位组件的刷新,保证内容和输入状态实时同步

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.04 17:45:03