Flutter custom FormField : validate and save methods are not called
Answer 1: Put the build method for the Builder
Replace the FormField's builder
builder: (FormFieldState<LocationData> state) {
return state.build(state.context);
});
with your custom builder function
builder: (FormFieldState<LocationData> state) {
return Column(
children: <Widget>[
TextField(
focusNode: _addressInputFocusNode,
controller: _addressInputController,
decoration: InputDecoration(labelText: 'Address'),
),
SizedBox(height: 10.0),
FlatButton(
color: Colors.deepPurpleAccent,
textColor: Colors.white,
child: Text('Locate me !'),
onPressed: _updateLocation,
),
],
});
Answer 2: Pseudo CustomFormFieldState
You can't extend the FormFieldState because overriding the "build" function causes errors (explained below)
However you can create a Widget that takes the FormFieldState as a parameter to make it a separate class to act like it extends the FormFieldState (which seems a bit cleaner to me then the above method)
class CustomFormField extends FormField<List<String>> {
CustomFormField({
List<String> initialValue,
FormFieldSetter<List<String>> onSaved,
FormFieldValidator<List<String>> validator,
}) : super(
autovalidate: false,
onSaved: onSaved,
validator: validator,
initialValue: initialValue ?? List(),
builder: (state) {
return CustomFormFieldState(state);
});
}
class CustomFormFieldState extends StatelessWidget {
FormFieldState<List<String>> state;
CustomFormFieldState(this.state);
@override
Widget build(BuildContext context) {
return Container(), //The Widget(s) to build your form field
}
}
Explanation
The reason why extending the FormFieldState doesn't work is because overriding the build method in the FormFieldState object causes the FormFieldState to not be registered with the Form itself.
Below is a list of functions that I followed to get my explanation
1) Your _LocationFormFieldState overrides the build method which means the build method of the FormFieldState never executes
@override
Widget build(BuildContext context)
2) The build method the FormFieldState registers itself to the current FormState
///function in FormFieldState
Widget build(BuildContext context) {
// Only autovalidate if the widget is also enabled
if (widget.autovalidate && widget.enabled)
_validate();
Form.of(context)?._register(this);
return widget.builder(this);
}
3) The FormState then saves the FormFieldState in a List
void _register(FormFieldState<dynamic> field) {
_fields.add(field);
}
4) Then when the FormState saves/validates it loops through the list of FormFieldStates
/// Saves every [FormField] that is a descendant of this [Form].
void save() {
for (FormFieldState<dynamic> field in _fields)
field.save();
}
By overriding the build method you cause the FormField to not be registered with the Form, which is why saving and loading the Form doesn't call the methods of your custom FormField.
If the FormState._register() method was public instead of private you could call this method in your _LocationFormFieldState.build method to register your app to the form, but sadly since it is a private function you cannot.
Also note that if you were to call the super.build() function in your CustomFormFieldState's build method it leads to a StackOverflow
@override
Widget build(BuildContext context) {
super.build(context); //leads to StackOverflow!
return _buildFormField(); //anything you want
}