Repository navigation
Update TLHC PlatformViewWrapper offset without requesting layout - #194899
Merged
auto-submit[bot] merged 1 commit intoOct 26, 2026
Merged
Conversation
Instead of mutating FrameLayout.LayoutParams and calling setLayoutParams (which triggers requestLayout() and forces a full view hierarchy measure, layout, and PlatformViewWrapper display list re-record on every scroll frame), move PlatformViewWrapper offset handling into PlatformViewWrapper.setOffset(left, top): - Keep FrameLayout.LayoutParams margins updated in place so any subsequent layout pass preserves the offset. - When already laid out, shift the view directly with offsetLeftAndRight and offsetTopAndBottom and call onScrollChanged so ViewTreeObserver.OnScrollChangedListeners (such as PopupWindow and ad viewability trackers) still fire during scroll without triggering requestLayout() or invalidating the wrapper/parent display lists. - Translate touches in PlatformViewWrapper.onTouchEvent using the view's actual layout bounds (getLeft()/getTop()), matching FlutterMutatorView.
gmackall
marked this pull request as ready for review
October 21, 2026 01:09
gmackall
requested review from
jesswrd and
mboetger
and removed request for
a team
October 21, 2026 01:09
Contributor
There was a problem hiding this comment.
Code Review
This pull request refactors how PlatformViewWrapper is positioned on Android. It introduces a setOffset method that directly translates the view using offsetLeftAndRight and offsetTopAndBottom without triggering a full layout pass when the view is already laid out, keeping the layout parameters in sync for future passes. It also simplifies touch event matrix translation by using the view's current position via getLeft() and getTop(). Corresponding unit tests have been added to verify the offset behavior and touch event translations. There are no review comments, and I have no feedback to provide.
mboetger
approved these changes
Oct 26, 2026
1 task done
This was referenced Oct 26, 2026
suryaKommana2662
pushed a commit
to suryaKommana2662/flutter
that referenced
this pull request
Oct 29, 2026
…tter#194899) Currently when a TLHC platform view scrolls, `PlatformViewsController.offset` updates the `FrameLayout.LayoutParams` margins and calls `viewWrapper.setLayoutParams(layoutParams)`. Calling `setLayoutParams` triggers `requestLayout()`, which forces a full measure/layout pass and causes `PlatformViewWrapper` to re-record its display list and re-render the embedded view into the render target on every scroll frame, even though only the wrapper's offset changed. 1. Moves offset handling into `PlatformViewWrapper.setOffset(newLeft, newTop)`. 2. Keeps `FrameLayout.LayoutParams` margins updated in place so that any subsequent layout pass preserves the offset, and when already laid out, translates the view directly with `offsetLeftAndRight` and `offsetTopAndBottom` instead of calling `setLayoutParams`. 3. Calls `onScrollChanged` after translating so `ViewTreeObserver.OnScrollChangedListener`s (e.g. `PopupWindow` and ad viewability trackers) still fire when the view moves without a layout pass. 4. Simplifies `PlatformViewWrapper.onTouchEvent` to translate touches using `getLeft()` and `getTop()`, matching `FlutterMutatorView`. Left is before, right is after. There's still some jitter on the right because the recording process running on the device seems to push us in to missing a couple frames, but it's nothing like the bad feedback loop on the left, and also isn't visible locally when I test. https://github.com/user-attachments/assets/8295b58f-4e3b-4399-8535-a6520c533220 Fixes flutter#183626 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [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. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [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/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md Co-authored-by: Gray Mackall <[email protected]>
9 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Currently when a TLHC platform view scrolls,
PlatformViewsController.offsetupdates theFrameLayout.LayoutParamsmargins and callsviewWrapper.setLayoutParams(layoutParams). CallingsetLayoutParamstriggersrequestLayout(), which forces a full measure/layout pass and causesPlatformViewWrapperto re-record its display list and re-render the embedded view into the render target on every scroll frame, even though only the wrapper's offset changed.PlatformViewWrapper.setOffset(newLeft, newTop).FrameLayout.LayoutParamsmargins updated in place so that any subsequent layout pass preserves the offset, and when already laid out, translates the view directly withoffsetLeftAndRightandoffsetTopAndBottominstead of callingsetLayoutParams.onScrollChangedafter translating soViewTreeObserver.OnScrollChangedListeners (e.g.PopupWindowand ad viewability trackers) still fire when the view moves without a layout pass.PlatformViewWrapper.onTouchEventto translate touches usinggetLeft()andgetTop(), matchingFlutterMutatorView.Left is before, right is after. There's still some jitter on the right because the recording process running on the device seems to push us in to missing a couple frames, but it's nothing like the bad feedback loop on the left, and also isn't visible locally when I test.
tlhc_master_vs_pr194899_slowmo_30fps.1.mp4
Fixes #183626
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.