[go_router] Fixes StatefulShellRoute PopScope behavior to respect back button#11432
[go_router] Fixes StatefulShellRoute PopScope behavior to respect back button#11432chunhtai wants to merge 1 commit intoflutter:mainfrom
Conversation
0135340 to
293106d
Compare
|
@gemini-code-assist review |
There was a problem hiding this comment.
Code Review
This pull request adds a PopScope to StatefulNavigationShellState to handle back button navigation by switching to the root branch when the current branch cannot pop. A review comment identifies a potential issue where this logic could interfere with nested PopScope widgets and suggests checking the navigator's state before switching branches.
| return PopScope( | ||
| canPop: widget.currentIndex == 0, | ||
| onPopInvokedWithResult: (bool didPop, Object? result) { | ||
| if (!didPop && widget.currentIndex != 0) { | ||
| goBranch(0); | ||
| } | ||
| }, | ||
| child: widget.containerBuilder(context, widget, children), | ||
| ); |
There was a problem hiding this comment.
The current implementation of PopScope might interfere with nested PopScope widgets within a branch.
If a child route on a non-root branch (e.g., Tab B) uses a PopScope to block a pop (for instance, to show a "Discard changes?" confirmation dialog), the didPop value in this shell-level PopScope will be false. Consequently, goBranch(0) will be triggered, switching the user to the root branch even though the intention was to stay on the current branch and handle the pop locally.
To fix this, you should check if the current branch's navigator can actually pop before deciding to switch branches. If navigator.canPop() is true but didPop is false, it implies a nested PopScope blocked the pop, and the shell should not intervene.
return PopScope(
canPop: widget.currentIndex == 0,
onPopInvokedWithResult: (bool didPop, Object? result) {
if (!didPop && widget.currentIndex != 0) {
final NavigatorState? navigator =
widget.shellRouteContext.navigatorKey.currentState;
// Only switch to the root branch if the current branch navigator
// cannot pop further. If it can pop but didn't, a nested PopScope
// likely blocked it.
if (navigator != null && !navigator.canPop()) {
goBranch(0);
}
}
},
child: widget.containerBuilder(context, widget, children),
);
Fixes issue #181945.
Root Cause
StatefulShellRoute manages multiple branches, each with its own parallel navigation stack. When the system back button is pressed on a non-root branch (index > 0), the framework does not inherently know that it should oscillate back to the root branch (index 0) when the current branch's navigator cannot pop further. This caused system back events to exit the app or behave inconsistently.
Fix
This PR adds a PopScope wrapper around the StatefulNavigationShell container:
canPop: widget.currentIndex == 0. This allows natural app exiting only from the root branch.onPopInvokedWithResulthandler callsgoBranch(0)to switch back to the main tab.