Ultimate Guide to Navigation and Deep Links in Jetpack Compose

Updated: Oct 23

Hey everyone. Welcome back to my channel. Jetpack Components introduced a new way of navigation in xml. The same follows in Jetpack Compose as well. So if you're wondering that, how do we do navigation in Jetpack, compose, stay until the end of this blog where we will discover exactly that.

As usual, I have an empty project, so the very first thing I'd like to do is actually create different screens so that I can navigate between them. They will be very simple screens, so let's quickly do that first.

 Here. If you notice, I have created three fairly simple screens, Nothing special, a home screen, a favorite screen, and a setting screen. All of them just contains a column with a text at the. Now comes the main topic of this blog navigation. How do we navigate between these screens? The very first thing you'd want to do is to actually go ahead and add the project dependency for navigation. So let's quickly do that.

Here you see that we have added navigation dependency and synced up the project, So let's first start with the bottom navigation because that is the most common use case of navigation. The very first thing we need to do for the bottom bar is to actually create the bottom items, which will contain the title for the item in the bottom bar. It's icon, and also the screen route name, which will denote the unique route to be followed by navigation. So let's quickly do that as well.

 Here I have added a bottom nav item sealed class that is taking in the title I can and screen route name, which I have already mentioned that screen route name would be the unique name that the navigation library will use to uniquely identify each different route. So since it is a sealed class, we can create all the screens items here itself. So let's quickly do that as well.

 Now here I have added three objects, which are all of the bottom nav item type. That means the home bottom nav item is having the title of home and Icon of home, and also the screen route name of home. Same goes for the other items as well. Now remember that you can always use different names for different routes. I personally prefer to use the same name for the screen route. It makes development much more. Now let's first create the bottom bar.

Starting with the bottom bar composable. We have a list of items that we would like to show in the bottom bar that is home favorites and the settings.

 After that, I have added a bottom navigation composable, which is provided by the navigation library itself. If we have a look at it, we'll see that it is just a simple row wrapped around a surface.

So here you can see that it is just a simple rope. And that is wrapped around the surface. Now, a surface is nothing but a simple composable that, as the name suggests, provides a surface for the user interaction. Wrapping a composable with a surface means that now the user can perform, click drag, or any kind of other actions on the composable, in this case, the row.

 Here we add a bottom navigation item for each item in list. That was the home settings and the favorites. So for each item we add a bottom navigation item. Bottom navigation item is also provided by the navigation library. Right. So let's place this bottom bar on the screen and let's see how this looks.

 Here I have added a bottom bar, but noticed that I have used a scaffold for this. Well, scaffold is a material component that allows us to place various different components in a common pattern, such as a bottom bar, top bar, or fab buttons. We'll see the use of scaffold much more as we progress through this series.


Anyways, the bottom bar is looking as it should. Perfect. So now that we have created the bottom bar, we also need to hook it up with the functionality to navigate between different screens. If you remember, we used a nav host for that in XML as well. That nav host was the component that held the screens and when hooked up with the bottom bar, it could change screens based on the item.


So similar to xml, we are going to do the same in compose as well. Let's create a nav host for our screens to be hosted.

Here you can see that I have added a new navigation component that is taking a nav controller as a parameter inside it is creating a nav host, which takes that nav controller and a starting destination. So a nav controller is a component that takes care of the navigation for us. When we specify that we need to navigate to a different screen, it is that nav controller, which takes care of navigating to the different screen, maintaining back-stack, and much more. Now, let's add the different screens into the nav host

So you can see that we have added all the screens inside the navhost. This will create a navigation graph with all the screens. So we create a composable with the screen route, and when the screened out navigation happens, we show the respective. For example, when we navigate the home screen route, NAV controller knows that it needs to show the home screen. With this, we have created our nav controller. Now let's place it onto the screen.

Here we place a navigation component in the main screen, and for the nav controller, we are passing the remember nav controller object.


Remember, nav controller is a saveable object so that it can save the navigation back-stack across the config changes as well. In the ultimate guide to states lifecycle and view models video, we learn that we can use, remember saveable when we want to persist the state across config changes.


By the way, if you'd want to learn more about that, You can check the video here, and the blog here. Check them out as well.

So if I go ahead and open the Remember navcontroller, you'll see that we save the navigator so that the current navigation is saved across config changes. If we do not do that and change the config of the view, we'll see that the nav controller will be initialized again and we'll be seeing the start screen once again. We obviously do not want that. We want the user to stay at the same screen when the config changes. That's why we use, Remember navcontroller.


 So now if we run the app, we'll see that we are at the home screen. That is because it is the starting destination in the nav host, but nothing happens when we click on the different bottom bar items. To do that, we need to navigate to the correct screen on bottom bar click, so let's do that as well.

So here we add the single instance of the navcontroller so that we can hook the bottom bar with the navigation. We passed this instance to both bottom bar and the nav host. Moving on to the bottom bar, you see that I have added the nav hosts controller as a parameter in the bottom bar composable as well.


Now we'll be using this nav controller to handle all the bottom bar item clicks. Now let's get the backsack and also the current route of the nav controller.

So here I have added the backs stack entry. It is no secret that the jet pack navigation maintains a backs stack. That means it has a stack and all the new navigation gets added on top of that stack.


So, for example, I have a screen A, then I open screen B from A, so B will be added on top of a. This is called the back stack, which is based on the launch mode. Anyway, so the nav controller dot current entry as state returns the same BACKSACK entry as a state.





 So in here, according to the docs itself, when the given nav controller changes the back stack due to a nav controller dot navigate or nav controller dot pop backs stack, this will trigger a recompose and return the top entry on the backs stack. So that means we get the backs stack, which is currently at the top, or in most simple words, we get the screen, which is at the top.


