signal_form 0.2.0
signal_form: ^0.2.0 copied to clipboard
Schema-based, strongly typed form management for Flutter. Declarative validation, fluent API, built-in widgets, and high performance.
0.2.0 #
New features #
- Mask support on typed
Field<T>—field.mask(pattern)now works correctly on typed fields (e.g.Field<int>,Field<double>). The mask is applied to the display text while the typed value is handled separately throughparse. Thetextgetter on genericFieldwidgets now returns the masked string representation. - Mask support on computed
Field.computed<DateTime>— Computed fields of typeDateTimecan now be decorated with a mask (e.g.maskData(),maskHora()). The formatted text is exposed via the.textgetter and can be bound directly to a read-onlyTextField. - Centralized text formatting logic — Internal text formatting was unified into a single code path shared by both regular and computed fields, eliminating duplication and ensuring consistent mask behavior across field types.
0.1.0 #
New features — Field<T> #
field.parse(fn)— Registers aString → Tconverter applied automatically when a string value is assigned. Enables connecting aTextFieldto a typed field likeField<int>orField<DateTime>without a separate adapter layer. When used together withmask, the mask runs first, then parse — soField<DateTime>('birth').mask('##/##/####').parse(parseDate)receives the already-formatted string.field.transform(fn)— Normalizes the value before storing it (e.g. trim, lowercase, force prefix). Runs after mask and parse in the assignment pipeline. Composable via chaining.field.reset({to})—reset()without arguments continues to restoreinitialValue. The new named parameterto:resets to an arbitrary value instead;isDirtyreflects the comparison againstinitialValue. Supportsreset(to: null)to explicitly set null.field.clearError()— Clears the current validation error without re-running validators. Useful for dismissing errors fromsetErrorsorinvalidatewithout triggering a full re-validation cycle.field.disable()/field.enable()— Disable a field to skip all its validators and exclude it from form-level checks. Re-enable to restore normal behavior. Disabled fields can be excluded fromtoJsonviaomitDisabled: true.Field.detached<T>(name, [initialValue])— Static factory that creates aFieldoutside anyformCtrltracking context. Intended for tests and reusable helper functions.Field.computed<T>(name, (valueOf) => value)— Static factory that creates a derived, read-only field. The value is recomputed automatically whenever any field in the form changes. The setter throwsUnsupportedError;isDirtyis alwaysfalse;reset()is a no-op. Computed fields appear intoJsonand are excluded fromcompletionPercent.
New features — FormController #
form.fromJson(map, {setAsInitial: false})— Populates fields from a JSON map (nested maps are automatically flattened to dot-notation paths). WhensetAsInitial: true, also updates each field'sinitialValueso that a subsequentreset()returns to the loaded data anddirtyValues()diffs correctly. Designed for edit-form (CRUD) patterns.form.dirtyValues()— Returns a nested map containing only the fields whose value differs frominitialValue. Use as the body of a PATCH request to send only what the user changed.form.clearErrors({path})— Clears all validation errors across the form in a single batched notification. Passpath:to restrict clearing to a specific field or group prefix.form.setErrors(Map<String, String>, {shouldFocusFirst, shouldScrollFirst})— Batch-applies server-side validation errors. Each map key is a dot-notation field path; unmatched keys are silently ignored. All updates are batched into a single notification.form.toQueryString({omitNulls: true})— Serializes the form as a URL query string. Nested maps are flattened with dot notation; values are percent-encoded.form.completionPercent— Returns adoublefrom0.0to1.0representing the fraction of non-disabled, non-computed fields that have a non-null, non-empty value.
Breaking changes #
Field.valuesetter now acceptsdynamicinstead ofT?. This enables assigning string input directly to typed fields (e.g.field<int>.value = '25'routes throughparse). Side effect: untyped empty list literals lose type inference —field.value = []on aField<List<String>>raises a runtime cast error. Usefield.value = <String>[]instead. Non-empty literals (['a', 'b']) and typed variables are unaffected.
Bug fixes #
oneOferror message no longer concatenates the allowed values. Previously the error was'$message admin, editor, viewer'; it is now'$message'exactly as provided.required()generic validator now works correctly on anyField<T>. The generic extension onField<T>checks fornull. OnField<String>, the more specific string extension continues to apply (also checks empty/whitespace).
0.0.1 #
- Initial release.
- Schema-based form management with strongly typed
Field<T>declarations. - Fluent validation API — chain sync and async validators in a single expression.
- Built-in validators for strings, numbers, dates, booleans, lists, and date ranges.
- Validation modes:
onChange,onBlur, andonSubmit. - Conditional validation with
applyWhenand cross-field references viavalueOf. - Debounce support for keystroke-heavy fields.
- Auto-scroll to first invalid field on
submit()andtrigger(). - Ready-made Material widgets:
SignalTextField,SignalDropdown,SignalCheckbox,SignalSwitch,SignalRadioGroup,SignalCheckboxGroup,SignalSlider,SignalRangeSlider,SignalDateTimePicker,SignalDateRangePicker,SignalChoiceChip,SignalFilterChip. SignalFormField<T>escape hatch to wrap any custom widget.
Input masking
Generic mask via field.mask(pattern) where # matches any alphanumeric character. Mask characters are stripped from toJson output by default (removeMaskOnJson: true).
Brazilian ready-made formatters:
| Method | Format |
|---|---|
maskCPF() |
XXX.XXX.XXX-XX |
maskCNPJ() |
99.999.999/9999-99 |
maskCNPJAlfanumerico() |
XX.XXX.XXX/XXXX-XX (alphanumeric, 2024 format) |
maskCPFOrCNPJ() |
dynamic — CPF up to 11 digits, CNPJ from 12 |
maskCEP({ponto}) |
XX.XXX-XXX / XXXXX-XXX |
maskCelular() |
(99) 99999-9999 |
maskData() |
DD/MM/YYYY |
maskHora() |
HH:mm |
maskDecimal({casasDecimais}) |
9.999.999.999,00 |
maskReal() |
999.999.999.999 |
maskCartaoCredito() |
0000 1111 2222 3333 |
maskCartaoTelefone() |
000 1111 2222 3333 |
maskAltura() |
1,82 |
maskPeso() |
103,8 |
maskTemperatura() |
10,8 |
maskKm() |
000.000 |
maskValidade({maxLength}) |
MM/AA / MM/AAAA |
maskIOF() |
1,234567 |
maskNCM() |
XXXX.XX.XX |
maskCEST() |
XX.XXX.XX |
maskNUP() |
XXXXXXX-XX.XXXX.X.XX.XXXX |
maskPlacaVeiculo() |
XXX-XXXX (old and Mercosul, uppercased) |
maskCertidaoNascimento() |
birth certificate (32 digits) |