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

