critical priority high complexity frontend pending frontend specialist Tier 3

Acceptance Criteria

A single DynamicFieldRenderer widget accepts a FieldConfig and renders the correct Flutter widget for each supported type: text_single, text_multi, checkbox, radio_group, dropdown, number, date, way_forward
Rendering an unsupported field type shows a visible fallback widget with the field label and a 'field type not supported' message (never a silent blank space or crash)
Each rendered field reads its current value from the form orchestrator state via BlocBuilder and dispatches FieldChanged events on user interaction
Each field renders an error text below itself when validationErrors[field.id] is non-null; error text is styled with the design token error color
Each field includes a SpeechToTextFieldOverlay trigger icon; tapping it starts speech recognition and fills the field on completion (applicable to text-based fields only)
Every field wrapper has a Semantics widget with label = field.label, hint = field.hint (if present), and error = validationErrors[field.id]?.message
OrgFieldConfigLoader filtering is applied: fields with visible = false for the current org are not rendered; fields are rendered in the order specified by the org config
DatePicker opens a date selection modal using Flutter's showDatePicker with org-locale date formatting
NumberField enforces numeric keyboard and rejects non-numeric input
RadioGroup and Dropdown render all options from field.options; options list is never hardcoded — always from FieldConfig
Widget handles null currentValues[field.id] gracefully (renders empty/unselected state without error)
Performance: a form with 20 fields renders within 200ms on a mid-range device

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
flutter_test
apis
OrgFieldConfigLoader
SpeechToTextFieldOverlay
FormOrchestratorBloc (read/dispatch)
data models
FieldConfig (id, type, label, hint, options, required, visible, order)
ValidationResult
FieldType enum
performance requirements
Use const constructors wherever possible on sub-widgets to minimize rebuilds
BlocSelector (not BlocBuilder) should be used per field to subscribe only to that field's value and error, preventing full list re-renders on single field change
security requirements
Text input values must be trimmed before dispatching FieldChanged (prevent accidental leading/trailing whitespace in reports)
Dropdown and radio options rendered from schema must have HTML/script content escaped (defense against schema injection)
ui components
DynamicFieldRenderer (core)
TextFieldWidget (single + multi-line variant)
CheckboxFieldWidget
RadioGroupWidget
DropdownFieldWidget
NumberFieldWidget
DatePickerFieldWidget
WayForwardSectionWidget (from task-011)
SpeechToTextFieldOverlay (from task-009)
FieldErrorText
UnsupportedFieldFallback

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Implement as a switch/factory pattern: a top-level DynamicFieldRenderer widget switches on FieldConfig.type and returns the appropriate sub-widget. Each sub-widget should be its own class for testability — avoid a monolithic switch inside one build method. Use BlocSelector where FieldRenderData = (currentValue, errorMessage) to make each field subscribe only to its own slice of state. This is critical for performance with large forms.

For the DatePicker, use intl package for locale-aware formatting. For the SpeechToTextFieldOverlay, the trigger icon should be placed inside the InputDecoration suffixIcon of text-based fields — this keeps the touch target within the field boundary and looks native. OrgFieldConfigLoader should be injected via Riverpod provider, not passed as constructor argument, to avoid prop drilling through deep widget trees.

Testing Requirements

Widget tests for each field type variant using flutter_test. For each type: (1) renders correctly with a null value; (2) renders correctly with a valid value; (3) user interaction dispatches FieldChanged with correct fieldId and new value; (4) validationErrors[field.id] set → error text is visible; (5) validationErrors[field.id] null → error text is absent; (6) field with visible=false is not present in widget tree (use findsNothing). Integration test: render a full schema with all 8 field types and assert no exceptions, no overflow, and all fields present. Golden tests for each field type in error state and normal state.

Verify BlocSelector granularity: changing field A does not rebuild field B's widget (use debugPrintRebuildDirtyWidgets in test).

Component
Dynamic Field Renderer
ui high
Dependencies (3)
Implement the multi-entry way-forward section UI widget. Render a scrollable list of WayForwardItem cards with add, edit, and remove actions. Each item card shows action text, optional assignee chip, and status badge. Include an Add Item bottom sheet with a text field supporting speech-to-text input via SpeechToTextFieldOverlay. Apply WCAG 2.2 AA: all interactive elements have 44×44dp touch targets, semantic labels, and focus order. Wire to WayForwardTaskService via the form orchestrator. epic-structured-post-session-report-form-engine-task-011 Create the infrastructure component that fetches and parses org-specific field configuration from Supabase, mapping raw JSON schema to typed FieldConfig domain objects. Support field types: text, textarea, checkbox, radio, select, number, date, and way_forward_section. Integrate with report schema cache for offline support. Expose a Riverpod provider returning AsyncValue<List<FieldConfig>>. epic-structured-post-session-report-form-engine-task-005 Implement the per-field microphone trigger overlay widget. Render a microphone IconButton that transitions through idle, listening (animated waveform), processing, and error states. Display partial transcription in a floating preview card above the field. Announce recording state transitions via Flutter Semantics live regions for WCAG 2.2 AA compliance. Tap outside or press ESC to cancel recording. On final transcription, invoke onTranscribed callback to fill the parent field. epic-structured-post-session-report-form-engine-task-009
Epic Risks (3)
high impact medium prob technical

Dynamically rendered form fields built from runtime JSON schema are significantly harder to make accessible than statically declared widgets — Flutter's Semantics tree must be correct for every possible field type and every validation state. Failures here block the entire feature for Blindeforbundet's visually impaired peer mentors.

Mitigation & Contingency

Mitigation: Define WCAG 2.2 AA semantics requirements for each field type before implementation and write widget tests using Flutter's SemanticsController for every type. Include a real-device VoiceOver test session in the acceptance gate for this epic before marking it done.

Contingency: If dynamic semantics prove too difficult to get right generically, implement field-type-specific Semantics wrappers (one per supported field type) instead of a single generic renderer, accepting slightly more code duplication in exchange for reliable accessibility.

high impact medium prob technical

The report-form-orchestrator must manage a complex state machine — schema loading, draft persistence, per-field validation, submission retries, and error recovery — across multiple async operations. Incorrect state transitions could result in lost user data, double submissions, or UI freezes.

Mitigation & Contingency

Mitigation: Define all Bloc states and events explicitly as sealed classes before writing any logic. Use a state machine diagram reviewed by the team before implementation. Write exhaustive Bloc unit tests covering every state transition, including concurrent events and network interruption mid-submission.

Contingency: If Bloc complexity becomes unmanageable, extract draft persistence into a separate DraftManagerCubit and keep report-form-orchestrator focused solely on the submit workflow. The additional granularity makes each component independently testable.

medium impact low prob scope

Organisations may require field types beyond the five currently specified (text, multiline, checkbox group, radio, date). If a new type is discovered during pilot testing, the dynamic-field-renderer must be extended, potentially requiring changes across multiple layers.

Mitigation & Contingency

Mitigation: Design dynamic-field-renderer as a registry of field-type renderers with a clear extension point. Document the pattern for adding a new field type so that it can be done in one file without touching existing renderers.

Contingency: If an unhandled field type is encountered at runtime, dynamic-field-renderer renders a labelled plain-text fallback widget and logs a warning so the missing type is surfaced in monitoring, preventing a crash while making the gap visible.