Skip to content

Fixes OverlayPortal semantics bounds corruption - #192139

Merged
auto-submit[bot] merged 2 commits into
flutter:masterfrom
LouiseHsu:semantic-tree-overlay-portal-bug
Sep 3, 2026
Merged

auto-submit[bot] merged 2 commits into
flutter:masterfrom
LouiseHsu:semantic-tree-overlay-portal-bug

Conversation

@LouiseHsu

@LouiseHsu LouiseHsu commented Sep 1, 2026 •

Copy link
Copy Markdown
Contributor

fixes #182604

Historically, the configuration/order of children and parents for widgets is identical across both traversal and hit test order. However, with introduction over OverlayPortal, fccfa97, this is no longer always the case.

For example when traversing, the actual portal child, eg, a floating drop down box, is a child of the OverlayPortal. However when hit testing, the portal child is a child of the Overlay.

Prior to this pr, both setChildrenInHitTestOrder and setChildren have identical implementations.

  for (SemanticsObject* child in _childrenInHitTestOrder) {
    if (child.parent == self) {
      child.parent = nil;
    }
  }
  _childrenInHitTestOrder = [childrenInHitTestOrder copy];
  for (SemanticsObject* child in _childrenInHitTestOrder) {
    child.parent = self;
  }

On every frame, setChildren and setChildrenInHitTestOrder is run sequentially on every widget.
So first, when setChildren is run for OverlayPortal, it sets portalChild.parent = overlayPortal. This is the correct traversal order.
Then, sometime later, it runs setChildrenInHitTestOrder for Overlay, which sets portalChild.parent = overlay`. This is incorrect.

This is why in the bug the accessibility nodes still exist, just weirdly offset - because its now anchored to 0, 0 of Overlay instead of the the position over OverlayPortal.

Because hittest order strictly walks top down (parent isnt ever accessed), we don't actually have to do any mutation to the parents.

Screen.Recording.new.iphone.17.26.2.09-02-2026.at.12.10.58.mp4

@LouiseHsu LouiseHsu added the CICD Run CI/CD label Sep 1, 2026
@github-actions github-actions Bot added platform-ios iOS applications specifically engine flutter/engine related. See also e: labels. team-ios Owned by iOS platform team labels Sep 1, 2026
@LouiseHsu LouiseHsu changed the title attempt Fixes OverlayPortal semantics regression Sep 2, 2026
@LouiseHsu LouiseHsu changed the title Fixes OverlayPortal semantics regression Fixes OverlayPortal semantics bounds corruption Sep 2, 2026
@LouiseHsu
LouiseHsu marked this pull request as ready for review September 2, 2026 20:05
@LouiseHsu
LouiseHsu requested a review from a team as a code owner September 2, 2026 20:05
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@LouiseHsu

Copy link
Copy Markdown
Contributor Author

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

i didnt ask ngl

@LouiseHsu LouiseHsu added the autosubmit Merge PR when tree becomes green via auto submit App label Sep 3, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Sep 3, 2026
Merged via the queue into flutter:master with commit 20c755d Sep 3, 2026
30 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Sep 3, 2026
@tuxfamily

Copy link
Copy Markdown

@LouiseHsu could this fix be considered for a cp: stable into flutter-3.47-candidate.0?

Filling in the cherry-pick request fields from our side:

Impacted users: any iOS app built with Flutter ≥ 3.41 whose semantics tree contains an OverlayPortal (directly, or through Material widgets built on it). On such apps, every Flutter accessibility element below the affected node reports a frame scaled down by the device pixel ratio (÷3 on iPhone, ÷2 on iPad) and anchored at the origin.

Impact: breaks VoiceOver focus rectangles / touch exploration, and every XCUITest-based tool that relies on accessibilityFrame (XCUITest itself, Xcode Accessibility Inspector, Maestro — see mobile-dev-inc/maestro#2883 and mobile-dev-inc/maestro#2138, which are the same symptom). For us it blocks the whole iOS UI-test campaign: taps land at the wrong place, typeText never reaches the field.

Reproduction on our side: Flutter 3.47.2 (stable), iOS 26.5 simulator (iPhone 17 Pro), also iPhone 16 / iOS 26.2 and iPad A16. Same tree on master (3.48.0-0.3.pre, engine f5abf4e, which contains this PR): frames are correct. Concrete numbers for one text field: [3,125][130,143] on 3.47.2 vs [10,375][392,429] on master (exactly ×3). The app code has no explicit OverlayPortal (only Material widgets such as Scaffold/AppBar/Drawer/TextField/Tooltip), so the affected population is wider than apps using OverlayPortal directly.

Workaround: none inside the app (the bug is in the engine's iOS accessibility bridge); the only workaround is to build with master.

Risk: low — the change removes 8 lines that mutated SemanticsObject.parent from the hit-test order pass; the traversal order pass keeps setting parents. Covered by the new unit test in SemanticsObjectTest.mm.

Validation steps: run any XCUITest / maestro hierarchy against a Flutter app containing an OverlayPortal on an iOS simulator and check that element frames are in screen points (not divided by the screen scale).

Thank you.

auto-submit Bot pushed a commit to flutter/packages that referenced this pull request Sep 4, 2026
…12760)

Manual roll Flutter from 70797e15325e to 5a6cfa7f3d26 (52 revisions)

Manual roll requested by [email protected]

flutter/flutter@70797e1...5a6cfa7

2026-09-04 [email protected] [tool] Migrate ScreenshotCommand to modular dependency injection (flutter/flutter#190767)
2026-09-04 [email protected] Roll Skia from 8fce167bdded to 93ac1e630d1d (3 revisions) (flutter/flutter#192306)
2026-09-04 [email protected] Add iOS Universal Link integration test (Simulator) (flutter/flutter#191443)
2026-09-04 [email protected] [AGP 9.1.0 Migration #5] Delete getLegacyAndroidExtension and migrate NDK fallback to public DSL (flutter/flutter#192116)
2026-09-04 [email protected] Reland "[tool_tests] Remove bringup from Windows tool_tests_commands_2_2" (#191732) (flutter/flutter#191802)
2026-09-04 [email protected] [flutter_tools] Fix unhandled DebugAdapterException on early process termination (flutter/flutter#192294)
2026-09-04 [email protected] Roll Packages from f9b3954 to 9af9c60 (4 revisions) (flutter/flutter#192292)
2026-09-04 [email protected] Roll Skia from 2e3f7e07eee6 to 8fce167bdded (5 revisions) (flutter/flutter#192283)
2026-09-04 [email protected] Roll Fuchsia Linux SDK from GTZNkoIR_WegRPWgL... to FgDQeF6jb1dVRVh3K... (flutter/flutter#192276)
2026-09-04 [email protected] Roll Skia from b6b00df360e5 to 2e3f7e07eee6 (2 revisions) (flutter/flutter#192275)
2026-09-04 [email protected] [iOS] TextField should support typing accented words with external keyboard (flutter/flutter#191062)
2026-09-04 [email protected] Roll Skia from 1f68c0b16f17 to b6b00df360e5 (7 revisions) (flutter/flutter#192269)
2026-09-04 [email protected] [flutter_tools] add typed enum and defaulted option descriptors (flutter/flutter#191760)
2026-09-04 [email protected] Roll Dart SDK from c2edc382a4f8 to 5501d02b583d (1 revision) (flutter/flutter#192263)
2026-09-04 [email protected] [Linux] Use fences to synchronize frames between OpenGL contexts (flutter/flutter#192098)
2026-09-03 [email protected] Defer semantics geometry updates for blocked branches (flutter/flutter#192146)
2026-09-03 [email protected] Roll Skia from 8d3e197531c6 to 1f68c0b16f17 (8 revisions) (flutter/flutter#192244)
2026-09-03 [email protected] Fix DeleteSurrounding splitting UTF-16 surrogate pairs (flutter/flutter#190514)
2026-09-03 [email protected] Remove no-shuffle from progress indicator test (flutter/flutter#186655)
2026-09-03 [email protected] [material] Remove unused parameters from constructors of generic test class (flutter/flutter#191764)
2026-09-03 [email protected] Handle linear and radial gradients in UberSDF (re-implement after fix) (flutter/flutter#192124)
2026-09-03 [email protected] Roll Dart SDK from fcbfb64f42a1 to c2edc382a4f8 (2 revisions) (flutter/flutter#192240)
2026-09-03 [email protected] Fix parsing of --enable-hcpp-and-surface-control switch and add --no-enable-hcpp tests (flutter/flutter#192202)
2026-09-03 [email protected] Fix issue where shapes rendered with gradient+blur+rotation end up incorrectly cut off (flutter/flutter#192136)
2026-09-03 [email protected] Fix sibling nodes crash under MergeSemantics (flutter/flutter#191587)
2026-09-03 [email protected] Makes backdrop groups operate on the union. (flutter/flutter#191838)
2026-09-03 [email protected] Roll Skia from 008936396810 to 8d3e197531c6 (1 revision) (flutter/flutter#192236)
2026-09-03 [email protected] [docs] Remove superseded AI rules for Flutter (flutter/flutter#192185)
2026-09-03 [email protected] Collect analytics about SwiftPM errors and warnings (flutter/flutter#191746)
2026-09-03 [email protected] Roll Packages from 18fe786 to f9b3954 (7 revisions) (flutter/flutter#192235)
2026-09-03 [email protected] [Linux] Remove frame sharing between OpenGL contexts (flutter/flutter#192150)
2026-09-03 [email protected] Roll Dart SDK from cf79067a1e44 to fcbfb64f42a1 (1 revision) (flutter/flutter#192228)
2026-09-03 [email protected] Roll Skia from 731fe9af4c0b to 008936396810 (3 revisions) (flutter/flutter#192225)
2026-09-03 [email protected] Roll Dart SDK from 700ab60bfe53 to cf79067a1e44 (2 revisions) (flutter/flutter#192219)
2026-09-03 [email protected] Roll Skia from 823f82e18177 to 731fe9af4c0b (5 revisions) (flutter/flutter#192217)
2026-09-03 [email protected] Fixes OverlayPortal semantics bounds corruption (flutter/flutter#192139)
2026-09-03 [email protected] Roll Fuchsia Linux SDK from idslm9FikVLy2K_A-... to GTZNkoIR_WegRPWgL... (flutter/flutter#192215)
2026-09-03 [email protected] Roll Fuchsia Test Scripts from u_fSMsPU22VvUsvSo... to VObjlh3xw2P9sEbC_... (flutter/flutter#192212)
2026-09-03 [email protected] Roll Skia from ae22da2e8308 to 823f82e18177 (13 revisions) (flutter/flutter#192209)
2026-09-02 [email protected] Expose skipTraversal in FocusableActionDetector (flutter/flutter#191308)
2026-09-02 [email protected] Add `IsProgram` mock method to `MockGLESImpl`, use it in `BufferBindingsGLESTest` (flutter/flutter#192196)
2026-09-02 [email protected] Fix nested scroll view fling crash (flutter/flutter#191130)
2026-09-02 [email protected] Fix TabBarView/PageView edge auto-scroll during mouse text selection (flutter/flutter#189544)
2026-09-02 [email protected] Bypass gamma correction when alpha >= 1.0 (flutter/flutter#192193)
...
victorsanni pushed a commit to victorsanni/packages that referenced this pull request Sep 9, 2026
…lutter#12760)

Manual roll Flutter from 70797e15325e to 5a6cfa7f3d26 (52 revisions)

Manual roll requested by [email protected]

flutter/flutter@70797e1...5a6cfa7

2026-09-04 [email protected] [tool] Migrate ScreenshotCommand to modular dependency injection (flutter/flutter#190767)
2026-09-04 [email protected] Roll Skia from 8fce167bdded to 93ac1e630d1d (3 revisions) (flutter/flutter#192306)
2026-09-04 [email protected] Add iOS Universal Link integration test (Simulator) (flutter/flutter#191443)
2026-09-04 [email protected] [AGP 9.1.0 Migration flutter#5] Delete getLegacyAndroidExtension and migrate NDK fallback to public DSL (flutter/flutter#192116)
2026-09-04 [email protected] Reland "[tool_tests] Remove bringup from Windows tool_tests_commands_2_2" (#191732) (flutter/flutter#191802)
2026-09-04 [email protected] [flutter_tools] Fix unhandled DebugAdapterException on early process termination (flutter/flutter#192294)
2026-09-04 [email protected] Roll Packages from f9b3954 to 9af9c60 (4 revisions) (flutter/flutter#192292)
2026-09-04 [email protected] Roll Skia from 2e3f7e07eee6 to 8fce167bdded (5 revisions) (flutter/flutter#192283)
2026-09-04 [email protected] Roll Fuchsia Linux SDK from GTZNkoIR_WegRPWgL... to FgDQeF6jb1dVRVh3K... (flutter/flutter#192276)
2026-09-04 [email protected] Roll Skia from b6b00df360e5 to 2e3f7e07eee6 (2 revisions) (flutter/flutter#192275)
2026-09-04 [email protected] [iOS] TextField should support typing accented words with external keyboard (flutter/flutter#191062)
2026-09-04 [email protected] Roll Skia from 1f68c0b16f17 to b6b00df360e5 (7 revisions) (flutter/flutter#192269)
2026-09-04 [email protected] [flutter_tools] add typed enum and defaulted option descriptors (flutter/flutter#191760)
2026-09-04 [email protected] Roll Dart SDK from c2edc382a4f8 to 5501d02b583d (1 revision) (flutter/flutter#192263)
2026-09-04 [email protected] [Linux] Use fences to synchronize frames between OpenGL contexts (flutter/flutter#192098)
2026-09-03 [email protected] Defer semantics geometry updates for blocked branches (flutter/flutter#192146)
2026-09-03 [email protected] Roll Skia from 8d3e197531c6 to 1f68c0b16f17 (8 revisions) (flutter/flutter#192244)
2026-09-03 [email protected] Fix DeleteSurrounding splitting UTF-16 surrogate pairs (flutter/flutter#190514)
2026-09-03 [email protected] Remove no-shuffle from progress indicator test (flutter/flutter#186655)
2026-09-03 [email protected] [material] Remove unused parameters from constructors of generic test class (flutter/flutter#191764)
2026-09-03 [email protected] Handle linear and radial gradients in UberSDF (re-implement after fix) (flutter/flutter#192124)
2026-09-03 [email protected] Roll Dart SDK from fcbfb64f42a1 to c2edc382a4f8 (2 revisions) (flutter/flutter#192240)
2026-09-03 [email protected] Fix parsing of --enable-hcpp-and-surface-control switch and add --no-enable-hcpp tests (flutter/flutter#192202)
2026-09-03 [email protected] Fix issue where shapes rendered with gradient+blur+rotation end up incorrectly cut off (flutter/flutter#192136)
2026-09-03 [email protected] Fix sibling nodes crash under MergeSemantics (flutter/flutter#191587)
2026-09-03 [email protected] Makes backdrop groups operate on the union. (flutter/flutter#191838)
2026-09-03 [email protected] Roll Skia from 008936396810 to 8d3e197531c6 (1 revision) (flutter/flutter#192236)
2026-09-03 [email protected] [docs] Remove superseded AI rules for Flutter (flutter/flutter#192185)
2026-09-03 [email protected] Collect analytics about SwiftPM errors and warnings (flutter/flutter#191746)
2026-09-03 [email protected] Roll Packages from 18fe786 to f9b3954 (7 revisions) (flutter/flutter#192235)
2026-09-03 [email protected] [Linux] Remove frame sharing between OpenGL contexts (flutter/flutter#192150)
2026-09-03 [email protected] Roll Dart SDK from cf79067a1e44 to fcbfb64f42a1 (1 revision) (flutter/flutter#192228)
2026-09-03 [email protected] Roll Skia from 731fe9af4c0b to 008936396810 (3 revisions) (flutter/flutter#192225)
2026-09-03 [email protected] Roll Dart SDK from 700ab60bfe53 to cf79067a1e44 (2 revisions) (flutter/flutter#192219)
2026-09-03 [email protected] Roll Skia from 823f82e18177 to 731fe9af4c0b (5 revisions) (flutter/flutter#192217)
2026-09-03 [email protected] Fixes OverlayPortal semantics bounds corruption (flutter/flutter#192139)
2026-09-03 [email protected] Roll Fuchsia Linux SDK from idslm9FikVLy2K_A-... to GTZNkoIR_WegRPWgL... (flutter/flutter#192215)
2026-09-03 [email protected] Roll Fuchsia Test Scripts from u_fSMsPU22VvUsvSo... to VObjlh3xw2P9sEbC_... (flutter/flutter#192212)
2026-09-03 [email protected] Roll Skia from ae22da2e8308 to 823f82e18177 (13 revisions) (flutter/flutter#192209)
2026-09-02 [email protected] Expose skipTraversal in FocusableActionDetector (flutter/flutter#191308)
2026-09-02 [email protected] Add `IsProgram` mock method to `MockGLESImpl`, use it in `BufferBindingsGLESTest` (flutter/flutter#192196)
2026-09-02 [email protected] Fix nested scroll view fling crash (flutter/flutter#191130)
2026-09-02 [email protected] Fix TabBarView/PageView edge auto-scroll during mouse text selection (flutter/flutter#189544)
2026-09-02 [email protected] Bypass gamma correction when alpha >= 1.0 (flutter/flutter#192193)
...
victorsanni pushed a commit to victorsanni/packages that referenced this pull request Sep 9, 2026
…lutter#12760)

Manual roll Flutter from 70797e15325e to 5a6cfa7f3d26 (52 revisions)

Manual roll requested by [email protected]

flutter/flutter@70797e1...5a6cfa7

2026-09-04 [email protected] [tool] Migrate ScreenshotCommand to modular dependency injection (flutter/flutter#190767)
2026-09-04 [email protected] Roll Skia from 8fce167bdded to 93ac1e630d1d (3 revisions) (flutter/flutter#192306)
2026-09-04 [email protected] Add iOS Universal Link integration test (Simulator) (flutter/flutter#191443)
2026-09-04 [email protected] [AGP 9.1.0 Migration flutter#5] Delete getLegacyAndroidExtension and migrate NDK fallback to public DSL (flutter/flutter#192116)
2026-09-04 [email protected] Reland "[tool_tests] Remove bringup from Windows tool_tests_commands_2_2" (#191732) (flutter/flutter#191802)
2026-09-04 [email protected] [flutter_tools] Fix unhandled DebugAdapterException on early process termination (flutter/flutter#192294)
2026-09-04 [email protected] Roll Packages from f9b3954 to 9af9c60 (4 revisions) (flutter/flutter#192292)
2026-09-04 [email protected] Roll Skia from 2e3f7e07eee6 to 8fce167bdded (5 revisions) (flutter/flutter#192283)
2026-09-04 [email protected] Roll Fuchsia Linux SDK from GTZNkoIR_WegRPWgL... to FgDQeF6jb1dVRVh3K... (flutter/flutter#192276)
2026-09-04 [email protected] Roll Skia from b6b00df360e5 to 2e3f7e07eee6 (2 revisions) (flutter/flutter#192275)
2026-09-04 [email protected] [iOS] TextField should support typing accented words with external keyboard (flutter/flutter#191062)
2026-09-04 [email protected] Roll Skia from 1f68c0b16f17 to b6b00df360e5 (7 revisions) (flutter/flutter#192269)
2026-09-04 [email protected] [flutter_tools] add typed enum and defaulted option descriptors (flutter/flutter#191760)
2026-09-04 [email protected] Roll Dart SDK from c2edc382a4f8 to 5501d02b583d (1 revision) (flutter/flutter#192263)
2026-09-04 [email protected] [Linux] Use fences to synchronize frames between OpenGL contexts (flutter/flutter#192098)
2026-09-03 [email protected] Defer semantics geometry updates for blocked branches (flutter/flutter#192146)
2026-09-03 [email protected] Roll Skia from 8d3e197531c6 to 1f68c0b16f17 (8 revisions) (flutter/flutter#192244)
2026-09-03 [email protected] Fix DeleteSurrounding splitting UTF-16 surrogate pairs (flutter/flutter#190514)
2026-09-03 [email protected] Remove no-shuffle from progress indicator test (flutter/flutter#186655)
2026-09-03 [email protected] [material] Remove unused parameters from constructors of generic test class (flutter/flutter#191764)
2026-09-03 [email protected] Handle linear and radial gradients in UberSDF (re-implement after fix) (flutter/flutter#192124)
2026-09-03 [email protected] Roll Dart SDK from fcbfb64f42a1 to c2edc382a4f8 (2 revisions) (flutter/flutter#192240)
2026-09-03 [email protected] Fix parsing of --enable-hcpp-and-surface-control switch and add --no-enable-hcpp tests (flutter/flutter#192202)
2026-09-03 [email protected] Fix issue where shapes rendered with gradient+blur+rotation end up incorrectly cut off (flutter/flutter#192136)
2026-09-03 [email protected] Fix sibling nodes crash under MergeSemantics (flutter/flutter#191587)
2026-09-03 [email protected] Makes backdrop groups operate on the union. (flutter/flutter#191838)
2026-09-03 [email protected] Roll Skia from 008936396810 to 8d3e197531c6 (1 revision) (flutter/flutter#192236)
2026-09-03 [email protected] [docs] Remove superseded AI rules for Flutter (flutter/flutter#192185)
2026-09-03 [email protected] Collect analytics about SwiftPM errors and warnings (flutter/flutter#191746)
2026-09-03 [email protected] Roll Packages from 18fe786 to f9b3954 (7 revisions) (flutter/flutter#192235)
2026-09-03 [email protected] [Linux] Remove frame sharing between OpenGL contexts (flutter/flutter#192150)
2026-09-03 [email protected] Roll Dart SDK from cf79067a1e44 to fcbfb64f42a1 (1 revision) (flutter/flutter#192228)
2026-09-03 [email protected] Roll Skia from 731fe9af4c0b to 008936396810 (3 revisions) (flutter/flutter#192225)
2026-09-03 [email protected] Roll Dart SDK from 700ab60bfe53 to cf79067a1e44 (2 revisions) (flutter/flutter#192219)
2026-09-03 [email protected] Roll Skia from 823f82e18177 to 731fe9af4c0b (5 revisions) (flutter/flutter#192217)
2026-09-03 [email protected] Fixes OverlayPortal semantics bounds corruption (flutter/flutter#192139)
2026-09-03 [email protected] Roll Fuchsia Linux SDK from idslm9FikVLy2K_A-... to GTZNkoIR_WegRPWgL... (flutter/flutter#192215)
2026-09-03 [email protected] Roll Fuchsia Test Scripts from u_fSMsPU22VvUsvSo... to VObjlh3xw2P9sEbC_... (flutter/flutter#192212)
2026-09-03 [email protected] Roll Skia from ae22da2e8308 to 823f82e18177 (13 revisions) (flutter/flutter#192209)
2026-09-02 [email protected] Expose skipTraversal in FocusableActionDetector (flutter/flutter#191308)
2026-09-02 [email protected] Add `IsProgram` mock method to `MockGLESImpl`, use it in `BufferBindingsGLESTest` (flutter/flutter#192196)
2026-09-02 [email protected] Fix nested scroll view fling crash (flutter/flutter#191130)
2026-09-02 [email protected] Fix TabBarView/PageView edge auto-scroll during mouse text selection (flutter/flutter#189544)
2026-09-02 [email protected] Bypass gamma correction when alpha >= 1.0 (flutter/flutter#192193)
...
frank-weindel pushed a commit to frank-weindel/flutter that referenced this pull request Oct 3, 2026
…er#190431)

`shouldFormSemanticsNode` ends in `return parentData!.explicitChildNodes
|| _hasSiblingConflict;`. When the parent data half changes,
`_didUpdateParentData` calls `markNeedsBuild`, because whether a
fragment forms a node may have changed. When the sibling conflict half
changes, nothing does. `markSiblingConfigurationConflict` just assigns
the flag and returns.

That gap is enough to corrupt the semantics tree. A fragment that stops
forming its own node keeps `built == true` and keeps its
`cachedSemanticsNode`. While it is out of the tree an ancestor can take
its children back, and its child list is never refreshed because
`_buildSemantics` short circuits on `built`. When the fragment forms a
node again it is handed straight back into the tree still holding
children that now belong to somebody else, and attaching it attaches
them too. The result is a `SemanticsNode` that is attached but has no
parent, which is what trips `assert(!child.attached)` in
`_replaceChildren`.

There are three earlier branches in `shouldFormSemanticsNode`
(`isSemanticBoundary`, `isRoot`, `contributesToSemanticsTree`). I went
looking for the same gap in those and could not find one, because
everything that feeds them goes through `markNeedsUpdate`, which nulls
parent data on its way up and so ends at `markNeedsBuild` anyway. I
would not swear to that the way I would to the paragraph above, so if
you know of a path I am not seeing, I would rather hear it now.

Outside debug builds this looks worse than an assertion. The orphan
still passes the `node.attached` check used when building
`childrenInTraversalOrder`, so its id goes to the engine as a child that
is not reachable, which is the shape of the `Failed to update
ui::AXTree, error: N will not be in the tree and is not the new root`
that @davidhicks980 reported in flutter#187198. I should be clear that this
part is me reading the code and not something I measured. I could not
find a way to drive VoiceOver from a test, so I have not confirmed that
the release mode failure is this and only this.

OverlayPortal runs into this reliably because showing an overlay child
creates and removes a sibling conflict for the anchors around it, so
node formation flips back and forth while the traversal graft moves
nodes between subtrees.

The change marks the fragment as needing a build when its sibling
conflict flag actually changes across a pass. It has to be a before and
after comparison rather than a check inside
`markSiblingConfigurationConflict`, because
`_marksConflictsInMergeGroup` deliberately resets every fragment to
false first and that reset is load bearing: `configToMergeUp` reads
`shouldFormSemanticsNode`, so leaving the previous pass's flag in place
while recomputing changes the result. The snapshot is taken in the
existing loop just before the reset, and the comparison rides along with
the loop that marks the conflicts, so there is no extra pass over
`mergeGroup`. Both directions are marked, because losing a conflict
changes node formation just as much as gaining one.

This runs during the parent data phase of `flushSemantics`, before the
build loop, so the fragment is rebuilt in the same flush.

### Why not repair the child list instead

The first version of this PR fixed the stale child list at the point of
the theft, and guarded `attach` the way `detach` already is. Both work,
and the test passes with either. @chunhtai pointed out that the fragment
should have known its children were taken and marked itself, which
turned out to be right, so this version drops both in favour of fixing
the invalidation. Happy to add the `attach` guard back as hardening if
you want it, since `detach` is still the only one of the ten methods in
`semantics.dart` that walk `_children` that checks whether the child is
still its own, but it is a separate concern from this bug.

While I was checking that number I found I had got it wrong earlier. I
said eight places here and on flutter#187198. It is ten, and the two I missed,
`_redepthChildren` and `_updateChildrenMergeFlags`, both assert on
`child.owner`, so a stale entry can trip there as well.

I did earlier report that invalidating was not possible because
`markNeedsBuild`, `RenderObject.markNeedsSemanticsUpdate` and the
`flushSemantics` exit assert all reject being dirtied during the flush.
That was true of invalidating at the moment of the theft, which is in
the middle of the build phase. Invalidating at the moment the decision
changes is a different place and does not have that problem.

### Test

The regression test goes in the existing `Semantics` group in
`overlay_portal_test.dart`. It needs two portal anchors before it will
fail, because the fragment whose conflict changes belongs to the sibling
anchor rather than the one being toggled. Taking the sibling out makes
it pass on unpatched master. It fails on master with `'!child.attached':
is not true` and passes with this change.

One thing worth flagging: the test has no semantics expectations of its
own, so its only failure mode is that framework assertion. Happy to add
some if you would rather it pinned the resulting tree down.

`test/semantics`, `test/rendering`, `test/widgets` and `test/material`
come out at 16171 passing with nothing failing.

Fixes flutter#189902

One request on that: @madenvel reported a `computeChildGeometry`
assertion variant in a comment on that issue, and closing it through
this PR would bury that report. Could that be split into its own issue?
I have not been able to reproduce it, it is described as Linux and
AT-SPI, and I do not know whether this change covers it. I said earlier
that I expected it to survive any fix in this area, but that was about
the two patches this version replaced, and the reasoning does not carry
over. Rebuilding the fragment refreshes its geometry too, so it is
genuinely an open question now rather than a no.

Partially addresses flutter#187198.
The assertions reported there go away. I said I would expect the AXTree
failure to go away as well, but when I tried to check that I could not
back it up. I recorded every semantics update sent to the engine in a
widget test and checked it against the rules the desktop bridge applies.
Neither of @davidhicks980's samples produces a bad update on current
master, with or without this change. The shape from flutter#182444 (Tooltips in
a ListView, and the MenuAnchor + Tooltip one reported there) does, with
the exact same `will not be in the tree and is not the new root` error,
and it does so with or without this change too. So that error has at
least one cause this PR does not touch, and I can't tell which one was
being hit in flutter#187198. The menu item positioning problem also reported
there is separate and this does not touch it.

For the record, flutter#182604 turned out not to be the same defect, even
though it sits downstream of the same refactor. It was fixed by flutter#192139
on the engine side, where `setChildrenInHitTestOrder` was overwriting
the traversal parent that `OverlayPortal` had set. I had said on the two
issues above that all three came down to one thing, which was wrong.

## 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.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

---------

Co-authored-by: Victor Sanni <[email protected]>
auto-submit Bot pushed a commit that referenced this pull request Oct 7, 2026
… + Fix OverlayPortal semantics bounds corruption (#192139) (#193420)

Cherry-picks #189686 and #192139 into `flutter-3.47-candidate.0` (stable). Both applied cleanly with `git cherry-pick -x`; both touch only `SemanticsObject.mm` (+ tests).

### Issue Link
#182604 · #100946

### Impacted Users
End users of iOS apps built with stable who rely on VoiceOver / Switch Control, and teams driving XCUITest / Maestro against Flutter apps. Any route that mounts a Material `Slider` is affected out of the box: `Slider` keeps its value-indicator `OverlayPortal` shown from construction (`slider.dart`: `OverlayPortalController(...)..show()`), so no popover, menu or reorder is needed to trigger it.

### Impact Description
Since fccfa97 an `OverlayPortal` child has a different parent in traversal order and in hit-test order. The iOS accessibility bridge overwrote the native `SemanticsObject.parent` with the hit-test one (and cleared parents of moved children), so the parent chain used to compute `accessibilityFrame` no longer reaches the root: every element of the route reports a frame scaled by 1/devicePixelRatio (exactly 1/3 on a 3x device) and anchored top-left. VoiceOver touch exploration focuses the wrong place; `XCUIElement.tap()` misses the visible control. Measured on iPhone 16 Pro Max / iOS 26.7 with Flutter 3.44.8 (stable 3.47.5 has the same `SemanticsObject.mm`): a full-width CTA laid out at `{{32, 834}, {376, 56}}` is published as `{{10.7, 278.0}, {125.3, 18.7}}`; a player route with a `Slider` is 1/3 from its first frame. The corruption persists after the portal unmounts and after background/foreground.

### Changelog Description
[flutter/182604](#182604) When a route shows an `OverlayPortal` on iOS (including any Material `Slider`), accessibility frames are shrunk by the device pixel ratio and anchored to the top-left, so VoiceOver focus and XCUITest taps miss the visible controls.

### Workaround
Replace Material `Slider` with `CupertinoSlider` (no `OverlayPortal`) on affected routes and avoid `OverlayPortal` altogether. Not viable for apps using Material menus / dropdowns or third-party popovers.

### Risk
Low. Parent bookkeeping only: #189686 clears `child.parent` only when the object still owns it; #192139 stops the hit-test-order setter from mutating `parent`. No behaviour change when traversal and hit-test parents coincide.

### Cherry-pick diff composition

The two upstream PRs touch the same function, so the composed diff on `SemanticsObject.mm` (`+6 -8`) matches neither of them individually — upstream, #189686 is `+9 -3` on this file and #192139 is `+0 -8`. Net against the `flutter-3.47-candidate.0` base:

| site | net effect vs. base |
|---|---|
| `dealloc` | `+3 -1` — guard becomes `if (child.parent == self) { child.parent = nil; }` |
| `setChildren` | `+3 -1` — same guard |
| `setChildrenInHitTestOrder` | `-6` — #189686 adds the guard here too, then #192139 deletes both parent loops outright, so those 3 added lines never survive and the block's 6 original lines go away |

Additions `3 + 3 = 6`; deletions `1 + 1 + 6 = 8`. The three "missing" additions are the ones the second commit removes from the first. No conflict was resolved by hand — both applied cleanly with `git cherry-pick -x`.

### Test Coverage
Yes. #189686 adds an iOS XCUITest (`testReorderableListAccessibilityHierarchyUpdates`) asserting the accessibility frame after reparenting; #192139 adds `SemanticsObjectTest.mm` coverage. Both have been on master since 2026-09-01/03 and ship in beta 3.49.0-0.1.pre.

### Validation Steps
1. On a 3x iOS device, push a route (e.g. `MaterialApp.router` + `GoRoute`) whose body contains a Material `Slider`.
2. Print `XCUIApplication().debugDescription` from an XCUITest, or inspect with Accessibility Inspector.
3. Before: every element of the route reports a frame at 1/3 of its logical size near (0,0). After: frames match the visible layout, and `XCUIElement.tap()` / VoiceOver touch exploration hit the visible controls.
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. platform-ios iOS applications specifically team-ios Owned by iOS platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Regression] OverlayPortal semantics bounds/coordinates corrupted after fccfa978a97

3 participants