Flutter add separator character after given interval in TextFormField

59 Views Asked by At

I want have a TextFormField that formats the input depending on a given separator and interval.

E.g. separator = '-' and interval = 4 the output should be the following:

Input: 1234 Output: 1234

Input 12345678 Output: 1234-5678

Input: 12345678910 Output: 1234-5678-910

The separator should be inserted right when the user types in the 5th character in this example.

I tried multiple things now, but can not get a general working solution. That was my last try:

  void _addSeperator(String value) {
    final separator = widget.separator!;
    final separatorInterval = widget.separatorInterval!;

    value = value.replaceAll(separator, ''); // Remove existing separators
    int length = value.length;
    int separatorCount = (length / separatorInterval).floor();
    for (int i = 1; i <= separatorCount; i++) {
      int index = i * separatorInterval +
          (i - 1); // Calculate the index to insert separator
      if (index < length) {
        value = value.substring(0, index) +
            separator +
            value.substring(index, value.length);
      }
    }

    _controller.text = value;
  }

I thought this shouldnt be that hard, but I can not make it work. Also didn't find anything on this. Let me know if you need more info.

3

There are 3 best solutions below

0
Chris On BEST ANSWER

I got it working with this:

class UISeparatorInputFormatOptions {
  final String separator;
  final int interval;

  UISeparatorInputFormatOptions({
    required this.separator,
    required this.interval,
  });
}

class UISeparatorInputFormatter extends TextInputFormatter {
  /// IMPORTANT: String must not contain the options.separator
  /// otherwise this InputFormatter will not work correctly.
  final UISeparatorInputFormatOptions options;

  UISeparatorInputFormatter({
    required this.options,
  });

  @override
  TextEditingValue formatEditUpdate(
    TextEditingValue oldValue,
    TextEditingValue newValue,
  ) {
    // Remove existing separators
    String newText = newValue.text.replaceAll(
      options.separator,
      '',
    );

    StringBuffer buffer = StringBuffer();
    for (int i = 0; i < newText.length; i++) {
      if (i > 0 && i % options.interval == 0) {
        buffer.write(options.separator);
      }
      buffer.write(newText[i]);
    }

    String formattedString = buffer.toString();
    return newValue.copyWith(
      text: formattedString,
      // This ensures that the cursor follows the text properly
      selection: TextSelection.collapsed(offset: formattedString.length),
    );
  }
}
0
Charles On

It looks like you are trying to format credit card numbers. Flutter already comes with 2 handful formatter that you could use, FilteringTextInputFormatter and LengthLimitingTextInputFormatter. You could also extends TextInputFormatter and implement your custom formatting logic.

Below is a simple example.

FilteringTextInputFormatter.allow(RegExp(r'\d')) only allows digits.
LengthLimitingTextInputFormatter(16) set the max input length to 16.
CreditCardNumberFormatter is a custom formatter that adds a separator between every 4 digits.

TextFormField(
  inputFormatters: [
    FilteringTextInputFormatter.allow(RegExp(r'\d')),
    LengthLimitingTextInputFormatter(16),
    const CreditCardNumberFormatter(),
  ],
  ...
),

class CreditCardNumberFormatter extends TextInputFormatter {
  const CreditCardNumberFormatter({
    this.separator = '-',
    this.interval = 4,
  });

  final String separator;
  final int interval;

  @override
  TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
    final text = newValue.text;
    final buffer = StringBuffer();
    for (var i = 0; i < text.length; i++) {
      if (i % interval == 0 && i != 0) {
        buffer.write(separator);
      }
      buffer.write(text[i]);
    }

    return TextEditingValue(
      text: buffer.toString(),
      selection: TextSelection.collapsed(offset: buffer.length),
    );
  }
}
2
Manoj Padia On

You can use the very popular library mask_text_input_formatter to format the text within a specific format. See its documentation on how to use it.

var textEditingController = TextEditingController(text: "12345678");
var maskFormatter = new MaskTextInputFormatter(mask: '####-####', filter: { "#": RegExp(r'[0-9]') });

TextField(controller: textEditingController, inputFormatters: [maskFormatter])  // -> "1234-5678"

textEditingController.value = maskFormatter.updateMask(mask: "##-##-##-##"); // -> "12-34-56-78"