androidkotlinandroid-jetpack-composekotlin-flowjetpack-compose-navigation

StateFlow collectAsState is not updating his value jetpack compose navigation


I am making simple navigation app in jetpack compose. I am changing the screen using compose navigation, using some simple condition but my destination value is always same what I have initialise. I don't understand why this value is always same as ScreenOne. If you check the function navigateToScreen in viewmodel, if condition is true but destination value is not changing.

FunAppNavigation

@Composable
fun FunAppNavigation(
    viewModel: FunAppViewModel = koinViewModel(),
    navController: NavHostController = rememberNavController()
) {
    val destination = viewModel.navigationHandler.destination.collectAsState()

    LaunchedEffect(Unit) {
        viewModel.navigateToScreen()
    }

    LaunchedEffect(key1 = destination) {
        navController.navigate(destination.value.route)
    }

     SideEffect {
        println(">> destination Value $destination")
     }

}

FunAppViewModel

class FunAppViewModel(
    val navigationHandler: NavigationHandler,
) : BaseViewModel() {

    fun navigateToScreen() {
        val destination = if (true) {
            DeviceRoutes.ScreenTwo
        } else {
            DeviceRoutes.ScreenOne
        }
        navigationHandler.navigate(destination)
    }
}

NavigationHandler

interface NavigationHandler {
    val destination: StateFlow<DeviceRoutes>
    fun navigate(deviceRoutes: DeviceRoutes)
}

Navigator

class Navigator : NavigationHandler {
    private val _destination: MutableStateFlow<DeviceRoutes> =
        MutableStateFlow(DeviceRoutes.ScreenOne)

    override val destination: StateFlow<DeviceRoutes> = _destination

    override fun navigate(deviceRoutes: DeviceRoutes) {
        _destination.value = deviceRoutes
    }
}

DeviceRoutes

sealed class DeviceRoutes(val route: String) {
    object ScreenOne : DeviceRoutes("ScreenOne")
    object ScreenTwo : DeviceRoutes("ScreenTwo")
}

Solution

  • I guess you forgot to put .value inside the key of LaunchedEffect. So it will compare the reference of your state variable (which is the same) instead of comparing the content of your state which is your destination.

    try in this way:

    LaunchedEffect(key1 = destination.value) {
        navController.navigate(destination.value.route)
    }