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!
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
)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With