high priority medium complexity frontend pending frontend specialist Tier 4

Acceptance Criteria

BenefitCalculatorScreen renders as a single vertically scrollable view with CalculatorInputPanel, BenefitResultsCard, and share CTA button in correct top-to-bottom order
BlocProvider/BlocBuilder wraps the screen and correctly provides BenefitCalculatorBLoC to all child widgets
BenefitResultsCard updates reactively within 300ms whenever BenefitCalculatorBLoC emits a new state after input changes
Share CTA button is always visible at the bottom, either pinned or scrolled into view, and is disabled when no calculation result exists
Screen registers as a named route and is reachable via the app's navigation graph without deep-link errors
On screen load, accessibility focus is placed on the CalculatorInputPanel's first interactive control
After each calculation completes, programmatic focus is moved to the BenefitResultsCard heading or first metric tile
Screen handles BLoC loading state by showing a progress indicator inside BenefitResultsCard without shifting layout
Screen handles BLoC error state by displaying an inline error message below BenefitResultsCard without dismissing input values
All layout measurements use design token spacing values — no hardcoded pixel values
Screen does not overflow on devices with screen widths from 320dp to 428dp

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
performance requirements
BenefitResultsCard re-render triggered within 300ms of BLoC state emission
Initial screen render completes within 200ms on mid-range Android devices
No jank during scroll — maintain 60fps on devices with 4GB RAM
security requirements
No calculation inputs or results persisted to device storage without explicit user action
Share payload reviewed to contain only aggregated/non-PII statistics
ui components
CalculatorInputPanel
BenefitResultsCard
BenefitMetricTile
Share CTA AppButton
CircularProgressIndicator (loading state)
Inline error banner widget

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Use BlocConsumer rather than two separate BlocBuilder/BlocListener widgets to avoid double rebuilds. Wrap the scrollable body in a CustomScrollView with SliverFillRemaining(hasScrollBody: false) so the share button pins to the bottom when content is short but scrolls naturally with long results. Use FocusScope.of(context).requestFocus() inside BlocListener's listener callback (not builder) to trigger focus shifts after calculation — doing it in builder causes focus thrash on every rebuild. Obtain the BLoC via context.read() in the listener and context.watch() in the builder.

All spacing values must reference AppSpacing design tokens (e.g., AppSpacing.md, AppSpacing.lg). Register the route in the app router before testing navigation.

Testing Requirements

Widget tests must verify: (1) all three child components render in correct vertical order; (2) BLoC state transitions from initial → loading → success update BenefitResultsCard content; (3) BLoC error state renders error message without clearing input panel; (4) share button is disabled when state is initial/loading and enabled on success; (5) focus order moves from input panel to results card after a BLoC calculation event. Use flutter_test with a MockBenefitCalculatorBLoC (mocktail). No integration tests required at this task level — covered in task-012.

Component
Benefit Calculator Screen
ui low
Dependencies (4)
Implement the Flutter widget that renders labelled numeric input fields with stepper controls for session count and session duration. Fields must validate min/max ranges and emit inline accessible error messages using Semantics labels. The widget exposes an onChange callback consumed by the parent BLoC. Supports dark mode via design tokens. epic-benefit-calculator-ui-and-share-task-004 Implement the visually rich Wrapped-style Flutter card widget that displays personal and societal benefit metrics using BenefitMetricTile instances. The card uses semantic markup for screen readers, is wrapped in RepaintBoundary to enable PNG capture for sharing, and adapts its layout to card and full-screen display contexts. Styled with design tokens; no inline CSS. epic-benefit-calculator-ui-and-share-task-008 Build the BLoC that manages calculator screen state: handles input change events from CalculatorInputPanel, calls BenefitCalculationService reactively on each change, dispatches state updates with the latest BenefitCalculationResultModel, and manages loading and error states. Wires the ActivitySummaryAggregator for pre-populating inputs from real activity data on screen load. epic-benefit-calculator-ui-and-share-task-007 Build the service that captures the BenefitResultsCard RepaintBoundary as a PNG image and invokes the native platform share sheet via the share_plus package. The service generates a plain-text summary of the metrics as a fallback for accessibility and for platforms that do not accept image payloads. Handles permission checks and error cases gracefully. epic-benefit-calculator-ui-and-share-task-009
Epic Risks (3)
high impact medium prob technical

The RepaintBoundary PNG capture approach for sharing the results card may produce blurry or oversized images on high-DPI devices, or may silently fail on certain Android OEM configurations that restrict off-screen rendering. A failed share would break one of the core use cases (recruitment tool).

Mitigation & Contingency

Mitigation: Implement the capture using the established screenshot-capture-utility pattern already present in the Wrapped summary feature (component 542-screenshot-capture-utility). Test on a range of iOS and Android devices including Samsung and Huawei OEM builds during development. Set explicit pixel ratio (3.0) when calling toImage() to guarantee resolution.

Contingency: If image capture fails on a platform, the BenefitShareService falls back to sharing the plain-text summary only, with a user-facing message explaining the image could not be generated. This ensures the share flow never fully blocks.

high impact medium prob technical

Implementing full WCAG 2.2 AA compliance for the results card and metric tiles — including live regions, focus management, and semantic labels that read naturally in Norwegian — requires deep familiarity with Flutter semantics APIs. Gaps may only surface during screen reader testing on physical devices, late in the sprint.

Mitigation & Contingency

Mitigation: Use the existing semantics-wrapper-widget (606) and live-region-announcer (608) components from the accessibility feature rather than implementing custom semantics. Assign screen reader testing on a physical iPhone with VoiceOver as a mandatory acceptance gate, not an afterthought. Write widget tests using Flutter's AccessibilityGuideline matchers early in development.

Contingency: If screen reader issues are found late, the pure semantic markup approach (no Canvas numbers, all Semantics wrappers) limits the blast radius to label text corrections. Escalate to the accessibility feature team for a pairing session to resolve complex focus management issues.

medium impact low prob integration

The BenefitResultsCard must match the Wrapped design language used in the annual summary feature. If design tokens or widget patterns are inconsistent between the two features, the results card will look out of place and undermine the intended emotional impact for sharing.

Mitigation & Contingency

Mitigation: Review the existing Wrapped summary screen (529-wrapped-summary-screen) and stat card widget (530-stat-card-widget) implementations before building the results card. Reuse design tokens from the existing design-token-theme (200) system. Involve the designer in a review of the results card mock-up against the Wrapped language before implementation begins.

Contingency: If design parity issues are discovered post-implementation, isolate visual adjustments to the BenefitResultsCard widget. The feature's business logic and accessibility compliance are unaffected by visual polish changes.