在不使用选择器的情况下,在 TextFormField 或 TextField 中以 hh:mm:ss 格式输入时间
Flutter input time in hh:mm:ss format in TextFormField or TextField without using pickers
任何人都可以帮助弄清楚如何设置 TextField 或 TextFormField 小部件以输入 hh:mm:ss 格式的时间吗?
格式应实时显示在字段中,因此前导零将在用户键入时被替换。
比如我们需要输入1小时59分27秒,就需要这样操作:
- 00:00:00 - 输入前的文字提示
- 00:00:01 - 开始输入并输入 1
- 00:00:15 - 输入 5
- 00:01:59 - 输入 9
- 00:15:92 - 输入2(这里可以接受92秒,输入完成后可以转换)
- 01:59:27 - 输入 7
它在 Android 的内置时钟应用程序中以类似的方式在计时器上工作。
我尝试使用 mask_text_input_formatter 包,但它无法正常工作。另外,我不想使用时间选择器。
import 'package:flutter/services.dart';
import 'package:mask_text_input_formatter/mask_text_input_formatter.dart';
class TimeInputField extends StatefulWidget {
TimeInputField({Key key}) : super(key: key);
@override
_TimeInputFieldState createState() => _TimeInputFieldState();
}
class _TimeInputFieldState extends State<TimeInputField> {
TextEditingController _txtTimeController = TextEditingController();
final MaskTextInputFormatter timeMaskFormatter =
MaskTextInputFormatter(mask: '##:##:##', filter: {"#": RegExp(r'[0-9]')});
@override
Widget build(BuildContext context) {
return TextFormField(
controller: _txtTimeController,
keyboardType: TextInputType.numberWithOptions(decimal: false),
decoration: InputDecoration(
hintText: '00:00:00',
),
inputFormatters: <TextInputFormatter>[
timeMaskFormatter
// Not sure if it can be done with RegExp or a custom class here instead
],
);
}
}
非常感谢任何帮助!
更新代码:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: TimeInputField(),
);
}
}
class TimeInputField extends StatefulWidget {
const TimeInputField({Key? key}) : super(key: key);
@override
_TimeInputFieldState createState() => _TimeInputFieldState();
}
class _TimeInputFieldState extends State<TimeInputField> {
String hrCounter = '00';
String minCounter = '00';
String secCounter = '00';
String temp="";
final TextEditingController _controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Stack Overflow"),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Stack(
children: [
SizedBox(
width: 250,
child: TextFormField(
controller: _controller,
keyboardType:
const TextInputType.numberWithOptions(decimal: false),
inputFormatters: [
LengthLimitingTextInputFormatter(9),
],
decoration: InputDecoration(
hintText: '$hrCounter:$minCounter:$secCounter',
floatingLabelBehavior: FloatingLabelBehavior.always,
labelText: '$hrCounter:$minCounter:$secCounter'),
onChanged: (val) {
String y="";
switch (val.length) {
case 0:
setState(() {
hrCounter = "00";
minCounter = "00";
secCounter = "00";
});
break;
case 1:
setState(() {
secCounter = "0"+val;
temp=val;
_controller.value = _controller.value.copyWith(
text: hrCounter+":"+minCounter+":"+secCounter,
selection: const TextSelection.collapsed(offset: 8),
);
});
break;
default: setState((){
for(int i=1;i<=val.length-1;i++){
y=y+val.substring(i,i+1);
}
y=y.replaceAll(":", "");
val=y.substring(0,2)+":"+y.substring(2,4)+":"+y.substring(4,6);
temp=val;
_controller.value = _controller.value.copyWith(
text: val,
selection: const TextSelection.collapsed(offset: 8),
);
});
break;
}
},
),
),
],
),
),
),
);
}
}
我在文本字段中使用了 TextInputFormatter 而不是 onChanged。这允许访问旧值并根据新字符是添加还是删除来格式化新输入。它还允许使用 RegExp 只接受数字。这就是解决方案:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:math' as math;
class TimeInputField extends StatefulWidget {
TimeInputField({Key key}) : super(key: key);
@override
_TimeInputFieldState createState() => _TimeInputFieldState();
}
class _TimeInputFieldState extends State<TimeInputField> {
TextEditingController _txtTimeController = TextEditingController();
@override
Widget build(BuildContext context) {
return TextFormField(
controller: _txtTimeController,
keyboardType: TextInputType.numberWithOptions(decimal: false),
decoration: InputDecoration(
hintText: '00:00:00',
),
inputFormatters: <TextInputFormatter>[
TimeTextInputFormatter() // This input formatter will do the job
],
);
}
}
class TimeTextInputFormatter extends TextInputFormatter {
RegExp _exp;
TimeTextInputFormatter() {
_exp = RegExp(r'^[0-9:]+$');
}
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
) {
if (_exp.hasMatch(newValue.text)) {
TextSelection newSelection = newValue.selection;
String value = newValue.text;
String newText;
String leftChunk = '';
String rightChunk = '';
if (value.length >= 8) {
if (value.substring(0, 7) == '00:00:0') {
leftChunk = '00:00:';
rightChunk = value.substring(leftChunk.length + 1, value.length);
} else if (value.substring(0, 6) == '00:00:') {
leftChunk = '00:0';
rightChunk = value.substring(6, 7) + ":" + value.substring(7);
} else if (value.substring(0, 4) == '00:0') {
leftChunk = '00:';
rightChunk = value.substring(4, 5) +
value.substring(6, 7) +
":" +
value.substring(7);
} else if (value.substring(0, 3) == '00:') {
leftChunk = '0';
rightChunk = value.substring(3, 4) +
":" +
value.substring(4, 5) +
value.substring(6, 7) +
":" +
value.substring(7, 8) +
value.substring(8);
} else {
leftChunk = '';
rightChunk = value.substring(1, 2) +
value.substring(3, 4) +
":" +
value.substring(4, 5) +
value.substring(6, 7) +
":" +
value.substring(7);
}
} else if (value.length == 7) {
if (value.substring(0, 7) == '00:00:0') {
leftChunk = '';
rightChunk = '';
} else if (value.substring(0, 6) == '00:00:') {
leftChunk = '00:00:0';
rightChunk = value.substring(6, 7);
} else if (value.substring(0, 1) == '0') {
leftChunk = '00:';
rightChunk = value.substring(1, 2) +
value.substring(3, 4) +
":" +
value.substring(4, 5) +
value.substring(6, 7);
} else {
leftChunk = '';
rightChunk = value.substring(1, 2) +
value.substring(3, 4) +
":" +
value.substring(4, 5) +
value.substring(6, 7) +
":" +
value.substring(7);
}
} else {
leftChunk = '00:00:0';
rightChunk = value;
}
if (oldValue.text.isNotEmpty && oldValue.text.substring(0, 1) != '0') {
if (value.length > 7) {
return oldValue;
} else {
leftChunk = '0';
rightChunk = value.substring(0, 1) +
":" +
value.substring(1, 2) +
value.substring(3, 4) +
":" +
value.substring(4, 5) +
value.substring(6, 7);
}
}
newText = leftChunk + rightChunk;
newSelection = newValue.selection.copyWith(
baseOffset: math.min(newText.length, newText.length),
extentOffset: math.min(newText.length, newText.length),
);
return TextEditingValue(
text: newText,
selection: newSelection,
composing: TextRange.empty,
);
}
return oldValue;
}
}
任何人都可以帮助弄清楚如何设置 TextField 或 TextFormField 小部件以输入 hh:mm:ss 格式的时间吗?
格式应实时显示在字段中,因此前导零将在用户键入时被替换。
比如我们需要输入1小时59分27秒,就需要这样操作:
- 00:00:00 - 输入前的文字提示
- 00:00:01 - 开始输入并输入 1
- 00:00:15 - 输入 5
- 00:01:59 - 输入 9
- 00:15:92 - 输入2(这里可以接受92秒,输入完成后可以转换)
- 01:59:27 - 输入 7
它在 Android 的内置时钟应用程序中以类似的方式在计时器上工作。
我尝试使用 mask_text_input_formatter 包,但它无法正常工作。另外,我不想使用时间选择器。
import 'package:flutter/services.dart';
import 'package:mask_text_input_formatter/mask_text_input_formatter.dart';
class TimeInputField extends StatefulWidget {
TimeInputField({Key key}) : super(key: key);
@override
_TimeInputFieldState createState() => _TimeInputFieldState();
}
class _TimeInputFieldState extends State<TimeInputField> {
TextEditingController _txtTimeController = TextEditingController();
final MaskTextInputFormatter timeMaskFormatter =
MaskTextInputFormatter(mask: '##:##:##', filter: {"#": RegExp(r'[0-9]')});
@override
Widget build(BuildContext context) {
return TextFormField(
controller: _txtTimeController,
keyboardType: TextInputType.numberWithOptions(decimal: false),
decoration: InputDecoration(
hintText: '00:00:00',
),
inputFormatters: <TextInputFormatter>[
timeMaskFormatter
// Not sure if it can be done with RegExp or a custom class here instead
],
);
}
}
非常感谢任何帮助!
更新代码:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: TimeInputField(),
);
}
}
class TimeInputField extends StatefulWidget {
const TimeInputField({Key? key}) : super(key: key);
@override
_TimeInputFieldState createState() => _TimeInputFieldState();
}
class _TimeInputFieldState extends State<TimeInputField> {
String hrCounter = '00';
String minCounter = '00';
String secCounter = '00';
String temp="";
final TextEditingController _controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Stack Overflow"),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Stack(
children: [
SizedBox(
width: 250,
child: TextFormField(
controller: _controller,
keyboardType:
const TextInputType.numberWithOptions(decimal: false),
inputFormatters: [
LengthLimitingTextInputFormatter(9),
],
decoration: InputDecoration(
hintText: '$hrCounter:$minCounter:$secCounter',
floatingLabelBehavior: FloatingLabelBehavior.always,
labelText: '$hrCounter:$minCounter:$secCounter'),
onChanged: (val) {
String y="";
switch (val.length) {
case 0:
setState(() {
hrCounter = "00";
minCounter = "00";
secCounter = "00";
});
break;
case 1:
setState(() {
secCounter = "0"+val;
temp=val;
_controller.value = _controller.value.copyWith(
text: hrCounter+":"+minCounter+":"+secCounter,
selection: const TextSelection.collapsed(offset: 8),
);
});
break;
default: setState((){
for(int i=1;i<=val.length-1;i++){
y=y+val.substring(i,i+1);
}
y=y.replaceAll(":", "");
val=y.substring(0,2)+":"+y.substring(2,4)+":"+y.substring(4,6);
temp=val;
_controller.value = _controller.value.copyWith(
text: val,
selection: const TextSelection.collapsed(offset: 8),
);
});
break;
}
},
),
),
],
),
),
),
);
}
}
我在文本字段中使用了 TextInputFormatter 而不是 onChanged。这允许访问旧值并根据新字符是添加还是删除来格式化新输入。它还允许使用 RegExp 只接受数字。这就是解决方案:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:math' as math;
class TimeInputField extends StatefulWidget {
TimeInputField({Key key}) : super(key: key);
@override
_TimeInputFieldState createState() => _TimeInputFieldState();
}
class _TimeInputFieldState extends State<TimeInputField> {
TextEditingController _txtTimeController = TextEditingController();
@override
Widget build(BuildContext context) {
return TextFormField(
controller: _txtTimeController,
keyboardType: TextInputType.numberWithOptions(decimal: false),
decoration: InputDecoration(
hintText: '00:00:00',
),
inputFormatters: <TextInputFormatter>[
TimeTextInputFormatter() // This input formatter will do the job
],
);
}
}
class TimeTextInputFormatter extends TextInputFormatter {
RegExp _exp;
TimeTextInputFormatter() {
_exp = RegExp(r'^[0-9:]+$');
}
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
) {
if (_exp.hasMatch(newValue.text)) {
TextSelection newSelection = newValue.selection;
String value = newValue.text;
String newText;
String leftChunk = '';
String rightChunk = '';
if (value.length >= 8) {
if (value.substring(0, 7) == '00:00:0') {
leftChunk = '00:00:';
rightChunk = value.substring(leftChunk.length + 1, value.length);
} else if (value.substring(0, 6) == '00:00:') {
leftChunk = '00:0';
rightChunk = value.substring(6, 7) + ":" + value.substring(7);
} else if (value.substring(0, 4) == '00:0') {
leftChunk = '00:';
rightChunk = value.substring(4, 5) +
value.substring(6, 7) +
":" +
value.substring(7);
} else if (value.substring(0, 3) == '00:') {
leftChunk = '0';
rightChunk = value.substring(3, 4) +
":" +
value.substring(4, 5) +
value.substring(6, 7) +
":" +
value.substring(7, 8) +
value.substring(8);
} else {
leftChunk = '';
rightChunk = value.substring(1, 2) +
value.substring(3, 4) +
":" +
value.substring(4, 5) +
value.substring(6, 7) +
":" +
value.substring(7);
}
} else if (value.length == 7) {
if (value.substring(0, 7) == '00:00:0') {
leftChunk = '';
rightChunk = '';
} else if (value.substring(0, 6) == '00:00:') {
leftChunk = '00:00:0';
rightChunk = value.substring(6, 7);
} else if (value.substring(0, 1) == '0') {
leftChunk = '00:';
rightChunk = value.substring(1, 2) +
value.substring(3, 4) +
":" +
value.substring(4, 5) +
value.substring(6, 7);
} else {
leftChunk = '';
rightChunk = value.substring(1, 2) +
value.substring(3, 4) +
":" +
value.substring(4, 5) +
value.substring(6, 7) +
":" +
value.substring(7);
}
} else {
leftChunk = '00:00:0';
rightChunk = value;
}
if (oldValue.text.isNotEmpty && oldValue.text.substring(0, 1) != '0') {
if (value.length > 7) {
return oldValue;
} else {
leftChunk = '0';
rightChunk = value.substring(0, 1) +
":" +
value.substring(1, 2) +
value.substring(3, 4) +
":" +
value.substring(4, 5) +
value.substring(6, 7);
}
}
newText = leftChunk + rightChunk;
newSelection = newValue.selection.copyWith(
baseOffset: math.min(newText.length, newText.length),
extentOffset: math.min(newText.length, newText.length),
);
return TextEditingValue(
text: newText,
selection: newSelection,
composing: TextRange.empty,
);
}
return oldValue;
}
}