Advanced TextField Customization (Input Formatters) in Flutter

TextFields are fundamental UI elements in Flutter applications, serving as the primary means for users to input text. Flutter provides a variety of customization options to enhance TextField functionality, including input formatters. Input formatters enable developers to control the format of text entered by users, ensuring that it conforms to specific requirements. In this comprehensive guide, we delve into advanced TextField customization using input formatters in Flutter.

What are Input Formatters?

Input formatters are classes that transform text as it is entered into a TextField. They can be used to enforce specific formatting rules, such as restricting input to numeric values, enforcing uppercase or lowercase, adding masks for phone numbers, or any other custom formatting requirement.

Why Use Input Formatters?

  • Data Validation: Enforce specific input patterns and validate user input in real-time.
  • Improved User Experience: Guide users by automatically formatting text as they type.
  • Data Consistency: Ensure data conforms to predefined standards and patterns.

How to Implement Input Formatters in Flutter

To implement input formatters, follow these steps:

Step 1: Create a TextEditingController

Associate a TextEditingController with your TextField to manage and control the text input:

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

class InputFormatterExample extends StatefulWidget {
  @override
  _InputFormatterExampleState createState() => _InputFormatterExampleState();
}

class _InputFormatterExampleState extends State {
  final TextEditingController _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Input Formatter Example'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: TextField(
          controller: _controller,
          decoration: InputDecoration(
            labelText: 'Enter text',
            border: OutlineInputBorder(),
          ),
          inputFormatters: [
            // Input formatters go here
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

Step 2: Implement Specific Input Formatters

Here are a few examples of common input formatters:

A. Numeric Input Formatter

Restricts input to numeric values:

FilteringTextInputFormatter.digitsOnly

Usage:

TextField(
  controller: _controller,
  keyboardType: TextInputType.number,
  decoration: InputDecoration(
    labelText: 'Enter numbers only',
    border: OutlineInputBorder(),
  ),
  inputFormatters: [
    FilteringTextInputFormatter.digitsOnly,
  ],
)
B. Uppercase Input Formatter

Transforms input to uppercase:

class UppercaseInputFormatter extends TextInputFormatter {
  @override
  TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
    return TextEditingValue(
      text: newValue.text.toUpperCase(),
      selection: newValue.selection,
    );
  }
}

Usage:

TextField(
  controller: _controller,
  decoration: InputDecoration(
    labelText: 'Enter uppercase text',
    border: OutlineInputBorder(),
  ),
  inputFormatters: [
    UppercaseInputFormatter(),
  ],
)
C. Lowercase Input Formatter

Transforms input to lowercase:

class LowercaseInputFormatter extends TextInputFormatter {
  @override
  TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
    return TextEditingValue(
      text: newValue.text.toLowerCase(),
      selection: newValue.selection,
    );
  }
}

Usage:

TextField(
  controller: _controller,
  decoration: InputDecoration(
    labelText: 'Enter lowercase text',
    border: OutlineInputBorder(),
  ),
  inputFormatters: [
    LowercaseInputFormatter(),
  ],
)
D. Masked Input Formatter (Phone Number)

Formats input to match a specific pattern, such as a phone number:

import 'package:mask_text_input_formatter/mask_text_input_formatter.dart';

final maskFormatter = MaskTextInputFormatter(
  mask: '(###) ###-####', 
  filter: { "#": RegExp(r'[0-9]') },
  type: MaskAutoCompletionType.lazy
);

Usage:

TextField(
  controller: _controller,
  keyboardType: TextInputType.phone,
  decoration: InputDecoration(
    labelText: 'Enter phone number',
    border: OutlineInputBorder(),
  ),
  inputFormatters: [
    maskFormatter,
  ],
)

Make sure to add mask_text_input_formatter to your pubspec.yaml:

dependencies:
  mask_text_input_formatter: ^3.0.0
E. Custom Formatting

For more complex formatting needs, you can create custom input formatters:

class CustomInputFormatter extends TextInputFormatter {
  @override
  TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
    String formattedText = newValue.text.replaceAll(RegExp(r'[^a-zA-Z0-9\s]'), ''); // Allow only alphanumeric and space
    return TextEditingValue(
      text: formattedText,
      selection: newValue.selection,
    );
  }
}

Usage:

TextField(
  controller: _controller,
  decoration: InputDecoration(
    labelText: 'Enter alphanumeric text',
    border: OutlineInputBorder(),
  ),
  inputFormatters: [
    CustomInputFormatter(),
  ],
)

Combining Multiple Input Formatters

You can combine multiple input formatters to enforce different rules simultaneously:

TextField(
  controller: _controller,
  keyboardType: TextInputType.number,
  decoration: InputDecoration(
    labelText: 'Enter positive numbers only',
    border: OutlineInputBorder(),
  ),
  inputFormatters: [
    FilteringTextInputFormatter.digitsOnly,
    FilteringTextInputFormatter.allow(RegExp(r'[1-9][0-9]*')), // Positive numbers only
  ],
)

Example: Currency Input Formatter

Let’s create a currency input formatter that formats the input as currency:

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

class CurrencyInputFormatter extends TextInputFormatter {
  @override
  TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
    String newText = newValue.text.replaceAll(RegExp(r'[^0-9]'), ''); // Allow only digits
    
    if (newText.isNotEmpty) {
      double value = double.parse(newText) / 100;
      final formatter = NumberFormat("#,##0.00", "en_US");
      newText = formatter.format(value);
    }

    return TextEditingValue(
      text: newText,
      selection: TextSelection.collapsed(offset: newText.length),
    );
  }
}

Usage:

TextField(
  controller: _controller,
  keyboardType: TextInputType.number,
  decoration: InputDecoration(
    labelText: 'Enter amount',
    prefixText: '\$ ',
    border: OutlineInputBorder(),
  ),
  inputFormatters: [
    CurrencyInputFormatter(),
  ],
)

Don’t forget to add the intl dependency to your pubspec.yaml file:

dependencies:
  intl: ^0.17.0

Conclusion

Advanced TextField customization using input formatters is a powerful technique in Flutter for validating and formatting user input. By leveraging input formatters, developers can enforce data consistency, improve user experience, and validate input in real-time. Whether it’s numeric input, uppercase conversion, or masked formatting, input formatters provide the flexibility to handle various formatting needs effectively.