Router and path-based navigation
path-based navigation was not supported in the original voyager library. Its implementation is derived from routing-compose .
for using path-based navigation you need to add the dependency
Path-based navigation (i.e. navigation to a Screen
with an URL
), is very natural for web apps, but is actually very useful for all platforms, for two main reasons:
support for deep links: for app deep links we need a way to associate a Screen to an URL
decoupling of an app into independent components: for big applications, where multiple features are developed independently in multiple modules, there is usually a central module that is the only one linked against all other modules, that implement a router that allows to navigate to Screens of any of the modules without the need of knowing anything of the other modules except the URL of the target screen.
We take a slightly different approach from other navigation libraries like the official Android Compose navigation library, and also routing-compose : in our implementation routing is not part of the composition. It is instead a separate operation that it is executed outside the composition that take as input an URL and return a Screen
that can be then pushed, as a separate operation, into the navigation stack. This way we gain a better separation between UI rendering and routing logic. It also makes it easier to add animations for screen transitions.
There is only one router for an app, and it is defined where the RootNavigator
is (or renderComposableWithNavigator
for compose Web). For activating path based-navigation Instead of passing to the RootNavigator a Screen, you pass to it a RouteResolver.
Defining a RouteResolver
RouteResolver
Let's see a full example:
As you can see we use a custom DSL(that we adopted from routing-compose ) to express matchers for all possible app routes as a hierarchical tree, where each node is a path segment (or noMatch
that means to no segment was matched at the current level of the input path) and each leaf is a Screen
associated to the associated match tree branch. Url parameters are specified the same way as url path segment matchers. Currently only one parameter per match tree node is supported, with two possible types: int
and string
the noMatch
clause in the DSL is required only after one or more route
clauses.
inside a route
clause, after an int
or string
subroute, it is optional.
How to navigate with a router
Once you have initialized the RootNavigator
with a RootResolver
you can navigate to an URL by first obtaining a reference to the router and the calling navigate("<URL>")
Under the hood, router.navigate
will resolve the Screen
associated to the requested URL and push it in the Screen stack of the RootNavigator
Matching multiple path segments at once
The path matching DSL support matching multiple path segments at once. For example
this resolver
will return the HelloWord
screen for any URL that has any of "hello", "hi", "welcome"
as the first path segment and any of "world", "galaxy", "universe"
as the second path segment.
Dynamic routes
RouteResolver
is executed every time a navigate request is received. So it is possible to incorporate logic that will change the resolved route according to parameters and/or calculations. For example:
Route with Query parameters
When a route is matched it is possible to access the associated query parameters with the variable
The map key is the parsed parameter name
The map value is the list of parsed value for that parameter name. (In the case that the same query parameter is present more than once in the list)
See for example the following code:
In the example above the URL /greeting?name=Albert;surname=Einstein
will be resolved to the screen HelloUser(name="Albert",surname="Einstein")
Redirects
It is possible to specify a redirect for some URL path. The redirect will cause the RouteResolver
to be evaluated a second time with new redirected URL path.
enableCheckForPathSegmentsExtraSlashes
flag in RouteResolver
enableCheckForPathSegmentsExtraSlashes
flag in RouteResolverBy default RouteResolver
when executed will run several checks on the path segments specified as arguments of route,
removing, if necessary, slashes at the beginning and end of the path segment definition. If you are sure that you defined the RouteResolver
correctly, without extra slashes, you can set this flag to false and avoid some unnecessary calculation.
Hashed vs. Non-Hashed URL Paths
When targeting Compose Web, when the root navigator is initialized, by default is set to use hashed URL paths. Hashed URL paths work without any special server configuration. You can change this behaviour by explicitly specifying the flag:
Last updated