https://kotlinlang.org logo
#compose
Title
# compose
k

Karthick

10/09/2020, 3:38 AM
Is compose navigation ready?
i

Ian Lake

10/09/2020, 3:39 AM
#soon
❤️ 8
Lots more to do after the first alpha though 😅
😂 2
👍 1
a

Afzal Najam

10/09/2020, 5:02 AM
@Ian Lake I'm playing with it right now, does it support sending data to a navigation destination yet? Just asking.
i

Ian Lake

10/09/2020, 5:04 AM
a

Afzal Najam

10/09/2020, 5:05 AM
Nice. Thanks!!
a

Archie

10/09/2020, 5:45 AM
hi @Afzal Najam, how were you able to try it? I would love to try it myself as well. 😄
a

Afzal Najam

10/09/2020, 6:46 AM
@Archie Yup! This should help you import it and then check out the sample on the androidx GitHub.
Copy code
maven { url = '<https://androidx.dev/snapshots/builds/6893483/artifacts/ui/repository>' }
Copy code
implementation "androidx.compose.navigation:navigation:1.0.0-SNAPSHOT"
❤️ 2
a

Archie

10/09/2020, 6:58 AM
awesome thank you very much 😄
Hi @Ian Lake, in the current navigation component in android we have
Copy code
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?
a

Afzal Najam

10/09/2020, 1:37 PM
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.
😮 1
a

Archie

10/09/2020, 2:28 PM
I see... 🤔
i

Ian Lake

10/09/2020, 3:13 PM
Right, you just recompose your app bar, bottom nav, etc. as the current destination changes. "Hiding" is just not emitting that Composable
a

Archie

10/12/2020, 9:58 AM
@Ian Lake Does that mean having the
AppBar
or any "shared UI across screen" be defined inside each destination like so?
Copy code
NavHost(startDestination = "Start") {
    composable("Start") {
        Scaffold(
            topBar = { MyCustomerAppBar() }
        ) {
            .... 
        }
    }
    composable("Screen2") {
        Scaffold(
            topBar = { MyCustomerAppBar() }
        ) {
            .... 
        }
    }
    composable("Screen3") {
        Scaffold(
            topBar = { DifferentAppBar() }
        ) {
            .... 
        }
    }
}
i

Ian Lake

10/12/2020, 4:21 PM
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`:
Copy code
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") {
    }
  }
}
❤️ 1
a

Archie

10/13/2020, 12:53 PM
Thank you very much
Hi @Ian Lake may I ask another question again please. So in this setup:
Copy code
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:
Copy code
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.
Copy code
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

10/18/2020, 2:54 PM
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).
❤️ 1
a

Archie

10/18/2020, 5:37 PM
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
Copy code
...
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

10/18/2020, 5:53 PM
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
a

Archie

10/19/2020, 10:27 AM
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

10/19/2020, 3:11 PM
That is correct. If you want something other than that, you'd use
ViewModelProvider
directly
👍 1
a

Archie

10/19/2020, 3:24 PM
Alright! Thank you very much! (and thank you for being very patient as well) ❤️
2 Views