androiddev321
androiddev321

Reputation: 275

Clear Bottom Nav Saved State - Jetpack Compose

The current flow of my app is as follows:

Login Screen -> Home Screen with 4 bottom nav tabs.

I'm saving the state of my bottom nav tabs using android documentation as stated here:
https://developer.android.com/jetpack/compose/navigation#bottom-nav

My issue is that when I logout the user then login again and go to the home screen the bottom nav tabs states are being persisted even though I cleared the back stack when going back to login. How can I clear the tab's saved states? I'm trying to find a solution without using two NavHosts because that isn't deep link friendly.

I can see why it's not clearing the saved state when popping the entire back stack because only a single route from the bottom tabs is actually on the back stack. I'm currently using navController.clearBackStack(route) to clear the saved states individually but I occasionally get an IllegalStateException when using it so I'm looking to see if there's another solution.

Edit with IllegalStateException stack trace:

java.lang.IllegalStateException: You cannot access the NavBackStackEntry's ViewModels until it is added to the NavController's back stack (i.e., the Lifecycle of the NavBackStackEntry reaches the CREATED state).
    at androidx.navigation.NavBackStackEntry.getViewModelStore(NavBackStackEntry.kt:197)
    at androidx.lifecycle.viewmodel.compose.ViewModelKt.get(ViewModel.kt:208)
    at androidx.lifecycle.viewmodel.compose.ViewModelKt.viewModel(ViewModel.kt:156)
    at androidx.navigation.compose.NavBackStackEntryProviderKt.SaveableStateProvider(NavBackStackEntryProvider.kt:97)
    at androidx.navigation.compose.NavBackStackEntryProviderKt.access$SaveableStateProvider(NavBackStackEntryProvider.kt:1)
    at androidx.navigation.compose.NavBackStackEntryProviderKt$LocalOwnersProvider$1.invoke(NavBackStackEntryProvider.kt:52)
    at androidx.navigation.compose.NavBackStackEntryProviderKt$LocalOwnersProvider$1.invoke(NavBackStackEntryProvider.kt:51)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
    at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
    at androidx.navigation.compose.NavBackStackEntryProviderKt.LocalOwnersProvider(NavBackStackEntryProvider.kt:47)
    at androidx.navigation.compose.NavHostKt$NavHost$4.invoke(NavHost.kt:162)
    at androidx.navigation.compose.NavHostKt$NavHost$4.invoke(NavHost.kt:141)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
    at androidx.compose.animation.CrossfadeKt$Crossfade$5$1.invoke(Crossfade.kt:133)
    at androidx.compose.animation.CrossfadeKt$Crossfade$5$1.invoke(Crossfade.kt:128)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
    at androidx.compose.animation.CrossfadeKt.Crossfade(Crossfade.kt:142)
    at androidx.compose.animation.CrossfadeKt.Crossfade(Crossfade.kt:73)
    at androidx.navigation.compose.NavHostKt.NavHost(NavHost.kt:141)
    at androidx.navigation.compose.NavHostKt.NavHost(NavHost.kt:67)
    at com.veryableops.business.AppNavigationKt.RootNavigation(AppNavigation.kt:138)
    at com.veryableops.business.MainActivity$MainApp$1$1$4.invoke(MainActivity.kt:182)
    at com.veryableops.business.MainActivity$MainApp$1$1$4.invoke(MainActivity.kt:181)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116)
    at androidx.compose.runtime.internal.ComposableLambdaImpl$invoke$1.invoke(ComposableLambda.jvm.kt:127)
    at androidx.compose.runtime.internal.ComposableLambdaImpl$invoke$1.invoke(ComposableLambda.jvm.kt:127)
    at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:162)
    at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2465)
    at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:2733)
    at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3364)
    at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3342)
    at androidx.compose.runtime.SnapshotStateKt__DerivedStateKt.observeDerivedStateRecalculations(DerivedState.kt:341)
    at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(Unknown Source:1)
    at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3342)
    at androidx.compose.runtime.ComposerImpl.recompose$runtime_release(Composer.kt:3307)
    at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:772)
    at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:1047)
    at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:124)
    2023-09-24 00:53:50.754 14075-14075 AndroidRuntime          com.veryableops.business.debug       E      at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:541)
    at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:510)
    at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:34)
    at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:109)
    at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41)
    at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1035)
    at android.view.Choreographer.doCallbacks(Choreographer.java:845)
    at android.view.Choreographer.doFrame(Choreographer.java:775)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1022)
    at android.os.Handler.handleCallback(Handler.java:938)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loopOnce(Looper.java:201)
    at android.os.Looper.loop(Looper.java:288)
    at android.app.ActivityThread.main(ActivityThread.java:7839)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
    Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [androidx.compose.runtime.PausableMonotonicFrameClock@ab4be20, androidx.compose.ui.platform.MotionDurationScaleImpl@da2e3d9, StandaloneCoroutine{Cancelling}@29d519e, AndroidUiDispatcher@a0e147f]

Upvotes: 1

Views: 704

Answers (1)

ibraheem lubbad
ibraheem lubbad

Reputation: 46

You can force it to recompose by creating a key that can be updated:

val navHostKey = remember { mutableStateOf(0) }

Assign the key to NavHos

   NavHost(
navController = navController,
startDestination = "login",
route = "nav_host_${navHostKey.value}"){.....}

When logging out, increase the value of the key:

fun onLogout() {
navHostKey.value++
navController.navigate("login") {
    popUpTo("login") { inclusive = true } // Clears the back stack
}

}

In this case when the value of navHostKey changes, the NavHost will be recomposed and will not retain its saved states,so all the tabs start with initial state after a user logs back in

Upvotes: 0

Related Questions