Popular Searches
Popular Course Categories
Popular Courses

Form Validation in Flutter

Form Validation in Flutter

Flutter Forms & User Input


Form Validation in Flutter


Form validation is the process of checking whether the data entered by a user is correct, complete, and follows the required rules before the application accepts or processes it. Flutter provides built-in widgets such as Form, FormField, and TextFormField to create and validate forms efficiently.


Flutter's Form widget groups multiple form fields, while FormState.validate() validates the descendant form fields. A field's validator returns an error message when the value is invalid and null when the value is valid.




1. Why Form Validation Is Important


Applications frequently collect information such as names, email addresses, passwords, phone numbers, dates, and other user details. Validation helps ensure that the entered information meets the expected requirements.



  • Prevents empty or incomplete fields.

  • Checks whether data follows a required format.

  • Improves the user experience by showing meaningful error messages.

  • Reduces invalid data being submitted to a server or database.

  • Helps maintain consistent application data.

  • Provides immediate feedback to users.




2. Important Flutter Widgets Used for Validation











Widget/ClassPurpose
FormGroups multiple form fields and provides form-level operations.
FormFieldRepresents a single form field and manages its state and validation.
TextFormFieldProvides a text input field integrated with Form and validation.
GlobalKeyProvides access to the form state so the form can be validated, saved, or reset.
validatorContains the validation rules for a form field.
FormStateProvides methods such as validate(), save(), and reset().
AutovalidateModeControls when validation errors are automatically displayed.



3. Basic Form Validation Flow



  1. Create a Form.

  2. Create a GlobalKey.

  3. Assign the key to the Form.

  4. Add one or more TextFormField widgets.

  5. Add validation logic using the validator property.

  6. Call _formKey.currentState!.validate() when the user submits the form.

  7. Process the data only when validation returns true.




4. Creating a GlobalKey for Form Validation


A GlobalKey can be used to access the state of a form. It allows the application to call methods such as validate(), save(), and reset().


final _formKey = GlobalKey();

The key is then assigned to the Form widget:


Form(
  key: _formKey,
  child: Column(
    children: [
      // Form fields
    ],
  ),
)



5. TextFormField


TextFormField is commonly used when a text input needs to participate in form validation. It combines text input functionality with FormField behavior.


TextFormField(
  decoration: const InputDecoration(
    labelText: 'Name',
    border: OutlineInputBorder(),
  ),
)



6. The validator Property


The validator function contains the rules used to check the value entered into a field.


A validator should return an error message when the value is invalid and null when the value is valid.


TextFormField(
  validator: (value) {
    if (value == null || value.isEmpty) {
      return 'Please enter your name';
    }
    return null;
  },
)

How It Works



  • value contains the current input.

  • If the value is invalid, return a descriptive error message.

  • If the value is valid, return null.




7. Required Field Validation


The simplest validation rule checks whether the user has entered a value.


TextFormField(
  decoration: const InputDecoration(
    labelText: 'Username',
  ),
  validator: (value) {
    if (value == null || value.trim().isEmpty) {
      return 'Username is required';
    }
    return null;
  },
)

Using trim() helps prevent input containing only spaces from being accepted.




8. Minimum Length Validation


Minimum-length validation is useful for passwords, usernames, addresses, and other fields where a certain amount of information is required.


TextFormField(
  decoration: const InputDecoration(
    labelText: 'Username',
  ),
  validator: (value) {
    if (value == null || value.trim().isEmpty) {
      return 'Username is required';
    }
    if (value.trim().length < 4) {
      return 'Username must contain at least 4 characters';
    }
    return null;
  },
)



9. Maximum Length Validation


Maximum length validation prevents users from entering excessively long values.


TextFormField(
  maxLength: 20,
  decoration: const InputDecoration(
    labelText: 'Username',
  ),
  validator: (value) {
    if (value != null && value.length > 20) {
      return 'Username cannot exceed 20 characters';
    }
    return null;
  },
)



10. Email Validation


Email validation checks whether the entered value follows a reasonable email structure.


TextFormField(
  keyboardType: TextInputType.emailAddress,
  decoration: const InputDecoration(
    labelText: 'Email',
  ),
  validator: (value) {
    if (value == null || value.trim().isEmpty) {
      return 'Email is required';
    }
    final email = value.trim();
    final emailPattern = RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$');
    if (!emailPattern.hasMatch(email)) {
      return 'Enter a valid email address';
    }
    return null;
  },
)

Client-side email validation should be treated as a basic format check. A backend should perform its own validation and business-rule checks.




11. Password Validation


Password validation can check minimum length and other requirements.


TextFormField(
  obscureText: true,
  decoration: const InputDecoration(
    labelText: 'Password',
  ),
  validator: (value) {
    if (value == null || value.isEmpty) {
      return 'Password is required';
    }
    if (value.length < 8) {
      return 'Password must contain at least 8 characters';
    }
    return null;
  },
)

Password Validation with Multiple Rules


validator: (value) {
  if (value == null || value.isEmpty) {
    return 'Password is required';
  }
  if (value.length < 8) {
    return 'Password must contain at least 8 characters';
  }
  if (!RegExp(r'[A-Z]').hasMatch(value)) {
    return 'Password must contain an uppercase letter';
  }
  if (!RegExp(r'[0-9]').hasMatch(value)) {
    return 'Password must contain a number';
  }
  return null;
}



12. Confirm Password Validation


A confirm-password field should match the original password.


final passwordController = TextEditingController();

TextFormField(
  controller: passwordController,
  obscureText: true,
  decoration: const InputDecoration(
    labelText: 'Password',
  ),
  validator: (value) {
    if (value == null || value.isEmpty) {
      return 'Password is required';
    }
    if (value.length < 8) {
      return 'Password must contain at least 8 characters';
    }
    return null;
  },
),

TextFormField(
  obscureText: true,
  decoration: const InputDecoration(
    labelText: 'Confirm Password',
  ),
  validator: (value) {
    if (value == null || value.isEmpty) {
      return 'Please confirm your password';
    }
    if (value != passwordController.text) {
      return 'Passwords do not match';
    }
    return null;
  },
)




13. Phone Number Validation


A phone number can be validated according to the format required by the application.


TextFormField(
  keyboardType: TextInputType.phone,
  decoration: const InputDecoration(
    labelText: 'Phone Number',
  ),
  validator: (value) {
    if (value == null || value.trim().isEmpty) {
      return 'Phone number is required';
    }
    final phone = value.trim();
    if (!RegExp(r'^[0-9]{10}$').hasMatch(phone)) {
      return 'Enter a valid 10-digit phone number';
    }
    return null;
  },
)



14. Numeric Validation


Numeric validation can be used for fields such as age, quantity, price, or PIN values.


TextFormField(
  keyboardType: TextInputType.number,
  decoration: const InputDecoration(
    labelText: 'Age',
  ),
  validator: (value) {
    if (value == null || value.isEmpty) {
      return 'Age is required';
    }
    final age = int.tryParse(value);
    if (age == null) {
      return 'Enter a valid number';
    }
    if (age < 18) {
      return 'Age must be 18 or above';
    }
    return null;
  },
)



15. Validating a Form on Submit


When the user presses the submit button, call validate() on the form state.


ElevatedButton(
  onPressed: () {
    if (_formKey.currentState!.validate()) {
      print('Form is valid');
    } else {
      print('Form contains errors');
    }
  },
  child: const Text('Submit'),
)

The validate() method validates every descendant FormField. It returns true when there are no validation errors and false when one or more fields are invalid.




16. Complete Basic Validation Example


import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const RegistrationPage(),
    );
  }
}

class RegistrationPage extends StatefulWidget {
  const RegistrationPage({super.key});

  @override
  State createState() => _RegistrationPageState();
}

class _RegistrationPageState extends State {
  final _formKey = GlobalKey();

  final nameController = TextEditingController();
  final emailController = TextEditingController();
  final passwordController = TextEditingController();

  @override
  void dispose() {
    nameController.dispose();
    emailController.dispose();
    passwordController.dispose();
    super.dispose();
  }

