setState is not working iqual on iOS than Android - ios

I am doing some charge information with shared preference but in iOS is not working while in Android is working as expected
#override
void initState() {
super.initState();
readData();
}
I have 2 textfields for fill it up when readData() have data
final email = TextFormField(
keyboardType: TextInputType.emailAddress,
autofocus: false,
initialValue: _email,
validator: (input) {
if(input.isEmpty) {
return 'Introduce un Email';
}
},
onSaved: (input) => _email = input,
decoration: InputDecoration(
hintText: 'Email',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(32.0))),
);
final password = TextFormField(
autofocus: false,
obscureText: true,
initialValue: _password,
validator: (input) {
if(input.isEmpty) {
return 'Introduce la contraseƱa';
}
},
onSaved: (input) => _password = input,
decoration: InputDecoration(
hintText: 'ContraseƱa',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(32.0))),
);
readData() async {
final prefs = await SharedPreferences.getInstance();
setState(() {
_email = prefs.getString('email');
_password = prefs.getString('password');
});
}
In android works well but not in iOS

I think the right way to update the value of the textfields is to use a TextEditingController.
Your code would look like this:
In your state
final TextEditingController emailCtrl = TextEditingController();
final TextEditingController passwordCtrl = TextEditingController();
readData() async {
final prefs = await SharedPreferences.getInstance();
emailCtrl.text = prefs.getString('email');
password.text = prefs.getString('password');
}
Your TextFields
final email = TextFormField(
// ... your other arguments
controller: emailCtrl
);
final password = TextFormField(
// ... your other arguments
controller: passwordCtrl
);

Related

How to dynamically setState variable in flutter?

I just start to learn flutter, and I'm curious about how can i setState() dynamically in flutter
in React Native i usually used a function like this to setState dynamically:
class foo extends React.Component{
state={
username:null,
password:null
}
toggleSetState(whatState, value){
this.setState({ [whatState]: value })
}
render() {
return (
<View>
<TextInput
value={this.state.username}
onChangeText={(text)=>{toggleSetState(username, text)}
/>
<TextInput
value={this.state.password}
onChangeText={(text)=>{toggleSetState(password, text)}
/>
</View>
);
}
}
what is an equivalent of above code in Flutter?
I've tried this in Flutter but it seems doesn't work
class _LoginFormState extends State<LoginForm> {
String username, password;
void ToogleState(typedata, text){
setState(() {
typedata = text;
});
}
//Widget
TextField(
onChanged: (text){ToogleState(username, text); print(username);},
decoration: InputDecoration(
hintText: 'input username', labelText: 'Username'
),
),
}
after some research and trying, i can achieve what i want with the code below:
class _LoginFormState extends State<LoginForm> {
String username, password;
//create an object
var loginForm = {};
final myController = TextEditingController();
void ToogleState(typedata, text){
setState(() {
//i can assign any different variable with this code
loginForm[typedata] = text;
//output of LoginForm: {username: foo, password: bar}
});
}
//widget
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
TextField(
onEditingComplete: (){print(loginForm);},
onChanged: (text){ToogleState("username", text); print(loginForm['username']);},
decoration: InputDecoration(
hintText: 'input username', labelText: 'Username'
),
),
TextField(
onEditingComplete: (){print(loginForm);},
onChanged: (text){ToogleState("password", text); print(loginForm['password']);},
decoration: InputDecoration(
hintText: 'input password', labelText: 'Password'
),
),
],
),
);
}
You just need to make a variable to hold the value. I am confused why you are calling setState when you are not modifying ephemeral state
Here are some helpful docs
https://flutter.dev/docs/development/data-and-backend/state-mgmt/ephemeral-vs-app
class _LoginFormState extends State<LoginForm> {
String _username = "";
String __password = "";
void ToogleState( text){
setState(() {
_username = text;
});
}
//Widget
TextField(
onChanged: (text){ToogleState( text); print(username);},
decoration: InputDecoration(
hintText: 'input username', labelText: 'Username'
),
),
}

Auto-validate Issue : Password and Confirm Password validation with autovalidation in Flutter

