Implement SensitiveFieldPrivacyGuard core service
epic-screen-reader-support-complex-widgets-task-001 — Build the SensitiveFieldPrivacyGuard service that orchestrates the full sensitive-field warning flow. Implement session-level suppression logic so once a user dismisses the warning it is not repeated for the session. Integrate with SensitiveFieldConfiguration to determine which fields require warnings and with ScreenReaderDetectionService to conditionally activate the guard.
Acceptance Criteria
Technical Requirements
Implementation Notes
Implement using Riverpod Notifier (not StateNotifier, which is deprecated in Riverpod 2.x) for forward compatibility. Keep the service as a pure Dart class with no Flutter import dependencies — this makes it trivially unit-testable without pumpWidget overhead. Cache ScreenReaderDetectionService.isScreenReaderActive() result at service build() time and re-evaluate on accessibility change events if the detection service exposes a stream; otherwise re-query on each shouldShowWarning call but wrap in a try/catch. The session suppression map should be a private final HashSet
Do not use a boolean per-field map initially — a HashSet is simpler and sufficient since undismiss is not a use case. Place the service in lib/features/accessibility/services/sensitive_field_privacy_guard.dart following the existing feature-layer directory convention. Register the provider in the accessibility providers barrel file. This service is the foundation for the screen reader support epic — correctness and testability here are more important than optimisation.
Testing Requirements
Write unit tests using flutter_test (no UI pump required) covering all shouldShowWarning branches: (1) screen reader inactive → returns false regardless of config; (2) screen reader active + field not in config → returns false; (3) screen reader active + field in config + not dismissed → returns true; (4) screen reader active + field in config + dismissed → returns false; (5) ScreenReaderDetectionService throws → returns false (fail-safe); (6) dismissWarning with unknown key is no-op, no exception. Mock both ScreenReaderDetectionService and SensitiveFieldConfiguration using mocktail. Verify that session state does not bleed between test cases (create a fresh provider container per test). Aim for 100% branch coverage on the service logic.
No widget or integration tests in this task — those are handled by downstream tasks in the epic.
Flutter does not natively enforce a focus trap within a bottom sheet or modal dialog in the semantic tree — VoiceOver and TalkBack can navigate outside the sheet to background content. Implementing a reliable focus trap requires overriding the semantic tree, which may conflict with the existing modal helper infrastructure in the app and require changes to shared components beyond this feature's scope.
Mitigation & Contingency
Mitigation: Prototype the focus trap on the first modal sheet implementation before building the remaining sheets. Evaluate Flutter's ExcludeSemantics and BlockSemantics widgets as the trap mechanism, and coordinate with the team owning the shared modal helpers to agree on a non-breaking integration point before writing production code.
Contingency: If a complete semantic focus trap cannot be implemented without breaking existing modal patterns, implement a partial solution using FocusScope with autofocus on the modal's first element and a prominent 'Return to main content' semantic action, documenting the deviation from WCAG 2.4.3 with a scheduled remediation item.
The activity wizard uses BLoC state management and the UI rebuilds the entire step widget subtree on transition. If the semantic tree is traversed by VoiceOver before the build cycle settles, focus may land on a stale or partially rendered step, causing the wrong step label or progress value to be announced. This is particularly problematic for blind users who cannot visually verify the announcement against the screen.
Mitigation & Contingency
Mitigation: Coordinate ActivityWizardStepSemantics with FocusManagementService (from the core services epic) to delay focus placement until the post-build callback confirms the new step's semantic tree is complete. Write integration tests using the AccessibilityTestHarness that assert the full announcement sequence across all five wizard steps.
Contingency: If post-build focus delay is insufficient due to async BLoC emission timing, add an explicit semantic notification barrier in the wizard cubit that emits a 'step ready' event only after the new widget tree has been marked as built, decoupling the announcement trigger from the raw state transition.
Automated WCAG contrast ratio checking on widget tree snapshots may produce false positives for gradient backgrounds, dark-mode overrides, or design token overrides that are resolved at runtime but appear as unresolvable colours at static analysis time. Excessive false positives would erode team trust in the CI gate, leading to suppression rules that also mask real violations.
Mitigation & Contingency
Mitigation: Scope the WCAGComplianceChecker to check only solid-colour backgrounds in the first iteration, explicitly excluding gradients from contrast checks with documented rationale. Design the check output to distinguish 'undetermined' (gradient/unknown) from 'fail' (solid colour below threshold) so the team can take targeted action on genuine failures only.
Contingency: If false positive rates exceed 20% of reported violations during initial CI runs, switch the CI gate from a hard build failure to a warning annotation on the pull request, combined with a mandatory manual review step, until the checker's rule set has been tuned to match actual design token values.