【问题标题】:Flutter input time in hh:mm:ss format in TextFormField or TextField without using pickers在不使用选择器的情况下,在 TextFormField 或 TextField 中以 hh:mm:ss 格式颤动输入时间
【发布时间】:2021-11-23 19:01:41
【问题描述】:

谁能帮助弄清楚如何设置一个 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 - typed 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
      ],
    );
  }
}

非常感谢任何帮助!

【问题讨论】:

    标签: regex flutter timer textfield textformfield


    【解决方案1】:

    更新代码:

    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;
    
                        }
                      },
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    }
    

    【讨论】:

    • 感谢您的帮助。但它对我不起作用。我的领域不会有标签。因为控制器文本在提供的代码中的第 6 位大小写之后更新,所以它只会在输入 6 位后更新文本编辑值。我需要在每个数字后更新它。在 switch 中的每个 case 之后更新控制器文本值不会让我在第一个数字之后输入任何内容。
    • 试试最新的。
    • 那个不行。如果使用此代码,控制器值长度将始终为 1 或 6(不为空时)。我将发布我自己的解决方案。再次感谢您的意见。
    【解决方案2】:

    我在文本字段中使用了 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;
      }
    }
    

    【讨论】:

    • 欢迎来到 Stack Overflow,感谢您返回问题以与社区分享您的解决方案。您能否编辑您的答案以包括对您的代码的解释?这将有助于未来的读者更好地了解正在发生的事情,尤其是那些刚接触该语言并难以理解概念的社区成员。
    猜你喜欢
    • 2020-07-15
    • 1970-01-01
    • 1970-01-01
    • 2021-12-26
    • 2020-02-02
    • 2017-05-26
    • 2021-08-17
    • 2022-11-10
    • 2021-06-28
    相关资源
    最近更新 更多