41

I want only two digits after the decimal point in the flutter input.User can't add more than two digits after the decimal point.

Federico Navarrete
  • 3,069
  • 5
  • 41
  • 76
Hitesh Danidhariya
  • 709
  • 1
  • 7
  • 14

16 Answers16

64

Here you go! Hope it helps :)

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'dart:math' as math;

void main() {
  runApp(new MaterialApp(home: new MyApp()));
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Flutter"),
      ),
      body: Form(
        child: ListView(
          children: <Widget>[
            TextFormField(
              inputFormatters: [DecimalTextInputFormatter(decimalRange: 2)],
              keyboardType: TextInputType.numberWithOptions(decimal: true),
            )
          ],
        ),
      ),
    );
  }
}

class DecimalTextInputFormatter extends TextInputFormatter {
  DecimalTextInputFormatter({this.decimalRange})
      : assert(decimalRange == null || decimalRange > 0);

  final int decimalRange;

  @override
  TextEditingValue formatEditUpdate(
    TextEditingValue oldValue, // unused.
    TextEditingValue newValue,
  ) {
    TextSelection newSelection = newValue.selection;
    String truncated = newValue.text;

    if (decimalRange != null) {
      String value = newValue.text;

      if (value.contains(".") &&
          value.substring(value.indexOf(".") + 1).length > decimalRange) {
        truncated = oldValue.text;
        newSelection = oldValue.selection;
      } else if (value == ".") {
        truncated = "0.";

        newSelection = newValue.selection.copyWith(
          baseOffset: math.min(truncated.length, truncated.length + 1),
          extentOffset: math.min(truncated.length, truncated.length + 1),
        );
      }

      return TextEditingValue(
        text: truncated,
        selection: newSelection,
        composing: TextRange.empty,
      );
    }
    return newValue;
  }
}
Ajay Kumar
  • 15,250
  • 14
  • 54
  • 53
  • 1
    @AjayKumar the code 'math.min(truncated.length, truncated.length + 1)' looks quite redundant. – thanhbinh84 Mar 30 '20 at 09:12
  • 3
    Problem with this is forcing a '.' doesn't support locales that swap periods and commas. – CodeGrue May 15 '20 at 20:30
  • @AjayKumar Thanks, worked for me. On some keyboards it is necessary to click the point to add a minus. In this class, you can enter minus after entering all values. I will add if I check this out. Thanks again. – Muhammet Ömer Sep 09 '20 at 10:32
  • If user add more numbers after decimal points removing not work until reach to first two numbers. – BeHappy Nov 12 '20 at 19:12
  • Hey, this won't work if the device uses a Locale that uses the comma `,` as decimal separator, like in Spanish – Roc Boronat Jun 30 '21 at 09:12
48

Here is a solution which works for me

TextFormField(
    inputFormatters: [
        WhitelistingTextInputFormatter(RegExp(r'^\d+\.?\d{0,2}')),
    ],
)

If you want to allow input like (.21) or (.25)

Here is a solution-

TextFormField(
    inputFormatters: [
        WhitelistingTextInputFormatter(RegExp(r'^(\d+)?\.?\d{0,2}')),
    ],
)
theboringdeveloper
  • 1,429
  • 13
  • 17
  • Your solution lets an user to input extra dots in the integer part – Nikssj_ Nov 02 '20 at 05:29
  • 1
    How to restrict the digits only for 4 before decimal @theboringdeveloper – hemandroid Mar 31 '21 at 09:24
  • 14
    Simplest solution, one thing to add, `WhitelistingTextInputFormatter` is deprecated use `FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,2}'))` instead. – joe_inz Sep 27 '21 at 14:39
  • This worked for me, note that services has to be imported with `import 'package:flutter/services.dart';` – nck Nov 01 '22 at 19:54
28

after Flutter Version v1.20.0-1.0. use

TextFormField(
  keyboardType: TextInputType.numberWithOptions(decimal: true),
  inputFormatters: [
    FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,2}')),
  ],
),
Nuqo
  • 3,793
  • 1
  • 25
  • 37
22

For Dart 2.00+

