Flutter - Set Min/Max Value for TextField/TextFormField

This tutorial shows you how to create a TextField or TextFormField that accepts numeric values within an allowed range.

In Flutter, you can create a text field for inputting numbers by using a TextField or TextFormField. It's possible to make it accepts numeric only values by adding an appropriate TextInputFormatter. If you don't know yet, you can read our tutorial about how to create a text field that accepts numeric values only..

In many cases, there is a range for valid values. In other words, there is a minimum value and a maximum value that the users can input. Therefore, you need to customize the text field so that it doesn't accept values outside the range. If you have that requirement, you can read the examples below.

Set Max Length

If the maximum allowed value is based on the number of character length, you can use the maxLength argument of TextField or TextFormField.

  TextField(
    maxLength: 3,
    // Other arguments
  )

Create Custom Formatter

Flutter's TextField and TextFormField have an argument inputFormatters where you can pass a list of TextInputFormatters. A TextInputFormatter can be used to add validation and formatting when the value changes. You can create your own with your custom logic.

For this purpose, we are going to create a custom TextInputFormatter for limiting the allowed numeric values. If the value is not within the allowed range, we need to replace it with a valid value. If it's smaller than the minimum value, it will be set to the minimum value. If it's bigger than the maximum value, it will be set to the maximum value. In addition, we should not update the input if it's not parseable to a number.

To create a custom TextInputFormatter, it's required to override the formatEditUpdate method. It has two parameters of type TextEditingValue and also returns a TextEditingValue. The first parameter represents the old value, while the second represents the new value.

For setting the minimum and maximum values, we need to check the new value. If it's empty, just return it as-is (no need to validate). If it's not parseable (e.g. contains invalid characters), just return the old value. Next, check whether it's within allowed range. If it's smaller than the minimum value, just return the smallest valid number. If it's bigger than the maximum value, just return the biggest valid number. Otherwise, the value is valid.

The formatter below has configurable min and max values, so it can be reused in many text fields.

  class NumericRangeFormatter extends TextInputFormatter {

    final double min;
    final double max;

    NumericRangeFormatter({required this.min, required this.max});

    @override
    TextEditingValue formatEditUpdate(
      TextEditingValue oldValue,
      TextEditingValue newValue,
    ) {
      if (newValue.text.isEmpty) {
        return newValue;
      }

      final newValueNumber = double.tryParse(newValue.text);

      if (newValueNumber == null) {
        return oldValue;
      }

      if (newValueNumber < min) {
        return newValue.copyWith(text: min.toString());
      } else if (newValueNumber > max) {
        return newValue.copyWith(text: max.toString());
      } else {
        return newValue;
      }
    }
  }

Then, create an instance of the formatter and pass it as one of the input formatters.

  TextField(
    inputFormatters: [
      // Other formatters
      NumericRangeFormatter(min: 1, max: 100),
    ],
    // Other arguments
  )

Full Code

  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(
        title: 'Woolha.com Flutter Tutorial',
        home: Home(),
        debugShowCheckedModeBanner: false,
      );
    }
  }

  class Home extends StatefulWidget {
    const Home({super.key});

    @override
    State<StatefulWidget> createState() {
      return HomeState();
    }
  }

  class HomeState extends State<Home> {

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: const Text('Woolha.com Flutter Tutorial'),
          backgroundColor: Colors.teal,
        ),
        body: Padding(
          padding: const EdgeInsets.all(10),
          child: Column(
            children: [
              TextField(
                maxLength: 3,
                decoration: const InputDecoration(
                  border: UnderlineInputBorder(),
                  labelText: 'Score',
                  labelStyle: TextStyle(color: Colors.teal, fontSize: 20),
                ),
                keyboardType: TextInputType.number,
                inputFormatters: [
                  FilteringTextInputFormatter.digitsOnly,
                  // To allow decimal point and/or signed numbers, read our tutorial
                  // https://www.woolha.com/tutorials/flutter-create-number-only-textfield-textformfield
                  NumericRangeFormatter(min: 1, max: 100),
                ],
              ),
            ],
          ),
        ),
      );
    }
  }

  // See the code above for NumericRangeFormatter

Summary

If you only want to limit the length of the inputted numbers, setting the maxLength argument is enough. Otherwise, you may need to create and pass a custom TextInputFormatter. This tutorial provides an example of how to create a custom formatter that forces the inputted values to be always within a specified range.

You can also read about: