Service Layer medium complexity Shared Component mobile
1
Dependencies
0
Dependents
0
Entities
0
Integrations

Description

Centralizes screen reader focus management for navigation transitions, dialog opens/closes, and dynamic content updates. Ensures that when routes change or overlays appear, focus is placed predictably and consistently so blind users can orient themselves immediately.

Feature: Screen Reader Support

focus-management-service

Summaries

The Focus Management Service ensures that blind and low-vision users who navigate the app exclusively through screen reader focus can always orient themselves after any navigation event, dialog interaction, or dynamic content update. Without centralized focus management, users may find themselves stranded — focus left on a dismissed element, or dropped to an unpredictable location after a route change — making the app effectively unusable for this audience. By guaranteeing predictable, consistent focus placement across all navigation patterns, this shared service protects the entire app's accessibility quality and reduces the risk that individual feature teams introduce focus regressions when building new screens or overlays. It is a force-multiplier for inclusive design across the entire product.

Focus Management Service is a medium-complexity shared service that underpins accessibility behaviour across every screen and overlay in the application. It depends on screen-reader-detection-service to ensure focus operations only fire when a screen reader is active. Because it is shared, it must be designed and delivered before feature teams begin integrating navigation transitions or modal dialogs that require accessible focus handling. The primary delivery risk is timing sensitivity: deferred focus operations (deferFocus()) must fire after the target widget's build cycle, which requires careful coordination with Flutter's frame scheduling.

Include integration tests for focus restoration after modal dismissal and after dynamic widget insertion, as these are the most common regression points. Document the API contract clearly so feature teams can adopt it consistently without requiring accessibility expert review on every PR.

Focus Management Service manages FocusNode lifecycle and SemanticsService focus requests in a centralized queue to avoid race conditions when multiple widgets compete for focus during a single frame. moveFocusTo() calls Scrollable.ensureVisible() if the target is off-screen, then calls FocusScope.of(context).requestFocus(node) or SemanticsService.announce() depending on whether the target has a FocusNode or is semantics-only. deferFocus() enqueues the operation via WidgetsBinding.addPostFrameCallback with an optional additional Future.delayed(Duration(milliseconds: delayMs)) for widgets that animate in. saveFocusPosition() stores the current FocusManager.instance.primaryFocus?.context key in a stack (Map), and restoreFocus() pops and re-requests it after modal dismissal.

clearFocusHistory() drains the stack on full navigation resets. All operations check screen-reader-detection-service.isScreenReaderActive() synchronously and no-op when inactive.

Responsibilities

  • Move screen reader focus to a target widget after navigation transitions
  • Restore focus to the originating element after modal dismissal
  • Handle focus for dynamically inserted widgets (e.g., error banners)
  • Provide a queue for deferred focus operations that must wait for build

Interfaces

moveFocusTo(widgetKey)
restoreFocus(savedKey)
saveFocusPosition(key)
deferFocus(widgetKey, delayMs)
clearFocusHistory()

Relationships

Dependencies (1)

Components this component depends on

API Contract

View full contract →
REST /api/v1/focus-positions 8 endpoints
GET /api/v1/focus-positions
GET /api/v1/focus-positions/:id
POST /api/v1/focus-positions
PUT /api/v1/focus-positions/:id
DELETE /api/v1/focus-positions/:id
POST /api/v1/focus-positions/move
+2 more