Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement a text mask in Flutter

As described in the title, how to implement text mask in Flutter.

I am just writing a one below but not that suitable, since it can't display the mask to the user when typing, for example, when the text mask is 'MM/HH', if we do not display the slash during the user's typing, the user will typing the slash '/' themself, as they do not know the slash will auto-fill the input. There is a example here that can explain what am I said (It's a web implementation, not Flutter), so how to implement the mask-like in this link in Flutter?

This is my current implementation.

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

class MaskedTextInputFormatter extends TextInputFormatter {
  final String mask;
  final String separator;

  MaskedTextInputFormatter({
    @required this.mask,
    @required this.separator,
  }) {
    assert(mask != null);
    assert(separator != null);
  }

  @override
  TextEditingValue formatEditUpdate(
      TextEditingValue oldValue, TextEditingValue newValue) {
    if (newValue.text.length > 0) {
      if (newValue.text.length > oldValue.text.length) {
        if (newValue.text.length > mask.length) return oldValue;
        if (newValue.text.length < mask.length &&
            mask[newValue.text.length - 1] == separator) {
          return TextEditingValue(
            text:
                '${oldValue.text}$separator${newValue.text.substring(newValue.text.length - 1)}',
            selection: TextSelection.collapsed(
              offset: newValue.selection.end + 1,
            ),
          );
        }
      }
    }
    return newValue;
  }
}

Any help are appreciated!

PS: I searched a Flutter package mask_text_input_formatter, seems it also can't display the mask when typing, see issue.

like image 928
Kevin Zhang Avatar asked Nov 14 '22 20:11

Kevin Zhang


2 Answers

you can achieve text mask using mask_text_input_formatter package.

Create mask formatter:

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

Set it to text field:

TextField(inputFormatters: [maskFormatter])
Get value
Get masked text:

print(maskFormatter.getMaskedText()); // -> "+0 (123) 456-78-90"
Get unmasked text:

print(maskFormatter.getUnmaskedText()); // -> 01234567890

Change the mask You can use the updateMask method to change the mask after the formatter was created:

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("##-##-##-##"); // -> "12-34-56-78"
like image 164
Abhishek Chhabra Avatar answered Jan 24 '23 08:01

Abhishek Chhabra


try by adding extra condition for modifying text value

  1. add 1 extra condition.

    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    class MaskedTextInputFormatter extends TextInputFormatter {
      final String mask;
      final String separator;
    
      MaskedTextInputFormatter({
        @required this.mask,
        @required this.separator,
      }) {
        assert(mask != null);
        assert(separator != null);
      }
    
      @override
      TextEditingValue formatEditUpdate(
          TextEditingValue oldValue, TextEditingValue newValue) {
        if (newValue.text.length > 0) {
          if (newValue.text.length > oldValue.text.length) {
            if (newValue.text.length > mask.length) return oldValue;
            if (newValue.text.length < mask.length &&
                mask[newValue.text.length - 1] == separator &&
                newValue.text.substring(newValue.text.length - 1) != separator) {
              return TextEditingValue(
                text:
                    '${oldValue.text}$separator${newValue.text.substring(newValue.text.length - 1)}',
                selection: TextSelection.collapsed(
                  offset: newValue.selection.end + 1,
                ),
              );
            }
          }
        }
        return newValue;
      }
    }
  2. by using regex depending your need.

      FilteringTextInputFormatter(r"[A-za-z][A-Za-z]\/[A-za-z][A-Za-z]", allow: false);
    

OR

     FilteringTextInputFormatter(r"[A-za-z0-9][A-Za-z0-9]\/[A-za-z0-9][A-Za-z0-9]", allow: false);

example as below allow parameter is for if you want to allow particular pattern to be accepted or not

Textfield(
  label: "CardNumber",
  hint: "CardNumber",
  focusNode: cardNumberFocus,
  textInputFormatter: [
    MaskedTextInputFormatter(mask: 'xxxx xxxx xxxx xxxx', separator: ' '),
    FilteringTextInputFormatter(r"[A-za-z0-9][A-Za-z0-9]\/[A-za-z0-9][A-Za-z0-9]", allow: true);
  ],
);
like image 25
kishan vekariya Avatar answered Jan 24 '23 07:01

kishan vekariya