Implement animated segments and tap-to-expand in breakdown widget
epic-annual-impact-summary-ui-components-task-011 — Drive bar/pie segment entrance animations via WrappedAnimationController (staggered reveal per segment). Implement tap-to-expand interaction that shows a detail row with absolute count and additional context for the tapped segment. Suppress animations when reduced-motion is detected via SummaryAccessibilityProvider.
Acceptance Criteria
Technical Requirements
Execution Context
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
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.
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.
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.