Build SearchResultsList with Semantics region
epic-contact-search-accessible-ui-task-005 — Implement the SearchResultsList widget as a scrollable ListView wrapped in a Semantics region (explicitChildNodes: true). Render ContactCard and PeerMentorCard items. Include offline indicator banner at the top, an accessible empty-state widget using text (not image-only), and a loading placeholder skeleton. Depends on existing contact-card-widget and peer-mentor-card-widget components.
Acceptance Criteria
Technical Requirements
Implementation Notes
Define a sealed class SearchResultsState with four variants (loading, loaded, empty, error) and pass it as the single input to SearchResultsList — avoid multiple nullable parameters. Reuse the existing ContactCard and PeerMentorCard widgets verbatim; do not inline their layout here. For offline detection, subscribe to a connectivity stream from a shared Riverpod provider rather than instantiating a new listener inside this widget. Use the design token spacing and color values for the offline banner background (use the warning/caution token, not a hardcoded color).
Skeleton cards should mirror the exact height of a real card item so the layout does not jump when results load.
Testing Requirements
Widget tests using flutter_test: render in each state (loading, loaded with mixed results, empty, offline+results) and assert correct child widgets are present. Use SemanticsController to verify Semantics labels on skeleton cards and empty state. Test that offline banner appears/disappears when connectivity mock toggles. Snapshot or golden test for skeleton layout to prevent regression.
Integration test: end-to-end query → result display with real Supabase test data in a staging environment.
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.