https://kotlinlang.org logo
#compose
Title
# compose
p

Pablo

03/20/2024, 11:46 AM
How to correctly pass parameters with Compose navigation if you must use enums for the routes? In the docu they show you this example for passing parameters:
Copy code
NavHost(startDestination = "profile/{userId}") {
    composable("profile/{userId}")
}
But how to work with enums for the routers? in previous codelabs and docu they told you that you must use enums for the routes:
Copy code
NavHost(startDestination = Screen.HomeScreen.name) {
    composable(Screen.HomeScreen.name)
}
How to pass parameters correctly using enums for the routes? How to add that userId parameter if you use an enum for the route?
s

Stylianos Gakis

03/20/2024, 11:56 AM
2 options: • You have to manually wire up the code necessary to have the parameters both read from the route, and when navigating to that route. See an example inside NowInAndroid. • Use navigation-compose-typed which does this work for you internally by using kotlinx.serialization (1 future option) • Wait for this https://issuetracker.google.com/issues/188693139 to be merged and you’ll get the behavior of navigation-compose-typed (more or less) natively shipped by the androidx.navigation library
😮 1
🌟 1
p

Pablo

03/20/2024, 12:07 PM
😕
too much complex I think
I'll wait and for now I'll concatenate it as a string to the enum route
something like this
Copy code
composable(route = Screen.PlacesScreen.name+"/{selectedCategory}")
I don't like any of these three options, they add too much complexity for simply passing a parameter
s

Stylianos Gakis

03/20/2024, 12:12 PM
It’s up to you. This approach that you suggest relies on your remembering to do this each time on each call-site and does not take care of what happens if the parameter that you pass in is not properly URL encoded and so on. Which is something that you need to handle if you want to be 100% correct here. Also your
composable(route = Screen.PlacesScreen.name+"/{selectedCategory}")
suggestion does not add the right arguments to the route as you you can see in the first link
Copy code
arguments = listOf(navArgument(TOPIC_ID_ARG) { type = NavType.StringType }),
Also something that you will need to do anyway, you can’t go around that. Both option 1 and 2 have all those problems covered. So it’s up to you to decide what you want to do now that you know what you need to think about 😊
p

Pablo

03/20/2024, 12:14 PM
I understand you, but I'm trying to add the lowest complexity possible because compose is still hard to understand for me, coming from sequential programming Its very hard for me to change to declarative programming
for example, now I'm stuck again, because using navigation in compose, hosted in the main app composable, I have two screens, screen A allows user to select a category, and that category must be passed to screen B as a parameter. The problem I found is that when you select the category in screen A, you have it on the state ui inside the viewmodel of that screen A. I know that I must pass that variable to the route of screen B using navigation arguments, but I don't know how to access that variable in the main app composable that hosts the navhost. I mean... the navhost doesn't have access to viewmodel of screen A, so, how can it knows that parameter that needs to pass to screen B in the navigation argument?
s

Stylianos Gakis

03/20/2024, 12:15 PM
Realistically, adding
navigation-compose-typed
is by far the simplest think you can do for this. All you’ll need to do is change the imports to use the right function from inside that library, add the serialization plugin, and mark your destinations are serializable. That’s it.
Your composables should just call a lambda where they just pass up the parameter that they want to use while navigating, and you can hook that up to call the right navigation call at the place where you are creating your nav graph. This thread https://kotlinlang.slack.com/archives/CJLTWPH7S/p1645595702068129?thread_ts=1645552485.247699&cid=CJLTWPH7S has more info.
p

Pablo

03/20/2024, 2:07 PM
Thank you, now I have the parameter, but how can I pass the parameter to the viewmodel of the screen B?
Copy code
class UserViewModel(
    savedStateHandle: SavedStateHandle,
    private val userInfoRepository: UserInfoRepository
) : ViewModel() {
    private val userId: String = checkNotNull(savedStateHandle["userId"])
    private val userInfo: Flow<UserInfo> = userInfoRepository.getUserInfo(userId)
}
p

Pablo

03/20/2024, 2:08 PM
but... they don't say how to set the value to the viewmodel
only says how to recover it
s

