Reputation: 6565
I have a simple Jetpack Compose application that uses the Navigation Component.
My UI is comprised of the following composables (redacted UI to be concise):
NavHost:
@Composable
fun NavHost(
navController: NavHostController = rememberNavController(),
startDestination: String = "main"
) {
NavHost(
navController = navController,
startDestination = startDestination) {
composable("main") {
SomeList(onNavigateToItemView = {
navController.navigate("listItem")
})
}
composable("listItem") {
ItemView()
}
}
}
SomeList:
@Composable
fun SomeList(onNavigateToItemView: () -> Unit) {
Column {
Row ( Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
Text(text = Constants.APP_TITLE, fontSize = 30.sp, fontWeight = FontWeight.Bold)
}
Box(modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
LazyColumn(
horizontalAlignment = Alignment.CenterHorizontally
) {
items(items) { item->
ItemCard(item, onNavigateToItemView)
}
}
}
}
}
ItemCard:
@Composable
fun ItemCard(item: ItemModel, onNavigateToItemView: () -> Unit) {
Card(
border = BorderStroke(2.dp, Color.Cyan),
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 5.dp)
.clickable {
onNavigateToItemView()
}
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
)
{
....
}
}
}
Now, whenever the user clicks on an ItemCard, he/she transitions to an ItemView. What I am seeing is that the ItemView is being recomposed several times when navigating to it and also when navigating back from it.
According to the guide linked above,
The NavController's navigate function modifies the NavController's internal state. To comply with the single source of truth principle as much as possible, only the composable function or state holder that hoists the NavController instance and those composable functions that take the NavController as a parameter should make navigation calls. Navigation events triggered from other composable functions lower in the UI hierarchy need to expose those events to the caller appropriately using functions.
And as you can see above, I am following that practice.
So, am I doing something wrong in my implementation or is this just how navigation and Jetpack Compose work together?
The multiple recomposition calls are affecting my UI unnecessarily.
Upvotes: 7
Views: 5837
Reputation: 393
There must be something that is being mutated in your state dependent for your child composable. Try to inspect what is changing like this:
Source: https://developer.android.com/studio/releases/past-releases/as-hedgehog-release-notes#compose-state-in-debugger
Upvotes: 0
Reputation: 31
How is item being used in ItemCard? When I have seen this glitch in the past it means that I have two parameters that are both being changed and causing each other to recompose.
This makes me think that your item parameter might be changing.
Upvotes: 0
Reputation: 1374
Yes, navigating using compose navigation will recompose your composable each time you are navigating to it.
When you are using navController.navigate()
it will automatically recompose the targeted route. However, you can use an option to save the current state of the screen that you leave and restore the state of the screen that you target.
For example :
A->B->A->B
The first time you will load A & B you will recompose because you have no state saved yet. The moment you go from B to A (B->A) the state will be restored so you'll not recompose. Same things occurs the second time you go from A to B (A->B)
Accord to documentation, you can make an extension function like this
fun NavController.popUpTo(destination: String) = navigate(destination) {
popUpTo(graph.findStartDestination().id) {
saveState = true
}
// Restore state when reselecting a previously selected item
restoreState = true
}
And use it like this
SomeList(onNavigateToItemView = {
navController.popUpTo("listItem")
})
Upvotes: 2