Jetpack Compose Navigation Is Triggering Recomposition
Asked Answered
S

3

7

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.

Scintilla answered 26/1, 2023 at 21:4 Comment(0)
T
2

Short Answer

Yes, navigating using compose navigation will recompose your composable each time you are navigating to it.

Workaround

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)

Use

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")
                })
Tracheitis answered 27/1, 2023 at 8:6 Comment(4)
Why does it recompose the route when I navigate away from it then?Scintilla
@Scintilla Tagging along. I get this same behaviour as well.Ransack
facing the same issueColoquintida
I face the same issue when I block the device or it goes background, the "composable" inside navigation is called again.Spent
A
0

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.

Adnate answered 30/4, 2023 at 1:31 Comment(1)
Item is just a data class with information to present inside ItemCard. It is not changing at all.Scintilla
T
0

There must be something that is being mutated in your state dependent for your child composable. Try to inspect what is changing like this:

enter image description here Source: https://developer.android.com/studio/releases/past-releases/as-hedgehog-release-notes#compose-state-in-debugger

Tilden answered 26/9, 2024 at 0:49 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.