Stylianos Gakis

03/20/2024, 2:09 PM
You "set" it by navigating to the route with the right parameter. If you go to
link/blah/3
for route
link/blah/{someId}
then it's
3
p

Pablo

03/20/2024, 2:10 PM
😮
ok, works like magic then, I'll try to memorize it
s

Stylianos Gakis

03/20/2024, 2:12 PM
That's the whole point of routes with parameters. Wouldn't call it magic, unless you consider websites also magic when you go to a url which also has parameters 🤷‍♂️
p

Pablo

03/20/2024, 2:12 PM
why in that sample they are passing a second parameter on the viewmodel? how and where is set that parameter and why is not a livedata, remember etc... parameter?
private val userInfoRepository: UserInfoRepository
well in this case seems very different from a website for me, because it's being received by the viewmodel, not by the composable, and I'm not setting it on the viewmodel at any point
s

Stylianos Gakis

03/20/2024, 2:14 PM
You can absolutely get it in the composable too. Inside the composable lambda you have a NavBackStackEntryb which contains the route, the parameters passed into it etc. The fact that the ViewModel even gets those values is just the default behavior of the
viewModels
function. Again, not magic.
why in that sample they are passing a second parameter on the viewmodel? how and where is set that parameter and why is not a livedata, remember etc... parameter?private val userInfoRepository: UserInfoRepository
You will probably benefit from reading the docs a bit https://developer.android.com/topic/architecture
p

Pablo

03/20/2024, 2:15 PM
thank you, and what about that second parameter on the sample?
s

Stylianos Gakis

03/20/2024, 2:16 PM
^^ my message above
p

Pablo

03/20/2024, 2:18 PM
well that's a generalistic article about app architecture which I already have been readed
can't figure out how can that help me understand why that viewmodel is receiving that second parameter
as I can remember, for setting parameters on viewmodels you are forzed to use factory patern
and without by remember, or mutablestate etc... the parameter will not be remembered in recompositions
so I can't understand why they are using that parameter there
s

Stylianos Gakis

03/20/2024, 2:20 PM
How you construct it has nothing to do with the fact that in that use case, it needs to use the repository to get access to... well stuff that the repository contains. Not sure how any of the rest of what you're saying here is relevant. They're giving an example of a VM having some other parameter as well.
And they're showing how to be able to call that
getUserInfo
function from the repo using the parameter which has come from the route parameters. That's it.
p

Pablo

03/20/2024, 2:22 PM
so you mean they are using a factory pattern to make viewmodel accepting that second parameter but they are not showing it on the sample? or you mean that in compose you can use viewmodels with parameters without needing to use factory pattern to add parameters to viewmodel?
s

Stylianos Gakis

03/20/2024, 2:24 PM
How it's constructed is irrelevant to the sample given here. You may be doing it yourself with a factory, you may be using the built-in functions by hilt, or even by koin. It doesn't matter in this context.
p

Pablo

03/20/2024, 2:28 PM
in that sample, they are not using hilt or koin, so you mean that they must be using
Copy code
ViewModelProvider.Factory
but they are not showing it
am I correct?
I'm sorry to ask you all those questions, this is my only way to understand things that reading documentation or programming samples doesn't allow me to understand
s

Stylianos Gakis

03/20/2024, 2:33 PM
What I'm thinking to help you understand is that it's a sample, so they only show the part relevant to the sample. It doesn't matter what one may use on the other side. In your app, you use what you need to use to make it work. If you're struggling with that part, there must be some other part of the documentation which explains how to actually get the ViewModel instance from inside your composable, with hilt, with whatever. I don't have an answer to your question because this is not a real app, it's a sample so I can't know what they're using because there's nothing on the other side, it's just a sample. If you're having trouble getting this to work on your app I suggest looking for a different part of the docs once you've understood this part.
p

Pablo

03/20/2024, 2:53 PM
thank you very much
🙌 1
I understand you
it is working now with your explanations and help
s

Stylianos Gakis

03/20/2024, 2:53 PM
Glad to help! 😊
p

Pablo

03/20/2024, 2:53 PM
🙂
13 Views