I have created Form Widget in that form there are multiple TextFormFeild which i created custom BoxFeild . I am facing problem related to auto-validation from Form Widget. Unable to validate password matching feild in confirm password BoxFeild
Should i validate password matching after formKeyData saved in current state ?
I am facing issue when passing key to BoxFeild constructor. Its showing multiple widgets used the same global key.
I need to add global FormFeild key to compare _password and _confirmPassword.
class _PageSignUpState extends State<PageSignUp> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
var passKey = GlobalKey<FormFieldState>();
bool _autoValidate = false;
String _name, _email, _phoneNo, _password, _confirmPassoword;
bool isLoading = false;
final _passwordController = TextEditingController();
final _confirmPassController = TextEditingController();
double width,height;
#override
Widget build(BuildContext context) {
width = MediaQuery.of(context).size.width;
height = MediaQuery.of(context).size.height;
return Scaffold(
backgroundColor: Colors.white,
body: Container(
color: Colors.grey.shade200,
child: Center(
child: SingleChildScrollView(
child: Container(
child: Column(
children: <Widget>[
Form(
key: _formKey,
autovalidate: _autoValidate,
child: Column(
children: <Widget>[
_nameWidget(),
_emailWidget(),
_passwordWidget(),
_confirmPassWidget(),
SizedBox(height: height/25),
_signUpButtonWidget()
],
)),
],
),
),
),
),
),
);
}
Container _signUpButtonWidget() {
return Container(
padding: EdgeInsets.symmetric(vertical: height / 40, horizontal: width / 15),
width: double.infinity,
child: RaisedButton(
padding: EdgeInsets.all(12.0),
child: Text(
"Sign Up",
style: TextStyle(color: Colors.white, fontSize: 20.0),
),
color: Colors.blue,
onPressed: () {
setLoading(true);
_validateInputs();
},
),
);
}
BoxFeild _confirmPassWidget() {
return BoxFeild(
hintText: "Confirm Password",
lableText: "Confirm Password",
obscureText: true,
icon: Icons.lock_outline,
validator: validatePasswordMatching,
onSaved: (String val) {
_confirmPassoword = val;
},
);
}
BoxFeild _passwordWidget() {
return BoxFeild(
key: passKey,
hintText: "Enter Password",
lableText: "Password",
obscureText: true,
icon: Icons.lock_outline,
controller: _passwordController,
validator: validatePassword,
onSaved: (String val) {
_password = val;
},
);
}
BoxFeild _emailWidget() {
return BoxFeild(
hintText: "Enter Email",
lableText: "Email",
keyboardType: TextInputType.emailAddress,
icon: Icons.email,
validator: validateEmail,
onSaved: (String val) {
_email = val;
},
);
}
BoxFeild _nameWidget() {
return BoxFeild(
hintText: "Enter Name",
lableText: "Name",
icon: Icons.person,
validator: validateName,
onSaved: (String val) {
_name = val;
},
);
}
String validateName(String value) {
String patttern = r'(^[a-zA-Z ]*$)';
RegExp regExp = RegExp(patttern);
if (value.length == 0) {
return "Name is Required";
} else if (!regExp.hasMatch(value)) {
return "Name must be a-z and A-Z";
}
return null;
}
String validateEmail(String value) {
RegExp regExp = RegExp(Constants.PATTERN_EMAIL, caseSensitive: false);
if (value.length == 0) {
return "Email is Required";
} else if (!regExp.hasMatch(value)) {
return "Enter valid email address.";
}
return null;
}
String validatePassword(String value) {
if (value.length == 0) {
return "Password is Required";
} else if (value.length < 6) {
return "Password Should be more than 6.";
}
return null;
}
String validatePasswordMatching(String value) {
var password = passKey.currentState.value;
if (value.length == 0) {
return "Password is Required";
} else if (value != password) {
return 'Password is not matching';
}
return null;
}
void _validateInputs() {
if (_formKey.currentState.validate()) {
//If all data are correct then save data to out variables
//Make a REST Api Call with success Go to Login Page after User Created.
_formKey.currentState.save();
setLoading(true);
Utils.checkConnection().then((connectionResult) {
if (connectionResult) {
} else {
setLoading(false);
Utils.showAlert(context, "Flutter",
"Internet is not connected. Please check internet connection.",
() {
Navigator.pop(context);
}, true);
}
});
}
} else {
// If all data are not valid then start auto validation.
setState(() {
_autoValidate = true;
});
}
}
}
BoxFeild.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class BoxFeild extends StatefulWidget {
final TextEditingController controller;
final FocusNode focusNode;
final TextInputType keyboardType;
final TextInputAction textInputAction;
final TextCapitalization textCapitalization;
final TextStyle style;
final TextAlign textAlign;
final bool autofocus;
final bool obscureText;
final bool autocorrect;
final int maxLines;
final Key key;
final int maxLength;
final bool maxLengthEnforced;
final ValueChanged<String> onChanged;
final VoidCallback onEditingComplete;
final ValueChanged<String> onSubmitted;
final List<TextInputFormatter> inputFormatters;
final bool enabled;
final IconData icon;
final String hintText;
final String lableText;
final double cursorWidth;
final Radius cursorRadius;
final Color cursorColor;
final Color defaultBorderColor;
final Brightness keyboardAppearance;
final EdgeInsets scrollPadding;
final FormFieldValidator<String> validator;
final ValueChanged<String> onFieldSubmitted;
final FormFieldSetter<String> onSaved;
const BoxFeild({
this.key,
this.controller,
this.focusNode,
TextInputType keyboardType,
this.textInputAction,
this.textCapitalization = TextCapitalization.none,
this.style,
this.icon,
this.textAlign = TextAlign.start,
this.autofocus = false,
this.obscureText = false,
this.autocorrect = true,
this.maxLines = 1,
this.maxLength,
this.onSaved,
this.hintText,
this.lableText,
this.maxLengthEnforced = true,
this.onChanged,
this.defaultBorderColor,
this.onEditingComplete,
this.onSubmitted,
this.inputFormatters,
this.enabled,
this.cursorWidth = 2.0,
this.cursorRadius,
this.cursorColor,
this.keyboardAppearance,
this.scrollPadding,
this.validator,
this.onFieldSubmitted,
}) : assert(textAlign != null),
assert(autofocus != null),
assert(obscureText != null),
assert(autocorrect != null),
assert(maxLengthEnforced != null),
assert(maxLines == null || maxLines > 0),
assert(maxLength == null || maxLength > 0),
keyboardType = keyboardType ??
(maxLines == 1 ? TextInputType.text : TextInputType.multiline);
#override
_BoxFeildState createState() => _BoxFeildState();
}
class _BoxFeildState extends State<BoxFeild> {
double width;
double height;
Color focusBorderColor = Colors.grey.shade400;
FocusNode _focusNode = FocusNode();
ValueChanged<Colors> focusColorChange;
#override
void dispose() {
super.dispose();
_focusNode.dispose();
}
#override
Widget build(BuildContext context) {
width = MediaQuery.of(context).size.width;
height = MediaQuery.of(context).size.height;
return Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
width: width / 30,
),
Expanded(
child: Container(
margin: EdgeInsets.only(top: height / 400, bottom: height / 400, left: width / 50, right: width / 50),
padding: EdgeInsets.all(height / 100),
alignment: Alignment.center,
height: height / 14,
decoration: BoxDecoration(
color: Colors.grey.shade100,
border: Border.all(color: focusBorderColor, width: 1.0),
borderRadius: BorderRadius.circular(8.0)),
child: TextFormField(
key: this.widget.key,
obscureText: this.widget.obscureText,
onSaved: this.widget.onSaved,
validator: this.widget.validator,
onFieldSubmitted: this.widget.onFieldSubmitted,
decoration: InputDecoration(
border: InputBorder.none,
prefixIcon: Icon(
this.widget.icon,
size: height/34,
),
hintText: this.widget.hintText),
),
)),
],
),
padding: EdgeInsets.only(bottom : height / 58),
margin: EdgeInsets.only(
top: height / 50, right: width / 20, left: width / 30),
);
}
}
Now If, I am using different Container for password feild then its working. And If I am passing key and controller to boxfeild then its show
multiple widgets used the same global key
Container _passwordWidget() {
return Container(
padding: const EdgeInsets.all(16.0),
alignment: Alignment.center,
height: 52.0,
decoration: BoxDecoration(
color: Colors.grey.shade100,
border: Border.all(color: Colors.transparent, width: 0.0),
borderRadius: BorderRadius.circular(12.0)),
child: TextFormField(
key: passKey,
obscureText: true,
validator: validatePassword,
onSaved: (String val) {
_password = val;
},
keyboardType: TextInputType.number,
style: TextStyle(
fontSize: 22.0,
color: Colors.black,
),
decoration:
InputDecoration.collapsed(hintText: "Password"),
),
);
}
You are declaring the passkey in a wrong way use final Globalkey instead of var

