Build calculation preview zero-state placeholder
epic-expense-type-selection-user-interface-task-006 — Implement the zero-state placeholder for the ExpenseCalculationPreview widget that is displayed when no expense type is selected. The placeholder must include an instructional message, use design tokens, and be screen-reader-accessible with a live-region announcement when it appears.
Acceptance Criteria
Technical Requirements
Implementation Notes
Use a simple conditional in the build method: if (state == null) return _ZeroStatePlaceholder(); else return _PreviewContent(state: state!). Extract _ZeroStatePlaceholder as a private StatelessWidget so it can be made const. For the live-region announcement, wrap the placeholder in a Semantics widget with liveRegion: true and a meaningful label (e.g., 'No expense type selected. Choose a type above to see the calculation.').
To ensure the announcement fires only on appearance rather than every rebuild, consider using a StatefulWidget with a didUpdateWidget check, or manage the announcement at the parent screen level when transitioning into the zero state. Keep the placeholder height consistent with the expected preview content height by using a SizedBox with a design-token-defined minimum height constant. The instructional copy should be localized via the app's l10n system — no hardcoded strings.
Testing Requirements
Widget tests: (1) render widget with null state, confirm placeholder is present and the preview content is absent, (2) render widget with a valid state, confirm placeholder is absent and preview content is present, (3) use tester.getSemantics() to confirm the live-region Semantics node is present when placeholder is shown, (4) transition from a valid state to null and confirm the live-region fires (use SemanticsController or a semantic update listener in test), (5) golden test the zero-state placeholder at standard and 1.5Ă— text scale. Coverage target: 100% of conditional branches.
If the expense calculation preview subscribes to the full BLoC state stream, every unrelated state property change (e.g. a loading flag toggle) triggers a widget rebuild. With complex card animations for the disabled-state transition, this could cause frame drops on low-end Android devices used by some peer mentors.
Mitigation & Contingency
Mitigation: Use select() on the Riverpod provider to subscribe only to the specific state slice each widget needs; write a performance test asserting rebuild count on a rapid sequence of toggle events.
Contingency: If jank is detected in device testing, replace animated disabled-state transitions with instant opacity changes and defer animation polish to a follow-up sprint.
The disabled card state requires a specific contrast-safe colour combination that communicates unavailability without relying solely on colour (WCAG 1.4.1). If the current design token palette does not include a disabled-state token with sufficient contrast for text on the disabled background, the widget will either fail WCAG AA or require a last-minute design token addition that could break other components.
Mitigation & Contingency
Mitigation: Audit the existing design token manifest for disabled-state tokens at the start of the epic; if missing, raise with the design lead and add a contrast-validated token before widget implementation begins.
Contingency: If no design review is available, use the established --color-text-disabled and --color-surface-disabled tokens with an added strikethrough or lock icon to satisfy WCAG 1.4.1 non-colour requirement, and document the deviation for design review.