Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to get route object from currentBackStackEntry in Compose Navigation outside NavHost composable block using toRoute extension

I am working on a project that utilizes Jetpack Compose Navigation with Type-Safe Navigation. Within my application, I have an composable function responsible for hosting the navigation graph. Here's a simplified version of the code:

@Composable
fun Content(
    navController: NavHostController = rememberNavController()
) {
    val currentBackStackEntry by navController.currentBackStackEntryAsState()
    val currentRoute = currentBackStackEntry?.toRoute<Route>()

    NavHost(navController = navController, startDestination = Route.Route1){
        composable<Route.Route1> {  }
        composable<Route.Route2> {  }
        composable<Route.Route3> {  }
    }
}

@Serializable
sealed interface Route {
    @Serializable
    data object Route1 : Route

    @Serializable
    data object Route2 : Route

    @Serializable
    data object Route3 : Route
}

I'm attempting to retrieve the current route object outside the composable block: currentBackStackEntry?.toRoute<Route>(). However, I encounter the following exception:

IllegalArgumentException: Polymorphic value has not been read for class null

It appears that polymorphic behavior is not supported/enabled in this context. Can someone provide guidance on how to solve this issue? I need to be able to obtain the current route object outside the NavHost composable block using toRoute<Route> function. Thank you!

like image 819
Inidam Leader Avatar asked Jan 18 '26 01:01

Inidam Leader


1 Answers

I solved this using Kotlin Reflect, check if this helps you out

@Serializable
sealed class Screen {
    companion object {
        fun fromRoute(route: String): Screen? {
            return Screen::class.sealedSubclasses.firstOrNull {
                route.contains(it.qualifiedName.toString())
            }.objectInstance
        }
    }

    @Serializable
    data object Home : Screen()

    @Serializable
    data class Detail(val id: Int) : Screen()
}

Make sure you have:

implementation(kotlin("reflect"))

Use case:

val backStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = remember(backStackEntry) {
    Screen.fromRoute(backStackEntry?.destination?.route ?: "")
}

EDIT:

If you want to get the arguments as well, I got into this solution:

companion object {
    fun fromRoute(route: String, args: Bundle?): Screen? {
        val subclass = Screen::class.sealedSubclasses.firstOrNull {
            route.contains(it.qualifiedName.toString())
        }
        return subclass?.let { createInstance(it, args) }
    }

    private fun <T : Any> createInstance(kClass: KClass<T>, bundle: Bundle?): T? {
        val constructor = kClass.primaryConstructor
        return constructor?.let {
            val args = it.parameters.associateWith { param ->
                bundle?.get(param.name)
            }
            it.callBy(args)
        } ?: kClass.objectInstance
    }
}

Use Case

val backStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = remember(backStackEntry) {
    Screen.fromRoute(
        route = backStackEntry?.destination?.route ?: "",
        args = backStackEntry?.arguments
    )
}
like image 155
GsEsdras Avatar answered Jan 20 '26 21:01

GsEsdras