How to implement navigation type safety in NiaNavHost?
According to this post, starting with version 2.8.0-alpha08 of Navigation objects can be used in navigation.
I'm trying to implement it, because I need to pass instances of an object called ItemUI instead of passing a simple value, but it doesn't work for me.
This is what I've been trying:
ForYouNavigation
fun NavController.navigateToForYou(navOptions: NavOptions) = navigate(FOR_YOU_ROUTE, navOptions)
@ExperimentalMaterial3AdaptiveApi
fun NavGraphBuilder.forYouScreen(onTopicClick: (ItemUI) -> Unit) {
composable(
route = FOR_YOU_ROUTE,
) {
ForYouRoute(onTopicClick = onTopicClick)
}
}
NiaNavHost
@Composable
fun NiaNavHost(
appState: NiaAppState,
onShowSnackbar: suspend (String, String?) -> Boolean,
modifier: Modifier = Modifier,
startDestination: String = FOR_YOU_ROUTE,
onReaderClick: () -> Unit
) {
val navController = appState.navController
NavHost(
navController = navController,
startDestination = startDestination,
modifier = modifier,
) {
forYouScreen(onTopicClick = navController::navigateToTodays)
// ...
}
navigateToTodays
fun NavController.navigateToTodays(topicId: ItemUI? = null, navOptions: NavOptions? = null) {
/*val route = if (topicId != null) {
"${INTERESTS_ROUTE_BASE}?${TOPIC_ID_ARG}=$topicId"
} else {
INTERESTS_ROUTE_BASE
}*/
navigate(topicId, navOptions)
}
At this line
navigate(topicId, navOptions)
I have this error:
Type mismatch: inferred type is ItemUI? but TypeVariable(T) was expected
In typeSafeNavigation (version 2.8.0) you can pass a route: T as argument, this class should be Serializable , you can pass item in this class like this:
@Serializable
object ScreenA
@Serializable
data class ScreenB(
val user: User
)
@Serializable
data class User(
val name: String,
val family: String
)
Then you should create an custom type for this type like this :
object CustomNavType {
val userType = object : NavType<User>(
isNullableAllowed = false
) {
override fun get(bundle: Bundle, key: String): User? {
return Json.decodeFromString(bundle.getString(key) ?: return null)
}
override fun parseValue(value: String): User {
return Json.decodeFromString(Uri.decode(value))
}
override fun serializeAsValue(value: User): String {
return Uri.encode(Json.encodeToString(value))
}
override fun put(bundle: Bundle, key: String, value: User) {
bundle.putString(key, Json.encodeToString(value))
}
}
}
Then you should use typeMap as argument for screenB like this:
composable<ScreenB>(
typeMap = mapOf(
typeOf<User>() to CustomNavType.userType
)
)
And now you can pass data from another screen like this :
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center
) {
Text("Home", modifier = Modifier
.clickable {
navController.navigate(ScreenB(
User("Ali", "Saeedi"),
), navOptions {
popUpTo(ScreenA) {
inclusive = true
}
})
}
}
In your way, class ItemUi must be annotated with Serialization. Also, if this class contains a custom variable, it must also be Serialized, then you can use :
navigate(ItemUi(...), navOptions)
For your issue:
Should add new composable to NiaNavHost, then from foryouScreen when onTopicCall , do a work like this
forYouScreen(
onTopicClick = navController::navigateToTodays
)
You should have this
composable<ItemUi> (
typeMap = mapOf ( typeOf ... ) -> if you have custom type
){ backStackEntry ->
val data = backStackEntry.toRoute<ItemUI>()
TodaysScreen(data, .....)
}
@padrecedano
Since this is not directly related to the Now in Android project I am closing.