Can we get the list of fields in error from Flutter forms after validation ? This will help developers use focus-nodes to redirect the attention to the field in error.
Flutter Forms: Get the list of fields in error
2.7k Views Asked by vzurd At
2
There are 2 best solutions below
1

Expanding on Guillaume's answer, I've wrapped the functionality into a reusable class.
You can view a working example on DartPad here: https://www.dartpad.dev/61c4ccddbf29a343c971ee75e60d1038
import 'package:flutter/material.dart';
class FormValidationManager {
final _fieldStates = Map<String, FormFieldValidationState>();
FocusNode getFocusNodeForField(key) {
_ensureExists(key);
return _fieldStates[key].focusNode;
}
FormFieldValidator<T> wrapValidator<T>(String key, FormFieldValidator<T> validator) {
_ensureExists(key);
return (input) {
final result = validator(input);
_fieldStates[key].hasError = (result?.isNotEmpty ?? false);
return result;
};
}
List<FormFieldValidationState> get erroredFields =>
_fieldStates.entries.where((s) => s.value.hasError).map((s) => s.value).toList();
void _ensureExists(String key) {
_fieldStates[key] ??= FormFieldValidationState(key: key);
}
void dispose() {
_fieldStates.entries.forEach((s) {
s.value.focusNode.dispose();
});
}
}
class FormFieldValidationState {
final String key;
bool hasError;
FocusNode focusNode;
FormFieldValidationState({@required this.key})
: hasError = false,
focusNode = FocusNode();
}
To use it, create your forms as usual, but add a FormValidationManager
to your state class, and then use that instance to wrap your validation methods.
Usage:
class _MyWidgetState extends State<MyWidget> {
final _formKey = GlobalKey<FormState>();
final _formValidationManager = FormValidationManager();
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
focusNode: _formValidationManager.getFocusNodeForField('field1'),
validator: _formValidationManager.wrapValidator('field1', (value) {
if (value.isEmpty) {
return 'Please enter a value';
}
return null;
})),
TextFormField(
focusNode: _formValidationManager.getFocusNodeForField('field2'),
validator: _formValidationManager.wrapValidator('field2', (value) {
if (value.isEmpty) {
return 'Please enter a value';
}
return null;
})),
ElevatedButton(
onPressed: () {
if (!_formKey.currentState.validate()) {
_formValidationManager.erroredFields.first.focusNode.requestFocus();
}
},
child: Text('SUBMIT'))
],
),
);
}
@override
void dispose() {
_formValidationManager.dispose();
super.dispose();
}
}
I don't think it is possible to get this kind of information from a
Form
object or aFormState
.But here is a way around to obtain the result you want (focus on the field in error) :
You simply need to create a
FocusNode
for each one of your fields, thanks to that you will be abla to callrequestFocus
on a precise field (in your case a field considered as invalid). Then in thevalidator
property of your form field, as it is the method called by theFormState.validate()
, you need to set a temporary variable which will contains the rightFocusNode
. In my example I only set the variable_fieldToFocus
if it was not already assigned using the??=
operator. After requesting the focus on the node I set_fieldToFocus
back tonull
so it will still works for another validation.You can try the full test code I have used on DartPad.
Sorry if I have derived a bit from your question but I still hope this will help you.