-
Notifications
You must be signed in to change notification settings - Fork 3.3k
[go_router] fix: PopScope not honored for initial Routes inside ShellRoutes #8162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…Routes [go_router] test: check whether PopScope is working properly for back button presses [go_router] chore: bump version by patch [go_router] chore: update changelog [go_router] chore: remove debugPrint statement left from testing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the issue is that the navigatorState.canPop returns false even if there is a popscope on the root route, I think that may be a bug and should probably fixed in the framework side
Hi @justinmc , since you are working on popscope, WDYT?
// returns false. | ||
if (route is GoRoute && | ||
route.onExit != null && | ||
!await route.onExit!( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this needed? this should be called automatically in _handlePopPageWithRouteMatch which will be called during Navigator.maybePop
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure, I migrated this basically from the original implementation.
Edit: removing this code will cause on_exit_test.dart:241
to fail because returning false from the onExit method will no longer prevent popping.
This was probably the root cause of the current implementation failing, yes. While looking at the documentation of canPop it mentions its not considering the Pop entries, so I'm interested to see what the reasoning for this was. A nice functionality to have on the framework would be to get the pop disposition of the routes / navigator without calling pop. |
I think that this is not necessarily a bug. I think the real solution here is that there should be a PopScope widget in the internals of ShellRoute (between the root Navigator and ShellRoute's Navigator). Here is how this situation is typically handled: Canonical nested Navigator setupNavigator(
...
child: PopScope( // or NavigatorPopHandler does this automatically.
canPop: !nestedNavigator.canPop(),
onPopInvokedWithResult: (bool didPop, Object? result) {
if (didPop) {
return;
}
nestedNavigator.maybePop();
},
child: Navigator(
child: ...,
// A page where the app developer wants to block pops.
child: PopScope(
canPop: false,
onPopInvokedWithResult: (bool didPop, Object? result) {
if (didPop) {
return;
}
print('Called onPopInvokedWithResult in the nested PopScope');
},
)
)
)
) See also NavigatorPopHandler's docs and code. Solution 1: in go_routergo_router could solve this by adding a PopScope widget into ShellRoute, above the nested Navigator. I think that's the correct approach rather than what's currently in this PR, if I'm understanding correctly. Solution 2: in the frameworkThe Flutter framework could automatically add a PopScope widget inside of Navigator if it detects that the Navigator is nested. This solution is implemented in flutter/flutter#152330. Game planI'm about to take a vacation for the rest of the year, but if everyone here agrees, then in January I think we should:
|
I wanna preface this with the fact that I probably do not know enough about Navigators to comment about changes to the flutter framework itself. However, I found it odd that there seems to be a second parallel system established (?) to handle passing the navigation events and pop behaviour next to the action intent framework. @justinmc wouldn't it be possible to inherit all the functionality from that system to manage basically route focus and the event propagation? Or am I in complete ignorance, that this is already the way its done internally? I'm unsure on the effects of adding a PopScope to the ShellRoute, but I can imagine it would basically be a very similar approach to the current one but instead of finding the pop-able navigators when the event is fired you would compute the canPop property during layout. I think it might then still be necessary/benefitial to interrogate the pop-disposition of the navigator without popping. Nevertheless, I'm happy to update the PR whenever there is an agreed solution that addresses the framework consistency since this fix is of high importance to the products I'm working on. |
For those who just want to use/test this fix in their apps, just update your "pubspec.yaml" to contain this: dependency_overrides:
go_router: # Issue described here: https://github.com/flutter/packages/pull/8162 remove the override when it's resolved.
git:
url: https://github.com/ElectricCookie/flutter_packages
ref: main
path: packages/go_router |
@ElectricCookie I agree that it would be nice if navigation and pops were done via Actions and Intents. See how NotificationListener is used to communicate navigation changes: https://github.com/flutter/flutter/blob/27ba2f279075a9a66b48ce78d94d2999b06d6e26/packages/flutter/lib/src/widgets/navigator_pop_handler.dart#L119. Android needs to know whether the Flutter app should handle the pop before the pop happens, though. I will sync with @chunhtai next week. |
hi @ElectricCookie , is this PR will be merged anytime soon? i already fork this and use it in my project, but kinda need this PR to be merged and use the official new version |
This is out of my control. Waiting on @justinmc and @chunhtai :) |
Hi @justinmc I think the issue here is different from the situation you describe. The issue is that the canpop doesn't correctly reflect the status of navigator. The doc said
if the initial route has PopScope with canpop=false, Navigator.canPop should return true because popping the navigator will not end up removing the initial route. |
simply repro // Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
/// Flutter code sample for [Scrollbar].
void main() {
runApp(const ScrollbarExampleApp());
}
final GlobalKey<NavigatorState> nav = GlobalKey<NavigatorState>();
class ScrollbarExampleApp extends StatelessWidget {
const ScrollbarExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: nav,
home: PopScope(
canPop: false,
child: Scaffold(
appBar: AppBar(title: const Text('Scrollbar Sample')),
body: Center(
child: ElevatedButton(
onPressed: () {
print(
'navigator can Pop should be true! but got ${nav.currentState!.canPop()}',
);
},
child: Text('canPop?'),
),
),
),
),
);
}
}
|
I think we need to decide on the definition of willHandlePopInternally and popDisposition. @chunhtai what do you think about the assumption in this test? It seems to think that willHandlePopInternally shouldn't consider PopScope because it concerns programmatic popping. It will fail if we make willHandlePopInternally consider PopScope (flutter/flutter#163884). // PopScope only prevents user trigger action, e.g. Navigator.maybePop.
// The page can still be popped by the system if it needs to.
expect(route.willHandlePopInternally, false); |
This matches the modified version in the example project (which fixes a bug, cf. flutter/packages#8162).
waiting for flutter/flutter#163884 to land |
I think we don't need this pr once the framework side is merged. closing this one |
This includes fixes to honor ShellRoutes with PopScope routes #140869 see the last couple comments.
Pot. also touches #8045
Pre-launch Checklist
dart format
.)[shared_preferences]
pubspec.yaml
with an appropriate new version according to the pub versioning philosophy, or this PR is exempt from version changes.CHANGELOG.md
to add a description of the change, following repository CHANGELOG style, or this PR is exempt from CHANGELOG changes.///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.