Now we take the route name from this backs stack entry to get the current route. So here we are simply getting the route name from the current backstack.


Moving ahead to denote if the bottom nav item is selected or not. The add current route is equals to item screen do route. So if the unique name for that item is equals to the current route, then obviously it would be the selected route. Here in the bottom nav item, I have added a condition Check that if the current route is equal to the current items screen route, then it'll be marked as selected, otherwise not.


So if you notice at the start, the starting destination for the nav graph would be the home screen. So the current route will be the home screen route. Anyways. Now let's finally add the functionality to actually navigate to different screens on the on click method.


onClick = { 
    navController.navigate(item.screen_route) 
}

Now nav controller has a navigate method, which takes in the screen route and navigates to the respective screens on the right. You'll see exactly that happening.


We have multiple navigation options with the Navigate method as well. For example, the Navigate Method will run a new instance of the screen every time we navigate to the respective.


So we had a mode for this in XML as well called Launch Single Top Mode. Similarly, we can pass the single top option and the navigation options as well.

navController.navigate(item.screen_route) { 
    launchSingleTop = true 
}

With that added now, only single instance of the screen will be launched once we navigate to the respective screen. Along with the launch mode. We also have the popup to method, which pops up to the destination to avoid large stacks.


Let's add that first.

Here. What we are doing is basically that we found the starting destination of the graph. In our case, that would be the home screen, so it gets the start destination. And if that is not nu, it sets the popup two method for the start destination. In simple words, this would mean that pressing the back arrow button from any destination would pop the entire backsack to home.


We also have set the save state to true. That means that when popping up or going back, it would actually store the save state. Along with that, we have something called as Restore State, which is another navigation option.


 Now this attribute determines whether this navigation action should restore any state previously saved by save state or the popup to save state attribute.


 So as expected, everything is working fine now. So now let's take a look at how we would have multiple nav graphs inside of the same project like we used to have in xml. In xml. If you remember, we could have multiple nav graphs such as a different nav graph for the auth module and a different nav graph for the main navigation of the project. So for that, let's create a detailed screen for the homepage and a language selection screen for the settings page.

So now I have created two more screens. One is the language screen, which simply contains column along with some text, and same is the detailed screen, which also contains a column along with some text. Also, notice that I have changed the home screen and added a button at the center.

Similarly, I have also changed the setting screen to add a button at the center. If I go ahead and show you the app, you'll see that on the home screen you'll find a go-to detail screen button, and on the setting screen you'll find a go-to language screen button. Now obviously on clicking, nothing will happen on these buttons because we haven't hooked up these screens with the navigation.


 Right. So with all the screens made, let's now create different nav graphs for the home screen and also the setting screen for the home screen. I have created a package navigation in which I will be having the nav graph for the home screen.

So as you can see that I am having this graph and I want the home screen to be shown here, I would want to shift the home screen to this nav graph because now our main navigation controller will use the entire nav graph to navigate. So in this nav graph, since we want the home screen to be the starting screen, that's why we shift the home screen in this nav graph itself.


 Similarly for the setting screen, let's create the setting screen's item as well.

 So we have created yet another item for the setting screens item. I have done nothing different from what I did in the home screen nav graph. Now let's create the nav graph for both of them, starting with the home screen.

Similar to before we create a new nav graph named Home Nav. It is an extension function to the nav graft builder, which contains the detailed screen, the navigation method takes in the start destination and then add the different screens as before. In our case, we want the start destination to be the home screen itself.


Similar to this, let's also create the setting nav graph as.

 Here we are. The language. Now that we have the nav graph for the screens ready, we need to inform the main nav host that home and setting screens have a nested navigation, so it needs to be aware of that.

So to make the main nav hosts aware. Now, instead of referencing the screens, we are now referencing the entire nav graph for those screens in the main navigation component. Along with this, we also want to change the bottom navigation to reference the graphs inside of the screen. So let's change that.

So here you can see that I have changed the screen routes to reference the respective graphs instead of the screens themselves. And for the screen routes, I have used the starting screen as the route names for them. That is done because on click off the bottom bar, we navigate to the respective screen. In the main nav controller, we have the nav graphs. Now upon clicking on the

home button, the navigation controller navigates to the home screen, which is the starting screen of the home nav graft. So the nav controller loads the home nav graph onto the nav host


 so now what I have done is that I have basically passed on the nav controller to the home and the settings nav graph. In the home nav graph, I have also passed down the nav controller to the home and the detailed screen. Similarly, in the settings nav graph, I have passed down the nav host controller to the settings and the language screen.


I have done this because I want to navigate from the setting screen to the language screen, and in the home screen, I want to navigate from the home screen to the detailed screen. So if I go ahead in the setting screen, let's say. In the on click method of the button, I am simply navigating to the language screen.


In here I am passing the language as the screen route name for the Navs controller. And in the home screen, I am similarly passing in the detailed page, screen route name to the navigation controller. Now, if you run the app. We'll see that we are able to now go to the detailed screen, come back to the home screen, go to the favorite screen setting, screen language screen back and back to home, so everything is working as expected and we are able to navigate between the different graphs as well.

 

Hey guys,

just a quick pause here. Just want to tell you that if you're interested in learning about the advanced concepts of jetpack compose such as creating our own animations, gestures, views, and layouts, then do check out my course here.