androidnavigationandroid-architecture-navigation

Extracting navigation args at destination activity when using type safe navigation


I am using navigation component's type safe APIs to navigate. The app which I inherited uses several activities (2-3), and fragments everywhere else. One of the flows requires to navigate from a fragment of one activity to another activity. This works ok when I declare my graph destinations like so:

//Routes.kt
@Serializable
data class SecondScreen(val foo: Foo, val bar: String = "")

//FirstActivity.kt
findNavController().createGraph(SomeStartDestination) {
  activity<SecondScreen>(
    typeMap = mapOf(
      typeOf<Foo> to NavType.EnumType(Foo::class.java)
    )
  ) {
    activityClass = SecondActivity::class
  }
}

//SecondActivity.kt
fun onCreate() {
  val route = findNavController().currentBackStackEntry?.toRoute<SecondScreen>()
  print(route?.foo) // null
  print(route?.bar) // null
}

The part that does not work is retrieving the args that were passed along. I suspect that when the SecondActivity gets created, its NavHost and NavController start out with an empty backstack. If I grab the intent extras, it seems to extract the data ok, yet I feel that it's more of a workaround than an idiomatic solution. I looked around but did not come across any documentation for such a scenario. Am I missing something?


Solution

  • Here is example to set Nav graph with inten.extras So toRoute<T>() works.

    When the activity starts, pass the incoming Intent.extras to NavController via setGraph(graphResId,startDestinationArg) (or setGraph(NavGraph,bundel)).

    // Get NavController from NavHostFragment
    val navHost = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
    val navController = navHost.navController
    
    // Option A: if graph is defined as resource, pass intent.extras when setting graph
    navController.setGraph(R.navigation.your_graph, intent.extras)
    
    // Option B: if you already have a NavGraph instance, do:
    // navController.setGraph(navController.graph, intent.extras)
    
    // Now the NavController has the startDestination entry populated with arguments
    val route = navController.currentBackStackEntry?.toRoute<SecondScreen>()
    Log.d("Args", "route foo=${route?.foo} bar=${route?.bar}")
    
    Second Activity class , Put below code in onCreate Method
    // If your route is serializable/Parcelable or you know how args were packed,
    // this is the direct way:
    val extras = intent.extras
    extras?.let {
        // If you used a single-bundle-arg convention, convert it:
        val route: SecondScreen? = extras.toRoute() // if you have helper
        // or extract each arg:
        val bar = extras.getString("barArgName")
        val foo = extras.getSerializable("fooArgName") as? Foo
    }
    

    Try above solution
    Thank you