Skip to content

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

Closed
0 of 1 issue completed
Closed

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

Activity

  1. added
    a: accessibilityAccessibility, e.g. VoiceOver or TalkBack. (aka a11y)
    fyi-accessibilityFor the attention of Framework Accessibility team
    on Mar 25, 2025
  2. changed the title [-]Need a new semantic property to force non-focus on widgets that have a value/label[/-] [+]Add unfocusable semantic property to force non-focus on widgets[/+] on Mar 25, 2025
  3. added
    team-accessibilityOwned by Framework Accessibility team (i.e. responsible for accessibility code in flutter/flutter)
    on Mar 25, 2025
  4. chunhtai commented on Mar 25, 2025

    @chunhtai
    Contributor

    I think adding the a accessibilityFocusable (to differentiate SemanticsProperties.focusable which is for keyboard focus) sounds good to me.

    We need to make sure we have the equivalent for iOS part though, we should not add single platform only api if possible.

    cc @loic-sharma , does iOS has the equivalent for this? ie. makes a single node skip voiceover focus, but everything else like liveregion still works. The only way I am aware of is isAccessibilityElement, but that will likely disable everything.

  5. removed their assignment
    on Mar 25, 2025
  6. ash2moon commented on Mar 25, 2025

    @ash2moon
    ContributorAuthor

    I like accessibilityFocusable too. By default this could be true, but whenever it is false, the function should auto return false regardless.

  7. added
    P1High-priority issues at the top of the work list
    on Mar 25, 2025
  8. chunhtai commented on Mar 25, 2025

    @chunhtai
    Contributor

    Thinking about it more, this property may need to be tristate.
    true, false, not provided.

    true, false should work as expected.

    When not provided, we will default to our current behavior. ie. if has label/value/hint and any non-system semantics semantic actions.

    We may also need to wait for SemanticsFlag refactor from @hannah-hyj since the bit map is going to overflow if we added a tristate semantics flag like accessibilityFocusable

  9. flutter-triage-bot commented on Mar 25, 2025

    @flutter-triage-bot

    The fyi-accessibility label is redundant with the team-accessibility label.

  10. jmagman commented on May 21, 2025

    @jmagman
    Member

    I checked with @hannah-hyj, this is waiting on the semantics flag refactoring to land #166101

  11. added
    ⏳Bot is counting down the days until it unassigns the issue
    on Sep 24, 2025
  12. flutter-triage-bot commented on Sep 24, 2025

    @flutter-triage-bot

    This issue is assigned to @hannah-hyj but has had no recent status updates. Please consider unassigning this issue if it is not going to be addressed in the near future. This allows people to have a clearer picture of what work is actually planned. Thanks!

  13. hannah-hyj commented on Sep 24, 2025

    @hannah-hyj
    Member

    i started working on a draft PR for this #175551

  14. removed
    ⏳Bot is counting down the days until it unassigns the issue
    on Sep 24, 2025
  15. github-actions commented on Jan 1, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  16. locked as resolved and limited conversation to collaborators on Jan 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions