Sitelet https://github.com/flutter/flutter/pull/188820
Skip to content

[web] Fix VoiceOver child focus direction - #188820

Merged
auto-submit[bot] merged 11 commits into
flutter:masterfrom
flutter-zl:issue-168458-voiceover-focus-rework
Sep 3, 2026
Merged

auto-submit[bot] merged 11 commits into
flutter:masterfrom
flutter-zl:issue-168458-voiceover-focus-rework

Conversation

@flutter-zl

@flutter-zl flutter-zl commented Jul 1, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #168458

Problem

When VoiceOver focuses a child semantics node inside a Flutter web view, ViewFocusBinding reports it as forward tab traversal. The framework then moves focus back to the first focusable widget instead of keeping VoiceOver's target.

Fix

ViewFocusBinding now uses the keyboard direction only when the Flutter view root is focused. Child focus reports ViewFocusDirection.undefined, preserving VoiceOver's selected element.

Demo

Before After
https://flutter-demo-46-before.web.app https://flutter-demo-46-after.web.app

Steps: with VoiceOver on, open the rotor or context menu(CTRL+OPTION+U) and jump between form controls.
Before: focus snaps back to the first form control after each VoiceOver jump.
After: focus stays on the control VoiceOver navigated to.

@flutter-zl flutter-zl added the CICD Run CI/CD label Jul 1, 2026
@github-actions github-actions Bot added engine flutter/engine related. See also e: labels. platform-web Web applications specifically f: focus Focus traversal, gaining or losing focus team-web Owned by Web platform team labels Jul 1, 2026
@flutter-zl
flutter-zl marked this pull request as ready for review July 22, 2026 21:37

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates ViewFocusBinding to assign an undefined focus direction when a child element of the view root is focused, rather than the root itself. It introduces a helper method _isViewRoot to verify if the focused element is the root element, and adds several unit tests to validate this behavior. Feedback suggests simplifying the _isViewRoot implementation by replacing the mutual contains checks with a direct equality comparison (element == rootElement) to improve performance and readability.

@flutter-zl flutter-zl closed this Aug 5, 2026
@flutter-zl flutter-zl reopened this Aug 10, 2026
@github-actions github-actions Bot added the framework flutter/packages/flutter repository. See also f: labels. label Aug 10, 2026
@fluttergithubbot

Copy link
Copy Markdown
Contributor

An existing Git SHA, 7bfcfc3dd06f99f63c00a836bd941dd38e884105, was detected, and no actions were taken.

To re-trigger presubmits after closing or re-opeing a PR, or pushing a HEAD commit (i.e. with --force) that already was pushed before, push a blank commit (git commit --allow-empty -m "Trigger Build") or rebase to continue.

@flutter-zl
flutter-zl requested review from chunhtai and mdebbar August 11, 2026 21:03
return viewManager.findViewForElement(element)?.viewId;
}

bool _isViewRoot(int viewId, DomElement? element) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If i understand correctly, this method is used to determine whether the focus is on the semantics dom or not. should probably rename to be more readable.

also what if it focuses on something else like dom from platformview or other plugins' created dom

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this function checks if the focus is exactly on the root element of the view. If the focus lands on any descendant, this function returns false.

@mdebbar mdebbar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

return viewManager.findViewForElement(element)?.viewId;
}

bool _isViewRoot(int viewId, DomElement? element) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this function checks if the focus is exactly on the root element of the view. If the focus lands on any descendant, this function returns false.

@flutter-zl flutter-zl added the autosubmit Merge PR when tree becomes green via auto submit App label Aug 31, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Aug 31, 2026
@auto-submit

auto-submit Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/188820, because - The status or check suite Dashboard Checks has failed. Please fix the issues identified (or deflake) before re-applying this label.

@flutter-zl flutter-zl added the autosubmit Merge PR when tree becomes green via auto submit App label Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CICD Run CI/CD engine flutter/engine related. See also e: labels. f: focus Focus traversal, gaining or losing focus framework flutter/packages/flutter repository. See also f: labels. platform-web Web applications specifically team-web Owned by Web platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[web] [a11y] Focus in VoiceOver jumps back to first form control when navigating via context menu

4 participants