high priority medium complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

StatCardWidget renders three visual zones: icon slot (top), value display (middle), descriptive label (bottom)
All colour references use design token constants — no hardcoded Color(0x…) or Colors.* calls anywhere in the widget file
All typography references use design token TextStyle constants — no hardcoded fontSize, fontWeight, or letterSpacing values
All padding and spacing values use design token spacing constants — no hardcoded EdgeInsets with literal numbers
Widget accepts iconWidget (Widget), value (String), and label (String) as required constructor parameters
Widget renders correctly at minimum card width of 140 dp and maximum card width of 320 dp without overflow
Widget respects the device text scale factor (tested at 1.0×, 1.5×, and 2.0×) without clipping or overflow
No LayoutBuilder or MediaQuery calls inside the widget — card is intrinsically sized and adapts via Flexible/Expanded in the parent grid
Widget is covered by a golden test capturing default state at 1.0× and 2.0× text scale

Technical Requirements

frameworks
Flutter
flutter_test
performance requirements
Widget must be const-constructible for subtrees where value does not change
Build method must not trigger unnecessary rebuilds — use const where possible
ui components
StatCardWidget
Design token theme provider
Icon slot (accepts any Widget)

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Structure the widget as a Column inside a Container (or Card) whose decoration pulls its color from the design token provider via Theme.of(context).extension(). Place the icon slot in a SizedBox with token-defined dimensions so different icon sizes are normalised. Use AutoSizeText or a FittedBox for the value display if the value string can be long (e.g. '1,234'), but prefer increasing the token-defined font size breakpoints rather than auto-shrinking, which can hurt readability for users with visual impairments.

The label should use a softer token colour (e.g. textSecondary) to create visual hierarchy without relying on size alone — important for WCAG 1.4.3 contrast compliance. Do not embed Semantics nodes in this task; that is handled in task-006.

Testing Requirements

Write widget tests verifying: (1) all three zones render given valid inputs; (2) overflow does not occur at min/max widths; (3) text scale 2.0× does not clip content. Write golden tests for default state. Lint check with flutter analyze must pass with zero warnings related to inline styles — add a custom lint rule or comment explaining the token-only policy if the linter cannot enforce it automatically.

Component
Animated Stat Card 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.