Skip to content

Detached traversal child should update its traversal parent#186118

Merged
auto-submit[bot] merged 9 commits into
flutter:masterfrom
QuncCccccc:invisible_items_traversal_fix
May 20, 2026
Merged

Detached traversal child should update its traversal parent#186118
auto-submit[bot] merged 9 commits into
flutter:masterfrom
QuncCccccc:invisible_items_traversal_fix

Conversation

@QuncCccccc
Copy link
Copy Markdown
Contributor

@QuncCccccc QuncCccccc commented May 6, 2026

Fixes #185784

When a traversal child is detached, its traversal parent was not marked dirty. So the framework didn't send an updated childrenInTraversalOrder for traversal parent. On the engine side, the out-of-dated subtree is still reachable, so Talkback still read invisible items after the menu closed.

This PR is to update the _dirtyNodes list when a traversal child is detached. When a traversal child is detached, its traversal parent will be marked dirty and added to the _dirtyNodes list to update its childrenInTraversalOrder.

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I signed the [CLA].
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is [test-exempt].
  • I followed the [breaking change policy] and added [Data Driven Fixes] where supported.
  • All existing and new tests are passing.

@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label May 6, 2026
@github-actions github-actions Bot added framework flutter/packages/flutter repository. See also f: labels. a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) labels May 6, 2026
@QuncCccccc QuncCccccc force-pushed the invisible_items_traversal_fix branch from 20d1562 to 5ffe0b5 Compare May 6, 2026 03:25
@github-actions github-actions Bot removed the CICD Run CI/CD label May 6, 2026
@QuncCccccc QuncCccccc added the CICD Run CI/CD label May 6, 2026
@QuncCccccc QuncCccccc changed the title Detached traversal child should mark its traversal parent dirty Detached traversal child should update its traversal parent May 6, 2026
@QuncCccccc QuncCccccc force-pushed the invisible_items_traversal_fix branch from 5ffe0b5 to e16cafb Compare May 6, 2026 03:59
@github-actions github-actions Bot removed the CICD Run CI/CD label May 6, 2026
@QuncCccccc QuncCccccc added the CICD Run CI/CD label May 6, 2026
@QuncCccccc QuncCccccc requested a review from chunhtai May 6, 2026 18:50
@QuncCccccc QuncCccccc marked this pull request as ready for review May 7, 2026 19:13
Copy link
Copy Markdown
Contributor

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

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 introduces a mechanism to track and update traversal parent nodes in SemanticsOwner when a child node is detached, and adds a corresponding test case. The reviewer suggests refactoring the implementation to avoid introducing a new _dirtyTraversalParentNodes set, recommending instead that parentNode._markDirty() be used to leverage the existing _dirtyNodes infrastructure for better consistency and robustness.

Comment thread packages/flutter/lib/src/semantics/semantics.dart
Comment thread packages/flutter/lib/src/semantics/semantics.dart Outdated
Comment thread packages/flutter/lib/src/semantics/semantics.dart Outdated
Comment thread packages/flutter/lib/src/semantics/semantics.dart Outdated
if (_traversalChildIdentifier case final Object identifier?) {
if (owner!._traversalParentNodes[identifier] case final SemanticsNode parentNode?
when parentNode.attached && parentNode._isTraversalParent) {
owner!._dirtyTraversalParentNodes.add(parentNode);
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.

why not use parentNode._markDirty() ?

Copy link
Copy Markdown
Contributor Author

@QuncCccccc QuncCccccc May 7, 2026

Choose a reason for hiding this comment

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

I tried that first but marking the traversal parent node dirty will fail a case when its traversal node is dirty. Because when we send a node to _addToUpdate, we have an assertion to check node.parent?._dirty != true.

So I'm thinking if the traversal parent node itself is not "dirty" and we just wanted to update its children lists, we can keep this in _dirtyTraversalParentNodes? What do you think?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done! Talking with @chunhtai offline and seems sorting the updated list to make sure parent is always sent to update first would avoid the error. Thank you!

final Set<SemanticsNode> _dirtyNodes = <SemanticsNode>{};
final Map<int, SemanticsNode> _nodes = <int, SemanticsNode>{};
final Set<SemanticsNode> _detachedNodes = <SemanticsNode>{};
final Set<SemanticsNode> _dirtyTraversalParentNodes = <SemanticsNode>{};
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.

why do we need a separate list? can we just use the _dirtyNodes?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed. Thanks!

@QuncCccccc QuncCccccc force-pushed the invisible_items_traversal_fix branch from e16cafb to ec56744 Compare May 12, 2026 21:53
@github-actions github-actions Bot removed the CICD Run CI/CD label May 12, 2026
@QuncCccccc QuncCccccc added the CICD Run CI/CD label May 12, 2026
@github-actions github-actions Bot removed the CICD Run CI/CD label May 12, 2026
@QuncCccccc QuncCccccc force-pushed the invisible_items_traversal_fix branch from 2bebc18 to 3711637 Compare May 12, 2026 23:24
@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label May 12, 2026
@github-actions github-actions Bot removed the CICD Run CI/CD label May 13, 2026
@QuncCccccc QuncCccccc added the CICD Run CI/CD label May 13, 2026
@github-actions github-actions Bot removed the CICD Run CI/CD label May 13, 2026
@QuncCccccc QuncCccccc force-pushed the invisible_items_traversal_fix branch from a76c7ce to e00cc76 Compare May 13, 2026 05:08
@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label May 13, 2026
@QuncCccccc QuncCccccc force-pushed the invisible_items_traversal_fix branch from e00cc76 to 65c0c25 Compare May 13, 2026 05:27
@github-actions github-actions Bot removed the CICD Run CI/CD label May 13, 2026
@QuncCccccc QuncCccccc added the CICD Run CI/CD label May 13, 2026
@QuncCccccc QuncCccccc requested a review from chunhtai May 13, 2026 18:53
}
}

updatedVisitedNodes.sort((SemanticsNode a, SemanticsNode b) => a.depth - b.depth);
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.

updatedVisitedNodes cames from visitedNodes which is already sorted above, do you know why we need to sort it again?

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.

it also feel like we are sorting excessively
localDirtyNodes is sorted to be added to visitedNodes
and visitedNodes is sorted to be added updatedVisitedNodes

and here is another point of sorting for updatedVisitedNodes. I felt we could eliminate some of them and just sort once at the end. Also maybe they should be stored in a set before the sorting

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I see, it makes sense. Seems we sort the list 3 times which is unnecessary. I'll update the logic a bit!

@QuncCccccc QuncCccccc requested a review from chunhtai May 19, 2026 04:26
@github-actions github-actions Bot removed the CICD Run CI/CD label May 19, 2026
Copy link
Copy Markdown
Contributor

@chunhtai chunhtai left a comment

Choose a reason for hiding this comment

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

code LGTM, just left some nit

Comment on lines +4999 to +5000
_traversalChildNodes[node.traversalChildIdentifier!] ??= <SemanticsNode>{};
_traversalChildNodes[node.traversalChildIdentifier!]!.add(node);
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.

_traversalChildNodes.putIfAbsent(node.traversalChildIdentifier!, <SemanticsNode>() =><SemanticsNode>{}).add(node)

@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label May 19, 2026
@github-actions github-actions Bot removed the CICD Run CI/CD label May 19, 2026
@QuncCccccc QuncCccccc added the CICD Run CI/CD label May 19, 2026
@github-actions github-actions Bot removed the CICD Run CI/CD label May 20, 2026
@QuncCccccc QuncCccccc added the CICD Run CI/CD label May 20, 2026
@QuncCccccc QuncCccccc requested a review from chunhtai May 20, 2026 01:20
Copy link
Copy Markdown
Contributor

@chunhtai chunhtai left a comment

Choose a reason for hiding this comment

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

LGTM

@QuncCccccc QuncCccccc added the autosubmit Merge PR when tree becomes green via auto submit App label May 20, 2026
@auto-submit auto-submit Bot added this pull request to the merge queue May 20, 2026
Merged via the queue into flutter:master with commit 3c96406 May 20, 2026
89 of 90 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label May 20, 2026
matthewhendrix pushed a commit to matthewhendrix/flutter that referenced this pull request May 23, 2026
…186118)

Fixes flutter#185784

When a traversal child is detached, its traversal parent was not marked
dirty. So the framework didn't send an updated childrenInTraversalOrder
for traversal parent. On the engine side, the out-of-dated subtree is
still reachable, so Talkback still read invisible items after the menu
closed.

This PR is to update the _dirtyNodes list when a traversal child is
detached. When a traversal child is detached, its traversal parent will
be marked dirty and added to the _dirtyNodes list to update its
childrenInTraversalOrder.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) CICD Run CI/CD framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Android] MenuAnchor items read with Talkback even when hidden

3 participants