KeyedFormController<Root> is a pure Dart ChangeNotifier. It owns the immutable draft independently of Flutter widgets.
final form = KeyedFormController<LoginSchema>(
initialValue: LoginSchema.create(),
mode: KeyedFormMode.onChange,
resolver: LoginSchema.validateData,
);
form.seed(userFromApi); // establish a clean baseline
form.field(LoginFields.email).set('alice@example.com');
print(form.isDirty); // true
form.reset(); // restore the baseline
Load data and track edits#
Call seed(value) when loading an existing record. It sets the current value and the comparison baseline together.
isDirty tracks whether the draft differs from that baseline; differs(ref) checks one field.
Submit and dispose#
submit(onValid, onInvalid: ...) validates before invoking the async callback. In Flutter,
handleSubmit(context, onValid) also reveals and focuses the first invalid field. Dispose the controller with its owning state.
Read state reactively#
Use context.watchField for one value and context.selectForm for a derived slice.
KeyedFormSelector scopes the rebuild to a subtree. watchForm and KeyedFormBuilder
observe the whole draft, so prefer narrower reads when possible.