I've two TextFormFields, it focus on the password field on validation error, even if email field has error already & comes before password field.
Any idea what's going wrong here?
//Email
TextFormField(
controller: _emailController,
focusNode: _emailFocus,
validator: (value) {
String? err = validateEmail(value);
if (err != null) {
_emailFocus.requestFocus();
}
return err;
},
),
//Password
TextFormField(
controller: _passwordController,
focusNode: _passwordFocus,
validator: (value) {
String? err = validatePassword(value);
if (err != null) {
_passwordFocus.requestFocus();
}
return err;
},
),
String? validateEmail(String? value) {
String pattern = r"^[a-zA-Z0-9.!#$%&'* /=?^_`{|}~-] @[a-zA-Z0-9](?:[a-zA-Z0-9-]"
r"{0,253}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]"
r"{0,253}[a-zA-Z0-9])?)*$";
RegExp regex = RegExp(pattern);
if (value == null || value.isEmpty || !regex.hasMatch(value)) {
return 'Enter a valid email address';
} else {
return null;
}
}
String? validatePassword(String? value) {
String pattern = r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$";
RegExp regex = RegExp(pattern);
if (value == null || value.isEmpty) {
return 'Required';
}
if (value.length < 8) {
return "Length should be 8 or more";
}
if (!regex.hasMatch(value)) {
return "Must contain atleast 1 uppecase, 1 lowercase, 1 special character,";
}
return null;
}
Ignore this silly paragraph:(This is just bunch of text, to tell SO that I have added more question details even if it is NOT required and NOT available)
CodePudding user response:
Wrap it with a form
widget and validate it only on a button click like the following.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
const appTitle = 'Form Validation Demo';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: const Text(appTitle),
),
body: const MyCustomForm(),
),
);
}
}
// Create a Form widget.
class MyCustomForm extends StatefulWidget {
const MyCustomForm({super.key});
@override
MyCustomFormState createState() {
return MyCustomFormState();
}
}
// Create a corresponding State class.
// This class holds data related to the form.
class MyCustomFormState extends State<MyCustomForm> {
// Create a global key that uniquely identifies the Form widget
// and allows validation of the form.
//
// Note: This is a GlobalKey<FormState>,
// not a GlobalKey<MyCustomFormState>.
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
// Build a Form widget using the _formKey created above.
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
// The validator receives the text that the user has entered.
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
// Validate returns true if the form is valid, or false otherwise.
if (_formKey.currentState!.validate()) {
// If the form is valid, display a snackbar. In the real world,
// you'd often call a server or save the information in a database.
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Processing Data')),
);
}
},
child: const Text('Submit'),
),
),
],
),
);
}
}
Check this for a detailed explanation https://docs.flutter.dev/cookbook/forms/validation
Edit
Please remove focus request if it's null. That will always keep the focus on password field if both are null
CodePudding user response:
Problem
You have 2 validators and the last one will work the last. That means if your both TextFormField is not valid the last one always works last and your focus goes to the last one.
Solution
check the other focusNode inside of another and focus password area if email focusNode has not focused like below
//Password
TextFormField(
controller: _passwordController,
focusNode: _passwordFocus,
validator: (value) {
String? err = validatePassword(value);
if (err != null) {
if(!_emailFocus.hasFocus){
_passwordFocus.requestFocus();
}
}
return err;
},
),