Skip to content

Do not wait until dispose before removing replaced/popped page#182315

Merged
auto-submit[bot] merged 7 commits intomasterfrom
navigator-flicker-fix
Feb 17, 2026
Merged

Do not wait until dispose before removing replaced/popped page#182315
auto-submit[bot] merged 7 commits intomasterfrom
navigator-flicker-fix

Conversation

@victorsanni
Copy link
Contributor

Regression was introduced in Cleans up navigator pop and remove logic.

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

bad.navigator.mov

After

good.navigator.mov

Fixes [Navigation] Popping a nested route while the parent is rebuilding causes a flicker

@flutter-dashboard
Copy link

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.

@victorsanni victorsanni requested a review from justinmc February 12, 2026 19:20
@github-actions github-actions bot added framework flutter/packages/flutter repository. See also f: labels. f: routes Navigator, Router, and related APIs. labels Feb 12, 2026
@victorsanni victorsanni requested a review from chunhtai February 12, 2026 19:20
Copy link
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 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.

Comment on lines +3272 to +3275
if (previousPresent != null && previousPresent._isPageBased) {
final page = previousPresent.settings as Page<Object?>;
navigator.widget.onDidRemovePage?.call(page);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

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.

Copy link
Contributor

Choose a reason for hiding this comment

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

Either way 🤷

Copy link
Contributor

@justinmc justinmc left a comment

Choose a reason for hiding this comment

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

The approach looks good. Are you sure that replace and pop are the only two things that can result in needing to call onDidRemovePage?

Comment on lines +3272 to +3275
if (previousPresent != null && previousPresent._isPageBased) {
final page = previousPresent.settings as Page<Object?>;
navigator.widget.onDidRemovePage?.call(page);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Either way 🤷

@victorsanni
Copy link
Contributor Author

Are you sure that replace and pop are the only two things that can result in needing to call onDidRemovePage?

Maybe removeRoute, but calling that method means the user already has the route to be removed, so they can just call onDidRemovePage(route.settings) directly I think?

@victorsanni victorsanni requested a review from justinmc February 12, 2026 23:20
Copy link
Contributor

@justinmc justinmc left a comment

Choose a reason for hiding this comment

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

LGTM 👍 . Heads up there's an analyzer failure.

Maybe removeRoute, but calling that method means the user already has the route to be removed, so they can just call onDidRemovePage(route.settings) directly I think?

Sounds good as long as the tests pass.

Copy link
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

@victorsanni victorsanni added the autosubmit Merge PR when tree becomes green via auto submit App label Feb 17, 2026
@auto-submit auto-submit bot added this pull request to the merge queue Feb 17, 2026
Merged via the queue into master with commit 7ac16a2 Feb 17, 2026
77 checks passed
@auto-submit auto-submit bot deleted the navigator-flicker-fix branch February 17, 2026 21:19
@flutter-dashboard flutter-dashboard bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Feb 17, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 18, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 18, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 18, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 18, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 18, 2026
auto-submit bot pushed a commit to flutter/packages that referenced this pull request Feb 18, 2026
…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
rickhohler pushed a commit to rickhohler/flutter that referenced this pull request Feb 19, 2026
…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)
ahmedsameha1 pushed a commit to ahmedsameha1/flutter that referenced this pull request Feb 27, 2026
…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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

f: routes Navigator, Router, and related APIs. framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Navigation] Popping a nested route while the parent is rebuilding causes a flicker

3 participants