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
Automatically create NavHost #28
Comments
Hey @cvb941 Generating the whole NavHost, i.e generating the whole navigation graph comes with some drawbacks. The second one is the parameters that your Route Accepts. Having the whole navGraph generated for you means, you are limited to what you can specify as the parameter, like how would you pass your lambda here?
Again, can be done by doing some hacky "injections" but I don't think the navigation library should be responsible for that. |
For your first concern, I think the To the second point, you could just take the parameters of the // Old generated extension
public fun NavGraphBuilder.login(content: @Composable () -> Unit): Unit {
route(LoginRoute, DefaultRouteTransition) {
content()
}
}
// New generated extension
public fun NavGraphBuilder.login(navHost: NavHostController, onLogin: () -> Unit): Unit {
route(LoginRoute, DefaultRouteTransition) {
LoginScreen(navHost, onLogin)
}
}
// Usage
NavHost(navController = navController, startDestination = MainGraph.route) {
navigation(LoginGraph) {
// This
login { LoginScreen(navController, onLogin = { ... }) }
// Would turn into this
login(navController, onLogin = { ... })
}
} While at it, you might be able to automatically provide the navController parameter of the public fun NavGraphBuilder.login(onLogin: () -> Unit): Unit {
route(LoginRoute, DefaultRouteTransition) {
LoginScreen( this@NavGraphBuilder.navController?? , onLogin)
}
}
NavHost(navController = navController, startDestination = MainGraph.route) {
navigation(LoginGraph) {
login(onLogin = { ... })
}
} The library could generate multiple levels of functions for the NavGraphBuilder, either for the Screens or the whole graphs. So that you could use the generated functions for most of the cases, but still customize anything your way if needed. NavHost(navController = navController, startDestination = MainGraph.route) {
// Just screen extensions
navigation(LoginGraph) {
login(onLogin = { ... })
register(onRegistered = { ... })
}
// Graph extension, which would just output the code above
loginGraph(onLogin = { ... }, onRegistered = { ... })
} Finally, a complete NavHost with all the graphs could be generated similarly as well. The example above could be made into a single navHost(navController = navController, onLogin = { ... }, onRegistered = { ... }) I think there might be some problems if you want to combine the pre-generated and manual approaches but then you would just revert to the way it is now and define stuff at the level in question manually. But I think that if you take additional care to structure your Screens in a compatible way, a single complete navHost extension from my last example would be all that is needed. But it might be that I'm missing something, so I'm awaiting your answer :) |
This I actually tried before, and I would love to pull this in, however, it doesn't work in Compose cases, since NavGraphBuilder is not a composable scope, you won't be able to pass any compose related stuff. If you know a way to do that, I'd would love to take this as an improvement 👍
An extension like this is also I gave some thought about, and I'm not entirely opposed to that (can be done if we are to solve the issue above), however having a whole navhost extension that takes all the parameters needed for all the destinations, is a no-go for me. For mid-size apps and beyond it will look very weird, to say the least |
// Old generated extension
public fun NavGraphBuilder.login(content: @Composable () -> Unit): Unit {
route(LoginRoute, DefaultRouteTransition) {
content()
}
}
// New generated extension
public fun NavGraphBuilder.login(navHost: NavHostController, onLogin: () -> Unit): Unit {
route(LoginRoute, DefaultRouteTransition) {
LoginScreen(navHost, onLogin)
}
} But it must be working since the content function is loginGraph(onLogin = { ... }, onRegistered = { ... }) Most of the time you handle all the actions in the screen's ViewModel anyways. Having to define tons of callbacks looks ugh to me too, however, it kind of encapsulates the way it is and surfaces all the actions that the object can make. I didn't like to provide a callback for every single action when I wanted to create screens without ViewModels (so that they can be previewed in the Android Studio editor), but Google recommends it that way too so ¯\(ツ)/¯ (for example here by having a Screen and a Content composable) |
Then in graph builder you might want to pass your viewModel
Calling Btw, as of now, I already generate this kind of builder when composable Route doesn't have any parameters. For example not having the navController dependency in your first example, would work as expected. |
Hi, thanks for your library, this is very much missing from the official compose navigation library.
I still quite don't understand the way the routes are set up in Navigation Compose, but it seems to me that there is still an unnecessary step when creating the NavHost composable.
Since all Screens are already annotated with the
@Route
annotation providing all the information needed, isn't it possible for this library to generate the whole NavHost composable without having to add new screens to it manually every time?Sorry if I'm missing something.
The text was updated successfully, but these errors were encountered: