Implement accessible empty state and offline banner
epic-contact-search-accessible-ui-task-006 — Add a text-based empty state widget to SearchResultsList that announces 'No results found for <query>' via Semantics. Implement an offline indicator banner that appears when the device is offline, using sufficient contrast colors and a Semantics label describing the connectivity state. Avoids image-only communication per WCAG 2.2 AA.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Extract EmptyStateWidget and OfflineBanner as their own StatelessWidget classes in the same file or a shared widgets sub-directory. Both widgets should accept their display strings as constructor parameters (not hardcoded) to support future localization. For the offline banner, use the design token for 'warning' or 'caution' surface color — typically an amber/yellow tone — and ensure the paired text token has sufficient contrast. Avoid using Icon alone in either widget; pair any icon with a visible text label.
The OfflineBanner should animate in/out using AnimatedSwitcher with a 200 ms fade for a polished but non-distracting transition.
Testing Requirements
Widget tests: render EmptyStateWidget with a sample query string and assert text content and Semantics label. Render OfflineBanner and assert Semantics label text. Use a contrast-check utility or manual review against the design token palette to confirm 4.5:1 ratio. Test that OfflineBanner is not rendered when connectivity flag is true.
Test that EmptyStateWidget is not rendered when results list is non-empty.
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.
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.