1---
2name: flutter-bloc-forms
3description: Use when managing complex form state, synchronous and asynchronous input validation, or form submission transitions in BLoC.
4metadata:
5 platforms: "flutter"
6 languages: "dart"
7 category: "ui"
8---
9
10# Form Architecture with BLoC
11
12- Manage form state in a dedicated `FormBloc` (not in widget `setState`)
13- Each form field maps to a property in the BLoC state
14- Validate on field change (real-time) or on submit (batch) depending on UX requirements
15- Emit `FormSubmitting`, `FormSuccess`, `FormError` states for submission flow
16
17## Form Events
18
19- `FieldChanged(field, value)`: update a single field in state
20- `FormSubmitted`: trigger validation and submission
21- `FormReset`: clear all fields and errors
22
23## Form State
24
25- Use a single state class with all field values, field-level errors, and form status:
26 ```dart
27 sealed class FormStatus { initial, submitting, success, failure }
28 ```
29- Field errors: `Map<String, String?>` keyed by field name (`null` means valid)
30
31# Validation Patterns
32
33- Validate in the domain layer (not in widgets or BLoCs)
34- Create pure validator functions that return `String?` (null = valid, string = error message):
35 ```dart
36 String? validateEmail(String value) =>
37 value.contains('@') ? null : 'Invalid email';
38 ```
39- Compose validators: `String? validate(String v) => validateRequired(v) ?? validateEmail(v)`
40- Use localized error messages via `context.l10n`: no hardcoded validation strings
41
42# Input Widgets
43
44- Use `TextFormField` with `InputDecoration` for consistent styling
45- Always set `textInputAction` for proper keyboard behavior (`next`, `done`)
46- Always set `keyboardType` matching the field type (`emailAddress`, `phone`, `number`)
47- Use `inputFormatters` to restrict input (e.g., `FilteringTextInputFormatter.digitsOnly`)
48- Assign `Key('feature_fieldName')` to every form field for test access
49- Use `AutofillHints` for login/signup forms (email, password, name)
50- Wrap form fields with `Focus` or `FocusTraversalGroup` for proper tab order
51
52# Controller Lifecycle
53
54- Declare `TextEditingController` as `late final` in `initState()`: dispose in `dispose()`
55- Sync controllers to BLoC via `onChanged` callback or controller listener
56
57# Form Submission
58
59- Disable submit button while `FormStatus.submitting` to prevent double-submission
60- Show inline field errors below each field (not just a top-level error)
61- On success: navigate, show success feedback, and reset form if staying on same page
62- On failure: show error feedback via `SnackBar` or inline, keep form data intact
63
64# Common Form Patterns
65
66- **Search**: Use `debounce` transformer on search events (300-500ms delay)
67- **Multi-step**: Each step is a separate form state within one `FormBloc`, validated independently
68- **Dependent fields**: Update dependent field options in `on<FieldChanged>` handler (e.g., country → city)