high priority medium complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

ActivityTypeBreakdownWidget renders all provided activity type segments without overflow or clipping at screen widths 320px–428px
Each segment displays: (1) the organisation-specific label from OrgLabelsProvider, (2) the percentage value formatted as '[N]%', and (3) a segment colour sourced from design tokens (not hardcoded hex values)
The widget supports both bar chart and pie/donut chart layouts, selectable via a layout parameter; the default layout is bar chart
Percentages across all segments sum to 100% (or the widget handles rounding gracefully without visual gaps or overflow)
The widget renders correctly with 1 segment, 5 segments, and 10 segments (edge case coverage)
When OrgLabelsProvider returns a label that is longer than the segment width, the label truncates with an ellipsis and does not cause layout overflow
The widget is stateless at this stage — no tap handling or animations are included (those are added in task-011)
The widget compiles without warnings and passes flutter analyze

Technical Requirements

frameworks
Flutter
flutter_test
apis
OrgLabelsProvider (Riverpod provider for organisation-specific terminology)
Design token system (color tokens for segment palette)
data models
ActivityTypeSegment (activityTypeId, label, percentage, absoluteCount, colorToken)
ActivityTypeBreakdownData (segments list, totalCount)
performance requirements
Widget must render initial layout within a single frame — no deferred painting
CustomPainter (if used for bar/pie rendering) must implement shouldRepaint correctly to avoid unnecessary repaints
security requirements
Activity type labels sourced from OrgLabelsProvider must be HTML-escaped if rendered in any web-targeted context (future-proofing)
ui components
ActivityTypeBreakdownWidget
OrgLabelsProvider
CustomPainter or fl_chart (if approved in tech stack) for chart rendering
Design token color palette

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Implement the chart using Flutter's CustomPainter for maximum control over rendering without introducing a chart dependency. Define an ActivityTypeSegment model with fields: activityTypeId (String), percentage (double), absoluteCount (int), colorToken (String — design token key, resolved to Color at render time). Source segment colours from a design token map (e.g. chartSegmentColors[index % palette.length]) rather than assigning per-activity-type to avoid coupling.

For bar layout, render segments as proportionally sized horizontal bars in a Column; for pie/donut, use CustomPaint with arc drawing. Wrap label text in a Flexible/Expanded to prevent overflow. Consume OrgLabelsProvider via Riverpod ref.watch — pass the resolved label strings into the widget as a parameter rather than reading the provider deep in the paint call, to keep CustomPainter pure. Keep the widget stateless at this stage; tap handling and animations are explicitly out of scope for this task.

Testing Requirements

Widget tests using flutter_test: (1) render with 1, 5, and 10 segments and assert no RenderFlex overflow errors; (2) assert each segment text matches the label returned by a mock OrgLabelsProvider; (3) assert percentage text renders as '[N]%' format; (4) assert segment colours match design token values (not raw hex); (5) test both bar and pie layout modes render without exception. Golden tests for both layout modes with 5 segments. Unit tests for percentage rounding logic if extracted to a utility function.

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.