Do not wait until dispose before removing replaced/popped page#182315
Do not wait until dispose before removing replaced/popped page#182315auto-submit[bot] merged 7 commits intomasterfrom
Conversation
|
It looks like this pull request may not have tests. Please make sure to add tests or get an explicit test exemption before merging. If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix? Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. If you believe this PR qualifies for a test exemption, contact "@test-exemption-reviewer" in the #hackers channel in Discord (don't just cc them here, they won't see it!). The test exemption team is a small volunteer group, so all reviewers should feel empowered to ask for tests, without delegating that responsibility entirely to the test exemption group. |
There was a problem hiding this comment.
Code Review
This pull request addresses a regression that caused a flicker effect when popping or replacing a nested route. The issue stemmed from onDidRemovePage being called too late in the _RouteLifecycle.dispose phase. The fix correctly moves the onDidRemovePage call to happen immediately within handlePop and handlePush (for replacements), ensuring the navigator's page list is updated promptly and preventing the outdated page from being rebuilt. The changes look correct and effectively resolve the described problem. I've added one suggestion to refactor the newly added code blocks into a helper method to reduce duplication and improve maintainability.
| if (previousPresent != null && previousPresent._isPageBased) { | ||
| final page = previousPresent.settings as Page<Object?>; | ||
| navigator.widget.onDidRemovePage?.call(page); | ||
| } |
There was a problem hiding this comment.
This logic is very similar to the code added in _RouteEntry.handlePop (lines 3332-3335). To improve maintainability and avoid duplication, consider extracting this into a shared private helper method within _RouteEntry.
For example:
void _didRemovePage(NavigatorState navigator, Route<dynamic> removedRoute) {
assert(removedRoute._isPageBased);
final page = removedRoute.settings as Page<Object?>;
navigator.widget.onDidRemovePage?.call(page);
}You could then call this helper from both handlePush and handlePop.
justinmc
left a comment
There was a problem hiding this comment.
The approach looks good. Are you sure that replace and pop are the only two things that can result in needing to call onDidRemovePage?
| if (previousPresent != null && previousPresent._isPageBased) { | ||
| final page = previousPresent.settings as Page<Object?>; | ||
| navigator.widget.onDidRemovePage?.call(page); | ||
| } |
Maybe |
justinmc
left a comment
There was a problem hiding this comment.
LGTM 👍 . Heads up there's an analyzer failure.
Maybe
removeRoute, but calling that method means the user already has therouteto be removed, so they can just callonDidRemovePage(route.settings)directly I think?
Sounds good as long as the tests pass.
packages/flutter/test/widgets/navigator_on_did_remove_page_test.dart
Outdated
Show resolved
Hide resolved
…11060) Manual roll requested by [email protected] flutter/flutter@6e4a481...c023e5b 2026-02-18 [email protected] [web] Pass form validation errors to screen readers via aria-description (flutter/flutter#180556) 2026-02-18 [email protected] Roll Packages from f83926f to 59f905c (10 revisions) (flutter/flutter#182547) 2026-02-18 [email protected] flutter_tools: Copy vendored frameworks from plugin podspecs in ios/macos-framework builds (flutter/flutter#180135) 2026-02-18 [email protected] Marks Windows framework_tests_misc_leak_tracking to be unflaky (flutter/flutter#182534) 2026-02-18 [email protected] Allow TabBar to receive a TabBarScrollController (flutter/flutter#180389) 2026-02-18 [email protected] Clean up cross imports in single_child_scroll_view_test.dart, decorated_sliver_test.dart, draggable_scrollable_sheet_test.dart (flutter/flutter#181613) 2026-02-18 [email protected] Roll Fuchsia Linux SDK from mcN42vw48OPH3JDNm... to Ihau0pUz3u5ajw42u... (flutter/flutter#182530) 2026-02-18 [email protected] Analyzer, require 10.1.0, fix deprecations in dependency_graph.dart (flutter/flutter#182507) 2026-02-17 [email protected] Refactor: Remove material from actions test (flutter/flutter#181702) 2026-02-17 [email protected] [a11y] RangeSlider mouse interaction should change keyboard focus (flutter/flutter#182185) 2026-02-17 [email protected] Remove more getters from userMessages class (flutter/flutter#182166) 2026-02-17 [email protected] Implement getUniformMatX and getUniformMatXArray functionality on web (flutter/flutter#182249) 2026-02-17 [email protected] Do not wait until dispose before removing replaced/popped page (flutter/flutter#182315) 2026-02-17 [email protected] Add contentTextStyle support to SimpleDialog (flutter/flutter#178824) 2026-02-17 [email protected] Filter error messages from `emulator -list-avds` output (flutter/flutter#180802) 2026-02-17 [email protected] [Reland] Cupertino cross imports (flutter/flutter#182416) 2026-02-17 [email protected] Roll Packages from 09104b0 to f83926f (1 revision) (flutter/flutter#182504) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC [email protected],[email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
…er#182315) Regression was introduced in [Cleans up navigator pop and remove logic](https://github.com/flutter/flutter/pull/175612/changes#top). That PR moved the `onDidRemovePage` call to `_flushHistoryUpdates` in phase `_RouteLifecycle.dispose`. But waiting until `dispose` causes potential problems if the widget is rebuilt in a phase preceding that (but after `pop` or `pushReplacement` has been called). So the outdated page remains in the pages list even after it has been marked for removal. This is causing the page to be pushed again before it is finally removed, causing the flicker. ## Before https://github.com/user-attachments/assets/73dba22d-e668-4b2d-84f3-a0beb1faebab ## After https://github.com/user-attachments/assets/6c8c6ffc-87f0-494f-bd41-7fde1f21d0e1 Fixes [[Navigation] Popping a nested route while the parent is rebuilding causes a flicker](flutter#178570)
…er#182315) Regression was introduced in [Cleans up navigator pop and remove logic](https://github.com/flutter/flutter/pull/175612/changes#top). That PR moved the `onDidRemovePage` call to `_flushHistoryUpdates` in phase `_RouteLifecycle.dispose`. But waiting until `dispose` causes potential problems if the widget is rebuilt in a phase preceding that (but after `pop` or `pushReplacement` has been called). So the outdated page remains in the pages list even after it has been marked for removal. This is causing the page to be pushed again before it is finally removed, causing the flicker. ## Before https://github.com/user-attachments/assets/73dba22d-e668-4b2d-84f3-a0beb1faebab ## After https://github.com/user-attachments/assets/6c8c6ffc-87f0-494f-bd41-7fde1f21d0e1 Fixes [[Navigation] Popping a nested route while the parent is rebuilding causes a flicker](flutter#178570)
Regression was introduced in Cleans up navigator pop and remove logic.
That PR moved the
onDidRemovePagecall to_flushHistoryUpdatesin phase_RouteLifecycle.dispose. But waiting untildisposecauses potential problems if the widget is rebuilt in a phase preceding that (but afterpoporpushReplacementhas been called). So the outdated page remains in the pages list even after it has been marked for removal.This is causing the page to be pushed again before it is finally removed, causing the flicker.
Before
bad.navigator.mov
After
good.navigator.mov
Fixes [Navigation] Popping a nested route while the parent is rebuilding causes a flicker