Adriano Marqueze
Adriano Marqueze

Reputation: 41

TextFormField with mask text input formatter clear the content while changing the value

Im building a form with several fields and Im initializing their values with controllers. Some fields are pure text fields but others have a mask, like a phone and zipcode fields.

Once I put the field on focus and start to backspace to erase the last characters it works fine on the fields without a mask. Fields with a mask the entire content of the field is cleared.

I`m using Flutter 3 and the mask_text_input_formatter ^2.4.0.

Here is my code to reproduce the problem with two fields. If you run this code and put the focus on the name field you can backspace it and only the last characters will be cleared. But in the phone field this causes the entire field to be cleared.

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

final phoneMaskFormatter = MaskTextInputFormatter(mask: '(###) ### ####');

class Test extends StatefulWidget {
  const Test({Key? key}) : super(key: key);

  @override
  State<Test> createState() => _TestState();
}

class _TestState extends State<Test> {
  final _formKey = GlobalKey<FormState>();

  late TextEditingController _nameController;
  late TextEditingController _phoneController;

  var myData = {'name': 'William', 'phone': '(305) 786 1234'};

  @override
  void initState() {
    super.initState();
    _nameController = TextEditingController();
    _phoneController = TextEditingController();

    _nameController.text = myData['name']!;
    _phoneController.text = myData['phone']!;
  }

  @override
  void dispose() {
    super.dispose();
    _nameController.dispose();
    _phoneController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Form(
          key: _formKey,
          child: Container(
            padding: EdgeInsets.all(50),
            child: Column(
              children: [
                Text('Name: ${myData['name']}'),
                Text('Phone: ${myData['phone']}'),
                SizedBox(height: 100),
                TextFormField(
                    controller: _nameController,
                    onChanged: (value) {
                      // do my stuff
                      setState(() {
                        myData['name'] = value;
                      });
                    }),
                TextFormField(
                    controller: _phoneController,
                    inputFormatters: [phoneMaskFormatter],
                    onChanged: (value) {
                      // do my stuff
                      setState(() {
                        myData['phone'] = value;
                      });
                    }),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

The only thing that worked for me now is removing the mask in the phone field.

Upvotes: 3

Views: 3562

Answers (2)

Julia Koba
Julia Koba

Reputation: 11

I've found out a solution which might be suitable for you because I've had the same case. Just replace MaskTextInputFormatter with next PhoneInputFormatter.

class PhoneInputFormatter extends TextInputFormatter {
  @override
  TextEditingValue formatEditUpdate(
    TextEditingValue oldValue,
    TextEditingValue newValue,
  ) {
    // Only process when the user is entering text
    if (newValue.text.isNotEmpty) {
      // Remove all non-digit characters
      String newText = newValue.text.replaceAll(RegExp(r'\D'), '');

      // If the new text length is greater than 10 (including the area code), truncate it
      if (newText.length > 10) {
        newText = newText.substring(0, 10);
      }

      // Format the text according to the mask
      String formattedText = '';
      for (int i = 0; i < newText.length; i++) {
        if (i == 0) {
          formattedText += '(';
        } else if (i == 3) {
          formattedText += ') ';
        } else if (i == 6) {
          formattedText += '-';
        }
        formattedText += newText[i];
      }

      // Return the formatted text
      return newValue.copyWith(
        text: formattedText,
        selection: TextSelection.collapsed(offset: formattedText.length),
      );
    }

    // Return the unchanged value
    return newValue;
  }
}

Upvotes: 1

Khyati Modi
Khyati Modi

Reputation: 718

Use extended_masked_text: ^2.3.1 It is satisfying your requirements.

Declaration of _phoneController and than initialise same as you did.

MaskedTextController _phoneController =
      MaskedTextController(mask: '(000) 000 0000');

void initState() {
...
  _phoneController.text = myData['phone']!;
...
}

and remove inputFormatters: [numberController].

Upvotes: 0

Related Questions