critical priority low complexity infrastructure pending infrastructure specialist Tier 3

Acceptance Criteria

All five foundation providers are declared at app-level scope (not page/feature scope) so they are available throughout the widget tree
ScreenReaderDetectionService.initialize() is called before the first frame is rendered (inside main() after WidgetsFlutterBinding.ensureInitialized())
WidgetsBindingObserver is attached to ScreenReaderDetectionService at bootstrap and detached on app dispose
The SemanticsWrapperWidget theme extension (if implemented as a ThemeExtension) is registered in the app's ThemeData at bootstrap
App bootstrap completes without throwing any ProviderException or StateError related to the five foundation providers
Existing app bootstrap logic (Supabase init, BankID config, routing) is unaffected and continues to function
No duplicate provider registrations; each provider is defined once and referenced everywhere else
App startup time does not increase by more than 50ms due to the five new provider registrations
Bootstrap code is documented with a comment block explaining the purpose of the accessibility foundation providers
The implementation is validated manually on both iOS (VoiceOver) and Android (TalkBack) in debug mode

Technical Requirements

frameworks
Flutter
Riverpod
apis
WidgetsFlutterBinding.ensureInitialized()
ProviderScope
WidgetsBindingObserver
ProviderContainer (for eager init)
ThemeData.extensions
data models
AccessibilitySettings
ScreenReaderState
SensitiveFieldConfig
performance requirements
All five providers are AutoDispose or kept-alive scoped appropriately; avoid memory leaks
ScreenReaderDetectionService init must complete synchronously or before runApp() is called
security requirements
No sensitive data (BankID tokens, Supabase keys) should be passed through accessibility providers
SensitiveFieldConfigurationRegistry must be initialised before any UI renders to prevent accidental label leakage

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

In main.dart, after WidgetsFlutterBinding.ensureInitialized() and existing Supabase/BankID init, add a call to bootstrap the accessibility foundation: create a top-level initAccessibilityFoundation() async function in lib/accessibility/bootstrap.dart that (1) reads ScreenReaderDetectionService from a temporary ProviderContainer, (2) calls initialize(), and (3) attaches WidgetsBindingObserver. Use ProviderScope at the root of runApp() — do not create a separate ProviderContainer for the app. For eager initialisation of ScreenReaderDetectionService, use ref.read(screenReaderDetectionServiceProvider).initialize() inside a Provider.overrideWithValue or an app-level ConsumerStatefulWidget initState. The cleanest pattern is an AppStartup ConsumerStatefulWidget that calls the init in initState and shows a loading indicator until done — this pattern is already common in Riverpod apps.

Ensure the WidgetsBindingObserver is removed in the service's dispose() method to prevent memory leaks when the provider is invalidated.

Testing Requirements

Smoke test: run the app in debug mode on an iOS simulator with VoiceOver enabled and confirm ScreenReaderDetectionService emits true within the first frame. Run on Android emulator with TalkBack enabled and confirm the same. Write a widget test that bootstraps the full ProviderScope (as in task-012 integration test) and confirms all five providers are accessible immediately after the first frame (tester.pump()). Confirm no errors appear in the debug console on cold start.

If CI runs integration tests, add a startup smoke test to the integration_test/ suite that reads all five providers and asserts non-null values.

Component
Screen Reader Detection Service
service medium
Dependencies (5)
Implement the SensitiveFieldConfigurationRegistry class with runtime-updatable field registration, lookup API (isSensitive(fieldId), getWarningKey(fieldId)), and default configuration seeded with known sensitive fields (personnummer, full name, address, health data). Expose as a Riverpod provider. epic-screen-reader-support-foundation-task-002 Implement the AccessibilitySettingsRepository backed by SharedPreferences or Hive. Expose a Stream<AccessibilitySettings> for reactive updates, plus synchronous read and async write methods. Ensure settings persist across app restarts and are loaded at startup. Register as a Riverpod StreamProvider. epic-screen-reader-support-foundation-task-004 Implement the ScreenReaderDetectionService that detects VoiceOver (iOS) and TalkBack (Android) activation state at app startup using MediaQuery.accessibleNavigation or AccessibilityFeatures. Expose a Stream<bool> isScreenReaderActive that re-evaluates on app foreground via WidgetsBindingObserver. Register as a Riverpod StreamProvider. epic-screen-reader-support-foundation-task-007 Create the SemanticsWrapperWidget Flutter widget that wraps any interactive child with a Semantics widget. Accept parameters for label, hint, role (button, textField, image, etc.), state (enabled, checked, selected), and a flag to mark decorative elements for exclusion (excludeSemantics). Follow WCAG 2.2 AA naming conventions. epic-screen-reader-support-foundation-task-009 Create the SemanticsServiceFacade class that wraps Flutter's SemanticsService.announce() and platform MethodChannel calls. Provide a testable interface (abstract class + concrete implementation) so tests can inject a mock. Support announcement queuing, priority levels (polite vs assertive), and locale-aware text. Register as a Riverpod provider. epic-screen-reader-support-foundation-task-005
Epic Risks (2)
high impact medium prob technical

Flutter's SemanticsService behaves differently between iOS (VoiceOver) and Android (TalkBack) in edge cases — e.g., announcement queuing, focus-gain timing, and attribute support. If the facade does not correctly abstract these differences, announcements may be silent or misfired on one platform, causing regression on the other platform to go unnoticed until device testing.

Mitigation & Contingency

Mitigation: Write platform-divergence unit tests early using SemanticsServiceFacade mocks. Validate announcement delivery on a physical iPhone (VoiceOver) and Android device (TalkBack) at the end of each sprint. Document known platform differences in the facade's inline API comments.

Contingency: If a platform difference cannot be abstracted cleanly, expose a platform-specific override path in the facade and implement targeted workarounds per platform, accepting the added complexity in exchange for correct behaviour.

medium impact medium prob scope

Accessibility preferences stored in local storage may need new fields as higher-tier epics are implemented (e.g., announcement verbosity, sensitive-field guard toggle). Schema changes to an already-persisted store risk data migration failures or silent defaults on existing installs, breaking user preferences.

Mitigation & Contingency

Mitigation: Design the AccessibilitySettingsRepository with a versioned JSON schema from the start, using merge-with-defaults on read so new fields fall back gracefully. Define the full expected field list upfront based on all downstream epic requirements before writing the first record.

Contingency: If migration fails on a live install, fall back to full reset-to-defaults with a one-time in-app notification informing the user that accessibility preferences have been reset and inviting them to reconfigure.