Integrate dictation UI into dynamic field renderer
epic-speech-to-text-input-user-interface-task-010 — Update the DynamicFieldRenderer (component 077-dynamic-field-renderer) to conditionally render a DictationMicrophoneButton alongside any field with type 'text' or 'textarea'. The renderer must pass the correct field context to TranscriptionPreviewField so cursor-position merging targets the right field. Ensure the integration is purely additive: fields without dictation support remain unchanged.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 4 - 323 tasks
Can start after Tier 3 completes
Implementation Notes
Use a thin wrapper widget (e.g., DictationAwareFieldWrapper) rather than embedding dictation logic directly in DynamicFieldRenderer — this keeps the renderer schema-agnostic. The wrapper checks `context.read
The DictationScopeGuard should be an InheritedWidget ancestor checked inside the wrapper; if the guard is active, render the button as permanently disabled with a tooltip explaining why. Keep the wrapper stateless where possible — let BLoC own all mutable state.
Testing Requirements
Write flutter_test widget tests: (1) render DynamicFieldRenderer with a schema containing one 'text' field and assert DictationMicrophoneButton is present; (2) render with a 'date' field and assert DictationMicrophoneButton is absent; (3) mock DictationAvailabilityState as unavailable and assert button is hidden; (4) tap the button and verify the correct fieldKey is dispatched via BLoC; (5) simulate two 'text' fields on the same screen and verify cursor merging targets the tapped field only. Achieve 100% branch coverage on the conditional rendering logic. No integration tests required for this task — covered by task-012.
Merging dictated text at the current cursor position in a TextField that already contains user-typed content is non-trivial in Flutter — TextEditingController cursor offsets can behave unexpectedly with IME composition, emoji, or RTL characters, potentially corrupting the user's existing notes.
Mitigation & Contingency
Mitigation: Implement the merge logic using TextEditingController.value replacement with explicit selection range calculation rather than direct text manipulation. Write targeted widget tests covering edge cases: cursor at start, cursor at end, cursor mid-word, existing content with emoji, and content that was modified during an active partial-results stream.
Contingency: If cursor-position merging proves too fragile for the initial release, scope the merge behaviour to always append dictated text at the end of the existing field content and add the cursor-position insertion as a follow-on task after the feature is in TestFlight with real user feedback.
VoiceOver on iOS and TalkBack on Android handle rapid sequential live region announcements differently. If recording start, partial-result, and recording-stop announcements arrive within a short window, they may queue, overlap, or be dropped, leaving screen reader users without critical state information.
Mitigation & Contingency
Mitigation: Implement announcement queuing in AccessibilityLiveRegionAnnouncer with a minimum inter-announcement delay and priority ordering (assertive recording start/stop always takes precedence over polite partial-result updates). Test announcement behaviour on physical iOS and Android devices with VoiceOver/TalkBack enabled as part of the acceptance test plan.
Contingency: If platform differences make reliable queuing impossible, reduce partial-result announcements to a single 'transcription updating' message with debouncing, preserving the critical start/stop announcements. Coordinate with the screen-reader-support feature team to leverage the existing SemanticsServiceFacade patterns already established in the codebase.
The DictationMicrophoneButton must integrate with the dynamic-field-renderer which generates form fields from org-specific schemas at runtime. If the renderer does not expose a stable field metadata API for dictation eligibility checks, the scope guard and button visibility logic will require invasive changes to the report form architecture.
Mitigation & Contingency
Mitigation: Coordinate with the post-session report feature team early in the epic to confirm that dynamic-field-renderer exposes a field metadata interface including field type and sensitivity flags. Add a dictation_eligible flag to the field schema that the renderer passes to DictationMicrophoneButton as a constructor parameter.
Contingency: If the renderer cannot be modified without breaking changes, implement dictation eligibility as a separate lookup against org-field-config-loader using the field key as the lookup identifier, bypassing the renderer integration and keeping the dictation components fully decoupled from the report form architecture.