Flutter TextFormField onSave() doesn't get called after successful validation

I have a weird problem in flutter TextFormField. I implemented form validation in TextFormField. But onSaved() function doesn't get called after successful validation.
First I created basic Widget using TextFormField
--- In AppWidgets class ---
static Widget buildTextFormField(
String labelText,
String helperText,
IconData prefixIcon, {
Widget suffixIcon,
bool obscureText = false,
TextInputType keyboardType = TextInputType.text,
TextInputAction textInputAction = TextInputAction.none,
FocusNode focusNode,
ValueChanged<String> onFieldSubmitted,
TextEditingController controller,
FormFieldValidator<String> validator,
FormFieldSetter<String> onSaved,
bool isLightTheme = false,
}) {
return Theme(
data: isLightTheme
? AppThemesLight.textFormFieldThemeData
: AppThemesDark.textFormFieldThemeData,
child: TextFormField(
controller: controller,
validator: validator,
onSaved: onSaved,
keyboardType: keyboardType,
textInputAction: textInputAction,
focusNode: focusNode,
onFieldSubmitted: onFieldSubmitted,
obscureText: obscureText,
decoration: InputDecoration(
filled: true,
fillColor: isLightTheme
? AppColorsLight.textFieldFillColor
: AppColorsDark.textFieldFillColor,
labelText: labelText,
helperText: helperText,
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(AppDimensions.textFieldBorderRadius),
),
),
prefixIcon: Icon(
prefixIcon,
color: isLightTheme
? AppColorsLight.primaryTextColor
: AppColorsDark.primaryTextColor,
),
suffixIcon: suffixIcon,
),
),
);
}
From that, created email text from field.
static Widget buildEmailTextFormField(LoginState loginState) {
return AppWidgets.buildTextFormField(
'Email address',
'Your email address',
Icons.email,
keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.next,
focusNode: loginState.focusNodes[0],
onFieldSubmitted: (String value) {
print('submitted $value');
loginState.onFocusChanged(index: 0);
},
validator: (String email) {
print('validator $email');
return InputValidators.validateEmail(email);
},
onSaved: (String email) {
print('saved $email');
loginState.email = email;
},
);
}
Here is the email validator I used.
static String validateEmail(String email) {
Pattern pattern =
r'^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regex = new RegExp(pattern);
if (email.isEmpty)
return 'Email can\'t be empty';
else if (!regex.hasMatch(email))
return 'Enter valid email address';
else
return null;
}
I tested above code by putting print statement inside onSaved() function, but it's not printing after successful validation.
The onSaved() function won't be called automatically after successful validation. We have to call _formKey.currentState.save() manually to save our variables.
Form(
key: key,
child: TextFormField(
onSaved: (val) {
print('saved');
},
validator: (val) {
print('validating');
},
),
),
RaisedButton(
child: Text('Click me'),
onPressed: () {
if (key.currentState.validate()) {
key.currentState.save();
print('valid');
}
},
),
Did you call this method formKey.currentState.save() ?
in my case i forgot to call this after adding this it has worked.

