keyed_form 0.1.0
keyed_form: ^0.1.0 copied to clipboard
A form-state controller for immutable aggregates, built on keyed_form_core: holds the working draft, FieldKey-addressed validation errors and touched/dirty/submit state.
0.1.0 #
Initial release.
KeyedFormController<Root>— owns the editable draft, theFieldKey-keyed validation errors, and the touched / dirty / revealed bookkeeping that decides when an error is shown.form.field(ref)— a statically-typed per-field facade (FieldHandle):set/update/value/error/dirty/key/touch(), pluslist()/dirtyRows()for a list field..set(value)rejects a wrongly-typed value at compile time. The raw primitives it funnels through (setField/updateField/list/mutateList) are@internal— a Dart inference hole means calling them directly would not catch a type mismatch until runtime; they become public again once thevariancelanguage feature lands (see the note inkeyed_form_controller.dart).KeyedFormMode— when a field's error becomes visible.- Scoped validation via
KeyedFormResolver/KeyedFormScopeOf. KeyedFormList— by-id list editing (append / insert / remove / move / update).KeyedFormSnapshot— an immutable copy of the coarse state.form.submit(onValid, {onInvalid})— validates, and on success runsonValidwith the current value while togglingsubmittingaround it; on failure runsonInvalidwith the visible error keys, if given. Returns whetheronValidran.keyed_form_flutter'shandleSubmitwraps this with a Flutter-aware defaultonInvalid(scroll to the first error).form.field(ref).isValidating/.validateAsync(check)— per-field async validation (e.g. "is this email already taken?"), for the case a synchronousresolvercan't cover.validateAsynctogglesisValidatingaroundcheck, merges a non-null result in as a server error on that field, and is safe against overlapping calls on the same field — a stale response can't clobber a newer one's result or reopen the spinner. The raw primitive underneath isKeyedFormController.setFieldValidating.validateAsyncaccepts optionaltimeout/onFailure. A thrown check, or one that exceedstimeout, marks the fieldisFailedValidation(FieldHandle.isFailedValidation/KeyedFormController.isFailedValidation) instead of propagating out of the returnedFutureor writing intoerrors— a technical fault is a different state from "the value is invalid". Not sticky: the nextvalidateAsynccall on the same field clears it, whether that call succeeds or fails in turn.form.field(ref).markReadOnly()/.unmarkReadOnly()/.isReadOnly(andKeyedFormController.markReadOnly/unmarkReadOnly/isReadOnly) freeze a field — and, byFieldKeyancestor coverage, everything nested under it — againstset/update/ list mutation, without affecting validation. Passforce: truetoset/update(and to everyKeyedFormListmutator) to write through the freeze anyway. Read-only status is configuration:seed()/reset()deliberately leave it in place, unlike the touched / revealed / validating / failed bookkeeping they clear.form.addRelation(source, select, onChange)— callsonChangewith the selected,==-deduplicated slice ofsourcewhenever it actually changes. Registering the relation does not itself callonChange. Returns a callback that unsubscribes it — call that from your owndispose(), since the controller does not track relations for you. Skips silently whilesourcedoes not resolve (for example, a row that has been removed from a list).