critical priority medium complexity frontend pending frontend specialist Tier 0

Acceptance Criteria

TranscriptionPreviewField renders a standard Flutter TextField as its primary text input, fully functional for manual keyboard input independent of dictation state
When TranscriptionStateManager emits a partial transcription result, the preview area appears below the TextField displaying the partial text in italic, muted style (per design tokens)
The preview area updates in real time as new partial results arrive — latency between state update and UI update is ≤1 frame (16ms)
When TranscriptionStateManager is in 'hidden' or 'idle' state, the preview area is not visible and does not occupy layout space
The widget accepts a TextEditingController parameter — if none provided, creates and manages one internally
Standard text editing gestures work correctly on the TextField: single tap to place cursor, double-tap to select word, long-press for context menu, drag to select range
Preview area is visually distinct from committed text: uses italic font style and text-tertiary color token
Preview area does not interfere with TextField focus — TextField retains focus while preview updates
Widget exposes a standard decoration parameter to allow callers to customize the TextField appearance (label, hint, prefix/suffix icons)
Widget is accessible: preview area has a Semantics node with label 'Speech preview' and liveRegion: true so screen readers announce updates
Widget disposes its internal TextEditingController only if it created one — never disposes a caller-provided controller
Golden test or widget snapshot verifies the visual layout of the combined TextField + preview area

Technical Requirements

frameworks
Flutter
Riverpod
apis
TranscriptionStateManager Riverpod provider
Flutter TextEditingController
Flutter TextField
Flutter Semantics API
performance requirements
Preview text updates within one frame (≤16ms) of receiving partial result
No layout reflow of parent widgets when preview area appears/disappears — use AnimatedSize or fixed min-height to prevent content shifting
security requirements
speech_to_text package DictationScopeGuard must be respected — widget must check guard state before displaying any transcription content
Partial transcription text must not be persisted to any storage layer — it is ephemeral preview only
ui components
TranscriptionPreviewField (new widget, component 658)
TextField (Flutter built-in)
AnimatedSize (Flutter built-in) for preview area height animation
Design tokens: text-tertiary color, font-size-sm, font-style-italic

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Structure the widget as a ConsumerStatefulWidget to manage the optional internal TextEditingController lifecycle. Use a Column with the TextField on top and an AnimatedSize-wrapped Container below for the preview area — AnimatedSize prevents sudden layout shifts when the preview appears. Watch TranscriptionStateManager with ref.watch and extract partial text via a pattern-match on the state type. Use a Text widget with italic TextStyle using design token colors for the preview.

Do NOT insert partial text into the TextEditingController — it must remain in a separate preview widget; merging happens only in task-007. Apply `Semantics(liveRegion: true, label: 'Speech preview', child: previewText)` to the preview widget. Expose standard TextField parameters (controller, decoration, keyboardType, textInputAction, onSubmitted) as pass-through constructor params to keep the widget flexible for reuse across multiple form fields.

Testing Requirements

Widget tests: Render TranscriptionPreviewField in isolation with a mocked TranscriptionStateManager. Assert preview area is absent when state is idle. Drive partial results into the provider and assert preview text matches. Assert TextField is focusable and receives text input normally.

Assert custom TextEditingController is not disposed if passed by caller. Golden tests: Capture widget snapshot in idle state, recording+partial state, and processing state for visual regression. Unit tests: Test the partial-result-to-preview-text mapping logic. Manual tests: Run on device, start dictation, verify preview updates in real time.

Verify all standard gestures (tap, double-tap, long-press, drag-select) function on the TextField while preview is active. Run with VoiceOver/TalkBack and confirm preview area announces updates.

Component
Transcription Preview Field
ui medium
Epic Risks (3)
medium impact medium prob technical

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.

high impact medium prob technical

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.

medium impact low prob integration

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.