controller and initial value in TextFormField do not work - flutter

I use TextFormField to get stored value from Firestore and display it to user in it's (edit profile) page,with ability to change field value.
My question is:
how can i enable initial value(to get stored value) and controller to pick entered value by the user.
I read the document and i know i can't use both together,but is there an idea to allow textfield to get stored value and edit it in the same time?
TextFormField(
controller: phone_controller,
keyboardType: TextInputType.number,
decoration: InputDecoration(
icon: const Icon(Icons.phone),
hintText: phone,
// labelText: phone,
floatingLabelBehavior: FloatingLabelBehavior.never,
),
validator: (value) {
String pattern = r'(^(?:[+0]9)?[0-9]{10,12}$)';
RegExp regExp = new RegExp(pattern);
if (value.isEmpty || !regExp.hasMatch(value) ) {
return 'Please enter valid phone number like 01001234567';
}
return null;
},
)

In your initState, you can set the initial value of the controller.
#override
void initState() {
super.initState();
phone_controller.text = 'phone_no';
}
phone_controller now has a default value;

Related

How to generate uuid every time when user typing in a textfield?

How to generate uuid every time when user typing in a textfield and show the result in another textfield?
in order to generate an uuid you can use this package: https://pub.dev/packages/uuid
For generate unique id in Texfield use, the onChange event.
For update the second TextField set - TextFieldController and update the .text from the on Change event.
// Declare a text controller
var _myController = TextEditingController()..text = '';
...
// The first TextField
TextField(
textInputAction: TextInputAction.next,
onChanged: (value) {
var uuid = Uuid();
_myController.text = uuid;
}
},
keyboardType: TextInputType.text,
),
// Second TextField
TextField(
controller: _myController,
textInputAction: TextInputAction.next,
keyboardType: TextInputType.text,
)
This will generate a new unique id every time the user type a new character.
If you want to do the same on field validation juste change onChanged to onSubmitted.
You can generate uuid evey time on onChanged
TextField(
onChanged: (value) {
setState(() {
var uuid = Uuid();
});
},
)

Disabled space caused by validator in TextFormField

I got multiple TextFormField widgets. The problem is when the user doesn't meet some of the requirements while filling the form , some error message will pop under the TextFormField .
The problem is that the error text is causing too much space between the widgets.
How can I reduce the amount of that space ?
TextFormField buildConformPassFormField() {
return TextFormField(
obscureText: true,
onSaved: (newValue) => conform_password = newValue,
onChanged: (value) {
if (value.isNotEmpty) {
removeError(error: kPassNullError);
} else if (value.isNotEmpty && password == conform_password) {
removeError(error: kMatchPassError);
}
conform_password = value;
},
validator: (value) {
if (value!.isEmpty) {
addError(error: kPassNullError);
return ""; // THIS IS WHAT CAUSING THE WHITE SPACE.
} else if ((password != value)) {
addError(error: kMatchPassError);
return ""; // THIS IS WHAT CAUSING THE WHITE SPACE.
}
return null;
},
decoration: InputDecoration(
labelText: "Confirmez le mot de passe",
hintText: "Re-entrez le mot de passe",
// If you are using latest version of flutter then lable text and hint text shown like this
// if you r using flutter less then 1.20.* then maybe this is not working properly
floatingLabelBehavior: FloatingLabelBehavior.always,
suffixIcon: CustomSurffixIcon(svgIcon: "assets/icons/Lock.svg"),
),
);
}
Just return null, remove all return ""; from the validator
share screen shoot of current output when error show's.
in general, try to add errorStyle: TextStyle(height: 0.7), to InputDecoration.

Is it possible to display error message along with FilteringTextInputFormatter on Flutter

I have implemented TextField with FilteringTextInputFormatter as below to allow enter only alphanumeric characters. But at the same time I want to display error message if user tap on a denied character. Is is possible with FilteringTextInputFormatter?
I know its possible to just display error message with a simple regex validation, But I want both FilteringTextInputFormatter and error message together.
static final _alphaNumericCharacters = RegExp('[0-9a-zA-Z]');
TextField(
controller: _myController,
onChanged: provider.onChanged,
decoration: InputDecorations.buildErrorInputDecoration(
hintText:
'Please enter your ID',
setError: isInputError,
errorText:
'Only alphanumeric characters are allowed'),
keyboardType: TextInputType.text,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(_alphaNumericCharacters),
],
),
Define a bool value for checking input value.
bool checkDigit = true;
In TextFormField,
TextFormField(
decoration: InputDecoration(
errorText: checkDigit == false
? "You can write only numeric values"
: ""),
keyboardType: TextInputType.text,
onChanged: (value) {
if (!_isNumeric(value)) {
setState(() {
checkDigit = false;
});
} else {
setState(() {
checkDigit = true;
});
}
},
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(_alphaNumericCharacters),
],
),
isNumeric function detects parameter is a number or not. If the given value is number, return true otherwise false.
bool _isNumeric(String result) {
return double.tryParse(result) != null;
}
You can custom TextInputFormatter like this
class Decimal extends TextInputFormatter {
final Function(bool) onError;
Decimal({this.onError});
#override
TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
// allow only one of common decimal separator
bool match = RegExp(r'^[0-9]+([.,])+[0-9]+$').hasMatch(newValue.text);
onError(match);
return newValue;
}
}
Your text input should look like:
TextField(
...
errorText: _isError ? 'error' : null,
inputFormatters: [
Decimal(
onError: (match){
setState((){
// if not match, error is true, otherwise
_isError= !match;
});
}),
],
)
Worked in my case, only allow for valid decimal format. You can give it a try to custom for your own TextInputFormatter.

How to create a simple unit test in flutter for user input validation and maxLength with null safety

I am trying to do simple unit test for the form I have created. I referred some documents and YouTube videos to do that. But there are not many resources for unit testing in form and the available once are not compatible with current version (null safety).
Can any one explain me how I can do a unit test for my validation part and for maxLength in form field.
This is the code I tried but it is showing error and unable to continue.
class FullNameValidator{
static String? validate(String value){
return value.isEmpty ? 'Please enter full name' : null;
}
}
TextFormField(
decoration: InputDecoration(
labelText: "FullName",
labelStyle: TextStyle(color: Colors.black),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
maxLength: 30,
keyboardType: TextInputType.text,
controller: fullName,
validator: FullNameValidator.validate, //Showing error in here
),
The test I tried
void main(){
test('empty email returns error string', (){
var result = FullNameValidator.validate("");
expect(result, 'Please enter your full name');
});
}
Can anyone please explain how to do unit test for validation and maxLength.
I searched every document/ video I can, but unable to find a solution. Please help
It shows you this error because the validator property of a TextField is expecting a String? Function(String?) as its parameter and your FullNameValidator.validate is a String? Function(String), here's how you can fix that:
class FullNameValidator{
// Make your value nullable
static String? validate(String? value) {
// Now that value can be null you cannot be sure that isEmpty can be called
// so you need to provide a default value, in this case I am returning
// true if isEmpty cannot be called so it will return 'Please enter full name'.
return (value?.isEmpty ?? true) ? 'Please enter full name' : null;
}
}
You don't need to test maxLength as by precising a maxLength the String inside your TextField won't be able to be longer than the number you've defined.
If you really want to validate the length of your String you could change your method like this:
class FullNameValidator{
static String? validate(String? value, [int? maxLength]) {
if (value != null && maxLength != null) {
return value.length > maxLength ? 'This is too long' : null;
}
return (value?.isEmpty ?? true) ? 'Please enter full name' : null;
}
}
But then you would need to call your validator like this in your TextField:
TextFormField(
decoration: InputDecoration(
labelText: "FullName",
labelStyle: TextStyle(color: Colors.black),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
// maxLength: 30, // You don't need this property anymore if you rely on your validator
keyboardType: TextInputType.text,
controller: fullName,
validator: (value) => FullNameValidator.validate(value, 30),
),

Detect 'enter key' press in flutter

In my case i need to scan barcode and fetch product details. Normally barcode scanner devices emit enter key(keycode=13) event at end of scanning, But in flutter enter key is not same as Done so how can code to detect enter key pressed in my TextFormField widget?
if you are using TextField then you have to add onSubmitted in your text field to detect when user press Enter key. For my case, I changed Done in keyboard to TextInputAction.Search. It also works for TextInputAction.Done too. here is a sample code
TextField(
onSubmitted: (value){
//value is entered text after ENTER press
//you can also call any function here or make setState() to assign value to other variable
},
textInputAction: TextInputAction.search,
)
The solution above works, but I believe RawKeyboardListener is a more reliable and flexible solution. You just need to cover the text field with it and start to listen to keyboard events:
var focusNode = FocusNode();
RawKeyboardListener(
focusNode: focusNode,
onKey: (event) {
if (event.isKeyPressed(LogicalKeyboardKey.enter)) {
// Do something
}
},
child: TextField(controller: TextEditingController())
)
As a second option you can use onKey method of the FocusNoded and pass the node to your text field:
var focusNode = FocusNode(onKey: (node, event) {
if (event.isKeyPressed(LogicalKeyboardKey.enter)) {
// Do something
// Next 2 line needed If you don't want to update the text field with new line.
// node.unfocus();
// return true;
}
return false;
});
TextField(focusNode: focusNode, controller: TextEditingController())
In case someone is looking for the same solution (as Al Walid Ashik) but for TextFormField, just use the following:
TextFormField(
/// ...
onFieldSubmitted: (value) {
/// do some stuff here
},
),
TextFormField(
maxLines: null,
autovalidate: true,
validator: (value){
if(value.contains('\n')){
doFun(value);
}
}
)
When user press enter key new line create in text box. We check with that.
maxLine:null - to hide multiline
autovalidate:true -to automatically run validator fun
'\n' - new line ('\s'-whitespace,'\t'-tab.. etc)
In addition to the Sergey Yamshchikov's answer:
In case if it is a multiline TextField (maxLines: null) and you want to catch up the entered key and prevent passing it into the text field, you can use this approach:
RawKeyboardListener(
focusNode: FocusNode(onKey: (node, event) {
if (event.isKeyPressed(LogicalKeyboardKey.enter)) {
return KeyEventResult.handled; // prevent passing the event into the TextField
}
return KeyEventResult.ignored; // pass the event to the TextField
}),
onKey: (event) {
if (event.isKeyPressed(LogicalKeyboardKey.enter)) {
// Do something
}
},
child: TextField(controller: TextEditingController())
)
But, if you need to detect the keys combination, like ctrl+enter, then you can use CallbackShortcuts:
CallbackShortcuts(
bindings: {
const SingleActivator(LogicalKeyboardKey.enter, control: true): _doSomething(),
},
child: Focus(
autofocus: true,
child: TextField(controller: TextEditingController()),
),
);