TextFormField(
  keyboardType: TextInputType.numberWithOptions(decimal: true),
  inputFormatters: [
    // Allow Decimal Number With Precision of 2 Only
    FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d{0,2}')),
  ],

It allows decimals like 1., .89, 8.99

JT501
  • 1,407
  • 15
  • 12
9

A shorter Version of DecimalTextInputFormatter using Regexp:

class DecimalTextInputFormatter extends TextInputFormatter {

  DecimalTextInputFormatter({int decimalRange, bool activatedNegativeValues})
  : assert(decimalRange == null || decimalRange >= 0,
    'DecimalTextInputFormatter declaretion error') {
    String dp = (decimalRange != null && decimalRange > 0) ? "([.][0-9]{0,$decimalRange}){0,1}" : "";
    String num = "[0-9]*$dp";

    if(activatedNegativeValues) {
      _exp = new RegExp("^((((-){0,1})|((-){0,1}[0-9]$num))){0,1}\$");
    }
    else {
      _exp = new RegExp("^($num){0,1}\$");
    }
  }

  RegExp _exp;

  @override
  TextEditingValue formatEditUpdate(
    TextEditingValue oldValue,
    TextEditingValue newValue,
  ) {
    if(_exp.hasMatch(newValue.text)){
      return newValue;
    }
    return oldValue;
  }
}
tvw
  • 316
  • 3
  • 4
7

Dart SDK version: 2.13.4 (stable) Flutter 2.2.3 • channel stable

i'm writing this answer on July 25, 2021, suggesting a little bit simpler solution, only using built-in TextField's inputFormatters.

i'm trying to ensure all of you, that the field won't accept floating point longer than 2(accepted: 12.25 vs notAccepted: 65.536). And also, it won't accept multiple dots, only accept a single dot(accepted: 12.25 vs notAccepted: 1.1.11, 1.11, .1.1, 1.1.1.1, whatsoever..).

The difference considering the other answers is that the code below won't accept .1 which is equal to 0.1 programmatically, which is actually way more user-friendly. And it is simple, nice to look at for us. You can just copy and paste the code below into inputFormatters: [].

If you want to accept both 0.1 and .1(not only 0.1), you can just comment out FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d*'))!

inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'^(\d+)?\.?\d{0,2}')), FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d*')), ]

When you want to change the maximum decimal point, you can change the N value in the line below:

inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'^(\d+)?\.?\d{0,N}')), FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d*')), ]

First example shows the case in which you want to limit decimal point to 2, and the N value exactly controls the limit. You might try within your own code [:

Appreciate all the other answers, hope this answer might help future wanderers! Have a wonderful day [:

tsitixe
  • 1,851
  • 3
  • 14
  • 24
4

May be this is a simpler solution

inputFormatters: [
            FilteringTextInputFormatter.allow(RegExp(r'(^\d*\.?\d{0,2})'))
          ]
Saurabh
  • 81
  • 4
2

Maybe a little late for you, but I also improved a little:

  1. Allow only 1 .
  2. Allow negative
  3. Place negative sign on beginning

Hope it helps ;)

import 'package:flutter/services.dart';

class NumberTextInputFormatter extends TextInputFormatter {
  NumberTextInputFormatter({this.decimalRange}) : assert(decimalRange == null || decimalRange > 0);

  final int decimalRange;

  @override
  TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
    TextEditingValue _newValue = this.sanitize(newValue);
    String text = _newValue.text;

    if (decimalRange == null) {
      return _newValue;
    }

    if (text == '.') {
      return TextEditingValue(
        text: '0.',
        selection: _newValue.selection.copyWith(baseOffset: 2, extentOffset: 2),
        composing: TextRange.empty,
      );
    }

    return this.isValid(text) ? _newValue : oldValue;
  }

  bool isValid(String text) {
    int dots = '.'.allMatches(text).length;

    if (dots == 0) {
      return true;
    }

    if (dots > 1) {
      return false;
    }

    return text.substring(text.indexOf('.') + 1).length <= decimalRange;
  }

  TextEditingValue sanitize(TextEditingValue value) {
    if (false == value.text.contains('-')) {
      return value;
    }

    String text = '-' + value.text.replaceAll('-', '');

    return TextEditingValue(text: text, selection: value.selection, composing: TextRange.empty);
  }
}

