I am trying to achieve the best UX on Flutter using AutovalidateMode.onUserInteraction.
Option 1
When I set the validation on the form key, when a user tries to type one one texField the whole form goes red with errors.
child: Form(
key: _formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
Option 2
When I set AutovalidateMode.onUserInteraction on each text field I end up getting the error on each textfield when the user tries to type and it disappears when they complete the input.
RoundedPasswordResetField(
validator: (value) {
if (value.isEmpty) {
return 'Please Re-enter Password';
}
if (password != confirmpassword) {
return "Password does not match";
}
//
return null;
},
autovalidateMode: AutovalidateMode.onUserInteraction,
hintText: "Please Re-enter Password",
onChanged: (value) {
confirmpassword = value;
},
),
Option 3
When I set it to autovalidateMode: AutovalidateMode.disabled,, when the user tries to submit the fields when they are empty, the errors appear, but when they key in the specific correct details the errors don't clear out
RoundedPasswordResetField(
validator: (value) {
if (value.isEmpty) {
return 'Please Re-enter Password';
}
if (password != confirmpassword) {
return "Password does not match";
}
//
return null;
},
autovalidateMode: AutovalidateMode.disabled,
hintText: "Please Re-enter Password",
onChanged: (value) {
confirmpassword = value;
},
),
How can I use setState, so that when the user starts typing the errors wont be shown immediately, and shown when they complete inputting wrong details - and achieve the best UX?
This is something I implemented in one of my Android projects two years ago. Although, it was Kotlin, still I opened my Github and looked for it.
First, let's understand your question: The question says that Autovalidate mode shouldn't be there or should be left to default (AutovalidateMode.disabled) to not do the validation whenever user starts typing which is obviously a bad UX. But, it should work only after the user stops with enough delay to consider it as completion of typing which means whenever there is a brief stoppage in the input data stream.
Now, for this, PublishProcessor.debounce() of RxJava is a good option in Kotlin, which can be used in dart using packages like rxdart. What it does is it prevents continuous execution of a function and only executes the function after a certain delay, if the function is triggered again, it resets the delay. So, the funtion executes only once and that too, only after a certain delay imitating the effect of user stopped typing.
Use this - Just Debounce It package, it's not as over-kill as RxDart but still, there are many alternatives.
Implement it as
void function validateForm() {
Debounce.seconds(2, //2 means run this function only after 2 second delay.
() => formKey.currentState.validate()) //This is a function passed as a Callback which is executed only when it crosses the delay.
}
And call this function from onChanged as:
onChanged: (val) => validateForm()
That's it. Now, your form will only be validated if the user stops typing. This is the go-to way, not just good UX but good logic. There are hacks to make this work with booleans and postDelayed but that's bad code.
Related
I am trying to make a otp field which I can fill manually and validate later. I am using the Pinput package in flutter as it is quite popular. I am facing following problems-
The pin does not start typing unless I enter a . / , in the first field
The Pinput() fields are cleared as soon as I enter the last digit.
How to use this pin and validate it later?
My code is-
Pinput(
length: 6,
keyboardType: TextInputType.number,
controller: pinController,
defaultPinTheme: defaultPinTheme,
focusedPinTheme: focusedPinTheme,
submittedPinTheme: submittedPinTheme,
pinputAutovalidateMode: null,
textInputAction: TextInputAction.next,
showCursor: true,
validator: (s) {
print('validating code: $s');
},
onCompleted: null,
),
Please help!!
Use onCompleted like this not null
onCompleted: (pin) => print(pin),
This onCompleted method do like if entering input got finish do something like navigation or check thing
When you entered last digit this will print
You need to use regex to validate just search and get what you need
// In validator you can check
Int validDigit = 1234; // this is test digit
Validator: (input){
return s == validDigit ? null : 'Pin is incorrect';
}
Validator works like if your input digit was not like the pattern you need, in this case if the input digit was not equal to the code that you send to user, you need to return error otherwise return null it means code is correct.
in above e.g the validDigit comes from api or somewhere else
And finally set auto validate like here not null:
pinputAutovalidateMode: PinputAutovalidateMode.onSubmit,
I hope it works, sorry they are not in order of your question.
When saving data to the Firebase database I want every data to be saved according to its data type. But my all data is stored in String. How can I do it in flutter... like Amount will be int, Through will be String
[
here is my function
sandDataToDB() async {
CollectionReference _collectionReferance =
FirebaseFirestore.instance.collection("Use-of-fund");
return _collectionReferance
.doc()
.set({
"Details of Sector": _DetailsofSectorController.text,
"Through": _ThroughController.text,
"Amount": _AmountController.text,
"Date": _DateController.text,
})
.then((value) => dialog())
.catchError((error) => Fluttertoast.showToast(msg: "something wrong"));
}
here is all code
customAdminTextField(
"Details of Sector", _DetailsofSectorController),
customAdminTextField("Through", _ThroughController),
customAdminTextField("Amount", _AmountController),
customAdminTextField("Date", _DateController),
const SizedBox(
height: 10,
),
customSubmitButton("Submit ", () {
sandDataToDB();
})
You have to parse your data. An example for an integer:
...
"Amount": int.parse(_AmountController.text),
...
Here you can find the supported data types.
According to your question, you might be using the controller in TextFormField, you can get the value of the TextFormField.
This widget covers a TextField widget in a FormField for convenience.
It is not necessary to have a Form ancestor. The Form merely simplifies the process of saving, resetting, or validating numerous fields at the same time. To use without a Form, supply a GlobalKey to the constructor and save or reset the form field with GlobalKey.currentState.
TextEditingController.text defines the initialValue when a controller is defined. A controller should be given if this FormField is part of a scrolling container that generates its children lazily, such as a ListView or a CustomScrollView.
A stateful widget ancestor of the scrolling container should handle the controller's lifetime.
Example code:
TextFormField(
decoration: const InputDecoration(
icon: Icon(Icons.person),
hintText: 'What do people call you?',
labelText: 'Name *',
),
onSaved: (String? value) {
// This optional block of code can be used to run
// code when the user saves the form.
},
validator: (String? value) {
return (value != null && value.contains('#')) ? 'Do not use the # char.' : null;
},
)
You can also use the onSubmitted parameter in TextFormField. Like: onSubmitted: (String value)
Additionally, you can use something like this in the following code:
_formKey.currentState.save(); calls the onSaved() on each textFormField item, which gives all the fields a value and allows you to utilize them as needed.
TextFormField(
obscureText: true,
decoration: InputDecoration(
hintText: "Enter password",
labelText: "Password",
),
validator: (value) {
if (value.isEmpty) {
return "Password can't be empty";
}
null;
},
),
I am watching a tutorial of flutter and i know its too basic but seems like , I can't fix this error I tried to search it but there is no proper guide to fix can someone help me in fixing this
when its working fine on tutorial but not for me , and i don't get it yet its showing The property 'isEmpty' can't be unconditionally accessed because the receiver can be 'null'. Try making the access conditional (using '?.') or adding a null check to the target ('!').
this error
This is because value is of type String? and can be null. In you case you have to check if value is null.
TextFormField(
obscureText: true,
decoration: InputDecoration(
hintText: "Enter password",
labelText: "Password",
),
validator: (String? value) {
if (value!.isEmpty) {
return "Password can't be empty";
}
null;
},
),
Welcome to stackoverflow and flutter. Recently flutter has been updated to flutter 2.0, which has a great feature called null safety. It is a little annoying to get started with, but has huge benefits I am sure you can find better listed elsewhere. Since null safety has recently come out, its not in most tutorials. Its job is to ensure that whenever there is a chance for a value to be null, it asks you to either say
A. Check if the value is becoming null. If it is, give an error and cancel the function
B. Allow the value to be null because you are going to be keeping track.\
In your case, the variable value in the onchanged method may be null because the flutter people who wrote it, used the B part mentioned above for their internal building. So, what you have to do is, the A part, that is checking if the value is null or not. You can simply do that with the help of an !. Now, you have to be careful on how to use this. Whenever a variable can be null and you have to perform a function on it or assign it to a variable which cannot be null, you just put an exclamation mark after it. Like value!\
I would recommend watching a good video on null safety in flutter and learn about it since I doubt you will find a good tutorial as of now that includes null safety (except the docs ofc). if you need any help please feel free to leave a comment and I'll be happy to help in any way possible
The method 'validate' was called on null.
Receiver: null
Tried calling: validate()
I don't understand this. I thought maybe the problem was the Form isn't the root element of the class, it's not return Form(child: Column(children: [... So I tried making the Form Widget the root, it stopped the error, but didn't activate the TextFormField validator or save, it just said 'everything fine, move along'.
It's just one field I presently wish to validate. I've looked up other such queries, both the Form widget & the TextFormField have keys, so I'm stuck.
I declare the form key with final _formKeyForDeposit = GlobalKey<FormState>();
And here is the un-cooperative form:
Form(key: _formKeyForDeposit, child:
TextFormField(
controller: _controllerDefaultDeposit,
key: Key('defaultLoanDeposit'),
decoration: InputDecoration(
//icon: Icon(Icons.location_city),
labelText: 'Per item deposit',
hintText: 'Whole numbers',
suffixIcon: IconButton(
icon: Icon(Icons.clear),
onPressed: () {
_controllerDefaultDeposit.clear();
},
),
),
keyboardType: TextInputType.numberWithOptions(decimal: false, signed: false),
onSaved: (String amountStr) {
print("saving deposit");
user.defaultItemDeposit = int.parse(amountStr.trim());
},
validator: (String value) {
print(LOG + "validator called");
if(int.tryParse(value.trim()) == null) {
inputCompletionAlert += "But your default item deposit is not a number, please correct.\n";
return 'Not a £-- whole number monetary amount';
}
if(value == "" || value == "0") {
print(LOG + 'deposit validator called, should launch Dialog from here');
inputCompletionAlert += "Would you like to set a default deposit?";
return "Would you like to set a deposit?";
}
return null;
},
),
),
Have you tried building a custom validator function and then directly calling it from the validator property.
For example :-
Validator (String value) {
print(LOG + "validator called");
if(int.tryParse(value.trim()) == null) {
inputCompletionAlert += "But your default item deposit is not a number, please correct.\n";
return 'Not a £-- whole number monetary amount';
}
}
This was an incomplete question, this array of ExpansionBoxes messes up the validator:
ExpansionPanelList.radio(initialOpenPanelValue: 2,
children: [
bicyclePanel,
carPanel,
floodPanel,
diyPanel,
surplusPanel,
gardeningPanel,
ballSportsPanel,
snowSportsPanel,
waterSportsPanel,
campingPanel,
backpackingPanel,
circusPanel,
]),
I presume that when _formKeyForDeposit.currentState.validate() is called it heads down into the ExpansionPanelList and can't escape to trigger the validator of TextFormFields above it.
Since I only have 1 TextFormField outwidth the ExpansionPanelList, I've used _controllerDefaultDeposit.text to get the Deposit FormField value and manually validate it. It's a hacky solution, but it'll do for now.
So i am making a chat app and i want user to submit username which should not be uppercase and have space, i can accept the name and change it later in onchanged feature but i want user to know it as well
Flutter's TextField or TextFormField has attribute named inputFormatters which takes a list of TextInputFormatter.
an example of TextInputFormatters that is useful in your case.
FilteringTextInputFormatter.allow(RegExp("[a-z]"))
TextField(
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(RegExp("[a-z]")),
],
)
you can see TextInputFormatters API docs here : Reference
(before flutter 1.20):
WhitelistingTextInputFormatter(RegExp("[a-z]")),
TextField(
inputFormatters: <TextInputFormatter>[
WhitelistingTextInputFormatter(RegExp("[a-z]")),
],
)
you can take those as reference if it's not clear enough : Reference 2, Reference 3.
Also check out this SOF Question: Reference 4
In regards to changing the text, try doing the following:
Let's say 's' is the username:
String s = ""
onChange(val) {
s = val.trim().toLowerCase()
}
If you want to notify the user, perhaps use an alert dialog with some text letting them know the username should not be uppercase and contain no spaces. Regardless, you can't assume the user will conform to what they "should do".
You can use a Form and then notify the user using the error field (Validator)
TextFormField(
validator: (name) {
Pattern pattern = r'^[a-z]+$'; // Regex for lowercase only
RegExp regex = new RegExp(pattern);
if (!regex.hasMatch(name))
return 'Username must be lowercase, this will be changed when saved';
else
return null;
},
),
FlatButton(
child: Text('Save'),
onPressed: () {
_formKey.currentState.validate(); // just check if its valid and notify user
// Other code to save the and change the value
print('Saving Username');
},
)
Dart Pad Example