The new navigation_compose 2.4.0-alpha05 introduce...
# compose
a
The new navigation_compose 2.4.0-alpha05 introduced crossfade. At the moment i used "Scaffold per Screen". Each screen in the application has its own Scaffold because some screens should show an docked FAB in the BottomBar. Now, with navigation 2.4.0-alpha05, my blue BottomBar flickers on every navigation, because of the crossfade. I think the solution is to put the scaffold out of the screens -> One global Scaffold Could someone give me an example on how to use the navigation library in compose with one Scaffold around the NavHost. Screen A should not show a fab, Screen B should show the docked FAB. My problem is the fab click handling
a
Don't think so, but let's hope
👍 1
c
I'm also waiting for that next release due to some flickers.
f
in your main composable, where you have the scaffold, you could use this
Copy code
val navBackStackEntry by navController.currentBackStackEntryAsState()
to get the current backstack entry and then, based on this, decide whether to show the FAB or not
i
If your bottom bar is part of your destinations, yes, it will crossfade, so you're on the right track on moving shared UI out of each destination and above the NavHost
Using your current destination as the source of truth for your FAB's location / existence is indeed the recommendation: https://kotlinlang.slack.com/archives/CJLTWPH7S/p1627490432392100?thread_ts=1627477759.324500&cid=CJLTWPH7S
I'm a big fan of the argument based approach, where you use default values on your arguments to control the global behavior rather than relying on a specific route and tightly coupling them together: https://kotlinlang.slack.com/archives/CJLTWPH7S/p1627400548238900?thread_ts=1627296022.083200&cid=CJLTWPH7S
Generally, Compose works with state down, events up, so sending events down to a screen feels a bit backward. Maybe having that top level Scaffold own a FabState object that owns the StateMap<NavBackStackEntry,()->Unit> callbacks. Then each screen could use a DisposableEffect to add itself as a listener when it starts being composed and remove itself when it is disposed (i.e., when it is popped or no longer visible)
Your onClick could then
lastOrNull { entry -> entry.lifecycle.isAtLeast(Lifecycle.State.RESUMED) }
to find the only resumed destination to send the click event to (only the current destination is resumed)
j
Sending events down may be bad practice in compose, but you also want to have some encapsulation, right? If my screen shows a list of data and i have a viewmodel that handles this data, this is the place where i can handle the fab button. Handling it all the way up where the NavHost lives (when using a single scaffold) feels wrong, too. Also, if using feature modules, i want to keep the logic inside them and not pull it up to the main module because the NavHost + Scaffold is there. Because "sending events down to a screen feels a bit backward", i decided to move all scaffolds down to the screens, which now breaks because of crossfade.
btw this applies to other use cases as well, for example when you have buttons in the top bar that are specific to a screen.
i
Did you read my suggestion for click events? That still allows individual screens to add a handler for click events without breaking the directionality
j
Yes, thanks. I actually didn't get it was meant as a good solution as events are still send down. Also, i have other cases where screens add additional content to the top bar. One is a tab bar (dynamic count and text) that appears on the same background my TopBar has, the other is a badge that overlaps the Topbar slightly. Both cases are easy with Scaffolds inside screens as i can just modify TopBar content in the screens, but i can't find good and/or easy solutions using a single Scaffold. I'm not even sure it's worth finding another approach, as multiple scaffolds are such a good solution for these cases.
Ok i finally moved to single scaffold design. Had to animate additional content in the topbar that is changed from screens myself. Another idea to manage custom content outside of the NavHost is described in this article: https://jossiwolf.medium.com/introducing-navigation-material-%EF%B8%8F-a19ed5cc33fd Code: https://gist.github.com/jossiwolf/63732616a9825f1d510c43d23a38d8c5#file-appbottomsheetlayout-kt
m
This kind of complexity is why i generally have each destination own it's own toolbar and scaffold. This only works, mind you, if each destination has distinct operations, and doesn't "contribute" to a larger toolbar that may have other items in it.
There's defintiely some things i avoid in the navigation library as well. In particular dialog and bottom sheet destinations. I view them as part of the actual page they are one, rather than specific destinations on their own.