Capture data from TextFormField on flutter for http POST request

i am trying to make a login with flutter.
I am consulting a web service.
I want to send in the body of the Post request the username and the password from different TextFormField. how can i do that?
Here is the code i've been working on.
import 'package:flutter/material.dart';
import 'package:device_id/device_id.dart';
import 'package:http/http.dart' as http;
import 'dart:async';
import 'dart:convert';
class SignIn extends StatefulWidget {
#override
_SignInState createState() => _SignInState();
}
class _SignInState extends State<SignIn> {
Future<String> getData() async {
final response = await http.post(
Uri.encodeFull("The route i'm consulting),
body: {
"username": user,
"password": password
},
I want to retrieve the input texts from username and password TextFormField below in here
headers: {
"Accept": "application/json",
});
print(response.statusCode);
print(response.body);
}
String _deviceid = 'Unknown';
String user = '';
String password = '';
TextEditingController controller = new TextEditingController();
TextEditingController controller2 = new TextEditingController();
#override
void dispose() {
controller.dispose();
controller2.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
username = TextFormField(
This is the first TextFormField i want to retrieve to send it in the body of the request
controller: controller,
keyboardType: TextInputType.text,
autofocus: false,
decoration: InputDecoration(
hintText: "Username",
hintStyle: TextStyle(fontSize: 16.0),
contentPadding: EdgeInsets.fromLTRB(20.0, 0.0, 20.0, 10.0),
border:
UnderlineInputBorder(borderRadius: BorderRadius.circular(32.0))),
);
final password = TextFormField(
This is the second TextFormField i want to retrieve to send it in the body of the request
controller: controller2,
autofocus: false,
obscureText: true,
decoration: InputDecoration(
hintText: "Password",
hintStyle: TextStyle(fontSize: 16.0),
contentPadding: EdgeInsets.fromLTRB(20.0, 25.0, 20.0, 10.0),
border:
UnderlineInputBorder(borderRadius: BorderRadius.circular(32.0))),
);
final loginButton = Padding(
padding: EdgeInsets.symmetric(vertical: 25.0),
child: Material(
borderRadius: BorderRadius.circular(30.0),
shadowColor: Colors.blueAccent.shade100,
elevation: 10.0,
child: MaterialButton(
minWidth: 200.0,
height: 42.0,
color: Colors.blueAccent,
onPressed: (){
},
child: Text(
"Login",
style: TextStyle(color: Colors.white),
),
),
),
);
return Form(
child: new Center(
child: ListView(
padding: EdgeInsets.only(left: 24.0, right: 24.0, top: 10.0),
children: <Widget>[
username,
SizedBox(height: 8.0),
password,
SizedBox(height: 24.0),
loginButton
]),
),
);
}
}
See Retrieve the value of a text field.
Wrap a StatefulWidget around your form
Add two TextEditingController fields in your State, one for each TextFormField
Pass the controllers to your form fields (controller constructor parameter)
Retrieve the values, for example in a button click listener using myController.text
I'm not sure if you are also asking how to send a HTTP post request.
Here is a very minimal example:
class LoginScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _usernameController = TextEditingController();
final _passwordController = TextEditingController();
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
TextFormField(controller: _usernameController,),
TextFormField(controller: _passwordController, obscureText: true,),
RaisedButton(
onPressed: _performLogin,
child: Text('Login'),
)
],
);
}
void _performLogin() {
String username = _usernameController.text;
String password = _passwordController.text;
print('login attempt: $username with $password');
}
}
Here is a full example of a login Screen ... where you can validate inputs and submit the data after passing the validation.
import 'package:flutter/material.dart';
import '../mixins/validate_mixin.dart';
class LoginScreen extends StatefulWidget{
final GlobalKey<ScaffoldState> scaffoldKey;
LoginScreen(this.scaffoldKey);
#override
State<StatefulWidget> createState() {
return LoginScreenState(scaffoldKey);
}
}
class LoginScreenState extends State<LoginScreen> with ValidateMixin{
final formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> scaffoldKey;
LoginScreenState(this.scaffoldKey);
String _email;
String _password;
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(40.0),
child: Form(
key: formKey,
child: Column(
children: <Widget>[
emailField(),
passwordField(),
Container(margin: EdgeInsets.only(bottom: 25.0),),
submitButton(),
],
),
),
);
}
Widget emailField() {
return TextFormField(
decoration: InputDecoration(hintText: 'ali#gmail.co', labelText: 'Email'),
keyboardType: TextInputType.emailAddress,
validator: validateEmail,
onSaved: (String value) {
_email = value;
},
);
}
Widget passwordField() {
return TextFormField(
obscureText: true,
decoration: InputDecoration(hintText: '*****', labelText: 'Password'),
onSaved: (String value) {
_password = value;
},
);
}
Widget submitButton() {
return RaisedButton.icon(
color: Colors.cyan[900],
textColor: Colors.white,
label: Text('Submit'),
icon: Icon(Icons.save),
onPressed: () {
final bool v = formKey.currentState.validate();
if (v) {
formKey.currentState.save();
_performLogin();
print('object');
}
},);
}
void _performLogin () {
var snackbar = new SnackBar(
content: Text('Email: $_email and Password $_password'),
);
scaffoldKey.currentState.showSnackBar(snackbar);
}
}
You can back to the full example.
https://github.com/anbturki/flutter_login_screen

