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

Flutter如何实现输入自动格式化为HH:MM格式的数字TextField

问题根因

你之前的正则写法导致无法输入,核心问题是FilteringTextInputFormatter.allow的校验逻辑是对输入过程中的每一个中间状态做匹配,而非只校验最终提交的完整字符串。你写的正则是匹配完整HH:MM结构的整串规则,用户输入第一个单个数字(比如输入"1")时,单字符完全不满足整串正则的匹配条件,会被formatter直接拦截,自然出现完全无法输入的情况。

实现方案

整体实现分三层逻辑,兼顾输入限制、合法性校验和自动格式化效果:

  • 第一层输入拦截:只允许数字输入,限制最大输入长度为4位(对应HHMM四位时间数字),从源头过滤非法字符
  • 实时合法性校验:对输入过程中的小时、分钟取值做范围校验,自动拦截超出00-23小时、00-59分钟范围的非法输入
  • 自动格式化拼接:输入过程中自动在小时和分钟之间插入冒号转为标准HH:MM格式,同时固定光标位置避免输入错位

完整可运行代码

import 'package:flutter/services.dart';
import 'package:flutter/material.dart';

class TimeInputTextField extends StatefulWidget {
  const TimeInputTextField({super.key});

  @override
  State<TimeInputTextField> createState() => _TimeInputTextFieldState();
}

class _TimeInputTextFieldState extends State<TimeInputTextField> {
  final TextEditingController _controller = TextEditingController();

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  void _formatTimeInput(String value) {
    // 提取纯数字,剔除所有非数字字符
    final digitsOnly = value.replaceAll(RegExp(r'\D'), '');
    if (digitsOnly.isEmpty) {
      _controller.value = TextEditingValue.empty;
      return;
    }

    final buffer = StringBuffer();
    int cursorPosition = 0;
    int totalLen = digitsOnly.length;

    // 处理小时部分
    String hourPart;
    if (totalLen == 1) {
      int firstNum = int.parse(digitsOnly[0]);
      // 小时首位大于2时自动补前导0,比如输入3直接转为03
      if (firstNum > 2) {
        hourPart = '0$firstNum';
        totalLen = 2;
      } else {
        hourPart = digitsOnly[0];
      }
    } else {
      hourPart = digitsOnly.substring(0, 2);
      // 小时值超过23时自动截断第二位
      if (int.parse(hourPart) > 23) {
        hourPart = hourPart[0];
        totalLen = 1;
      }
    }
    buffer.write(hourPart);
    cursorPosition += hourPart.length;

    // 处理分钟部分
    if (totalLen >= 3) {
      // 插入时分分隔冒号
      buffer.write(':');
      cursorPosition += 1;

      String minutePart;
      if (totalLen == 3) {
        int minuteFirst = int.parse(digitsOnly[2]);
        // 分钟首位大于5时自动补0,比如12:7自动转为12:07
        if (minuteFirst > 5) {
          minutePart = '0$minuteFirst';
        } else {
          minutePart = digitsOnly[2];
        }
      } else {
        minutePart = digitsOnly.substring(2, 4);
        // 分钟值超过59时自动截断第二位
        if (int.parse(minutePart[0]) > 5) {
          minutePart = minutePart[0];
        }
      }
      buffer.write(minutePart);
      cursorPosition += minutePart.length;
    }

    final formattedTime = buffer.toString();
    // 更新内容并固定光标在输入末尾,避免跳转错位
    _controller.value = TextEditingValue(
      text: formattedTime,
      selection: TextSelection.collapsed(offset: cursorPosition),
    );

    // 此处可将formattedTime通过回调传给上层业务逻辑
  }

  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: _controller,
      style: const TextStyle(
        color: Colors.white,
        fontSize: 24,
      ),
      keyboardType: TextInputType.number,
      inputFormatters: [
        FilteringTextInputFormatter.digitsOnly,
        LengthLimitingTextInputFormatter(4),
      ],
      onChanged: _formatTimeInput,
    );
  }
}

实现效果

  • 唤起纯数字键盘,无法输入任何非数字内容
  • 支持3位快速输入:比如输入930自动转为09:30,输入145自动转为01:45,符合01-09点时段3位输入的需求
  • 支持4位标准输入:比如输入2230自动转为22:30,输入2359自动转为23:59
  • 自动拦截非法值:不会出现小时超过23、分钟超过59的非法时间格式
  • 输入全程光标固定在内容末尾,无跳转错位问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 21:42:12