and (don't forget to import the previous class)

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class NumberFormField extends StatelessWidget {
  final InputDecoration decoration;
  final TextEditingController controller;
  final int decimalRange;

  const NumberFormField({Key key, this.decoration, this.controller, this.decimalRange}) :super(key: key);

  @override
  Widget build(BuildContext context) {
    return TextFormField(
      decoration: this.decoration,
      controller: this.controller,
      keyboardType: TextInputType.numberWithOptions(decimal: true),
      inputFormatters: [
        WhitelistingTextInputFormatter(RegExp(r'[\d+\-\.]')),
        NumberTextInputFormatter(decimalRange: this.decimalRange),
      ],
    );
  }
}
Thiago Cordeiro
  • 589
  • 3
  • 9
1

I extended the functionalities ... I hope you can find it useful. Tell me if you can improve even more please.

import 'package:flutter/services.dart';
import 'dart:math' as math;

class DecimalTextInputFormatter extends TextInputFormatter {
  DecimalTextInputFormatter({this.decimalRange, this.activatedNegativeValues})
      : assert(decimalRange == null || decimalRange >= 0,
            'DecimalTextInputFormatter declaretion error');

  final int decimalRange;
  final bool activatedNegativeValues;

  @override
  TextEditingValue formatEditUpdate(
    TextEditingValue oldValue, // unused.
    TextEditingValue newValue,
  ) {
    TextSelection newSelection = newValue.selection;
    String truncated = newValue.text;


if (newValue.text.contains(' ')) {
  return oldValue;
}

if (newValue.text.isEmpty) {
  return newValue;
} else if (double.tryParse(newValue.text) == null &&
    !(newValue.text.length == 1 &&
        (activatedNegativeValues == true ||
            activatedNegativeValues == null) &&
        newValue.text == '-')) {
  return oldValue;
}

if (activatedNegativeValues == false &&
    double.tryParse(newValue.text) < 0) {
  return oldValue;
}

if (decimalRange != null) {
  String value = newValue.text;

  if (decimalRange == 0 && value.contains(".")) {
    truncated = oldValue.text;
    newSelection = oldValue.selection;
  }

  if (value.contains(".") &&
      value.substring(value.indexOf(".") + 1).length > decimalRange) {
    truncated = oldValue.text;
    newSelection = oldValue.selection;
  } else if (value == ".") {
    truncated = "0.";

    newSelection = newValue.selection.copyWith(
      baseOffset: math.min(truncated.length, truncated.length + 1),
      extentOffset: math.min(truncated.length, truncated.length + 1),
    );
  }

  return TextEditingValue(
    text: truncated,
    selection: newSelection,
    composing: TextRange.empty,
  );
}
return newValue;
}
}
DomDunk
  • 837
  • 6
  • 11
  • the solutions here are pretty good. i think that it would make it even better if the comma is included when formatting in the thousands. – chitgoks Jun 10 '20 at 13:24
  • Hi Sir! Its possible to add a Number format to your code with decimalRange max 2? I want to achieve this: "100.25" "1,000.25" "10,000.25" – Nikssj_ Nov 02 '20 at 04:52
1

If you are looking for a regexp to handle comma and dot with 2 decimals try this version:

TextFormField(
              keyboardType: const TextInputType.numberWithOptions(decimal: true),
              decoration: textFieldDecoration("Price with only 2 decimals and handling comma"),
              inputFormatters: [
                FilteringTextInputFormatter.allow(RegExp(r'(^\d*[\.\,]?\d{0,2})')),
              ],
            );
Large
  • 587
  • 4
  • 16
0

The answer by @AjayKumar allows to limit text input to required decimal places.But my requirement was to avoid another character from keyboard except the number and a dot.So, I updated @AjayKumar's above answer

import 'package:flutter/services.dart';
import 'dart:math' as math;   

class DecimalTextInputFormatter extends TextInputFormatter {
DecimalTextInputFormatter({this.decimalRange})
  : assert(decimalRange == null || decimalRange > 0);

final int decimalRange;

@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, // unused.
TextEditingValue newValue,
) {
TextSelection newSelection = newValue.selection;
String truncated = newValue.text;

if (decimalRange != null) {
  String value = newValue.text;

  if (value.contains(',') ||
      value.contains('-') ||
      value.contains(' ') ||
      value.contains('..')) {
    truncated = oldValue.text;
    newSelection = oldValue.selection;
  } else if (value.contains(".") &&
      value.substring(value.indexOf(".") + 1).length > decimalRange) {
    truncated = oldValue.text;
    newSelection = oldValue.selection;
  } else if (value == ".") {
    truncated = "0.";

    newSelection = newValue.selection.copyWith(
      baseOffset: math.min(truncated.length, truncated.length + 1),
      extentOffset: math.min(truncated.length, truncated.length + 1),
    );
  }

  return TextEditingValue(
    text: truncated,
    selection: newSelection,
    composing: TextRange.empty,
  );
}
return newValue;

} }

