Synchronise list fallback filter state with map view
epic-geographic-peer-mentor-map-ui-accessibility-task-010 — Ensure the MentorListFallback receives and applies the same FilterChanged events as the map canvas so that toggling views never resets the filter selection. Both views must read from the same BLoC filter state. When the filter panel emits a change, the list updates immediately and announces the new result count via an accessibility live region ('Showing 12 mentors').
Acceptance Criteria
Technical Requirements
Execution Context
Tier 4 - 323 tasks
Can start after Tier 3 completes
Implementation Notes
Provide the shared FilterBloc (or MapBLoC) at the MapViewScreen level using BlocProvider. Both MapCanvas and MentorListFallback consume it via BlocBuilder. The view toggle FAB should only change a local ViewMode flag (map vs. list) — it must not create or reset the BLoC.
For the live region: use a BlocListener wrapping MentorListFallback that fires SemanticsService.announce only when the mentor count actually changes (compare previous and new state count before announcing to avoid spurious announcements on view toggle). Avoid placing filter state in widget local state (setState) — all state must live in the BLoC.
Testing Requirements
Write a BLoC unit test: emit FilterChanged, assert both the map mentor list selector and the list fallback mentor list selector return the same filtered set. Write a widget integration test that renders MapViewScreen with both views, fires a FilterChanged event, switches views, and asserts the list item count matches the expected filtered count. Test with BlocProvider.value to verify single BLoC instance is shared. Test that toggling views without changing filters does not re-announce the count live region.
Flutter's map canvas (flutter_map) does not natively support semantic focus traversal for screen readers, meaning map markers may be entirely invisible to VoiceOver/TalkBack users. If the accessible list fallback is not treated as a first-class view, screen reader users will have no access to the feature.
Mitigation & Contingency
Mitigation: From sprint 1, treat mentor-list-fallback as a fully featured primary view, not an afterthought. Implement and test it in parallel with the map canvas. Make the view-toggle-button keyboard focusable and announced on every screen state. Conduct VoiceOver testing on device before submitting each PR touching UI components.
Contingency: If map canvas accessibility cannot be achieved for marker focus traversal, make the view-toggle-button the default focus target on screen load for VoiceOver users (detected via screen-reader-detection-service) so they are immediately directed to the list fallback without needing to discover the toggle.
The mentor-info-popup must occupy no more than 40% of visible map area on small screens. On devices with screen heights under 667px (iPhone SE), overlapping with the filter panel or obscuring most of the map could severely degrade usability.
Mitigation & Contingency
Mitigation: Implement the popup as a bottom sheet capped at 40% of screen height with a ScrollView for overflow content. Test on iPhone SE (375x667pt) and the smallest commonly used Android form factor in the device lab. Define max-height as a percentage constant in location-privacy-config or design tokens.
Contingency: If the popup cannot fit all required fields within 40% height on smallest targets, truncate assigned contact count and certification badge to icons-only in the compact view, with a 'View Profile' button always visible at the bottom of the popup regardless of scroll position.
Filter state must remain perfectly synchronised between the map view and the list fallback. If the filter panel emits state that is not consumed identically by both views, coordinators switching between views will see inconsistent mentor sets, eroding trust in the feature.
Mitigation & Contingency
Mitigation: Store active filter criteria in a single shared Riverpod provider owned by the map-view-screen and consumed by both map-marker-widget (via mentor-location-service) and mentor-list-fallback. Write integration tests that apply a filter, switch views, and assert identical mentor counts in both views.
Contingency: If filter sync proves brittle, simplify to a single filter state object passed explicitly as a constructor argument to both views on each rebuild, eliminating indirect state sharing.