-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Deep linking: automatically push the route hiearchy on load. #10894
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
|
Sorry this is such a giant patch. cc @yjbanov for review of the flutter driver and devicelab parts |
yjbanov
left a comment
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.
LGTM w/ comments.
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.
There's a utility function in utils.dart (already imported) called section that you can use to print headings like this one.
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.
done
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'd say 2 minutes is optimistic but ok playing by ear.
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.
removed timeout, i'll let cocoon deal with it
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.
This is 2mm away from being a custom request handler that takes a String and returns a String. 2 things would close the 2mm gap:
- rename this method and
DataHandlerto something that implies "you can do anything in the DataHandler and return a result" - have
DataHandlerreturnFuture<String>instead ofString
I feel like we should do it now so we don't have to break API or introduce an almost identical API later because users want to do more than request data.
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've changed DataHandler to return a future string.
Any ideas what requestData, RequestData, DataHandler, and RequestDataResult should be called, if not those? I couldn't come up with anything more generic...
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.
Ideas: RequestWithData, CustomRequest, SimpleRequest
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 try to avoid words like Simple in APIs because if the API is simple, that should be obvious, and if it's not, the name will be a constant source of anger.
"customRequest()" doesn't really convey anything different than "requestData()" but is less verb-like, which is suboptimal for a method.
"requestWithData()" vs "requestData()" seems like a wash except the latter is shorter, so...
I dunno. I've left it as requestData() for now. I don't feel strongly about this though, feel free to change it.
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.
If this returned Future<String> it could be used for more use-cases (such as being able to wait for data to become available). You shouldn't lose anything in generality given that _requestData below and the network separation by its nature are already asynchronous.
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.
done
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.
nit: += is not incorrect, but ++ is much more idiomatic in Dart code.
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.
The problem with ++ is that in some situations, the postfix operator is less efficient than += 1. Since loops are often performance critical, people get all worried about this and end up writing ++moves. And the prefix operator is super ugly and horrible. So I'm on a mission to entirely kill the ++ and -- operators. :-)
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 is this? The docs on addOption don't seem to explain.
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.
Yeah I filed a bug on the docs being suboptimal.
It makes the help say that the syntax is --keep-app-running=<url>.
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 in flutter drive you still need the -t
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.
As far as I can tell, this is not the case. I ran it with "flutter drive --route '/smuggle-it' lib/route.dart" and it worked.
|
Perhaps we should skip null intermediate route names rather than bailing out entirely. We could make the routing behavior configurable via a delegate so that if developers don't like the default behavior for deep links, they could easily replace it with a different deep link handling algorithm. (This could be done in a separate CL, but should probably be addressed before make a lot of noise about this feature.) |
|
I agree with @collinjackson. We may have a route like As a side, we don't structure our routes hierarchically like this. I'm not sure if that is the intention of routing in flutter or if this is just a convenience? Also, it might be handy for navigator to have a |
|
@lukef Do you want the |
|
Route names are generally of no consequence. It was more just an understanding that this is how you guys considered the routing to work. We didn't assume that if we have a re: root. I feel like there are times when you will want The example I gave was around a news app displaying an article. If I get a push notification i might want to open the app and display the article. If the user hits close, then i want to just exit the app because the expectation is that the stack would only have the article screen/widget and nothing else. Other times I could see it would be desirable to have the option for |
|
@lukef The current patch only applies this model if the strings starts with a Based on @collinjackson's idea, my long-term plan is to make this pluggable, so next to the routes table you could provide a route handler delegate that implements the behavior, and then you could just do whatever you want with the initialRoute (and presumably any other routes that get pushed later). |
examples/stocks/lib/main.dart
Outdated
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.
A comment that explains what you're up to here would be helpful.
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.
added lots of comments here.
|
That would work. It might be handy for me to show you our set up soon also because we're running a somewhat different setup (https://github.com/goposse/fluro) and that creates interesting side effects. My personal wish would be a mechanism where we can just receive the incoming deep link info and deal with it ourselves manually if we wanted :) |
examples/stocks/lib/stock_data.dart
Outdated
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.
Shouldn't we close the http client first?
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.
good call
examples/stocks/lib/stock_data.dart
Outdated
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.
Close the http client first? I assume that there's some finalization protocol that closes the connection at GC, but it seems at least tidier to just explicitly close it.
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.
done
You can do that with this patch -- if you set |
|
That's what we are doing now. I set initial route to |
examples/stocks/lib/stock_home.dart
Outdated
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.
Maybe a comment or an issue that explains what's needed to complete this?
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.
done
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 should probably use the NumberFormat class here, just to demo how to format prices.
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'm going to leave this for now and we can revisit it when we do i18n.
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.
This is pretty slick!
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.
Another way to format this, to make the if then else structure a little clearer:
secondChild: stock != null
? new _StockSymbolView(
stock: stock,
arrow: new Hero(
tag: stock,
child: new StockArrow(percentChange: stock.percentChange),
),
),
: new Padding(
padding: const EdgeInsets.all(20.0),
child: new Center(child: new Text('$symbol not found')),
),
),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.
done
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.
Maybe explain why you're waiting a minute at a time? I would have thought that a no-params pumpAndSettle() call would have been OK.
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.
done
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 want to merge this sentence into the previous paragraph.
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.
wow, i don't know what i was doing there. removed that second sentence.
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.
Do you really want to describe this in Android-specific terms ("intent")?
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.
It's just an example. Right now there's no automatic way to do this in iOS.
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.
on => or
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.
Note sure why we refer to this route as a "page"
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.
Because routes are pages on the Web and old habits die hard.
Changed the two "page"s to "route"s.
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.
It might be slightly easier to read if this occurrence of this was [home]
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.
/// If [home] is specified, then [routes] must not include an entry for `/`,
/// as [home] takes its place.
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.
By "loaded" you mean pushed. You could even refer to [Navigator.push] once in this explanation.
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.
Fixed.
Added to see-also.
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.
this => this process
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.
Fixed
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.
Maybe we need to use a generic term, like "deep link", here and link to a TBD explanation of how the idea maps to something concrete on iOS and Android.
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.
Added a mention of "deep link".
We could put the docs you mention in Navigator.initialRoute. I'm not sure what they should say though.
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.
this will only see => the onGenerateRoute callback will only be applied to
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.
good call, fixed
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.
Don't need the braces?
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.
ok
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.
this will only see => the onGenerateRoute callback will only be applied to
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.
done
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.
Same comment as before; loaded => pushed
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.
done
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.
but with => with
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.
skipped per our discussion
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 don't remember a character named HOOLI appearing in Hamilton
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.
skipped per our discussion
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.
Should the value be routeSeparator instead?
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.
removed routeSeparator instead
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.
If we're going to have a constant instead of a convention, then our examples and framework probably need a survey, to see where '/' is assumed.
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.
removed
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.
You could put the initial values on the RHS instead of using .add()
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.
done
|
LGTM modulo some doc word smithing and various nits. I think we're going to need provide a comprehensive explanation (somewhere) about about how Flutter deep linking relates to intents on Android and TBD on iOS. |
The main purpose of this PR is to make it so that when you set the initial route and it's a hierarchical route (e.g. `/a/b/c`), it implies multiple pushes, one for each step of the route (so in that case, `/`, `/a`, `/a/b`, and `/a/b/c`, in that order). If any of those routes don't exist, it falls back to '/'. As part of doing that, I: * Changed the default for MaterialApp.initialRoute to honor the actual initial route. * Added a MaterialApp.onUnknownRoute for handling bad routes. * Added a feature to flutter_driver that allows the host test script and the device test app to communicate. * Added a test to make sure `flutter drive --route` works. (Hopefully that will also prove `flutter run --route` works, though this isn't testing the `flutter` tool's side of that. My main concern is over whether the engine side works.) * Fixed `flutter drive` to output the right target file name. * Changed how the stocks app represents its data, so that we can show a page for a stock before we know if it exists. * Made it possible to show a stock page that doesn't exist. It shows a progress indicator if we're loading the data, or else shows a message saying it doesn't exist. * Changed the pathing structure of routes in stocks to work more sanely. * Made search in the stocks app actually work (before it only worked if we happened to accidentally trigger a rebuild). Added a test. * Replaced some custom code in the stocks app with a BackButton. * Added a "color" feature to BackButton to support the stocks use case. * Spaced out the ErrorWidget text a bit more. * Added `RouteSettings.copyWith`, which I ended up not using. * Improved the error messages around routing. While I was in some files I made a few formatting fixes, fixed some code health issues, and also removed `flaky: true` from some devicelab tests that have been stable for a while. Also added some documentation here and there.
…#10894) The main purpose of this PR is to make it so that when you set the initial route and it's a hierarchical route (e.g. `/a/b/c`), it implies multiple pushes, one for each step of the route (so in that case, `/`, `/a`, `/a/b`, and `/a/b/c`, in that order). If any of those routes don't exist, it falls back to '/'. As part of doing that, I: * Changed the default for MaterialApp.initialRoute to honor the actual initial route. * Added a MaterialApp.onUnknownRoute for handling bad routes. * Added a feature to flutter_driver that allows the host test script and the device test app to communicate. * Added a test to make sure `flutter drive --route` works. (Hopefully that will also prove `flutter run --route` works, though this isn't testing the `flutter` tool's side of that. My main concern is over whether the engine side works.) * Fixed `flutter drive` to output the right target file name. * Changed how the stocks app represents its data, so that we can show a page for a stock before we know if it exists. * Made it possible to show a stock page that doesn't exist. It shows a progress indicator if we're loading the data, or else shows a message saying it doesn't exist. * Changed the pathing structure of routes in stocks to work more sanely. * Made search in the stocks app actually work (before it only worked if we happened to accidentally trigger a rebuild). Added a test. * Replaced some custom code in the stocks app with a BackButton. * Added a "color" feature to BackButton to support the stocks use case. * Spaced out the ErrorWidget text a bit more. * Added `RouteSettings.copyWith`, which I ended up not using. * Improved the error messages around routing. While I was in some files I made a few formatting fixes, fixed some code health issues, and also removed `flaky: true` from some devicelab tests that have been stable for a while. Also added some documentation here and there.
|
@Hixie I found this merge request and I'm wondering why the deep linking works how it works. If I have, for example, this initial route: Why do you want to generate a route for |
The main purpose of this PR is to make it so that when you set the
initial route and it's a hierarchical route (e.g.
/a/b/c), itimplies multiple pushes, one for each step of the route (so in that
case,
/,/a,/a/b, and/a/b/c, in that order). If any of thoseroutes don't exist, it falls back to '/'.
As part of doing that, I:
Changed the default for MaterialApp.initialRoute to honor the
actual initial route.
Added a MaterialApp.onUnknownRoute for handling bad routes.
Added a feature to flutter_driver that allows the host test script
and the device test app to communicate.
Added a test to make sure
flutter drive --routeworks.(Hopefully that will also prove
flutter run --routeworks, thoughthis isn't testing the
fluttertool's side of that. My mainconcern is over whether the engine side works.)
Fixed
flutter driveto output the right target file name.Changed how the stocks app represents its data, so that we can
show a page for a stock before we know if it exists.
Made it possible to show a stock page that doesn't exist. It shows
a progress indicator if we're loading the data, or else shows a
message saying it doesn't exist.
Changed the pathing structure of routes in stocks to work more
sanely.
Made search in the stocks app actually work (before it only worked
if we happened to accidentally trigger a rebuild). Added a test.
Replaced some custom code in the stocks app with a BackButton.
Added a "color" feature to BackButton to support the stocks use case.
Spaced out the ErrorWidget text a bit more.
Added
RouteSettings.copyWith, which I ended up not using.Improved the error messages around routing.
While I was in some files I made a few formatting fixes, fixed some
code health issues, and also removed
flaky: truefrom some devicelabtests that have been stable for a while. Also added some documentation
here and there.