Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to implement navigation type safety in NiaNavHost? #1613

Open
padrecedano opened this issue Sep 18, 2024 · 1 comment
Open

How to implement navigation type safety in NiaNavHost? #1613

padrecedano opened this issue Sep 18, 2024 · 1 comment

Comments

@padrecedano
Copy link

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

@saeedishayan76
Copy link
Contributor

saeedishayan76 commented Sep 18, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants