androidkotlinandroid-jetpack-composejetpack-compose-navigation

Getting current destination from navigator controller


I am implementing a Navigation Controller for my app: https://developer.android.com/guide/navigation/navcontroller.

I defined the following Screen interface with some instances of it:

interface Screen {
    /**
     * Unique name to define the path for a composable
     */
    val route: String

    /**
     * String resource id to that contains title to be displayed for the screen.
     */
    val title: Int
}


object WelcomeDestination : Screen {
    override val route = "welcome"
    override val title = R.string.app_name
}

object AnotherDestination : Screen {
    override val route = "another"
    override val title = R.string.title_another
}

I define the app with the viewController and the navController. Then, I want to get the current screen instance from the navigation controller using toRoute():

@Composable
fun MyApp(
    navController: NavHostController = rememberNavController(),
) {
    val backStackEntry by navController.currentBackStackEntryAsState()
    val currentScreen : Screen = backStackEntry?.toRoute<Screen>() ?: WelcomeDestination
}

But I am getting this error in execution time:

java.lang.IllegalArgumentException: Polymorphic value has not been read for class null

I think it could be because I'm using object instances, so maybe I should change my implementation to a @Serializable class with data object and data class inside that class for every Screen option? But I'm not actually sure if that would solve the problem.

I would like to know the standard and proper way to implement this.


Solution

  • So far, the best approach I found is to create a Map that links the last route that backStackEntry has with the corresponding Screen instance:

    val screensByRoute : Map<String, Screen> =
        mapOf(
            WelcomeDestination.route to WelcomeDestination,
            AnotherDestination.route to AnotherDestination
        )
    

    So now I can get the currentScreen instance with:

    val currentScreen : Screen = screensByRoute[backStackEntry?.destination?.route] ?: WelcomeDestination
    

    Although, I'm still a bit surprised that there is no any method, something like toRoute(), to get directly that Screen instance directly.