  void submitForm() {
    if (_formKey.currentState!.validate()) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('Form submitted successfully'),
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Registration'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Form(
          key: _formKey,
          child: ListView(
            children: [
              TextFormField(
                controller: nameController,
                decoration: const InputDecoration(
                  labelText: 'Name',
                  border: OutlineInputBorder(),
                ),
                validator: (value) {
                  if (value == null || value.trim().isEmpty) {
                    return 'Name is required';
                  }
                  return null;
                },
              ),
              const SizedBox(height: 16),
              TextFormField(
                controller: emailController,
                keyboardType: TextInputType.emailAddress,
                decoration: const InputDecoration(
                  labelText: 'Email',
                  border: OutlineInputBorder(),
                ),
                validator: (value) {
                  if (value == null || value.trim().isEmpty) {
                    return 'Email is required';
                  }
                  final email = value.trim();
                  final pattern =
                      RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$');
                  if (!pattern.hasMatch(email)) {
                    return 'Enter a valid email';
                  }
                  return null;
                },
              ),
              const SizedBox(height: 16),
              TextFormField(
                controller: passwordController,
                obscureText: true,
                decoration: const InputDecoration(
                  labelText: 'Password',
                  border: OutlineInputBorder(),
                ),
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return 'Password is required';
                  }
                  if (value.length < 8) {
                    return 'Password must contain at least 8 characters';
                  }
                  return null;
                },
              ),
              const SizedBox(height: 20),
              ElevatedButton(
                onPressed: submitForm,
                child: const Text('Register'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}




17. AutovalidateMode


Flutter provides AutovalidateMode to control when validation happens automatically.







ModeDescription
AutovalidateMode.disabledAutomatic validation is disabled.
AutovalidateMode.alwaysValidation runs automatically whenever the field is rebuilt.
AutovalidateMode.onUserInteractionValidation occurs after the user interacts with the field.

Example


TextFormField(
  autovalidateMode: AutovalidateMode.onUserInteraction,
  decoration: const InputDecoration(
    labelText: 'Email',
  ),
  validator: (value) {
    if (value == null || value.isEmpty) {
      return 'Email is required';
    }
    return null;
  },
)



18. Validating Multiple Fields


A single Form can contain multiple fields. Calling validate() validates all descendant form fields.


Form(
  key: _formKey,
  child: Column(
    children: [
      TextFormField(
        validator: (value) {
          if (value == null || value.isEmpty) {
            return 'Name is required';
          }
          return null;
        },
      ),
      TextFormField(
        validator: (value) {
          if (value == null || value.isEmpty) {
            return 'Email is required';
          }
          return null;
        },
      ),
      TextFormField(
        validator: (value) {
          if (value == null || value.length < 8) {
            return 'Password must contain at least 8 characters';
          }
          return null;
        },
      ),
    ],
  ),
)



19. FormState Methods








MethodPurpose
validate()Validates the form fields and returns a boolean result.
save()Calls the onSaved callback of descendant form fields.
reset()Resets form fields to their initial values and resets their validation state.
validateGranularly()Validates the form and returns the invalid form-field states.



20. Using onSaved


The onSaved callback can be used to store validated field values when FormState.save() is called.


String name = '';

TextFormField(
  decoration: const InputDecoration(
    labelText: 'Name',
  ),
  onSaved: (value) {
    name = value ?? '';
  },
)


Then save the form:


if (_formKey.currentState!.validate()) {
  _formKey.currentState!.save();
  print(name);
}



21. Resetting a Form


The reset() method restores form fields to their initial state.


ElevatedButton(
  onPressed: () {
    _formKey.currentState!.reset();
  },
  child: const Text('Reset'),
)



22. Using TextEditingController with Validation


TextEditingController is useful when the application needs to read, modify, or compare field values.


final emailController = TextEditingController();

TextFormField(
  controller: emailController,
  decoration: const InputDecoration(
    labelText: 'Email',
  ),
  validator: (value) {
    if (value == null || value.isEmpty) {
      return 'Email is required';
    }
    return null;
  },
)


When controllers are created in a stateful widget, dispose of them when they are no longer needed.


@override
void dispose() {
  emailController.dispose();
  super.dispose();
}



23. Checkbox Validation


Some forms require the user to accept terms and conditions. A checkbox can be validated by maintaining its value in state.


bool acceptedTerms = false;

CheckboxListTile(
  title: const Text('I accept the Terms and Conditions'),
  value: acceptedTerms,
  onChanged: (value) {
    setState(() {
      acceptedTerms = value ?? false;
    });
  },
)


Before submitting the form, check the checkbox value:


if (!acceptedTerms) {
  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(
      content: Text('Please accept the Terms and Conditions'),
    ),
  );
  return;
}

if (_formKey.currentState!.validate()) {
  print('Form is valid');
}




24. Dropdown Validation


Dropdown form fields can also participate in form validation.


String? selectedRole;

DropdownButtonFormField(
  value: selectedRole,
  decoration: const InputDecoration(
    labelText: 'Role',
    border: OutlineInputBorder(),
  ),
  items: const [
    DropdownMenuItem(
      value: 'student',
      child: Text('Student'),
    ),
    DropdownMenuItem(
      value: 'developer',
      child: Text('Developer'),
    ),
    DropdownMenuItem(
      value: 'designer',
      child: Text('Designer'),
    ),
  ],
  onChanged: (value) {
    setState(() {
      selectedRole = value;
    });
  },
  validator: (value) {
    if (value == null) {
      return 'Please select a role';
    }
    return null;
  },
)




25. Radio Button Validation


Radio buttons are useful when the user must choose one option from a group. The selected value can be checked before submission.


String? gender;

RadioListTile(
  title: const Text('Male'),
  value: 'male',
  groupValue: gender,
  onChanged: (value) {
    setState(() {
      gender = value;
    });
  },
)

RadioListTile(
  title: const Text('Female'),
  value: 'female',
  groupValue: gender,
  onChanged: (value) {
    setState(() {
      gender = value;
    });
  },
)




26. Date Validation


Date values can be validated according to application requirements. For example, an application can require a date to be selected before submission.


DateTime? selectedDate;

Future selectDate() async {
  final picked = await showDatePicker(
    context: context,
    initialDate: DateTime.now(),
    firstDate: DateTime(2000),
    lastDate: DateTime(2100),
  );

  if (picked != null) {
    setState(() {
      selectedDate = picked;
    });
  }
}


Before submission:


if (selectedDate == null) {
  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(
      content: Text('Please select a date'),
    ),
  );
  return;
}



27. Creating Reusable Validators


When an application contains many forms, reusable validation functions can reduce duplicated code.


String? validateRequired(String? value, String fieldName) {
  if (value == null || value.trim().isEmpty) {
    return '$fieldName is required';
  }
  return null;
}

String? validateEmail(String? value) {
  if (value == null || value.trim().isEmpty) {
    return 'Email is required';
  }

  final pattern = RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$');

  if (!pattern.hasMatch(value.trim())) {
    return 'Enter a valid email';
  }

  return null;
}

String? validatePassword(String? value) {
  if (value == null || value.isEmpty) {
    return 'Password is required';
  }

  if (value.length < 8) {
    return 'Password must contain at least 8 characters';
  }

  return null;
}


These validators can then be reused:


TextFormField(
  validator: (value) {
    return validateRequired(value, 'Name');
  },
)

TextFormField(
  validator: validateEmail,
)

TextFormField(
  obscureText: true,
  validator: validatePassword,
)




28. Combining Multiple Validation Rules


Multiple rules can be applied to the same field.


validator: (value) {
  if (value == null || value.trim().isEmpty) {
    return 'Username is required';
  }

  final username = value.trim();

  if (username.length < 4) {
    return 'Username must contain at least 4 characters';
  }

  if (username.length > 20) {
    return 'Username cannot exceed 20 characters';
  }

  if (!RegExp(r'^[a-zA-Z0-9_]+$').hasMatch(username)) {
    return 'Only letters, numbers, and underscore are allowed';
  }

  return null;
}




29. Focus Management After Validation


For multi-field forms, FocusNode can be used to move the user's focus between fields.


final emailFocus = FocusNode();

TextFormField(
  textInputAction: TextInputAction.next,
  onFieldSubmitted: (_) {
    FocusScope.of(context).requestFocus(emailFocus);
  },
)

TextFormField(
  focusNode: emailFocus,
  keyboardType: TextInputType.emailAddress,
)


Focus nodes should be disposed when they are no longer needed.


@override
void dispose() {
  emailFocus.dispose();
  super.dispose();
}



30. Validation and Submission Flow


A typical form submission process can follow these steps:



