high priority medium complexity frontend pending frontend specialist Tier 4

Acceptance Criteria

Switching from map view to list view never clears or resets the active filter selections; the same FilterState is applied in both views
Switching from list view to map view never clears active filters; both views reflect identical filtered mentor sets
When the filter panel emits a FilterChanged event while the list view is active, the list updates within one frame and announces 'Showing [N] mentors' as a live region
When the filter panel emits a FilterChanged event while the map view is active, switching to the list view immediately shows the correctly filtered list without requiring a manual refresh
A BLoC unit test demonstrates that a single FilterChanged event updates the state consumed by both MapCanvas and MentorListFallback
No duplicate live region announcements are fired when switching views without changing filters
Filter state survives app backgrounding and foregrounding (persisted in BLoC, not ephemeral widget state)
The BLoC is provided at a scope level above both views (e.g., MapViewScreen or the route) so both share the same instance

Technical Requirements

frameworks
Flutter
BLoC
data models
FilterState
PeerMentor
performance requirements
List update after filter change must complete within one build frame (no artificial delays)
State sharing must not require an additional network call when toggling views; use cached filtered data from BLoC
security requirements
BLoC scope must not expose filter state to sibling routes outside the map screen context
ui components
MentorListFallback (244-mentor-list-fallback)
MapFilterPanel (242-map-filter-panel)
MapViewScreen (239-map-view-screen) — BLoC provider host

Execution Context

Execution Tier
Tier 4

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.

Component
Mentor List Fallback View
ui low
Epic Risks (3)
high impact high prob technical

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.

medium impact medium prob technical

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.

medium impact medium prob integration

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.