high priority medium complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

On widget mount, bar/pie segments animate in with a staggered reveal: each segment starts its entrance animation with a delay of index × stagger_interval_ms after the previous, driven by WrappedAnimationController
Segment entrance animations use a grow-from-zero pattern (bar: width tween 0→full; pie: sweep angle tween 0→full) combined with opacity 0→1
When SummaryAccessibilityProvider.reducedMotion is true, all segments render at their final state immediately (no stagger, no tween) — verified by asserting animation value is 1.0 from frame 0
Tapping a segment toggles an expanded detail row below (or adjacent to) the segment showing: absolute activity count and any additional context string for that segment
Only one segment can be expanded at a time — tapping a second segment collapses the first and expands the second
Tapping an already-expanded segment collapses it
The expand/collapse transition is animated (slide-down + fade) unless reducedMotion is true, in which case it is instant
The widget correctly handles rapid successive taps without state corruption or animation glitches
All behaviour is tested and passes flutter analyze with no warnings

Technical Requirements

frameworks
Flutter
flutter_test
apis
WrappedAnimationController API
SummaryAccessibilityProvider (Riverpod provider)
data models
ActivityTypeSegment (activityTypeId, label, percentage, absoluteCount, context)
ActivityTypeBreakdownWidgetState (expandedSegmentId: String?)
performance requirements
Stagger interval must be configurable (default 80ms) and not block the UI thread
AnimatedBuilder scoped to animated subtree only — no full widget tree rebuild per animation frame
Expand/collapse animation completes within 250ms
security requirements
Segment tap events must not trigger navigation or external calls — interaction is purely local state
ui components
ActivityTypeBreakdownWidget (stateful, extends task-010 scaffold)
WrappedAnimationController
SummaryAccessibilityProvider
AnimatedContainer or SizeTransition for expand/collapse
GestureDetector per segment

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Convert ActivityTypeBreakdownWidget to a StatefulWidget. Store expandedSegmentId as a nullable String in state. Use a single WrappedAnimationController with a Tween(begin: 0, end: 1) and stagger each segment by offsetting its interval using an Interval(start, end) on the parent CurvedAnimation — e.g. segment i uses Interval(i * stagger / total, (i * stagger + duration) / total).

For reduced-motion, skip forward the controller to 1.0 immediately after initState. For tap-to-expand, manage a single selectedSegmentId in State and use AnimatedSize or SizeTransition on the detail row. Wrap each segment's GestureDetector in an InkWell with splash limited to the segment bounds. Keep the detail row content (absolute count + context) sourced from the ActivityTypeSegment model passed to the widget — do not make additional provider calls inside the expanded row.

Ensure WrappedAnimationController is properly disposed in the widget's dispose() method.

Testing Requirements

Widget tests using flutter_test: (1) pump widget and advance animation clock — assert all segments reach full opacity and full size by end of animation; (2) with reducedMotion=true, assert all segments are at final state on first frame without needing to pump animation; (3) tap segment 0 — assert expanded detail row for segment 0 is visible; (4) tap segment 1 — assert segment 0 detail is hidden and segment 1 detail is visible; (5) tap segment 1 again — assert detail row collapses; (6) simulate rapid alternating taps on two segments and assert no exception thrown and final state is consistent. Golden test for expanded state with one segment open.

Component
Activity Type Breakdown Widget
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.