Bradleycorn
11/21/2020, 5:14 PMnavigation
component, is there a way to know the size of the backstack?
I’m setting up a TopAppBar
(via Scaffold
) and I’d like to show a back arrow if the current destination is not at the top of the back stack. I’m essentially trying to do something like navController.backstack.size > 0
but the backstatck
property access is restricted to the navigation lib itself. How can I determine the backstack size to know if I should show a back icon or not?Ian Lake
11/21/2020, 5:27 PMnavController.navigateUp()
)Bradleycorn
11/21/2020, 5:52 PMisTopLevelDestination
property on the sealed class and use it to determine whether or not to show an up button.Ian Lake
11/21/2020, 6:26 PMnavigateUp()
and popBackStack()
are different things - navigateUp()
does the right thingBradleycorn
11/21/2020, 6:54 PMScreen
that goes with a route
. I had to use reflection to do it.
sealed class Screen(val route: String, val title: String, val isTopLevel: Boolean = false) {
object Home: Screen("home", "Home", true)
object Post: Screen("post/{postId}", "Blog Post")
object Settings: Screen("settings", "Settings", true)
companion object {
// create a map of routes to the Screen's they go with.
// Uses reflection (uggh), so build the map once and keep it here,
// instead of building it every time we need to look up a screen from a route.
private val routeMap = Screen::class
.nestedClasses
.mapNotNull { it.objectInstance as? Screen }
.associateBy { it.route }
fun fromRoute(route: String?): Screen? {
return routeMap[route]
}
}
}
With that, I can get the Screen
from the current route
and setup the Up Button Accordingly in my Activity:
setContent {
val navController = rememberNavController()
KsbTheme {
Scaffold(
topBar = {
val destination by navController.currentBackStackEntryAsState()
val route = destination?.arguments?.getString(KEY_ROUTE)
val isTopLevel = Screen.fromRoute(route)?.isTopLevel ?: true
AppBar(title = "My App", !isTopLevel)
}
) {
NavHost(navController = navController, startDestination = Screen.Home.route) {
...
}
}
}
}
Ian Lake
11/21/2020, 8:51 PMsetContent {
val navController = rememberNavController()
KsbTheme {
Scaffold(
topBar = {
val destination by navController.currentBackStackEntryAsState()
// Use arguments to determine whether this is a top level destination, defaulting to false
val isTopLevel = destination?.arguments?.getBoolean("topLevel", false)
AppBar(title = "My App", !isTopLevel)
}
) {
NavHost(navController = navController, startDestination = Screen.Home.route) {
composable(
Screen.Home.route,
arguments = listOf(navArgument("topLevel") { defaultValue = true })
) {
...
}
...
}
}
}
}
Archie
03/02/2021, 4:08 PMstringResource(...)
.. which couldn't be called or defined as a default value as its a @Composable
function. Because of this the burden of defining the `AppBar`'s title falls under the previous screen calling the navigation.
Also if I passed the value as a navArgument(..)
is it possible to change its value? Lets say I passed in topLevel = true
to show the back button but I would like to hide it (topLevel = false
) if for example a network request is ongoing?Bradleycorn
03/02/2021, 4:18 PMstringResource
ComposableArchie
03/02/2021, 4:23 PMstringResource()
some title would be defined as a string itself if it came from other source (for example the title should be the name of a selected item in a list). So then you'll have to check two sources of title.. one from an StringRes
and another a String
.Bradleycorn
03/02/2021, 4:37 PM<string name="placeholder">%1$s</string>
. That can be useful if you have some titles that are purely dynamic.Archie
03/02/2021, 4:39 PMBradleycorn
03/02/2021, 4:54 PMstringResource()
is a composable function, but it would be nice if there was a way to get a string from a resource without needing an @Composable
at all (much like in the old view system, it would be nice to be able to get a string from a resource in a viewmodel without needing a context).