Assemble complete light and dark ThemeData objects
epic-visual-design-accessibility-theme-integration-task-005 — Combine the color scheme, text theme, and all component themes into complete ThemeData instances for both light and dark modes. Register both variants with a Riverpod provider so the MaterialApp can consume them via a single theme notifier. Ensure the builder returns an immutable ThemeBuildResult containing both variants.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 4 - 323 tasks
Can start after Tier 3 completes
Handles integration between different epics or system components. Requires coordination across multiple development streams.
Implementation Notes
Define ThemeBuildResult as a Dart record (Dart 3+): record ThemeBuildResult(ThemeData light, ThemeData dark). The Riverpod provider should watch both the token provider and the accessibility preferences provider using ref.watch so any change to either triggers a rebuild. Use a synchronousProvider if token resolution is synchronous (preferred) to avoid loading states in MaterialApp. Register custom ThemeExtension subclasses via ThemeData.extensions to expose brand colors (e.g., badge colors, status colors) that have no Material 3 ColorScheme counterpart — this prevents scattered DesignTokenProvider.instance calls throughout the widget tree.
Ensure the provider is placed above MaterialApp in the ProviderScope to avoid lookup errors.
Testing Requirements
Unit tests: (1) ThemeBuildResult contains non-null light and dark ThemeData, (2) both have useMaterial3 true, (3) provider invalidation produces a new ThemeBuildResult instance (not the cached one), (4) structural equality confirms colorScheme/textTheme/component themes are populated. Widget test: wrap a minimal MaterialApp with the Riverpod provider, change the mocked accessibility_preferences, and assert the theme color seen by a child widget changes within one pump cycle. Integration test: verify the full provider graph from accessibility_preferences → ThemeBuildResult → MaterialApp renders without errors. 90% coverage target on the builder and provider setup.
One or more of the four partner organisations may supply brand primary colors that cannot be paired with any standard foreground at 4.5:1 contrast (for example, a mid-range hue that is too light for dark text and too dark for white text). Rejecting these colors programmatically could cause a political dispute with the organisation and delay the feature.
Mitigation & Contingency
Mitigation: Before implementation begins, run all four organisations' existing brand primary colors through the contrast-ratio-validator against both white (#FFFFFF) and a near-black (#1A1A1A). Share the results with each organisation's contact person ahead of the theme builder sprint so any problematic colors can be adjusted collaboratively with advance notice.
Contingency: If an organisation insists on a non-compliant brand color, produce a compliant near-match (lightened or darkened along the hue's luminance axis) and present both options with contrast ratio evidence. Document the adjusted token in the manifest with an explicit note that the original brand color was non-compliant, and obtain written sign-off from the organisation.
Flutter's ThemeData contains over 30 component theme properties. If the theme-builder only addresses the most common ones (Button, InputDecoration, Card) and leaves others at Flutter defaults, downstream feature teams may unknowingly use default-themed widgets that do not meet sizing or contrast requirements.
Mitigation & Contingency
Mitigation: Produce a full inventory of all ThemeData component theme properties and map each to either a token-driven override or an explicit pass-through decision documented in the theme builder code. Prioritise the inventory by frequency of use in the existing codebase (identified via Grep). Include a check in the CI lint runner that flags widgets using Flutter default component themes not covered by the theme builder.
Contingency: If the full inventory scope exceeds the sprint budget, ship with the highest-frequency components covered and add a tracked backlog item for each uncovered component theme, pairing with a temporary lint suppression comment that includes the backlog reference.