Rakesh Verma
  • 766
  • 6
  • 14
0

And avoiding non necessary zeros... Please debug this code.

import 'dart:math' as math;

import 'package:flutter/services.dart';

class DecimalTextInputFormatter extends TextInputFormatter {
  DecimalTextInputFormatter({this.decimalRange, this.activatedNegativeValues})
      : assert(decimalRange == null || decimalRange >= 0,
            'DecimalTextInputFormatter declaretion error');

  final int decimalRange;
  final bool activatedNegativeValues;

  @override
  TextEditingValue formatEditUpdate(
    TextEditingValue oldValue, // unused.
    TextEditingValue newValue,
  ) {
    TextSelection newSelection = newValue.selection;
    String truncated = newValue.text;

    if (newValue.text.contains(' ')) {
      return oldValue;
    }

    if (newValue.text.isEmpty) {
      return newValue;
    } else if (double.tryParse(newValue.text) == null &&
        !(newValue.text.length == 1 &&
            (activatedNegativeValues == true ||
                activatedNegativeValues == null) &&
            newValue.text == '-')) {
      return oldValue;
    }

    if (activatedNegativeValues == false &&
        double.tryParse(newValue.text) < 0) {
      return oldValue;
    }

    if ((double.tryParse(oldValue.text) == 0 && !newValue.text.contains('.'))) {
      if (newValue.text.length >= oldValue.text.length) {
        return oldValue;
      }
    }

    if (decimalRange != null) {
      String value = newValue.text;

      if (decimalRange == 0 && value.contains(".")) {
        truncated = oldValue.text;
        newSelection = oldValue.selection;
      }

      if (value.contains(".") &&
          value.substring(value.indexOf(".") + 1).length > decimalRange) {
        truncated = oldValue.text;
        newSelection = oldValue.selection;
      } else if (value == ".") {
        truncated = "0.";

        newSelection = newValue.selection.copyWith(
          baseOffset: math.min(truncated.length, truncated.length + 1),
          extentOffset: math.min(truncated.length, truncated.length + 1),
        );
      }

      return TextEditingValue(
        text: truncated,
        selection: newSelection,
        composing: TextRange.empty,
      );
    }
    return newValue;
  }
}
DomDunk
  • 837
  • 6
  • 11
0

For TextFeild in Flutter with Before decimal & After decimal length validation class.

import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'dart:math' as math;

class DecimalTextInputFormatter extends TextInputFormatter {
  DecimalTextInputFormatter({this.decimalRange,this.beforeDecimalRange})
      : assert(decimalRange == null || decimalRange > 0 || beforeDecimalRange == null || beforeDecimalRange > 0 );

  final int decimalRange;
  final int beforeDecimalRange;

