Home > Mobile >  Flutter Textformfield validator Focuses Last TextFormfield on validation error instead of first
Flutter Textformfield validator Focuses Last TextFormfield on validation error instead of first

Time:06-20

I've two TextFormFields, it focus on the password field on validation error, even if email field has error already & comes before password field.

enter image description here 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;
  },           
),    
  • Related