critical priority low complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

VoiceOver on iOS announces the field's semantic label when the user focuses the field via swipe navigation
VoiceOver on iOS announces the hint text after the label when the field is focused and empty
TalkBack on Android announces the field's semantic label and hint when focused
The screen reader identifies the widget as a text field (role announcement: 'text field' on iOS, 'Edit box' on Android)
After the user types, the screen reader announces the current text value on field blur
The clear button is announced as a button with label 'Clear search' and is reachable via swipe navigation
When the clear button is not visible (field empty), it is excluded from the screen reader's focus order
No duplicate announcements occur (e.g., field label not read twice due to nested Semantics nodes)
The widget passes Blindeforbundet's mandatory WCAG 2.2 AA screen-reader checklist items for search fields
Semantic behavior is validated on a physical iOS device with VoiceOver and a physical Android device with TalkBack (or emulator with TalkBack enabled)

Technical Requirements

frameworks
Flutter
performance requirements
Semantics tree updates must not introduce frame drops — use semanticsLabel on Text widgets rather than additional Semantics wrappers where possible
security requirements
Semantic label must not expose internal field identifiers or database field names to assistive technology
ui components
AccessibleSearchInputField (from task-001)
Semantics (Flutter framework — label, hint, textField flags)
ExcludeSemantics (for decorative icons)

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Flutter's TextField widget already sets isTextField: true in its internal SemanticsNode. To avoid double-announcing, do NOT wrap the inner AppTextField with another Semantics(textField: true) node — this creates a conflict. Instead, use Semantics on the outer container with only label and hint; the inner TextField handles the textField role. To provide the label to the inner TextField's semantics, pass it via TextField(decoration: InputDecoration(semanticCounterText: ..., labelText: ...)) or use the TextField's own semanticsLabel if supported by the version in use.

Test for duplicate nodes using the SemanticsHandle debugDumpSemanticsTree output. For the clear button exclusion: use ExcludeSemantics(excluding: controller.text.isEmpty, child: clearButton) to toggle semantic visibility. This is preferable to Visibility(maintainState: ...) for semantic-only exclusion.

Testing Requirements

Automated: use flutter_test SemanticsHandle to assert the semantics tree contains a node with isTextField: true, hasEnabledState: true, label matching the expected string, and hint matching the expected string. Assert that when field is empty, no node with label 'Clear search' is present in the semantics tree. Assert that when field contains text, a node with label 'Clear search' IS present. Manual: test on a physical iOS device with VoiceOver enabled — swipe to each element and verify announcements match expected strings.

Test on Android emulator with TalkBack enabled. Document manual test results in the PR description.

Epic Risks (2)
medium impact medium prob technical

Flutter's Semantics live region support for announcing dynamic result count changes may behave inconsistently between VoiceOver (iOS) and TalkBack (Android), particularly regarding announcement throttling and focus management, causing the feature to pass testing on one platform and fail on the other.

Mitigation & Contingency

Mitigation: Test live region announcements on both iOS (VoiceOver) and Android (TalkBack) early in development using the existing accessibility test harness. Reference the existing LiveRegionAnnouncer component (608-live-region-announcer) patterns used elsewhere in the app.

Contingency: If cross-platform consistency cannot be achieved, implement a platform-specific announcement strategy using the SemanticsService.announce API with platform-conditional announcement timing to work around OS-specific throttling behaviour.

low impact low prob dependency

Voice-to-text progressive enhancement for Blindeforbundet may not be available or may behave unpredictably on all device/OS combinations, particularly older Android devices, potentially causing crashes or silent failures that degrade the search experience.

Mitigation & Contingency

Mitigation: Implement voice-to-text as a strictly optional enhancement: detect availability at runtime, show the microphone button only when the platform speech API reports availability, and wrap all voice invocations in try/catch with graceful degradation to standard text input.

Contingency: If voice-to-text causes instability on a subset of devices discovered during TestFlight/beta, disable the feature flag for that platform version while a fix is investigated, without impacting the core text-based search functionality.