  1. User enters information.

  2. Application performs client-side validation.

  3. Invalid fields display useful error messages.

  4. User corrects the invalid fields.

  5. The form is validated again.

  6. When validation succeeds, the application prepares the data.

  7. The application can then send the data to an API or save it locally.

  8. The server should perform its own validation before accepting or processing the data.




31. Example: Registration Form with Complete Validation


import 'package:flutter/material.dart';

class RegistrationForm extends StatefulWidget {
  const RegistrationForm({super.key});

  @override
  State createState() => _RegistrationFormState();
}

class _RegistrationFormState extends State {
  final _formKey = GlobalKey();

  final nameController = TextEditingController();
  final emailController = TextEditingController();
  final passwordController = TextEditingController();
  final confirmPasswordController = TextEditingController();

  bool acceptedTerms = false;

  @override
  void dispose() {
    nameController.dispose();
    emailController.dispose();
    passwordController.dispose();
    confirmPasswordController.dispose();
    super.dispose();
  }

  void submit() {
    if (!acceptedTerms) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('Please accept the Terms and Conditions'),
        ),
      );
      return;
    }

    if (!_formKey.currentState!.validate()) {
      return;
    }

    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(
        content: Text('Registration successful'),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Create Account'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Form(
          key: _formKey,
          child: ListView(
            children: [
              TextFormField(
                controller: nameController,
                decoration: const InputDecoration(
                  labelText: 'Full Name',
                  border: OutlineInputBorder(),
                ),
                validator: (value) {
                  if (value == null || value.trim().isEmpty) {
                    return 'Full name is required';
                  }
                  if (value.trim().length < 3) {
                    return 'Name must contain at least 3 characters';
                  }
                  return null;
                },
              ),
              const SizedBox(height: 16),
              TextFormField(
                controller: emailController,
                keyboardType: TextInputType.emailAddress,
                decoration: const InputDecoration(
                  labelText: 'Email Address',
                  border: OutlineInputBorder(),
                ),
                validator: (value) {
                  if (value == null || value.trim().isEmpty) {
                    return 'Email is required';
                  }

                  final pattern =
                      RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$');

                  if (!pattern.hasMatch(value.trim())) {
                    return 'Enter a valid email address';
                  }

                  return null;
                },
              ),
              const SizedBox(height: 16),
              TextFormField(
                controller: passwordController,
                obscureText: true,
                decoration: const InputDecoration(
                  labelText: 'Password',
                  border: OutlineInputBorder(),
                ),
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return 'Password is required';
                  }

                  if (value.length < 8) {
                    return 'Password must contain at least 8 characters';
                  }

                  return null;
                },
              ),
              const SizedBox(height: 16),
              TextFormField(
                controller: confirmPasswordController,
                obscureText: true,
                decoration: const InputDecoration(
                  labelText: 'Confirm Password',
                  border: OutlineInputBorder(),
                ),
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return 'Please confirm your password';
                  }

                  if (value != passwordController.text) {
                    return 'Passwords do not match';
                  }

                  return null;
                },
              ),
              const SizedBox(height: 10),
              CheckboxListTile(
                contentPadding: EdgeInsets.zero,
                title: const Text(
                  'I accept the Terms and Conditions',
                ),
                value: acceptedTerms,
                onChanged: (value) {
                  setState(() {
                    acceptedTerms = value ?? false;
                  });
                },
              ),
              const SizedBox(height: 10),
              ElevatedButton(
                onPressed: submit,
                child: const Text('Create Account'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}




32. Validation Error Messages


Good validation messages should clearly explain what the user needs to fix.









Bad MessageBetter Message
InvalidEnter a valid email address
ErrorPassword must contain at least 8 characters
RequiredEmail address is required
WrongPasswords do not match
Not validEnter a valid 10-digit phone number



33. Client-Side and Server-Side Validation


Client-Side Validation


Client-side validation happens inside the Flutter application before the request is sent to a server.



  • Provides quick feedback.

  • Improves user experience.

  • Prevents obvious invalid submissions.

  • Reduces unnecessary requests.


Server-Side Validation


Server-side validation happens on the backend. It is essential because client-side validation can be bypassed.



  • Never trust client-side validation alone.

  • Validate incoming API data on the server.

  • Apply authentication and authorization rules on the server.

  • Validate business rules before saving data.




34. Common Mistakes in Flutter Form Validation



  • Forgetting to assign the GlobalKey to the Form.

  • Calling validate() without checking its return value.

  • Returning an error message even when the input is valid.

  • Forgetting to return null for valid input.

  • Using unclear validation messages.

  • Creating a new GlobalKey inside the build() method.

  • Forgetting to dispose TextEditingController objects.

  • Using only client-side validation for security.

  • Allowing whitespace-only input.

  • Not handling loading or duplicate submissions.




35. Best Practices for Form Validation



  • Use Form to group related fields.

  • Use GlobalKey when convenient for accessing form state.

  • Keep validation rules simple and readable.

  • Use reusable validator functions for repeated rules.

  • Display clear and actionable error messages.

  • Use appropriate keyboard types such as email and phone keyboards.

  • Use autovalidateMode carefully so validation does not become distracting.

  • Dispose controllers and focus nodes in dispose().

  • Validate the form before submitting data.

  • Perform server-side validation as well.

  • Disable or guard against duplicate submissions while an API request is running.

  • Use scrollable layouts for long forms.

  • Keep sensitive information such as passwords protected and never log passwords.




36. Practical Exercise


Create a Flutter registration form containing the following fields:



  1. Full Name

  2. Email Address

  3. Phone Number

  4. Password

  5. Confirm Password

  6. Age

  7. Gender

  8. Country

  9. Terms and Conditions checkbox


Apply the following validation rules:



  • Name should not be empty.

  • Email should follow a valid email format.

  • Phone number should contain the required number of digits.

  • Password should contain at least 8 characters.

  • Confirm Password should match Password.

  • Age should be a valid number and meet the application's minimum-age requirement.

  • Gender should be selected.

  • Country should be selected.

  • Terms and Conditions must be accepted.




37. Interview Questions


Q1. What is form validation in Flutter?


Form validation is the process of checking whether user-entered data satisfies predefined rules before the application processes or submits it.


Q2. What is the purpose of the Form widget?


The Form widget groups form fields and provides access to operations such as validation, saving, and resetting.


Q3. What is GlobalKey used for?


It provides access to the state of a specific form, allowing methods such as validate(), save(), and reset() to be called.


Q4. What does validator return?


The validator returns an error message when the input is invalid and null when the input is valid.


Q5. What does validate() return?


FormState.validate() returns true when the descendant form fields have no validation errors and false when validation errors exist.


Q6. What is the difference between TextField and TextFormField?


TextField provides general text input, while TextFormField integrates text input with FormField functionality and can participate in form validation.


Q7. Why should TextEditingController be disposed?


A controller manages resources associated with text editing and should be disposed when it is no longer needed.




38. Quick Revision



  • Form groups multiple fields.

  • GlobalKey can access form state.

  • TextFormField is commonly used for validated text input.

  • validator contains field validation logic.

  • Return an error message for invalid input.

  • Return null for valid input.

  • validate() checks all descendant form fields.

  • save() triggers onSaved callbacks.

  • reset() restores fields to their initial state.

  • AutovalidateMode controls automatic validation.

  • Client-side validation improves UX but does not replace server-side validation.

  • Dispose controllers and focus nodes when they are no longer needed.




39. Official Flutter Resources





40. JustAcademy Flutter Training Resources





Key Takeaways


Flutter provides a structured approach to form validation using Form, FormField, TextFormField, validators, and FormState. The most common workflow is to create a GlobalKey, attach it to a Form, define validation rules through validator, and call validate() before processing the submitted data.


whatsapp