Android compose navigation controller popBackStack recompose the previous composable

349 Views Asked by At

Hi I am developing one simple android app. I am using NavHostController. When I try to come back from one compose to its previous compose it refreshes the full composable. my looks like this:

@Composable
fun FirstScreen(navigation: NavController) {
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .fillMaxHeight(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        val counter = remember{ mutableStateOf(0) }
        Button(onClick = {
            counter.value = counter.value+1
        }) {
            Text("Counter : ${counter.value}")
        }
        Button(onClick = {
            navigation.navigate("SecondScreen")
        }) {
            Text("Go to second screen")
        }
    }
}

@Composable
fun SecondScreen(navigation: NavController) {
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .fillMaxHeight(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text("Second screen")
        Button(onClick = {
            navigation.popBackStack()
        }) {
            Text(text = "Back to first screen")
        }
    }
}

NavGraph looks like as follow:

navController = rememberNavController()
                NavHost(navController = navController, startDestination = "FirstScreen") {
                    composable("FirstScreen") {
                        FirstScreen(navigation = navController)
                    }
                    composable("SecondScreen") {
                        SecondScreen(navigation = navController)
                    }
                }

In above example when I come back from second screen to first screen my counter value is reset to zero.

1

There are 1 best solutions below

2
NewPartizal On

I'm not sure why the counter is 0 when you popbackstack, it should be under normal conditions, but you can use remembersaveable instead of remember. RememberSaveable automatically saves any value that can be saved in a Bundle.

you can do something like this.

 Column(
        modifier = Modifier
            .fillMaxWidth()
            .fillMaxHeight(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
     var counter by rememberSaveable { mutableStateOf(0) }
     ...

SECOND SOLUTION:

I think it would be better to update the counter variable in the viewmodel because the life of the viewmodel is longer than the UI and the problem seems to stay from there because other than that, there seems to be no problem in your codes.

you can try something like this:

VM

    private val _state = MutableStateOf(FirstScreenState())
    val state:StateFlow<FirstScreenState> = _state.asStateFlow()
    
    fun onChangeCounter(counterValue:Int){
         _state.update {
                it.copy(
                    counter = counterValue
                )
            }
    }
    
   
   data class FirstScreenState(
     val counter:Int?=null
   ) 

UI:

@Composable
fun FirstScreen(
 navigation: NavController,
 viewModel: FirstViewModel = hiltViewModel()
) {
    val state by viewModel.state.collectAsState()
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .fillMaxHeight(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        
        Button(onClick = {
            viewModel.onChangeCounter(state.counter+1)
        }) {
            Text("Counter : ${state.counter}")
        }
        Button(onClick = {
            navigation.navigate("SecondScreen")
        }) {
            Text("Go to second screen")
        }
    }
}