7

I need to restrict when the user types 1.. like this. i'm using text input form field. I need input like 1.23 with decimal input text formatter

Jaya kumar
  • 79
  • 1
  • 1
  • 4
  • Please provide some code as well. So this will be a better reference for others. See [this](https://stackoverflow.com/help/minimal-reproducible-example) to understand why. – om-ha Dec 30 '19 at 13:32
  • how about marking the answer as accepted? – om-ha Jan 07 '20 at 08:38

6 Answers6

21

With WhitelistingTextInputFormatter deprecated

TextField(
 keyboardType: TextInputType.numberWithOptions(decimal: true),
 inputFormatters: <TextInputFormatter>[
      FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d*')),
  ], // Only numbers can be entered
),
Mathulan
  • 530
  • 5
  • 7
15

We can create our own TextInputFormatter.

Check this

import 'package:flutter/services.dart';

class DecimalTextInputFormatter extends TextInputFormatter {
  @override
  TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
    final regEx = RegExp(r"^\d*\.?\d*");
    String newString = regEx.stringMatch(newValue.text) ?? "";
    return newString == newValue.text ? newValue : oldValue;
  }
}

Usage:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: FirstPage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class FirstPage extends StatefulWidget {
  @override
  _FirstPageState createState() => _FirstPageState();
}

class _FirstPageState extends State<FirstPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: TextField(
          inputFormatters: [DecimalTextInputFormatter()],
        ),
      ),
    );
  }
}
Crazy Lazy Cat
  • 13,595
  • 4
  • 30
  • 54
5

Flutter 1.20 update

As the user @ZaH mentioned, WhitelistingTextInputFormatter has been deprecated as of Flutter 1.20, instead FilteringTextInputFormatter.allow() should be used. Check out ZaH's answer here and give em an upvote. You can find the docs for the class and constructor.

TextFormField(
    inputFormatters: [
        FilteringTextInputFormatter.allow(RegExp(r"\d+([\.]\d+)?")),
    ],
);

Solution

Here is what you need to do for your specific use case:

TextFormField(
    inputFormatters: [
        WhitelistingTextInputFormatter(RegExp(r"\d+([\.]\d+)?")),
    ],
);

Explanation

You need to use TextInputFormatter class. Specifically WhitelistingTextInputFormatter.

The above code only allows numbers of the pattern you provided in your question. Numbers with optionally any number of decimal digits with one decimal point allowed.

The r prefix before the string as in r"" makes the string a raw string. This prevents filtering and treatment of special characters within the string. From the docs:

Note the use of a raw string (a string prefixed with r) in the example above. Use a raw string to treat each character in a string as a literal character.

Regex Dissection

Here's a dissection of the regex pattern ^\d+([\.]\d+)?$:

  • \d digit -- allows digits from all languages.
  • + one or more occurrence.
  • (PATTERN)? zero or one occurrences -- this allows numbers without decimal dot/digits.
  • [\.] allows dot character -- the \ is used to escape it since it's a control character in regex.

Sources

om-ha
  • 3,102
  • 24
  • 37
  • 1
    Just as an additional note ```(PATTERN)?``` means zero or **one** ocurrence of ```(PATTERN)```. – Naslausky Sep 25 '21 at 13:12
  • 1
    Correct, `*` means **zero or more**, `?` means **zero or one**. `(PATTERN)?` is a correct expression to use here but the explanation is incorrect as you mentioned. Thanks I just updated it. – om-ha Sep 25 '21 at 15:28
  • `\.` is redundant and if we don't escape, decimal dot is not allowed. Seems like a wrong answer. – Shahood ul Hassan Nov 30 '22 at 04:30
  • Thanks for your comment @ShahoodulHassan If it's redundant then what part of the regex already allows the dot? – om-ha Nov 30 '22 at 10:07
  • Ah well, the Android Studio says it is redundant is what I meant. Removing it didn't serve the purpose either. `RegExp(r'^\d+\.?\d*'))` suggested by @Mathulan worked perfect. – Shahood ul Hassan Nov 30 '22 at 12:53
  • Weird, I just tried the regex on regexr.com. `123` and `12.3` are correct. `1..23` is incorrect (doesn't get a match). So my answer here should be correct. – om-ha Nov 30 '22 at 16:32
2

the answer from om-ha is correct, however for future reference, WhitelistingTextInputFormatter is deprecated in Flutter as of 1.20, now we should use FilteringTextInputFormatter.allow() instead.

or use FilteringTextInputFormatter.deny instead of BlacklistingTextInputFormatter if that is what you want.

above code should become:

   TextFormField(
     inputFormatters: [
       FilteringTextInputFormatter.allow(RegExp(r"\d+([\.]\d+)?")),
     ],
    );
Zah
  • 106
  • 5
  • Thanks for the update ZaH! I updated my answer accordingly & linked yours. https://stackoverflow.com/a/59531639/10830091 – om-ha Sep 25 '21 at 12:04
0

you can also try decimal validation on form submit.

bool isValidDecimal(String value){
    String regex = r'^[0-9]+[.]{1}[0-9]+$';
    RegExp regExp = RegExp(regex);
    return regExp.hasMatch(value);
  }
Vishal Zaveri
  • 1,372
  • 2
  • 5
  • 12
0

This will allow any number, using the locale specific decimal separator (afaik always either ',' or '.'). The intl package is used to get that separator.

import 'package:intl/intl.dart';
...
TextFormField(
  inputFormatters: [
    TextInputFormatter.withFunction((oldValue, newValue) {
      var decimalSeparator = NumberFormat().symbols.DECIMAL_SEP;
      var r = RegExp(r'^\d*(\' + decimalSeparator + r'\d*)?$');
      return r.hasMatch(newValue.text) ? newValue : oldValue;
    })
  ],
...

As an aside, the answers using FilteringTextInputFormatter will work to validate initial input but will show strange behavior when trying to edit a valid number to an invalid one - some characters will get overwritten, others deleted.

As per the docs:

Consider using a different TextInputFormatter ... for accepting/rejecting new input based on a predicate on the full string. As an example, FilteringTextInputFormatter typically shouldn't be used with RegExps that contain positional matchers (^ or $) since these patterns are usually meant for matching the whole string.

Ben Roberts
  • 199
  • 1
  • 8