Does passing arguments with Compose Navigation hav...
# compose
n
Does passing arguments with Compose Navigation have an effect on performance? I have quite a few Strings to pass, but I might be able to find a solution to avoid it if needed. If it has no effect on performance, I'll take the easy route (no pun intended). ๐Ÿค”
m
I assume youโ€™re talking navigationExtras? Think of it as equivalent to passing bundle arguments to a fragment in the xml world.
n
r
No performance hit. If you want to pass arguments from one screen to another, this is the way to do it in Compose
๐Ÿ‘ 1
k
If data is related, could you wrap the data into the data class, serialize it, pass it as 1 string and deserialize it in second screen?
n
That's exactly what I was wondering. I was wondering which would be better. Any thoughts?
r
k
If I really had to, I'd do it like that. Not that I usually get in a situation that I have to pass a lot of data from one screen to another tho.
๐Ÿ‘ 1
r
you need a bit of setup, but anything you can put in a string, you can pass it.
๐Ÿ‘ 1
I hope this doesnโ€™t go against any rule of this channel, and I am biased ofc, but you can check my library where you could pass these types of arguments in a type-safe way
you donโ€™t even need to worry about routes at all
and youโ€™d still be using compose navigation in the end, just simplified ๐Ÿ˜‰
n
Looks good! ๐Ÿ™‚
๐Ÿ‘ 1
i
Just remember that arguments go against your global savedInstanceState, which is extremely limited in size (500KB in total across the entire system); you shouldn't be sending anything large through arguments ever
๐Ÿ‘ 2
r
good to know ๐Ÿ™‚ When you say entire system, is it like all apps compete for that space or is the system our app only?
And also, is this different than the limitations with โ€œnormalโ€ navigation component, ex: if passing arguments between fragments?
i
Everything going through Android's Binder at one time counts. The 95% case is it is just your app saving its state at the same time, but for cases like multi-window, activities from separate apps might be saved at the same time
(hence why you need to really be way, way under that limit to avoid
TransactionTooLargeException
cases)
r
Makes sense, thank you for clarifying @Ian Lake ๐Ÿ™‡
i
Navigation Compose is more efficient than Navigation with Fragments in this regard as Fragments themselves maintain a second copy of every argument passed to them, independent from the copy Navigation is keeping
But being better than a Fragment based system is kind of a low bar ๐Ÿ˜‰
๐Ÿ˜„ 5
r
ahah true, still good to know that there arenโ€™t any new limitations people need to worry. Anything they could send as an argument before, they can still do now.
albeit with a little bit more work in some cases, since it goes through a route string
i
You shouldn't be passing anything that couldn't be in a route string in the fragment world either - it was just as bad of an idea then ๐Ÿ™‚
๐Ÿ‘ 1
r
True, but yeah if you were sending lets say a simple and small Parcelable data class, it was a little bit easier. Now there is a bit of setup with the custom nav type. Thatโ€™s what I meant as โ€œa bit more workโ€. But the fact that it can be done is great ๐Ÿ™‚
e
@Rafael Costa, Sorry to ask here. I use your Compose Destination library, it is great. I have nested
DestinationNavHost
, first for the main/root navigation and the second for my
BottomNavigationBar
. The problem is I want to access the root
DestinationsNavigator
from my BottomNavigation screen, but the
DestinationNavigator
passed to my BottomNavigation screen is for the child graph, not the root one. So I got "Destination not found" error when doing that. Sorry my question is not clear, I'll comeback with the code.
r
@Eko Prasetyo I know what you mean
you need to receive the NavController for the โ€œroot nav hostโ€ on MainScreen.
Copy code
@Destination
@Composable
fun MainScreen(
    // compose destinations will provide this for you
    rootNavController: NavController
)
and then, when you want to navigate to screen that belongs to the outer (the root) nav host, you should use this navController:
rootNavController.navigate(ScreenXDestination) // there is a extension function that receives a Destination
Now.. every example I see like yours, it has never been needed thing to have such a nested Nav Host strategy. You guys are not making it easy on yourselves ๐Ÿ™‚ If the issue is to hide the bottom bar on those root level screens, just collect current destination as state on the Scaffold and show/hide depending on which destination is resumed at the moment.
e
Copy code
@BottomNavGraph(start = true)
@Destination(style = NavigationTransition::class)
@Composable
fun BottomNavScreenX(
    navigator: DestinationsNavigator, // bottom nav host
    rootNavController: NavController, // root nav host
    viewModel: DashboardViewModel = hiltViewModel()
)
I'm sorry, is this what you mean?
r
I believe you can also use
DestinationsNavigator
instead of
NavController
๐Ÿค” So yeah, on MainScreen you can also use that instead ๐Ÿ™‚
No
You receive the root nav controller on the screen that calls Scaffold
(I believe that is MainScreen)
Then, if you need to pass that to screens like your
BottomNavScreenX
, you need to either manually call them, or wrap it in some other type (because, you know, if you request two DestinationsNavigators, Compose destinations has no way of knowing how to provide that to you) and use
dependenciesContainerBuilder
when calling
DestinationsNavHost
and pass that new type there as a dependency.
e
Let me try it, thanks you so much!
r
Copy code
class RootNavController(val value: NavController)

// --------------------------

DestinationsNavHost(
    dependenciesContainerBuilder = { //this: DependenciesContainerBuilder<*>
        dependency(RootNavController(rootNavController))
    }
)
basically like this (just an example)
and then you can do
Copy code
@BottomNavGraph(start = true)
@Destination(style = NavigationTransition::class)
@Composable
fun BottomNavScreenX(
    navigator: DestinationsNavigator, // bottom nav host
    rootNavController: RootNavController, // root nav host
    viewModel: DashboardViewModel = hiltViewModel()
) {
    rootNavController.value.navigate(ScreenXDestinatioN)
}
You can make this more elegant, using kotlin delegation with DestinationsNavigator instead of NavController btw ๐Ÿ˜‰
Copy code
class RootNavigator(rootNavigator: DestinationsNavigator) : DestinationsNavigator by rootNavigator
ofc, in MainScreen you ask for
DestinationsNavigator
instead of
NavController
and you use that here โ˜๏ธ
then you can use RootNavigator as any other
DestinationsNavigator
๐Ÿ™‚
rootNavigator.navigate(ScreenXDestination)
up to you ๐Ÿ™‚
e
Thanks sir, very helpful!
and its working, again thanks ๐Ÿ˜„
๐Ÿ‘ 1