Disabled space caused by validator in TextFormField - flutter

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.

Related

Set labelText color, but respect error validation color

I have a TextFormField with a labelText. I would like the labelText to be orange, unless when the form validation fails, then the text should be standard error red.
But if I put a TextStyle in the decoration options, and setting the color to orange, this text style overrides the standard validation color:
TextFormField(
decoration: InputDecoration(
labelText: 'I want to be orange',
labelStyle: TextStyle(color: Colors.orange),
),
validator: (value) {
if (value == null || value.isEmpty || value != 'test') {
return 'Validation error';
}
return null;
},
),
I can´t seem to find any properties on the TextFormField that supports this case. I also searched for a solution where I could set the TextStyle based on the status of the validation, but it seems like it is not possible to access the validation state inside the TextFormField.
One would think this is a pretty common case. What am I missing here? 🤔
You could save the labelColor inside a variable
Color labelColor = Colors.orange;
Inside your validate you can set the color to red
validator: (value) {
if (value == null || value.isEmpty || value != 'test') {
setState(() {
labelColor = Colors.red;
});
return 'Validation error';
}
return null;
},
Set the color as the labelColor
labelStyle: TextStyle(color: labelColor),

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.

controller and initial value in TextFormField do not work

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;

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),
),

How to handle external barcode scanner enter suffix added to scanned string in Flutter

I'm new to Flutter and I'm stuck trying to handle the scan in an Android smartphone that has a physical barcode scanner in it.
It scans a barcode and returns a string like "S3XAHKBC" but with a suffix "\n".
So I'm not talking about a software camera driven barcode reader.
In an Android application written in Java I would do something like:
myEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_ACTION_NEXT)) {
submit();
}
return false;
}
});
In Flutter I've tried everything I found in the documentation and this is what I'd like to get working:
TextFormField(
controller: barcodeStringController,
autofocus: true,
textCapitalization: TextCapitalization.characters,
style: TextStyle(fontSize: 20),
textInputAction: TextInputAction.done,
onFieldSubmitted: (v) => submit(),
);
Do anyone have experience with something like this?
You can use Textfield onChanged event or TextEditingController i guess .
https://flutter.dev/docs/cookbook/forms/text-field-changes
I've found a solution that works but isn't really nice
TextFormField(
controller: barcodeStringController,
textAlign: TextAlign.right,
autofocus: true,
textInputAction: TextInputAction.done,
textCapitalization: TextCapitalization.characters,
style: TextStyle(fontSize: 20),
keyboardType: TextInputType.text,
maxLines: null,
onFieldSubmitted: (value) => submit(), //to handle submit from keyboard
validator: (value){
//to handle submit from hardware barcode reader
if(value.contains('\n') || value.contains('\t')){
barcodeStringController.text = value.replaceAll('\n', '').replaceAll('\t', '');
submit();
}
return null;
},
);
After many attempts, I finally achieved the desired effect by listening to the TextField.onChanged method.
TextField(
keyboardType: TextInputType.multiline,
// maxLines !=1, if maxLines==1 can't type '\n', and onChanged can't capture ‘\n’
maxLines: null,
autofocus: true,
controller: state.ipController,
focusNode: state.ipFocusNode,
onChanged: (text) {
print('onChanged : -$text-');
if (text.endsWith('\n')) {
// remove '\n'
String filter = text.substring(0, text.length - 1);
// update
state.ipController.value = TextEditingValue(
text: filter,
selection: TextSelection.collapsed(offset: filter.length),
);
/// you can do something
......
}
},
),
if (Platform.isAndroid) {
return RawKeyboardListener(
focusNode: state.keyboardFocusNode,
onKey: (keyEvent) {
// key down
if (keyEvent.runtimeType.toString() == 'RawKeyDownEvent') {
RawKeyEventDataAndroid key = keyEvent.data;
if (isOnScan(key.keyCode)) {
if (!state.ipFocusNode.hasFocus) {
// focus
state.ipFocusNode.requestFocus();
}
// clear text
state.ipController.clear();
}
} else {
// key up
}
},
child: Scaffold(……),
);
}
// second method :
RawKeyboard.instance.addListener((RawKeyEvent keyEvent) {
if (keyEvent.runtimeType.toString() == 'RawKeyDownEvent') {
RawKeyEventDataAndroid key = keyEvent.data;
if (ScanUtils.isOnScan(key.keyCode)) {
if (!state.ipFocusNode.hasFocus) {
state.ipFocusNode.requestFocus();
}
state.ipController.clear();
}
} else { }
});
// left scan keyCode
static final int LEFT_SCAN_KEY = 132;
// right scan keyCode
static final int RIGHT_SCAN_KEY = 133;
// scan keyCode
static final int SCAN_KEY = 165;
// bluetooth scan keyCode (i'm not test)
static final int SCAN_KEY = 113;
static bool isOnScan(int keyCode) {
return keyCode == LEFT_SCAN_KEY ||
keyCode == RIGHT_SCAN_KEY ||
keyCode == SCAN_KEY ||
keyCode == BLUETOOTH_SCAN_KEY;
}