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
[go_router] Implement popUntil
#2728
Conversation
Also relates to flutter/flutter#99112 |
Is there something else I need to do in this PR? |
I think these issues are closed due to we plan to use naive navigator API to handle imperative API. @johnpryan can chime in. |
@@ -146,6 +146,18 @@ class GoRouterDelegate extends RouterDelegate<RouteMatchList> | |||
notifyListeners(); | |||
} | |||
|
|||
/// Calls [pop] repeatedly until the predicate returns true. | |||
void popUntil(bool Function(RouteMatch) predicate) { |
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 you can just use go
to build up to a certain routematch list? If we do refactor the imperative API this will be obsolete
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.
What do you mean?
Are you suggesting that instead of calling popUntil
(as a dev using go_router
), I should call go
?
I don't think it would do the same right?
If I understood correctly, go
is kind of a replaceAll
, after using go
, there is no page to pop and canPop
is false
.
While popUntil
will allow you to only pop some pages but not all. The use case is when the user finishes a wizard to set up something, you want to remove all the wizard's pages from the history but still keep the history the user accumulated before starting the wizard.
go
wouldn't work in this scenario, right?
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.
no I meant it seems like the go
can replace popUntil
. Since developer creates their own route table, they will be able to use go
to achieve functional equivalence of popUntil
. For example if they have route table like this
GoRouteA(
routes: [
GoRouteB(
routes: [GoRouteC()]
)
],
)
lets say route match list is [GoRouteA, GoRouteB, GoRouteC]
.
popUntil((route) => route == GoRouteB)
will be the same as go(GoRouteB)
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 this comes back to flutter/flutter#99112 's discussion.
Here you are assuming that the match list is [GoRouteA, GoRouteB, GoRouteC]
and matches perfectly the structure
GoRouteA(
routes: [
GoRouteB(
routes: [GoRouteC()]
)
],
)
And pop
from GoRouteC
always goes "back" to GoRouteB
.
But as I described in flutter/flutter#99112 (comment), this is not what users are usually expecting. They don't always access GoRouteC
from GoRouteB
. So always "going back" to GoRouteB
from GoRouteC
doesn't work.
The way I understand it is that you are trying to merge/combine "app location" and "history"? But that's 2 different things that should be independent.
Am I missing something?
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.
no, you are right, the previous comment is under the assuming that the match list matches the structure. I am assuming history
means the internal route stack, not the browser history. In that case, my proposal user can still modify the history through imperative API, it just that they won't be able to change app location with imperative API. Let's continue the discussion in the issue.
# Conflicts: # packages/go_router/CHANGELOG.md # packages/go_router/pubspec.yaml
Hello @chunhtai , @johnpryan , can I ask for a follow up on this? Should I close this PR? If yes, is there a plan to make the delegate and route match list public? And can I help on that? |
/// Calls [pop] repeatedly until the predicate returns true. | ||
void popUntil(bool Function(RouteMatch) predicate) { | ||
bool hasPopped = false; | ||
while (!predicate(_matchList.last)) { |
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.
things has changed a bit, its probably the best to call pop directly since it will remove any pageless route that are on top such as dialog.
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.
@chunhtai It's hard to use go router.... Us project previous use more ‘pop with value on pages’, now have the requirement need do the web compatible, but go router no this feature to provide, then use 'Go_Router_Flow' lib, but looks like Pop function have been changed....
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.
No I mean the implementation of popUntil should use pop
method. I am on board with implementing popUntil
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.
@chunhtai I am calling pop in
# Conflicts: # packages/go_router/CHANGELOG.md # packages/go_router/pubspec.yaml
|
||
await tester.pumpAndSettle(); | ||
expect(goRouter.routerDelegate.matches.matches.length, 3); | ||
goRouter.routerDelegate.addListener(expectAsync0(count: 2, () {})); |
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.
With the current implementation (simply calling pop
, the notifiers are notified for each pop
). In this case, 2 times. Is it correct or should the router delegate notify only once?
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.
we may need to refactor the code a bit, every time it notify the listener, the Gorouter will also notify its listener about location change, so if url = '/a/b/c/d/e' and popUtil to '/a'
if we notify each time it pop
goRoute will notify its listener with
'/a/b/c/d'
'/a/b/c'
'/a/b'
'/a'
instead just
'/a'
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.
So, to be correct, this test should pass with count: 1
, is that correct?
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.
yes
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.
Hello @chunhtai , I'm not sure it will be possible to notify the listeners only once even when popping several times (and make the test pass with count: 1
).
I pushed some code in 2c48f39 . Itis not correct as I guess it doesn't work with multiple nested navigators, it's more of a demonstration example. it uses directly popUntil
from the NavigatorState
and it still notifies the listeners twice.
Or would you know a way to make this work?
# Conflicts: # packages/go_router/CHANGELOG.md # packages/go_router/pubspec.yaml
Will this work for dialogs? I mean if I've open the dialog and then will call this popUntill, will |
@chunhtai What's the status of this PR? It looks like it's probably waiting for another review. |
@stuartmorgan Actually, it requires some work from me first. #2728 (comment) I need to find a way to make the delegate notify its listeners only once. I was waiting for #2846 to be merged. Because I personally have a bigger need for |
Thanks, I'm going to go ahead and mark this as a draft then just to get it off of our review queue. Please set it back to Ready for Review once you've had a chance to update it! |
# Conflicts: # packages/go_router/CHANGELOG.md # packages/go_router/lib/src/misc/extensions.dart # packages/go_router/pubspec.yaml
@ValentinVignal The #2846 is about to be merged (just waiting for ci), will you work on this PR next? |
I'm not sure I'll work on it right away as I'm quite busy right now, but I'll work on it at some point when #2846 is merged yes. But feel free to take it over if you want to |
Let me close this one for now, feel free to reopen once you have time. |
I'm still missing popUntil. It is irreplace-able feature that I'm missing. go_router should have it if it wants to be a full featured navigator. Some guy told me I could just use .go instead, but isn't .go like a push, won't it create another item in the navigation stack? I don't want that. I want to pop several pages. Use case is very simple. Page with a button, when the button is clicked navigates to a page with camera view, take photo, navigate to a full page preview pf the photo, click on confirm button, pop the preview page and the camera view. The only way to make this now is with 2 pops and have a special logic to get a value from the first pop. What if I have a wizard form with multiple pages. Having to implement a bunch of custom logic and calls of unknown number of pop. |
@talamaska go() is different from push(), go() replaces your current Navigator stack with a new one. For example, take this sample with two routes: GoRoute(
path: '/library',
routes: [
GoRoute(
path: 'album/:albumId',
routes: [
GoRoute(
path: 'song/:songId',
),
],
),
],
), If you navigate to |
So I wanted to work on flutter/flutter#107052 for
pushAndRemoveUntil
but it was closed saying it was a duplicate of flutter/flutter#106402 which was not because it didn't contain the request forpushAndRemoveUntil
. And flutter/flutter#106402 got also closed because it got implemented.So do you want me to re-create an issue? Or should we re-open flutter/flutter#107052?
In any case, this PR first implements
popUntil
which is simpler thanpushAndRemoveUntil
. I will implementpushAndRemoveUntil
if the implementation of this one is acceptable.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.///
).