how to validate a form field based on the value of the other?

I have 2 form fields,I want to validate the second form field to match the password from the first one,I tried but no success..thanks for answering.
Updated : I already have submit button and its working,I want Validator in the second field to validate the first field text to match the second field.
new TextFormField(
controller: _registerPassController,
decoration: new InputDecoration(labelText: 'Password'),
obscureText: true,
validator: (value) =>
value.isEmpty ? 'Password can\'t be empty' : null,
onSaved: (value) => _password = value,
),
],
),
new Stack(
alignment: const Alignment(1.0, 1.0),
children: <Widget>[
new TextFormField(
controller: _registerPassController2,
decoration: new InputDecoration(labelText: 'Retype Password'),
obscureText: true,
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
},),
I finally find the answer,its so simple actually.
new TextFormField(
controller: _registerPassController2,
decoration: new InputDecoration(labelText: 'Retype Password'),
obscureText: true,
validator: (value) {
if (value != _registerPassController.text) {
return 'Password is not matching';
}
},
),
Since you are using formfield it will be appropriate to use the key to access the value of other field. You can declare the global key like this
var passKey = GlobalKey<FormFieldState>();
Then pass it to the password field to retrieve its value during validation.
TextFormField(
key: passKey,
obscureText: true,
decoration: InputDecoration(
labelText: "Password"
),
validator: (password){
var result = password.length < 4 ? "Password should have at least 4 characters" : null;
return result;
},
);
Then you can use the password inside the confirmation validator like this
TextFormField(
obscureText: true,
decoration: InputDecoration(
labelText: "Confirm Password"
),
validator: (confirmation){
var password = passKey.currentState.value;
return equals(confirmation, password) ? null : "Confirm Password should match password";
},
);
The way I do this is to validate on a submit button and then show a message.
String _password;
Widget _passwordField() {
return new TextFormField(
autocorrect: false,
obscureText: true,
decoration: InputDecoration(labelText: 'Password'),
validator: (value) =>
value.isEmpty ? "Password can't be empty" : null,
onSaved: (val) => _password = val;
}
String _passwordConfirm;
Widget _passwordConfirmField() {
return new TextFormField(
autocorrect: false,
obscureText: true,
decoration: InputDecoration(labelText: 'Retype Password'),
validator: (value) =>
value.isEmpty ? "Password can't be empty" : null,
onSaved: (val) => _passwordConfirm = val;
}
final formKey = new GlobalKey<FormState>();
Widget _buildForm() {
return new Form(
autovalidate: true,
key: formKey,
child: new Column(children: <Widget>[
_passwordField(),
_passwordConfirmField(),
new RaisedButton(
child: Text('Sign Up'),
onPressed: () => submit()
)
]
}
void submit() {
final form = formKey.currentState;
if (form.validate()) {
form.save(); //this will cause the onSaved method to get called
//you will need to do some additional validation here like matching passwords
if(_password != _passwordConfirm) {
showDialog(....)
} else {
//complete
}
}
}
Note: This is a StatefulWidget
This is my method, big thanks to Suhair. It is based on Suhair's but his didn't work for me...
(First Password)
TextFormField(
key: passKey,
obscureText: true,
decoration: InputDecoration(labelText: "Password"),
validator: (password) {
var result = password.length < 4
? "Password should have at least 4 characters"
: null;
return result;
},
),
(Second)
TextFormField(
obscureText: true,
decoration: InputDecoration(labelText: "Confirm Password"),
validator: (confirmation) {
if (confirmation != passKey.currentState.value) {
return 'Passwords do not match';
} else {
return null;
}
},
),
and you also need to declare you variable
var passKey = GlobalKey<FormFieldState>();
To have the operation done in the TextField's validator, we can use the onChanged event to capture the value of the password field and use it later in the validator of the confirm password field.
String _password;
Widget _passwordField() {
return new TextFormField(
autocorrect: false,
obscureText: true,
decoration: InputDecoration(labelText: 'Password'),
onChanged: (value) => _password = value,
validator: (value) =>
value.isEmpty ? "Password can't be empty" : null,
}
Widget _passwordConfirmField() {
return new TextFormField(
autocorrect: false,
obscureText: true,
decoration: InputDecoration(labelText: 'Retype Password'),
validator: (value) =>
value != _password ? "Passwords do not match!" : null,
}
final formKey = new GlobalKey<FormState>();
Widget _buildForm() {
return new Form(
autovalidate: true,
key: formKey,
child: new Column(children: <Widget>[
_passwordField(),
_passwordConfirmField(),
new RaisedButton(
child: Text('Sign Up'),
onPressed: () => submit()
)
]
}
void submit() {
final form = formKey.currentState;
if (form.validate()) {
form.save(); //this will cause the onSaved method to get called
}
}
You can use a validator.
Define the validator:
class TwoFieldsMatchValidator extends TextFieldValidator {
String checkField;
TwoFieldsMatchValidator({required this.checkField, required String errorText}) : super(errorText);
#override
bool get ignoreEmptyValues => true;
#override
bool isValid(String? v) {
return checkField == v;
}
}
Use the validator:
final _controller = TextEditingController();
TextFormField(
...
validator: MultiValidator(
[
TwoFieldsMatchValidator(
checkField: _controller.text,
errorText: "<Error>",
),
],
),
),
Hope this helps anyone in future.

Resources