Build Dynamic Field Renderer Widget
epic-structured-post-session-report-form-engine-task-012 — Implement the core dynamic field renderer that maps a FieldConfig schema object to the appropriate Flutter widget at runtime. Support all field types: TextField (single/multi-line), CheckboxListTile, RadioGroup, DropdownButton, NumberField, DatePicker, and WayForwardSectionWidget. Each rendered field must include a SpeechToTextFieldOverlay trigger, proper Semantics label derived from field schema, and error text display bound to ValidationResult. Read current value and onChange from the form orchestrator state. Apply org-specific field visibility and ordering from OrgFieldConfigLoader.
Acceptance Criteria
Technical Requirements
Execution Context
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
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).
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.
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.
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.