Reputation: 41
Im building a form with several fields and I
m 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
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
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