Thread
#compose
    Karthick

    Karthick

    1 year ago
    Is compose navigation ready?
    i

    Ian Lake

    1 year ago
    Lots more to do after the first alpha though 😅
    Afzal Najam

    Afzal Najam

    1 year ago
    @Ian Lake I'm playing with it right now, does it support sending data to a navigation destination yet? Just asking.
    i

    Ian Lake

    1 year ago
    Afzal Najam

    Afzal Najam

    1 year ago
    Nice. Thanks!!
    Archie

    Archie

    1 year ago
    hi @Afzal Najam, how were you able to try it? I would love to try it myself as well. 😄
    Afzal Najam

    Afzal Najam

    1 year ago
    @Archie Yup! This should help you import it and then check out the sample on the androidx GitHub.
    maven { url = '<https://androidx.dev/snapshots/builds/6893483/artifacts/ui/repository>' }
    implementation "androidx.compose.navigation:navigation:1.0.0-SNAPSHOT"
    Archie

    Archie

    1 year ago
    awesome thank you very much 😄
    Hi @Ian Lake, in the current navigation component in android we have
    navController.addOnDestinationChangedListener { 
        ...
        // Hide/show shared UI here
        ...
    }
    We usually do this for switching persistent
    AppBar
    or any shared UI across screens. Is this still the case for
    Compose
    or will Compose Navigation Component introduce something different?
    Afzal Najam

    Afzal Najam

    1 year ago
    There's a
    currentBackstackEntryAsState
    function or something like that. Since we don't have listeners or classes much in the UI anymore, a state would be a compose-like approach. I'm guessing that all the rest of the functionality will get polished over time according to usage.
    Archie

    Archie

    1 year ago
    I see... 🤔
    i

    Ian Lake

    1 year ago
    Right, you just recompose your app bar, bottom nav, etc. as the current destination changes. "Hiding" is just not emitting that Composable
    Archie

    Archie

    1 year ago
    @Ian Lake Does that mean having the
    AppBar
    or any "shared UI across screen" be defined inside each destination like so?
    NavHost(startDestination = "Start") {
        composable("Start") {
            Scaffold(
                topBar = { MyCustomerAppBar() }
            ) {
                .... 
            }
        }
        composable("Screen2") {
            Scaffold(
                topBar = { MyCustomerAppBar() }
            ) {
                .... 
            }
        }
        composable("Screen3") {
            Scaffold(
                topBar = { DifferentAppBar() }
            ) {
                .... 
            }
        }
    }
    i

    Ian Lake

    1 year ago
    No, that's not sharing any UI at all between destinations. Just like other composables, it is about hoisting state (in this case, the
    NavController
    ) to outside of the NavHost:
    val navController = rememberNavController()
    val currentBackStackEntry = navController.currentBackStackEntryAsState()
    Scaffold(
      topBar = {
        // Use whatever logic you want here
        if (currentBackStackEntry?.value?.arguments?.getBoolean("showAppBar") == true) {
          MyCustomerAppBar()
        } else {
          DifferentAppBar()
        }
    ) {
      NavHost(navController, startDestination = "start") {
        composable("start") {
        }
      }
    }
    Archie

    Archie

    1 year ago
    Thank you very much
    Hi @Ian Lake may I ask another question again please. So in this setup:
    val navController = rememberNavController()
    val currentBackStackEntry = navController.currentBackStackEntryAsState()
    Scaffold(
      topBar = {
        // Use whatever logic you want here
        if (currentBackStackEntry?.value?.arguments?.getBoolean("showAppBar") == true) {
          MyCustomerAppBar()
        } else {
          DifferentAppBar()
        }
    ) {
      NavHost(navController, startDestination = "start") {
        composable("start") {
        }
      }
    }
    The app bar is shared across screens. Assuming that the app bar have app bar menu and when clicked, depending on the current screen it triggers a different action. In the current way of doing this with fragments, we do something like:
    class MyFragment : Fragment() {
        override fun onCreate(context: Context) {
            ....
            setHasOptionsMenu(true)
            ....
        }
    
        // To set the menu
        override fun onCreateOptionsMenu(menu: Menu) {
            ....
            menuInflater.inflate(R.menu.myMenu, menu)
            ....
        }
    
        // to Recieve the event when the menu is clicked
        override fun onOptionsItemSelected(item: MenuItem): Boolean {
            // Handler action when menu item is clicked
        }
    
        ....
    }
    How should the same thing be handled in compose? Since the AppBar is now in the scope of the current View. The only way I can think of is doing something like this in compose.
    val navController = rememberNavController()
    val currentBackStackEntry = navController.currentBackStackEntryAsState()
    
    // create a state which I could pass down and listen to when value gets set
    val someState = remember { SomeState() }
    Scaffold(
      topBar = {
        Button(
           onClick = { someState.value  = SomeValue() }, // Set the value of the state 
           content = { Text("Click ME!")  },
        )
    ) {
      NavHost(navController, startDestination = "start") {
        composable("start") {
            MyScreen(someState) // Check value of someState inside My Screen to know when the button is clicked.
        }
      }
    }
    But I feel like this is the wrong way.
    i

    Ian Lake

    1 year ago
    Think about what is actually happening under the hood in the old world: you have an activity scoped menu (a hoisted list of menu items in the Compose world), then each destination is adding elements to that list when it is added (committed in the Compose world) and removing them when it is removed (disposed in the Compose world).
    Archie

    Archie

    1 year ago
    Aha! YESS! thank you! that makes so much sense... I apologize for not getting it sooner... Thank you very much! ❤️
    @Ian Lake Last question, So I noticed by doing
    viewModel()
    inside a
    composable(...) {}
    the
    ViewModel
    is now scoped to that
    composable(...)
    instead of the
    Activity
    . as seen in this code snippet inside
    NavHost
    ...
    if (destination is ComposeNavigator.Destination) {
        // while in the scope of the composable, we provide the navBackStackEntry as the
        // ViewModelStoreOwner and LifecycleOwner
        Providers(
             AmbientNavController provides navController,
             ViewModelStoreOwnerAmbient provides currentNavBackStackEntry!!,
             LifecycleOwnerAmbient provides currentNavBackStackEntry!!,
             children = destination.content
        )
    }
    ....
    I was wondering If is also possible to scope a
    ViewModel
    across multiple
    composable(...)
    just like how
    navGraphViewModels(...)
    work?
    i

    Ian Lake

    1 year ago
    viewModel()
    isn't doing anything special, just calling through to
    ViewModelProvider
    with the right
    ViewModelStoreOwner
    . Which is the same thing
    navGraphViewModels()
    does: https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:navigation/navigation-fragment-ktx/src/main/java/androidx/navigation/NavGraphViewModelLazy.kt;l=56
    Archie

    Archie

    1 year ago
    I think I get it now.. so basically calling
    viewModel()
    across
    composable(...){...}
    will still give the same
    ViewModel
    since its using the current
    backStackEntry.viewModelStore
    . did i get it right?
    i

    Ian Lake

    1 year ago
    That is correct. If you want something other than that, you'd use
    ViewModelProvider
    directly
    Archie

    Archie

    1 year ago
    Alright! Thank you very much! (and thank you for being very patient as well) ❤️