fabrik_forms
fabrik_forms is a clean, testable, UI-agnostic form state management system for Flutter. It handles field values, validation, dirty/touched state, and reactive rebuilds — without coupling your logic to any specific widget.
Installation
Section titled “Installation”Add the package to your pubspec.yaml:
dependencies: fabrik_forms: ^0.1.0Then run:
flutter pub getImport it in your Dart files:
import 'package:fabrik_forms/fabrik_forms.dart';Getting Started
Section titled “Getting Started”Define your form with typed fields and validators:
final formNotifier = FabrikFormNotifier<String>( FabrikForm({ 'email': FabrikField( value: '', validators: [const EmailValidator()], ), 'password': FabrikField( value: '', validators: [ const PasswordValidator( requireDigit: true, requireSpecialChar: true, ), ], ), }),);Wrap your UI with FabrikFormBuilder to rebuild reactively:
FabrikFormBuilder<String>( formNotifier: formNotifier, builder: (context, form, get) { final email = get<String>('email'); final password = get<String>('password');
return Column( children: [ TextField( onChanged: (val) => formNotifier.update('email', val), decoration: InputDecoration( labelText: 'Email', errorText: email.visibleError, ), ), TextField( onChanged: (val) => formNotifier.update('password', val), obscureText: true, decoration: InputDecoration( labelText: 'Password', errorText: password.visibleError, ), ), ElevatedButton( onPressed: () { if (form.isValid) { submit(form.values); } else { formNotifier.markAllTouched(); // reveal all errors } }, child: const Text('Sign In'), ), ], ); },);Field Metadata
Section titled “Field Metadata”Every FabrikField tracks:
| Property | Type | Description |
|---|---|---|
value | T | Current field value |
error | String? | Active validation error |
visibleError | String? | Error shown only after the field is touched |
isValid | bool | No active errors |
isTouched | bool | User has interacted with the field |
isDirty | bool | Value differs from initial |
Resetting a Form
Section titled “Resetting a Form”Call reset() to restore all fields to their initial values and clear all touched/dirty state:
formNotifier.reset();This is useful for clearing a form after submission or on cancel.
Built-in Validators
Section titled “Built-in Validators”RequiredValidator
Section titled “RequiredValidator”const RequiredValidator()const RequiredValidator(message: 'Name is required', trim: false)MinLengthValidator / MaxLengthValidator
Section titled “MinLengthValidator / MaxLengthValidator”const MinLengthValidator(min: 6)const MaxLengthValidator(max: 100, message: 'Too long')EmailValidator
Section titled “EmailValidator”const EmailValidator() // required by defaultconst EmailValidator(isRequired: false) // optional — empty is validconst EmailValidator(invalidMessage: 'Bad email')PasswordValidator
Section titled “PasswordValidator”const PasswordValidator() // requires 8+ chars, non-emptyconst PasswordValidator(isRequired: false) // optional passwordconst PasswordValidator( minLength: 12, requireUppercase: true, requireDigit: true, requireSpecialChar: true,)UrlValidator
Section titled “UrlValidator”const UrlValidator() // accepts http and httpsconst UrlValidator(requireHttps: true) // https onlyconst UrlValidator(isRequired: false) // optional — empty is validPhoneValidator
Section titled “PhoneValidator”const PhoneValidator() // required by defaultconst PhoneValidator(isRequired: false) // optional — empty is valid// Accepts: +1 234 567 8900 · (123) 456-7890 · 123-456-7890 · 1234567890RangeValidator
Section titled “RangeValidator”const RangeValidator(min: 1, max: 100)const RangeValidator(min: 0.0, max: 1.0, minMessage: 'Too low', maxMessage: 'Too high')Custom Validators
Section titled “Custom Validators”Extend FabrikValidator<T> to create your own:
class UsernameValidator extends FabrikValidator<String> { const UsernameValidator();
@override String? call(String value) { if (value.contains(' ')) return 'No spaces allowed'; return null; }}Features
Section titled “Features”- Field-level validation with reusable, composable validators
- Form-level state:
.isValid,.isDirty,.isTouched,.values,.errors markAllTouched()to reveal all errors on submitreset()to restore initial stateValueNotifier-based reactivity withFabrikFormNotifier- Declarative UI with
FabrikFormBuilder - Works independently of the widget tree — fully unit-testable