https://kotlinlang.org logo
#compose-android
Title
# compose-android
k

KotlinLeaner

03/14/2024, 12:24 PM
I have an Android application with multiple screens managed by Jetpack Navigation. In my app, I'm using a
SavedStateHandle
in my ViewModel to preserve certain states across configuration changes and navigation events. Specifically, I have a scenario where I navigate from
ScreenFirst
to
ScreenSecond
, and on
ScreenSecond
, I toggle the visibility of a button based on the value of
isBackButtonVisible
. However, despite saving this value in the
SavedStateHandle
, when I press the Back button to return to
ScreenFirst
and then navigate back to
ScreenSecond
, the visibility of the button is always reset to its initial state (false), instead of retaining the previously set value. Additionally, I want to clear the back stack when navigating from one screen to another to ensure a consistent navigation flow. Although I've implemented
popUpTo
to clear the back stack, it doesn't seem to affect the preservation of the
isBackButtonVisible
value. How can I ensure that the
SavedStateHandle
in my ViewModel preserves the state across navigation events and back stack clearing? What could be causing the issue with the state not being retained properly?
Copy code
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.compose.SavedStateHandleSaveableApi
import androidx.lifecycle.viewmodel.compose.saveable
import androidx.navigation.NavController
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import org.koin.androidx.compose.navigation.koinNavViewModel

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            NavigationScreen()
        }
    }
}

@Composable
fun NavigationScreen(navController: NavHostController = rememberNavController()) {
    NavHost(
        navController = navController,
        startDestination = "ScreenFirst",
        route = "parentRoute"
    ) {

        composable("ScreenFirst") {
            Button(onClick = {
                navController.navigateWithClearStack("ScreenSecond")
            }) {
                Text(text = "Move to Second Screen")
            }
        }

        composable("ScreenSecond") {

            val viewModel: SecondViewModel = koinNavViewModel()

            Column(modifier = Modifier.fillMaxSize()) {
                Button(onClick = { viewModel.update(true) }) {
                    Text(text = "Show Back Button")
                }

                AnimatedVisibility(visible = viewModel.isBackButtonVisible) {
                    Button(onClick = {
                        navController.navigateWithClearStack("ScreenFirst")
                    }) {
                        Text(text = "Move to Back Screen")
                    }
                }
            }
        }
    }
}

fun NavController.navigateWithClearStack(screenRoute: String) {
    this.navigate(screenRoute) {
        // Clear the back stack up to the start destination
        popUpTo(currentBackStackEntry?.destination?.route ?: return@navigate) {
            inclusive = true
        }
        // Specify the new start destination
        launchSingleTop = true
    }
}

class SecondViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {

    @OptIn(SavedStateHandleSaveableApi::class)
    var isBackButtonVisible by savedStateHandle.saveable { mutableStateOf(false) }
        private set

    fun update(value: Boolean) {
        isBackButtonVisible = value
    }
}
s

Stylianos Gakis

03/14/2024, 12:31 PM
If you pop an item from the backstack completely, your second screen in your case, it’s just gonna get destroyed completely, and its ssh along with it as well. When it will be preserved is if you go forward and then come back to it. Or if you leave the app and process death happens, and you then come back to the app and there is process recreation Sounds to me like you want that state to be preserved in a permanent way to disk or something like that instead here.