Forms, subforms and widgets
A form controller is the root of a tree of fields and subforms; a widget subscribes to exactly one field.
A form controller owns fields and subforms
AdvancedFormController is the root of a tree. Its direct leaves are the fields you registerFields, and its subtrees
are other form controllers attached with addSubform. Almost everything it does is a broadcast over that tree:
validate, resetAll, clearErrors, markReadOnly, setValidationMode, setValidationEnabled.
It is also a ValueListenable<AdvancedFormState>, whose derived members — canSubmit, validating,
hasFailedValidation, validationErrors — are computed from the live fields on every read, so they can never report
a field that has since been removed. Form-level state covers them.
Ownership follows registration: the form disposes every field it was ever given and every subform it was ever
attached, in its own dispose(). You dispose the form, and only the form. Lifecycle and ownership
has the full set of rules.
Widgets subscribe, one field each
AdvancedFieldBuilder<T, E> is a thin wrapper over ValueListenableBuilder that hides the state type argument. It
rebuilds its subtree when — and only when — its field notifies. A form with twenty fields rebuilds one input per
keystroke.
AdvancedFieldBuilder<bool, SignupError>(
field: form.acceptTerms,
builder: (context, state, _) => Switch(
value: state.value,
onChanged: form.acceptTerms.getValueSetter(), // null while read-only
),
)For anything the form as a whole knows — is it modified, is something validating — subscribe to the form controller
the same way with ValueListenableBuilder<AdvancedFormState>.