critical priority high complexity frontend pending frontend specialist Tier 4

Acceptance Criteria

Screen renders with no bottom navigation bar visible — it must not be a child of the app's StatefulShellRoute
Full-screen dark background uses the design token for the branded dark surface color (not a hardcoded hex value)
Hero widget wraps the screen entry with a unique heroTag derived from the peer mentor's userId to support multiple concurrent heroes
Close button is positioned top-right (or top-left for RTL), tappable, and calls Navigator.pop() to correctly reverse the Hero transition
BlocProvider<WrappedSummaryBloc> is created at this screen level and provides the BLoC to all descendant widgets
LoadSummary event is dispatched automatically in initState (or via BlocProvider.create) so data loads on entry
Screen passes flutter_test widget test: pumping the widget with a mocked BLoC does not throw, and the close button is findable via a semantics label
Screen is navigable via a named route or go_router path defined in the app router configuration
Status bar icons are light-coloured (white) against the dark background using SystemChrome.setSystemUIOverlayStyle
Screen is WCAG 2.2 AA compliant: close button has a minimum touch target of 48x48dp and a visible focus indicator

Technical Requirements

frameworks
Flutter
BLoC
flutter_bloc
data models
WrappedSummaryBloc
WrappedSummaryState
WrappedSummaryEvent
performance requirements
Screen must reach first meaningful paint within one frame after Hero animation completes
BLoC creation must not block the UI thread — use create: (context) => WrappedSummaryBloc(...) lazy pattern
security requirements
Do not pass sensitive user data through Hero tag or route arguments in plaintext; use userId reference only
ui components
Hero
Scaffold
BlocProvider
SafeArea
SystemChrome overlay style
close IconButton

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Use Scaffold with extendBodyBehindAppBar: true and set appBar: null to achieve full-bleed dark background. Apply SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light) in initState and restore the app default in dispose. The Hero tag should follow the pattern 'wrapped-summary-{userId}' — the calling screen must use the same tag on its entry widget. BlocProvider.create() is preferred over BlocProvider.value() here because this screen owns the BLoC lifecycle.

Dispatch LoadSummary inside BlocProvider.create's create callback: create: (context) => WrappedSummaryBloc(...)..add(LoadSummary()). Keep this widget thin — delegate all slide rendering and progress UI to child widgets (implemented in task-009). Ensure go_router or Navigator 2.0 route is registered so back-stack behaviour is predictable on both iOS and Android.

Testing Requirements

Widget tests using flutter_test. Test: (1) scaffold renders without overflow on 375px and 414px screen widths, (2) close button tap calls Navigator.pop (use NavigatorObserver mock), (3) BlocProvider is found in the widget tree via context lookup, (4) dark background color matches the design token value. Accessibility test: run SemanticsChecker to confirm close button has a valid semantic label. No integration tests required for this scaffold task.

Component
Wrapped Summary Screen
ui high
Epic Risks (3)
medium impact medium prob technical

If the device transitions between online and offline states while the user is mid-session in the wrapped screen, the BLoC may emit conflicting state transitions (loaded → error → offline) that cause visual flickering or an inconsistent UI state such as showing the offline banner over an already-loaded summary.

Mitigation & Contingency

Mitigation: Implement a connectivity stream listener in the BLoC that only triggers a state re-evaluation when transitioning from online to offline, not on every connectivity event. Once a summary is in the Loaded state, the BLoC should not transition to error/offline unless the user explicitly requests a refresh. Store the last-loaded data in BLoC state so it survives connectivity changes.

Contingency: If state flickering is observed in testing, add a minimum 3-second debounce on connectivity state changes before the BLoC reacts, and display a non-blocking top banner rather than replacing the entire screen state.

high impact medium prob integration

The push notification deep-link to the wrapped-summary-screen must work correctly whether the app is in the foreground, background, or terminated state. Handling all three app launch states on both iOS and Android is a common source of edge-case bugs, particularly when authentication state must be restored before the deep link can be resolved.

Mitigation & Contingency

Mitigation: Implement deep-link handling through the existing notification-deep-link-handler component which already manages app-state-aware routing. Define the wrapped-summary route in the navigation config early in the epic so the router is ready before notification dispatch is wired. Test all three app states (foreground, background, terminated) explicitly in the QA checklist.

Contingency: If terminated-state deep-linking fails on specific platforms, fall back to launching the app to the home screen with an in-app notification banner prompting the user to open their summary, rather than direct deep-link navigation.

high impact low prob technical

The wrapped-summary-screen manages a large number of AnimationController instances (one or more per slide) via the wrapped-animation-controller. If disposal is not triggered correctly when the user exits mid-flow (e.g., via system back gesture or deep-link away), memory leaks will accumulate across session navigation.

Mitigation & Contingency

Mitigation: Implement screen disposal via Flutter's dispose() lifecycle method calling a single wrapped-animation-controller.disposeAll() method that iterates the named controller registry. Write a test that navigates to the screen, starts animations, then navigates away and verifies no active AnimationController listeners remain using Flutter's test binding.

Contingency: If disposal bugs are detected in production via memory profiling, patch by converting all AnimationControllers to use AutomaticKeepAliveClientMixin false and wrap each slide in a widget that disposes its own controller when removed from the widget tree.