Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jetpack Compose navigation crashes app after orientation change

I use Jetpack Compose Navigation in my app.

  • Compose Navigation version: 2.7.7
  • lifecycleRuntimeKtx: 2.8.4
  • Kotlin version: 1.9.25
  • Device: Xiaomi 220333QNY

I have 2 screens: A, B.

The navigation graph looks like: A⟷B

Where → is navigate(route), and ← is navigateUp() or hardware back button.

I use ViewModel for screen A:

class MyViewModel(private val _openScreenB: () -> Unit) : ViewModel() {
    fun openScreenB() {
        _openScreenB()
    }
}

NavContoller and NavHost:

setContent {
    val navController = rememberNavController()
    NavHost(
        navController = navController,
        ...
    ) {
        composable("ScreenA") {
            val viewModel: MyViewModel = viewModel(
                factory = MyViewModelFactory(
                    openScreenB = {
                        navController.navigate("ScreenB") {
                            launchSingleTop = true
                        }
                    }
                )
            )
            ScreenA(
                navigateToScreenB = {
                    viewModel.openScreenB()
                }
            )
        }
        composable("ScreenB") {
            ScreenB(
                navigateBack = {
                    navController.navigateUp()
                }
            )
        }
    }
}

Everything works fine until activity recreation (orientation change or switch light/dark mode). After recreation, line val navController = rememberNavController() is called again. The NavController recreates, and now navigateUp() does nothing, hardware back works, and navigate(route) crashes app with error.

java.lang.IllegalStateException: State must be at least CREATED to move to DESTROYED, but was INITIALIZED in component NavBackStackEntry(2d8f0727-c13b-4635-ac94-f279608c3cfc) destination=Destination(0x67884f5a) route=Editor
    at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.jvm.kt:131)
    at androidx.lifecycle.LifecycleRegistry.setCurrentState(LifecycleRegistry.jvm.kt:107)
    at androidx.navigation.NavBackStackEntry.updateState(NavBackStackEntry.kt:186)
    at androidx.navigation.NavBackStackEntry.setMaxLifecycle(NavBackStackEntry.kt:159)
    at androidx.navigation.NavController.updateBackStackLifecycle$navigation_runtime_release(NavController.kt:1100)
    at androidx.navigation.NavController.dispatchOnDestinationChanged(NavController.kt:996)
    at androidx.navigation.NavController.navigate(NavController.kt:1882)
    at androidx.navigation.NavController.navigate(NavController.kt:1817)
    at androidx.navigation.NavController.navigate(NavController.kt:2225)
    at androidx.navigation.NavController.navigate$default(NavController.kt:2220)
    at ...

I tried to navigate with saveState = true, restoreState = true, and launchSingleTop = true properties, but this doesn't fix the problem. I also tried to use navController saveState() in onSaveInstanceState and restoreState(bundle) after rememberNavController, but it also doesn't help.

like image 892
Hilarious Harius Avatar asked Nov 18 '25 17:11

Hilarious Harius


1 Answers

At the first version of my question I don't know that problem was in my ViewModel. I tried to create a minimal reproducible example without ViewModel:

setContent {
    val navController = rememberNavController()
    NavHost(
        navController = navController,
        ...
    ) {
        composable("ScreenA") {
            ScreenA(
                navigateToScreenB = {
                    navController.navigate("ScreenB") {
                        launchSingleTop = true
                    }
                }
            )
        }
        composable("ScreenB") {
            ScreenB(
                navigateBack = {
                    navController.navigateUp()
                }
            )
        }
    }
}

When I created it, I realised all was working perfectly. So I guess that the problem was in my ViewModel.

I was passing navigation lambdas to ViewModel constructor for a long time. It caused crashes. I think that saving a navController as a ViewModel property breaks lifecycle of the navController.

Removing all the lambdas with navController from ViewModel constructor solved my problem

P.S. I passed that navigation lambdas directly to composables

like image 142
Hilarious Harius Avatar answered Nov 20 '25 06:11

Hilarious Harius



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!