high priority medium complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

SummaryShareOverlay renders a live preview of the shareable image card using ScreenshotCaptureUtility within 500ms of the overlay opening
Preview accurately reflects the current StatCardWidget stat values and all earned MilestoneBadgeWidget states at the moment the overlay opens
Preview updates if underlying data changes while the overlay is open (reactive binding, not stale snapshot)
Tapping the share button invokes the native share sheet (share_plus or platform channel) with the captured PNG/JPEG image attached
Share action completes without throwing on both iOS and Android; any platform error is caught and surfaces a user-visible snackbar with a localised error message
Preview image is rendered at a minimum resolution of 1080x1080 logical pixels regardless of device pixel ratio
A loading indicator is shown while ScreenshotCaptureUtility is capturing; the share button is disabled during capture to prevent double-tap
Memory is released after capture (RepaintBoundary key disposed or reused correctly — no memory leaks detectable via Flutter DevTools)
Overlay close button dismisses correctly before and after a share action
Works correctly in both light and dark design-token themes

Technical Requirements

frameworks
Flutter
BLoC (for reading SummaryBloc state into overlay)
Riverpod (if ScreenshotCaptureUtility is provided as a provider)
share_plus (Flutter plugin for native share sheet)
flutter_test (widget tests)
apis
ScreenshotCaptureUtility.capture() — internal utility returning Future<Uint8List>
Share.shareXFiles() or Share.shareBytes() from share_plus
RepaintBoundary widget key for off-screen rendering
data models
SummaryStatCardData (stat values fed into StatCardWidget)
MilestoneBadgeData (milestone state for badge widgets)
ActivityTypeBreakdownData (breakdown segments shown in preview)
performance requirements
Preview render must complete within 500ms on mid-range devices (e.g. iPhone SE 2022)
Captured image must not exceed 2MB before passing to the native share sheet
No frame drops (>16ms) in the main UI thread during capture — use compute() or isolate if encoding is heavy
security requirements
Captured image must not include any PII beyond what the user has explicitly chosen to share (no names, national IDs, or contact details visible in the card unless explicitly included in the design)
Temporary file written for sharing must be placed in the app's cache directory and deleted after the share sheet is dismissed
ui components
SummaryShareOverlay (bottom sheet or modal scaffold)
ScreenshotPreviewWidget (RepaintBoundary wrapper around the card)
ShareableImageCardWidget (the card being previewed — from task-013)
AppButton (primary share CTA, loading/disabled states)
CircularProgressIndicator (capture loading state)

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Use a GlobalKey on the RepaintBoundary wrapping ShareableImageCardWidget to capture via RenderRepaintBoundary.toImage(). Do NOT use screenshot packages that require a BuildContext walk — this causes issues with bottom sheets rendered outside the main widget tree. Wrap the capture call in a try/catch and emit loading/error states via a local ValueNotifier or small Cubit to keep the overlay stateless at the BLoC level. Pass the captured Uint8List to share_plus as XFile (write to cache dir first) for broadest iOS/Android compatibility.

Ensure the RepaintBoundary is part of the overlay's own subtree, not the background page, to avoid capturing unintended UI. For the loading state, set the share button's onPressed to null and show a CircularProgressIndicator inside it rather than replacing the button widget to avoid layout shift.

Testing Requirements

Write Flutter widget tests (flutter_test) for: (1) overlay opens and preview renders without throwing; (2) share button is disabled during capture and re-enabled after; (3) share button triggers the share action handler (mock share_plus); (4) error path shows snackbar when capture throws; (5) overlay dismisses cleanly. Also write a manual smoke-test checklist for TestFlight covering both iOS share sheet and Android intent chooser. Target 80% line coverage on the overlay widget file.

Component
Summary Share Overlay
ui medium
Epic Risks (2)
medium impact medium prob technical

Simultaneous count-up animations across multiple stat cards and chart draw-in animations on lower-end Android devices may cause frame drops below 60fps, degrading the premium Wrapped experience and making the feature feel unpolished.

Mitigation & Contingency

Mitigation: Stagger animation starts using AnimationController with staggered intervals rather than starting all animations simultaneously. Use RepaintBoundary around each animated widget to isolate rasterisation. Profile on a mid-range Android device (e.g., equivalent to Pixel 4a) during development, not just at QA.

Contingency: If frame rate targets cannot be met on low-end devices, implement a device-capability check at startup and substitute simpler fade-in animations for the count-up and chart draw-in on devices below a CPU performance threshold.

medium impact low prob integration

The activity-type-breakdown-widget must render organisation-specific activity type labels sourced from the terminology system. If the terminology provider is not yet integrated at the time this widget is built, the widget will display hardcoded system labels, which is a regression risk for multi-org support.

Mitigation & Contingency

Mitigation: Accept activity type labels as a typed parameter in the widget constructor rather than reading from the terminology provider directly inside the widget. The BLoC or repository layer resolves labels before passing them to the widget, maintaining clean separation and testability.

Contingency: If terminology resolution is unavailable at widget integration time, display internal activity type keys as a temporary fallback with a localised suffix '(label pending)' visible only in non-production builds so QA can identify unresolved labels.