Does passing arguments with Compose Navigation have an effect on performance? I have quite a few Str...
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?
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
280 Views