这是indexloc提供的服务,不要输入任何密码
Skip to content

Add unfocusable semantic property to force non-focus on widgets #165857

@ash2moon

Description

@ash2moon

Affected platform: Android

Currently, in the AccessibilityService, the isFocusable method in AccessibilityBridge.java determines if a widget is focusable for accessibility. While this method allows widgets to become focusable based on various properties and flags, there is no direct mechanism to explicitly mark a widget as permanently unfocusable. The only workaround to make a widget unfocusable is to ensure it lacks a label, value, hint, and doesn't match any of the predefined FOCUSABLE_FLAGS. This indirect approach is limiting and doesn't provide explicit control over focusability in all scenarios.

Use Case

This feature would be particularly useful for creating more nuanced and accessible UI components, mirroring patterns found in other modern UI frameworks.

In Jetpack Compose's Material Date Picker, the <month year> portion is implemented as an unfocusable live region. The parent composable, which wraps both the <month year> text and the "Switch to selecting a year" action, is focusable. This design is intentional: when users interact with the next/previous month buttons, only the updated <month year> text is announced by screen readers, without the month/year being focusable on its own.

Here's the semantic tree structure from Jetpack Compose illustrating this:

(159133)971.View:(99, 816 - 442, 942):GRANULARITY{0}(action:FOCUS/CLEAR_A11Y_FOCUS/CLICK/SCROLL_TO_POSITION):focusable:screenReaderfocusable:clickable:accessibilityFocused
  (159195)971.TextView:(131, 852 - 326, 905):TEXT{March 2025}:CONTENT{March 2025}:GRANULARITY{11}(action:A11Y_FOCUS/SCROLL_TO_POSITION):supportsTextLocation:politeLiveRegion
  (159257)971.View:(347, 847 - 410, 910):CONTENT{Switch to selecting a year}:GRANULARITY{11}(action:A11Y_FOCUS/SCROLL_TO_POSITION)

You can explore a Jetpack Compose example here: https://github.com/ash2moon/jetpack-calendar

In Flutter, achieving the same component structure as in Jetpack Compose is currently challenging without resorting to semantic announcements as a workaround. The current isFocusable logic doesn't allow for explicitly marking a node as unfocusable regardless of its other semantic properties.

Here's the Flutter Date Picker example for reference: https://github.com/flutter/flutter/blob/master/examples/api/lib/material/date_picker/show_date_picker.0.dart

Semantic tree structure from Flutter:

(35607)1011.View:(152, 878 - 677, 1014):GRANULARITY{0}(action:A11Y_FOCUS):focusable
(35638)1011.View:(152, 878 - 677, 1014):CONTENT{August 2021 Select year}:GRANULARITY{0}(action:A11Y_FOCUS/CLICK/SCROLL_TO_POSITION):focusable:clickable:politeLiveRegion

Expected Behavior and Solution

Add a new semantic property, named unfocusable. When set to true for a semantic node, this property would explicitly force the node to be unfocusable by accessibility services, overriding all other conditions that might otherwise make it focusable.

  • Pros:

    • Potentially more semantically aligned with the purpose of live regions, which are primarily for announcements, not direct interaction.
    • Simple, easy to understand solution.
  • Cons:

    • Confusion between setting focusable = false and unfocusable = false. Developer messages should be logged when conflicting flags are used.

Alternative Solutions

  1. Special Handling for Live Regions: Currently, live regions can still become focusable if they possess other focus-inducing properties. We could modify the system so that by default, nodes marked as live regions are implicitly unfocusable unless they are explicitly configured to be focusable through actions or other focus-related semantic properties. This would naturally address the use case outlined in the Date Picker example, where the <month year> text is a live region intended for announcements but not direct focus.

    • Pros:

      • Potentially more semantically aligned with the purpose of live regions, which are primarily for announcements, not direct interaction.
      • Could simplify the API and reduce the need for explicit unfocusability control in common live region scenarios.
    • Cons:

      • Implicit behavior change that could surprise developers who expect live regions to be focusable in certain situations.
      • May not be a general solution for all cases where unfocusability is desired for elements that are not live regions.
      • Could make it more complex to intentionally make a live region focusable if needed.
  2. New LiveRegion Widget: Another approach is to create a dedicated LiveRegion widget. This widget would inherently manage the semantic properties of a live region but allow developers to provide a closure that returns the text to be announced whenever the content within the LiveRegion needs to be updated. This widget would have an option to become focusable but by default would stay unfocusable and designed specifically for dynamic announcements.

    • Pros:

      • Provides a clear and declarative way for developers to create live regions within their UI.
      • Encapsulates the logic of live regions within a dedicated widget, making it easier to understand and use.
      • Naturally addresses the unfocusability requirement for announcement-only elements.
      • Could potentially offer more control over announcement behavior and customization within the widget itself.
    • Cons:

      • Complicated design and estimated work to complete this would take significantly longer than the proposed solution.
      • Might be perceived as more verbose compared to simply setting an unfocusable property on existing semantic nodes.
      • Could be less flexible if developers want to apply live region semantics to existing widgets without wrapping them in a dedicated LiveRegion widget.

@reidbaker @jmagman

Sub-issues

Metadata

Metadata

Assignees

Labels

P1High-priority issues at the top of the work lista: accessibilityAccessibility, e.g. VoiceOver or TalkBack. (aka a11y)team-accessibilityOwned by Framework Accessibility team (i.e. responsible for accessibility code in flutter/flutter)triaged-accessibilityTriaged by Framework Accessibility team

Type

No type

Projects

Status

No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions