Is the navigation graph obsolete when I use Jetpack Compose Navigation? - android

Before I use a navigation graph in my Android Studio project, just like the article says.
I use an xml file located in res\navigation folder to include all my different destinations.
At present, I'm learning Jetpack Compose Navigation by the article.
Code A is from the official sample project mentioned in the above article.
It seems that Jetpack Compose Navigation use Code A and other codes to navigate, and I can't find any XML file in res\navigation folder.
1: Is the navigation graph obsoleted when I use Jetpack Compose Navigation ?
2: Don't I need to use the navigation graph located in res\navigation folder again when I use Jetpack Compose Navigation?
Code A
#Composable
fun RallyNavHost(navController: NavHostController, modifier: Modifier = Modifier) {
NavHost(
navController = navController,
startDestination = Overview.name,
modifier = modifier
) {
composable(Overview.name) {
OverviewBody(
onClickSeeAllAccounts = { navController.navigate(Accounts.name) },
onClickSeeAllBills = { navController.navigate(Bills.name) },
onAccountClick = { name ->
navigateToSingleAccount(navController, name)
},
)
}
composable(Accounts.name) {
AccountsBody(accounts = UserData.accounts) { name ->
navigateToSingleAccount(navController = navController, accountName = name)
}
}
composable(Bills.name) {
BillsBody(bills = UserData.bills)
}
...
}
}

Navigation has always had three ways of building a NavGraph object:
Manually, by using the NavGraph constructor itself. While this serves as the basis for all other methods listed here, you aren't meant to use these APIs directly.
Building the graph via Navigation XML. This is a way of building a graph at compile time using the Navigator Editor tooling and the Safe Args plugin. This method only supports Navigation with Fragments.
Using the Navigation Kotlin DSL. This provides a type-safe way of building a navigation graph programmatically, at runtime by using a Kotlin DSL. This method supports both Navigation with Fragments and Navigation Compose.
As Compose is a way of programmatically building your UI, Navigation only supports the programmatic version of building the graph - via that Kotlin DSL that is exactly what that trailing lambda of NavHost provides you: it is that same NavGraphBuilder scope that allows you to call navigation(...) {} to build a nested graph or composable to add a new destination to your graph.
All of the concepts are the same because the underlying NavGraph you construct ends up being the exact same set of objects at runtime, no matter how you actually build the graph.

Related

What to put in parameters of a Screen when putting it in a composable of nav host in jetpack navigation

I have a Screen ( composable function ) that gets It's data from view model ( a list and two function to remove and add data in it ).
#Composable
fun MainScreen(
notes: List<Note>,
onAddNote: (Note) -> Unit,
onRemoveNote: (Note) -> Unit
){}
Now when i call this function inside the composable of my Nav host, I get errors stating that i should fill the parameters.
#Composable
fun NotesNavigation(){
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Navigation.MainScreen.name){
composable(Navigation.MainScreen.name){
MainScreen() // error here
}
}
}
Now I am wondering what is the best practice to sort it out, do i need to provide default values for my parameters like supplying an empty list
or
there is better way to get around it.
You can set default values to the MainScreenFunction, but since you are using navigation, this would become useless. I would suggest to set the viewmodel as a parameter. The viewmodel should still be passed through the navigation though.
I don't know if you use any dependency injection. If so, that would make it a bit easier. Then you can set it up like this:
#Composable
fun MainScreen(
viewModel: MainScreenViewModel = getViewModel() //If using Koin DI
){
...
}
This way, the navigation doesn't have to know about the viewmodel. You can still set it though, if you do need a different viewModel then the one injected for example.

Scalable way to use the Snackbar in Android Compose

This is not a post to have a solution but to discuss the project structure on Compose.
I'm currently learning Compose and I have difficulties to understand how to use the Snackbar in a project. Most examples I've seen are basic examples to call the Snackbar once with the SnackbarHostState from the Scaffold.
However, when working on an app that has an MVVM structure, I don't understand how the snackbar can be called from anywhere, except transmitting the state from a composable to another.
However, I would like the snackbar use to not be that restrictive. Is there any scalable way to use the Snackbar yet with Compose?
This might be a good use-case for CompositionLocals - a way to pass dependencies down the tree implicitly. First, make sure each screen-level composable provides a SnackbarHostState or some kind of snackbar manager, which wraps the SnackbarHostState (if you'd like to add some logic on top of the standard behaviour). After that at any place in your app use the provided CompositionLocal to show a snackbar.
Setup;
ExampleTheme {
val snackbarHostState = remember { SnackbarHostState() }
val LocalSnackbarHostState = compositionLocalOf<SnackbarHostState>{
error("No Snackbar Host State")
}
CompositionLocalProvider(
values = arrayOf(
LocalSnackbarHostState provides snackbarHostState
)
) {
Scaffold(
snackbarHost = { SnackbarHost(snackbarHostState) },
) {
//
}
}
}
Call wherever you want;
val snackbarHostState = LocalSnackbarHostState.current
snackbarHostState.showSnackbar()

Navigation component, popping back to another modules Destination

I have a multi module project that uses Navigation Component to navigate between Fragments. This means that in order to get to another module, I have to launch a DeepLink using findNavController().navigate(NavDeepLinkRequest, NavOptions). When it comes time to log out of the application, I need to pop the back stack inclusive to a Destination in another module that is not visible to that module. How do I achieve this?
I had exactly the same issue and I believe Google doesn't support it by default using Navigation Component (which is sad). However I managed to do it in a bit hacky way (but it works) using old friend getIdentifier.
In your Fragment you may have a navigation request like this:
val navigateToOtherModuleRequest = NavDeepLinkRequest.Builder
.fromUri("yourapp://othermoduledestination".toUri())
.build()
Then get the resource id you need to pop-up to using getIdentifier() (it can be Fragment id or in my case nav graph id):
val homeNavGraphResourceId = resources.getIdentifier(
"home_nav_graph",
"id",
requireContext().packageName
)
Define navigationOptions like this:
val navigationOptions = NavOptions.Builder()
.setPopUpTo(
destinationId = homeNavGraphResourceId,
inclusive = true
)
.build()
And navigate using
findNavController().navigate(
navigateToOtherModuleRequest,
navigationOptions
)
Hope this will help!

Jetpack Compose Navigation: Get route of current destination as string

I'm using NavHost and a NavHostController to navigate in my Jetpack Compose application. To specify destinations, I use string-based routes:
NavHost(
navController,
startDestination = "FOO"
) {
composable("FOO") {
Foo()
}
composable("BAR") {
Bar()
}
}
I would like to retrieve the current route as a string (FOO or BAR) using the navController. The closest alternative I can find is navController.currentDestination.id that, however, returns a numeric ID, not the route as a string.
Is there any way to retrieve this value?
I'm using Jetpack Compose beta01 and Compose Navigation alpha08.
From androidx.navigation:navigation-compose version 2.4.0-alpha01, androidx.navigation.compose.KEY_ROUTE is replaced with navBackStackEntry?.destination?.route.
The KEY_ROUTE argument has been replaced with the route property on NavDestination, allowing you to call navBackStackEntry.destination.route directly.
Release Note for 2.4.0-alpha01
Example code:
val currentRoute = navController.currentBackStackEntry?.destination?.route
Or, if you need to observe the navigation:
val navBackStackEntry by navController.currentBackStackEntryAsState()
when (val currentRoute = navBackStackEntry?.destination?.route) {
// do something with currentRoute
}
Resolved!
import androidx.navigation.compose.KEY_ROUTE
val currentRoute = navController.currentBackStackEntry?.arguments?.getString(KEY_ROUTE)
Not the cleanest solutions IMO, but it is used in the official Android documentation for navigating with compose: https://developer.android.com/jetpack/compose/navigation?hl=it
For those who have a problem with getting a route from NavDestination object, such as me. Check the class type. There are two classes of NavDestination.kt and NavDestination.java. Cast it to NavDestination.kt to be able to get the route value.

Android Navigation Architecture Component - Programmatically

Looking for examples or anything similar that takes Swift (iOS) code like this:
let navController = UINavigationController(rootViewController: initialView)
and sets it up in Kotlin, via the new Navigation component. I've referenced the following examples, but it's not making complete sense to me:
val myNavHostController: NavHostFragment = nav_host_fragment as NavHostFragment
val inflater = myNavHostController.navController.navInflator
val graph = inflater.inflate(R.layout.nav_graph)
myNavHostController.navController.graph = graph
and
val finalHost = NavHostFragment.create(R.navigation.example_graph)
supportFragmentManager.beginTransaction()
.replace(R.id.nav_host, finalHost)
.setPrimaryNavigationFragment(finalHost) // this is the equivalent to app:defaultNavHost="true"
.commit()
It appears the Android examples I'm coming across, still require an Activity/Fragment to already be established in the XML file created by the navigation component ... but, what if I want this functionality to be dynamic? What if I need to set the 'host' activity for the nav component based on data passed in? I am in need of the ability to do this all via code, which is what the Swift line is doing (setting the 'initialView' UIViewController, as the 'host', which has the navigation controller embedded in it). None of it is done via storyboarding, which seems to be what Android wants me to do regardless...
I am certain the issue is me not fully understanding how this works in Android, which is what I really would like to learn.

Categories

Resources