  @override
  TextEditingValue formatEditUpdate(
      TextEditingValue oldValue, // unused.
      TextEditingValue newValue,
      ) {
    TextSelection newSelection = newValue.selection;
    String truncated = newValue.text;

    String value;

    if(beforeDecimalRange != null){
      value = newValue.text;

      if(value.contains(".")){
        if(value.split(".")[0].length > beforeDecimalRange){
          truncated = oldValue.text;
          newSelection = oldValue.selection;
        }
      }else{
        if(value.length > beforeDecimalRange){
          truncated = oldValue.text;
          newSelection = oldValue.selection;
        }
      }
    }

    if (decimalRange != null) {
      value = newValue.text;

      if (value.contains(".") &&
          value.substring(value.indexOf(".") + 1).length > decimalRange) {
        truncated = oldValue.text;
        newSelection = oldValue.selection;
      } else if (value == ".") {
        truncated = "0.";

        newSelection = newValue.selection.copyWith(
          baseOffset: math.min(truncated.length, truncated.length + 1),
          extentOffset: math.min(truncated.length, truncated.length + 1),
        );
      }

      return TextEditingValue(
        text: truncated,
        selection: newSelection,
        composing: TextRange.empty,
      );
    }
    return newValue;
  }
}    

In TextFeild , Ex. Before decimal 9 digit & after Decimal 2 digit allowed code below.

inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,2}')),
                   DecimalTextInputFormatter(decimalRange: 2, beforeDecimalRange: 9)
                 ],
keyboardType: TextInputType.numberWithOptions(decimal: true),
0

This worked for me (Feb/2022)

Restricts the text to 2 decimal point and have only 1 '.' (period)

0 will be automatically added to the start of the number if user go and manually remove the digit before the '.'

PS: A Slight edit to the answer

TextFormField

TextFormField(
          autofocus: false,
          keyboardType: TextInputType.number,
          controller: controller,
          inputFormatters: [
            DecimalTextInputFormatter(decimalRange: 2),
          ],
          validator: (value) {
            if (value == null || value.isEmpty) {
              return 'Please add amount.';
            }
            return null;
          },

DecimalTextInputFormatter

import 'package:flutter/services.dart';
import 'dart:math' as math;

class DecimalTextInputFormatter extends TextInputFormatter {
  DecimalTextInputFormatter({required this.decimalRange})
      : assert(decimalRange > 0);

  final int decimalRange;

  @override
  TextEditingValue formatEditUpdate(
    TextEditingValue oldValue,
    TextEditingValue newValue,
  ) {
    TextSelection newSelection = newValue.selection;
    String truncated = newValue.text;

    String value = newValue.text;

    if (value.contains(".") &&
        value.substring(value.indexOf(".") + 1).length > decimalRange) {
      truncated = oldValue.text;
      newSelection = oldValue.selection;
    } else if (value == ".") {
      truncated = "0.";

      newSelection = newValue.selection.copyWith(
        baseOffset: math.min(truncated.length, truncated.length + 1),
        extentOffset: math.min(truncated.length, truncated.length + 1),
      );
    } else if (value.contains(".")) {
      String tempValue = value.substring(value.indexOf(".") + 1);
      if (tempValue.contains(".")) {
        truncated = oldValue.text;
        newSelection = oldValue.selection;
      }
      if (value.indexOf(".") == 0) {
        truncated = "0" + truncated;
        newSelection = newValue.selection.copyWith(
          baseOffset: math.min(truncated.length, truncated.length + 1),
          extentOffset: math.min(truncated.length, truncated.length + 1),
        );
      }
    }
    if (value.contains(" ") || value.contains("-")) {
      truncated = oldValue.text;
      newSelection = oldValue.selection;
    }

    return TextEditingValue(
      text: truncated,
      selection: newSelection,
      composing: TextRange.empty,
    );
  }
}
0
 keyboardType: TextInputType.numberWithOptions(decimal: true),
 inputFormatters: [LengthLimitingTextInputFormatter(5)],

This works for me.

Nandan Wewhare
  • 445
  • 5
  • 11
-1

I think this is the most simplest and only way working for me:

inputFormatters: [ LengthLimitingTextInputFormatter(2), ]

Zeeshan Ahmad
  • 606
  • 5
  • 19
  • 3
    The question was to restrict user input of more than two "decimal digits". The given code restricts input of more than 2 characters. Totally different things. – Abhimanyu Apr 04 '21 at 14:32