Flutter实现从右到左的时间输入格式化及焦点自动切换
Flutter 实现反向时间输入效果
需求明确
要实现一行内两个TextField组件,左侧显示小时、右侧显示分钟,输入规则如下:
- 从右侧分钟框开始输入,每次输入的字符追加到分钟末尾
- 输入效果严格遵循以下流程:
- 输入2 → 显示
00:02 - 输入6 → 显示
00:26 - 输入3 → 显示
02:63 - 输入1 → 显示
26:31
- 输入2 → 显示
- 分钟框每输入满两位字符后,自动将焦点切换至左侧小时TextField
现有代码的问题
当前代码未实现核心逻辑,主要问题包括:
- 焦点逻辑反向:现行为小时框输入4位后切换到分钟框,与需求不符
- 缺少文本转移逻辑:输入溢出时不会将分钟的字符转移到小时框
- 分钟输入范围限制:原有规则限制分钟为0-59,不符合需求中显示63、31的要求
- 格式化规则不匹配:无法实现动态补零与文本转移的联动效果
修正后的完整代码
先在State类中定义焦点节点与文本控制器,再替换目标Widget代码:
// 在你的State类中声明变量 final FocusNode _hourFocus = FocusNode(); final FocusNode _minuteFocus = FocusNode(); final TextEditingController _hourCtrl = TextEditingController(text: '00'); final TextEditingController _minuteCtrl = TextEditingController(text: '00'); // ... 保留其他原有代码,替换以下Widget部分 ... if (goalData.goalType == "du") ...[ Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: AppColors.grayLightColor.withOpacity(0.3), ), margin: const EdgeInsets.symmetric(horizontal: 18), padding: const EdgeInsets.only(bottom: 5), height: 40, child: Row( children: [ Flexible( flex: 1, child: TextField( focusNode: _hourFocus, controller: _hourCtrl, textAlign: TextAlign.center, keyboardType: TextInputType.number, decoration: const InputDecoration( border: InputBorder.none, hintText: "HH", ), inputFormatters: [ FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(2), // 小时固定显示两位 ], onChanged: (value) { // 可选逻辑:小时输入满两位后切回分钟框 if (value.length == 2) { _minuteFocus.requestFocus(); } // 自动补零,确保小时始终显示两位 if (value.length < 2) { _hourCtrl.text = value.padLeft(2, '0'); _hourCtrl.selection = TextSelection.collapsed(offset: _hourCtrl.text.length); } }, ), ), const Text(":"), Flexible( child: TextField( focusNode: _minuteFocus, controller: _minuteCtrl, textAlign: TextAlign.center, keyboardType: TextInputType.number, decoration: const InputDecoration( border: InputBorder.none, hintText: "MM", ), inputFormatters: [ FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(3), // 允许临时输入3位,用于字符转移 ], autofocus: true, // 默认焦点在分钟框,符合输入顺序 onChanged: (value) { if (value.length > 2) { // 取出最左侧字符,转移到小时框 final transferChar = value[0]; // 分钟保留最后两位 final newMinute = value.substring(1); // 更新分钟框文本与光标位置 _minuteCtrl.text = newMinute; _minuteCtrl.selection = TextSelection.collapsed(offset: newMinute.length); // 更新小时框:追加转移字符,保留最后两位 final newHour = (_hourCtrl.text + transferChar).substring((_hourCtrl.text + transferChar).length - 2); _hourCtrl.text = newHour.padLeft(2, '0'); _hourCtrl.selection = TextSelection.collapsed(offset: newHour.length); // 自动切换焦点到小时框 _hourFocus.requestFocus(); } else { // 不足两位时自动补零,保证显示格式统一 _minuteCtrl.text = value.padLeft(2, '0'); _minuteCtrl.selection = TextSelection.collapsed(offset: _minuteCtrl.text.length); } }, ), ), ], ), ), ],
核心修改说明
- 焦点与初始化:默认焦点设置在分钟框,控制器初始值设为
00,确保初始显示00:00 - 文本转移逻辑:分钟输入超过两位时,将最左侧字符转移到小时框末尾,小时与分钟均保留最后两位,完全匹配需求的输入效果
- 格式化调整:移除分钟的范围限制,仅允许数字输入,同时自动补零保持两位显示
- 焦点切换:分钟输入溢出时自动切换焦点到小时框,符合需求流程
额外提醒
记得在State的dispose方法中销毁焦点节点与控制器,避免内存泄漏:
@override void dispose() { _hourFocus.dispose(); _minuteFocus.dispose(); _hourCtrl.dispose(); _minuteCtrl.dispose(); super.dispose(); }
内容的提问来源于stack exchange,提问作者Shubham vyas
相关产品推荐
相关产品推荐

