In my app the user has to insert a name in the textformfield. While the user is writing a query should be made to the database, that controls if the name already exists. This query returns the number of how many times the name exists. Till now I am able to make it when I press a button.
This is the function that returns the count of the names:
checkRecipe(String name) async{
await db.create();
int count = await db.checkRecipe(name);
print("Count: "+count.toString());
if(count > 0) return "Exists";
}
And this is the TextFormField, which should be validated async:
TextField(
controller: recipeDescription,
decoration: InputDecoration(
hintText: "Beschreibe dein Rezept..."
),
keyboardType: TextInputType.multiline,
maxLines: null,
maxLength: 75,
validator: (text) async{ //Returns an error
int count = await checkRecipe(text);
if (count > 0) return "Exists";
},
)
The error of the code is:
The argument type Future can't be assigned to the parameter type
String
I do know what the error means. But I do not know how to work around could look like. It would be awesome if somebody could help me.
I have found a solution.
My Code looks now like this:
//My TextFormField validator
validator: (value) => checkRecipe(value) ? "Name already taken" : null,
//the function
checkRecipe<bool>(String name) {
bool _recExist = false;
db.create().then((nothing){
db.checkRecipe(name).then((val){
if(val > 0) {
setState(() {
_recExist = true;
});
} else {
setState(() {
_recExist = false;
});
}
});
});
return _recExist;
}
Perhaps you could run your async check using the onChange handler and set a local variable to store the result.
Something like:
TextFormField(
controller: recipeDescription,
decoration: InputDecoration(hintText: "Beschreibe dein Rezept..."),
keyboardType: TextInputType.multiline,
maxLines: null,
maxLength: 75,
onChanged: (text) async {
final check = await checkRecipe(text);
setState(() => hasRecipe = check);
},
validator: (_) => (hasRecipe) ? "Exists" : null,
)
I wanted a same behavior for one of our apps and ended up writing a widget (which I recently published to pub.dev).
AsyncTextFormField(
controller: controller,
validationDebounce: Duration(milliseconds: 500),
validator: isValidPasscode,
hintText: 'Enter the Passcode')
You can pass in a Future<bool> function for the validator and set an interval before the text is sent to server.
The code is available on github.
Related
I'm new in Flutter and have been trying to find a solution for this, but is there a way I can just accept the username ignoring if it's lowercase or uppercase? As long as it matches with their username it's fine to proceed.
Widget emailField() {
return TextFormField(
textInputAction: TextInputAction.next,
keyboardType: TextInputType.emailAddress,
focusNode: emailNode,
validator: validateEmpty,
decoration: InputDecoration(
hintText: 'Username',
prefixIcon: const Icon(Icons.account_circle),
labelStyle: Label,
fillColor: Colors.transparent,
),
//validator: validateEmail,
onSaved: (String? value) {
email = value as String;
},
);
}
submit() async {
if (formKey1.currentState!.validate()) {
formKey1.currentState!.save();
if (_prefs.userSession!.username == email) {
EasyLoading.instance.userInteractions = false;
EasyLoading.show(status: 'Deleting account...');
final response = await authService.masterDelete();
if (_prefs.userSession!.username != email) {
AwesomeDialog(
context: context,
dialogType: DialogType.ERROR,
title: 'ERROR',
desc: 'Incorrect username',
btnCancelText: 'Accept',
btnCancelOnPress: () {},
).show();
}
If it's only about ignoring lowercase/uppercase, then use toLowerCase() on both strings and compare them:
String mailOne = "TeSt123#gmail.com";
String mailTwo = "test123#gMAIL.com";
print(mailOne.toLowerCase() == mailOne.toLowerCase()); // -> true
I have TextFormFields for email and password authentication. I want to display different messages depending on the error thrown by FireBaseAuthException, but the method I use to let the TextFormField know that there is an error is fundamentally flawed. Here's the code before I explain:
bool _mailError = false;
bool _passwordError = false;
String _errorMessage = 'default error message';
startAuth() async {
final validity = _formKey.currentState.validate();
FocusScope.of(context).unfocus();
if (validity) {
_formKey.currentState.save();
submitForm(_email, _password);
}
}
submitForm(String email, String password) async {
final auth = FirebaseAuth.instance;
UserCredential userCredential;
try {
if (_isLoginPage) {
userCredential = await auth.signInWithEmailAndPassword(
email: email, password: password);
} else {
// for auth
userCredential = await auth.createUserWithEmailAndPassword(
email: email, password: password);
// for Firestore
String uid = userCredential.user.uid;
await FirebaseFirestore.instance.collection("users").doc(uid).set({
"email": email,
"password": password,
});
}
} on FirebaseAuthException catch (err) {
switch (err.code) {
case 'email-already-in-use':
_mailError = true;
_errorMessage = 'E-mail already in use';
break;
case 'user-not-found':
_errorMessage = 'User not found';
_mailError = true;
break;
.
.
.
}
print("zort");
print(err);
}
}
#override
Widget build(BuildContext context) {
return SizedBox(
.
.
.
TextFormField(
keyboardType: TextInputType.emailAddress,
key: const ValueKey("email"),
validator: (val) {
if (val.isEmpty || !val.contains("#")) {
return "Invalid e-Mail";
> } else if (_mailError) {
_mailError = false;
return _errorMessage;
}
return null;
},
.
.
.
Container(
padding: const EdgeInsets.all(5),
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
),
onPressed: () {
> startAuth();
},
child: _isLoginPage
? const Text("Log in")
: const Text("Sign up"),
),
),
Now here's what happens. For example, when an email of an already existing user is entered and the sign up button is pressed, the error with the code email-already-in-use gets thrown, _mailError gets set to true and _errorMessage gets set to User not found. But since _mailError is initially false, the if statement in the validator does not get executed on the first button press, so I have to press the button a second time, that is, when _mailError is true, for the if statement to get executed and _mailError to be set to false again. See the logical problem here? This also means that pressing it a third time removes the error, although it should remain on the screen.
I know, this is a very basic logic error. I feel pretty silly for even attempting this, but I don't know what other way I can use to let the TextFormField know that there is an error. What can I do?
Im using email validator package on flutter to validate the email for login. I have one issue with this package that I want to allow spaces in the email when the user sign in because Im gonna trim the text anyway so I dont want it to show error when there is Spaces at the end.
child: TextFormField(
keyboardType: TextInputType.emailAddress,
controller: emailController,
cursorColor: Colors.white,
textInputAction: TextInputAction.next,
decoration: const InputDecoration(labelText: 'Email'),
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (email) =>
email != null && !EmailValidator.validate(email)
? 'Enter a valid Email' : null,
try {
await _auth.signInWithEmailAndPassword(
email: emailController.text.trim(),
password: passwordController.text.trim(),
);
Anyone knows how to do it or if there is a better way than using this package?
You don't need to use any package for validating email you can simply do it with RegExp like below in this space is allows and then you can trim it where ever you want to use
validator: (value) {
bool emailValid = RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+#[a-zA-Z0-9]+\.[a-zA-Z]+").hasMatch(value!);
if (value == null || value.isEmpty) {
return 'Please Enter Email Address';
}else if (emailValid == false){
return 'Please Enter Valid Email Address';
}
return null;
},
Let me know if you have any questions. Thanks
You can trim email first then you can check for validation.
validator: (email) {
if(email != null) {
email = email.trim();
(!EmailValidator.validate(email))
? 'Enter a valid Email' : null,
}
return null;
}
best practice to use TextFormField validation is to not to allow user to put irrelevant data
TextFormField(
controller: _etcEmail,
keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.done,
inputFormatter: [
// FilteringTextInputFormatter.allow(RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+#[a-zA-Z0-9]+\.[a-zA-Z]+")),
// FilteringTextInputFormatter.deny(RegExp(r" ")),
FilteringTextInputFormatter.allow(RegExp(r" ")),
],
hintText: 'Email',
validator: (value) {
if (value!.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
backgroundColor: Colors.pinkAccent,
behavior: SnackBarBehavior.floating,
padding: EdgeInsets.all(5),
content: (Text('Email Field is Required'))));
}
},
read: false,
)
How to can I implement a callback that fires after every few seconds or when user stop typing in TextField ?
Or is it performant to just implement in onChanged callback directly ?
input Field onChanged gives the input value when ever user types in, So you may use onChnaged callback function to save the input, like below,
TextFormField(
controller: _nameController,
onChanged: (value) {
saveData();
},
initialValue: widget.user.userName,
onSaved: (val) {
widget.user.userName = val;
},
validator: (val) =>
val.length > 3 ? null : 'Full name is invalid',
decoration: InputDecoration(
labelText: 'Full Name',
hintText: 'Enter your full name',
icon: Icon(Icons.person),
isDense: true,
),
),
I am added text field in a flutter, but the keypad is overlapping with a text field when we added this in Flutter as view in the existing android app. if the same code runs independently as only Flutter application it will work.
TextFormField(
focusNode: payTMFocus,
controller: payTMController,
inputFormatters: [
LengthLimitingTextInputFormatter(10),
WhitelistingTextInputFormatter.digitsOnly,
],
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: "Enter mobile number",
filled: true,
hintStyle: getTextStyle(),
hasFloatingPlaceholder: true),
},
validator: (value) {
if (value.length != 10) {
return "Enter valid mobile number";
} else {
return null;
}
},
)
tried seting true to resizeToAvoidBottomPadding for root Scaffold
Github issue link -https://github.com/flutter/flutter/issues/47107
By overlapping means i guess you are not able to see textField as keypad shows over it.
If this is the case then you can use SingleChildScrollView to give scrollable view to area in which your text field is.
child:SingleChildScrollview(
...//container or column or some other widgets you have above in hierarchy
child:TextFormField(
focusNode: payTMFocus,
controller: payTMController,
inputFormatters: [
LengthLimitingTextInputFormatter(10),
WhitelistingTextInputFormatter.digitsOnly,
],
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: "Enter mobile number",
filled: true,
hintStyle: getTextStyle(),
hasFloatingPlaceholder: true),
},
validator: (value) {
if (value.length != 10) {
return "Enter valid mobile number";
} else {
return null;
}
},
)
),
Hope this helps ! please comment if you are expecting some another solution.
happy to help :)