high priority medium complexity frontend pending frontend specialist Tier 8

Acceptance Criteria

RefreshIndicator wraps the scrollable content (CustomScrollView or SingleChildScrollView) and its onRefresh callback dispatches RefreshPeerMentorDetail to PeerMentorDetailBLoC
Pull-to-refresh is available in both Loaded and PartialError states; it is disabled (RefreshIndicator replaced by non-interactive scroll) in Loading and Error states to prevent double-fetch
After dispatching RefreshPeerMentorDetail, the BLoC transitions to a refreshing sub-state (or re-emits Loading) and the RefreshIndicator spinner remains visible until the new state is emitted
CertificationAlertBanner has an onTap callback that fires only when the organization is HLF and certification.isExpired == true
Tapping CertificationAlertBanner navigates to the course enrollment screen using context.push('/contacts/peer-mentor/:mentorId/course-enrollment') with the correct mentorId path parameter
The GoRouter navigation call passes the mentorId extracted from the current route parameters, not hardcoded
Navigation does not trigger a BLoC event or state change on the detail screen — it is a pure UI navigation action
For non-HLF organizations or when certification is not expired, CertificationAlertBanner has no tap behavior (onTap is null)
Back navigation from the course enrollment screen returns to the peer mentor detail screen with its state intact (no reload triggered by pop)
The pull-to-refresh gesture works correctly on both iOS and Android without conflicting with the vertical scroll of the main content

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
GoRouter
data models
PeerMentorDetailModel
CertificationModel
RefreshPeerMentorDetail (BLoC event)
performance requirements
RefreshIndicator onRefresh must return a Future that completes when the BLoC emits a non-loading state — use a Completer or stream first-where to avoid the spinner dismissing prematurely
Navigation to course enrollment must be instant (< 16ms frame budget) — no async work before context.push
security requirements
mentorId passed to the course enrollment route must be validated as a non-empty string before navigation to prevent route injection
The course enrollment route must require authentication — confirm GoRouter redirect guard covers nested routes
ui components
RefreshIndicator
CertificationAlertBanner (with onTap parameter)
GoRouter context.push call

Execution Context

Execution Tier
Tier 8

Tier 8 - 48 tasks

Can start after Tier 7 completes

Implementation Notes

For the RefreshIndicator Future completion, listen to the BLoC stream after dispatching RefreshPeerMentorDetail and complete the onRefresh Future when the stream emits a Loaded or Error state: `final completer = Completer(); context.read().add(RefreshPeerMentorDetail()); context.read().stream.firstWhere((s) => s is! PeerMentorDetailLoading).then((_) => completer.complete()); return completer.future;`. For the certification tap, pass onTap as a nullable VoidCallback to CertificationAlertBanner and build it conditionally in the screen assembly: `onTap: (isHlf && cert.isExpired) ? () => context.push('/contacts/peer-mentor/$mentorId/course-enrollment') : null`.

Extract the mentorId from GoRouterState.pathParameters at screen initialization (in initState or via StatefulWidget) rather than re-parsing the URI on each tap.

Testing Requirements

Widget-test the pull-to-refresh: pump the screen with a BLoC stub in Loaded state, simulate a drag-to-refresh gesture, and assert RefreshPeerMentorDetail is added to the BLoC. Test that the refresh is not available in Error state. For certification tap routing, use GoRouter's test utilities (GoRouter.of mock or ProviderScope with override) to assert that context.push is called with the correct path and mentorId when tapping CertificationAlertBanner with HLF org context and expired certification. Assert no navigation occurs for non-HLF org context.

Use integration_test package for an end-to-end test of the full refresh → reload cycle on a real device or emulator.

Component
Peer Mentor Detail Screen
ui medium
Epic Risks (3)
medium impact medium prob technical

The parallel Future.wait aggregation pattern may produce race conditions or incorrect merged state when some repositories resolve significantly faster than others, particularly if the BLoC receives a RefreshDetail event while a prior fetch is still in flight.

Mitigation & Contingency

Mitigation: Implement cancellation token pattern in the aggregation service to abort in-flight fetches on new events. Add BLoC test scenarios for rapid successive refresh events to validate state consistency.

Contingency: If race conditions persist, switch to a sequential-with-timeout fetch strategy for the first release and profile the performance impact before deciding whether parallel fetch optimization is worth the complexity.

medium impact medium prob integration

Integrating PeerMentorDetailScreenWidget into the existing StatefulShellRoute navigation structure may conflict with the Contacts tab's existing route hierarchy, requiring changes to navigation-route-config that could affect other teams' features.

Mitigation & Contingency

Mitigation: Coordinate with the Contact List and Contact Detail feature teams before adding the new route. Review the existing StatefulShellRoute configuration and confirm the peer mentor detail route can be nested under the Contacts branch without path conflicts.

Contingency: If route conflicts arise, temporarily implement the peer mentor detail as a modal overlay (push route) rather than a shell route child, preserving functionality while the navigation architecture conflict is resolved.

low impact high prob dependency

The course enrollment screen that the certification alert banner links to may not yet exist or may be implemented in a separate feature epic, leaving a broken navigation tap for HLF users in the initial release.

Mitigation & Contingency

Mitigation: Check the certification management feature implementation status before finalizing Epic 4 scope. If the enrollment screen is not available, design the tap action to open the HLF course enrollment URL in an external browser as an interim solution.

Contingency: Implement the CTA as a configurable action: if the enrollment route exists in the router, push it; otherwise, launch the configured org-specific enrollment URL via url_launcher, ensuring HLF users can always take action on expired certifications.