Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter TextField input validation for a date

I am trying to write a date input control which accepts a date like 23/12/1997. What I would like it to do is automatically insert the / characters for the user. So as they type in 23 the listener returns 23/, so that they can then type in 12. At this point the listener again adds a / leaving the user to complete the date by typing 1997. My TextEditingController code half works and looks like this:

final _controller = TextEditingController();
_controller.addListener(() {
      String text = _controller.text;
      if (text.length == 2) {
        text += '/';
      }
      if (text.length == 5) {
        text += '/';
      }
      _controller.value = _controller.value.copyWith(
        text: text,
        selection:
            TextSelection(baseOffset: text.length, extentOffset: text.length),
        composing: TextRange.empty,
      );
      print(_controller.text);
    }

So it works fine until the user makes a mistake and needs to backtrack. As soon as a / is deleted it is immediately replaced stopping any further editing of the date.

In order to get it to work I need to access is the previously entered text to determine if the user is backspacing. So if text == 23/ && previous_text == 23/1 then I can remove the / from text.

I found this question textfield must only accept numbers and I think it may help me, but I am not sure how to implement an existing widget and override its methods. Of course there may be a simpler way to do this within the TextEditingController?

like image 455
Paul Pritchard Avatar asked Jun 19 '20 10:06

Paul Pritchard


People also ask

How do I select a date in TextField in Flutter?

To Implement Date Picker on TextField() and TextFormField(): First of all, you need to add intl Flutter package in your dependency to get formatted date output from date picker. Add the following line in your pubspec. yaml file to add intl package to your project.

How do you add validation to TextField in Flutter?

In this example, learn how to add validation to a form that has a single text field using the following steps: Create a Form with a GlobalKey . Add a TextFormField with validation logic. Create a button to validate and submit the form.

How do you validate a dart date?

If you pass a non-existing/non-real date like: '20181364' (2018/13/64) into DateTime (constructor or parse-method), no exception is thrown. Instead a calculated DateTime is returned.


Video Answer


2 Answers

I have found what I needed to solve my date validation input. It is not perfect, but it is good enough for what I am trying to do. All I needed was to look at the inputFormatters method of a TextField(). This allow manipulation of the input text to put it into any number of user-defined formats. I am including a segment of my code for anyone who would like to try it out:

  class _DateFormatter extends TextInputFormatter {
  @override
  TextEditingValue formatEditUpdate(
      TextEditingValue prevText, TextEditingValue currText) {
    int selectionIndex;

    // Get the previous and current input strings
    String pText = prevText.text;
    String cText = currText.text;
    // Abbreviate lengths
    int cLen = cText.length;
    int pLen = pText.length;

    if (cLen == 1) {
      // Can only be 0, 1, 2 or 3
      if (int.parse(cText) > 3) {
        // Remove char
        cText = '';
      }
    } else if (cLen == 2 && pLen == 1) {
      // Days cannot be greater than 31
      int dd = int.parse(cText.substring(0, 2));
      if (dd == 0 || dd > 31) {
        // Remove char
        cText = cText.substring(0, 1);
      } else {
        // Add a / char
        cText += '/';
      }
    } else if (cLen == 4) {
      // Can only be 0 or 1
      if (int.parse(cText.substring(3, 4)) > 1) {
        // Remove char
        cText = cText.substring(0, 3);
      }
    } else if (cLen == 5 && pLen == 4) {
      // Month cannot be greater than 12
      int mm = int.parse(cText.substring(3, 5));
      if (mm == 0 || mm > 12) {
        // Remove char
        cText = cText.substring(0, 4);
      } else {
        // Add a / char
        cText += '/';
      }
    } else if ((cLen == 3 && pLen == 4) || (cLen == 6 && pLen == 7)) {
      // Remove / char
      cText = cText.substring(0, cText.length - 1);
    } else if (cLen == 3 && pLen == 2) {
      if (int.parse(cText.substring(2, 3)) > 1) {
        // Replace char
        cText = cText.substring(0, 2) + '/';
      } else {
        // Insert / char
        cText =
            cText.substring(0, pLen) + '/' + cText.substring(pLen, pLen + 1);
      }
    } else if (cLen == 6 && pLen == 5) {
      // Can only be 1 or 2 - if so insert a / char
      int y1 = int.parse(cText.substring(5, 6));
      if (y1 < 1 || y1 > 2) {
        // Replace char
        cText = cText.substring(0, 5) + '/';
      } else {
        // Insert / char
        cText = cText.substring(0, 5) + '/' + cText.substring(5, 6);
      }
    } else if (cLen == 7) {
      // Can only be 1 or 2
      int y1 = int.parse(cText.substring(6, 7));
      if (y1 < 1 || y1 > 2) {
        // Remove char
        cText = cText.substring(0, 6);
      }
    } else if (cLen == 8) {
      // Can only be 19 or 20
      int y2 = int.parse(cText.substring(6, 8));
      if (y2 < 19 || y2 > 20) {
        // Remove char
        cText = cText.substring(0, 7);
      }
    }

    selectionIndex = cText.length;
    return TextEditingValue(
      text: cText,
      selection: TextSelection.collapsed(offset: selectionIndex),
    );
  }
}

To use it simply call it from the Textfield() as shown below. I've also incorporated two built in methods as well. WhitelistingTextInputFormatter() to only allow digits and a slash(/) character to be entered and LengthLimitingTextInputFormatter() to restrict the number of characters allowed. The latter could be achieved using the maxLength parameter of TextField() but it is here by way of example. Note that there is also a BlacklistingTextInputFormatter() which does as you would expect.

WhitelistingTextInputFormatter was removed use FilteringTextInputFormatter.allow(RegExp("[0-9-]")), to replace, and If you want to change split symbol (current is "/"), Pls add it to RegExp(....).

TextField(
  // maxLength: 10,
  keyboardType: TextInputType.datetime,
  controller: _controllerDOB,
  focusNode: _focusNodeDOB,
  decoration: InputDecoration(
    hintText: 'DD/MM/YYYY',
    counterText: '',
  ),
  inputFormatters: [
    WhitelistingTextInputFormatter(RegExp("[0-9/]")),
    LengthLimitingTextInputFormatter(10),
    _DateFormatter(),
  ],
),
like image 113
Paul Pritchard Avatar answered Oct 23 '22 20:10

Paul Pritchard


You can use datepicker dialog made by flutter.

DateTime _date = DateTime.now()


onPressed: () {
  showDatePicker(
    context: context,
    initialDate: _date,
    firstDate: DateTime(2020),
    lastDate: DateTime(2021),
    ).then((date) {
      setState(() {
    _date = date;
    });
  });
},
like image 41
Nehal Avatar answered Oct 23 '22 19:10

Nehal