How to handle Navigation in Jetpack Compose

Gastón Saillén
Google Developer Experts
7 min readSep 22, 2020

Since I started using Jetpack Compose I fell in love with it, there are so much new ways to create interactive UI’s with just a few lines of code, no need of boilerplate, NullPointers when binding views, and anything like we use to deal before.

After spending some time understanding the whole concept of compose to create declarative UI’s I started questioning myself how to handle navigation with it, then I just deg into reading the example apps and understanding how it's done, and in this post, I gonna show you how to use navigation before an official approach comes up.

All the code we are gonna use is extracted from the official jetpack compose examples, more on jetSnack and Owl app, since the Navigator they provide handles multiple backstacks.

How we used to handle navigation

Before Jetpack Compose we used to handle Navigation with Navigation Components library, Simple Stack library, intents, or with the FragmentManager itself.

Navigation with jetpack navigation looks like this

This is how we actually do navigation with multiple destinations, the concept is one Activity and multiple fragments, the navigation components library handles all the backstack logic of poping fragments and pushing them into the stack, this is a really useful and easy API to handle navigation nowadays, but with the arrival of Jetpack Compose things became a little different.

What about Fragments?

The concept of Fragments in Jetpack Compose is described here, basically, in Fragments we don't have the extension setContent { } that we usually have in our Activity component when we create a fresh Jetpack Compose example, instead, we need to add to our fragment a ComposeView that is used to bundle the setContent { } into the Fragment and start working into it.

After this, we can start writing our @Composable functions inside our Fragments, but take in mind that if we write Composables inside our Fragments we are gonna need to also handle the Fragment lifecycle with our Composables, this will make it hard for us to be less prone to problems.

What about Jetpack Compose?

In Jetpack Compose, composables does not rely on a lifecycle (they only live untill the composition ends, hence, any observers that are attached to these composables also die), and this is great because we only host the composables inside our MainActivity and we only care about writing our views.

Quick note

The importance of a clean way to write composables is really important since what we want to accomplish with Composable functions is to remove the state of many composables that we can (state hoisting), doing this we can just have in the Composable tree just a few or one composable that depends on the state, making the rest of the Composables stateless, this is great to avoid any problems when the composables need to recompose.

When we talk about Navigation in Jetpack Compose, we don't talk about Fragments or Activities, we talk about poping and pushing Views into the current Screen, and this is what we are going to do next.

Without any fragments and just 1 Activity we are gonna build a 2 screen app that handles Navigation, a backstack and no Fragments.

Important

Since all of these examples provide now are running on the 1.0.0-alpha03 version of Jetpack Compose, this could change in favor of a new compose version, also, take in mind that the Jetpack Navigation team will work an easy to use API in the future to integrate it with Jetpack Compose, making all the navigation simpler than what we are going to see right now.

The example provide below takes the Navigation code from the official jetpack compose example repo.

How to handle Navigation

Let's start coding our app, this app is a Poll app that I’m currently working on in my public repo, you can check it here

The app is called JetPoll and is an interactive Poll app where users can create polls and others can vote them up, it uses Firebase to store the polls and register all the interactions, currently, this app is WIP, so any changes to the app will be updated on the repo.

What we are gonna do is to add Navigation to the Create poll button to navigate to another screen that will let the user Create a Poll

Since this article will not cover how to create composables but how to handle navigation, I’m gonna talk about Navigation itself instead of how to create this UI.

Define a Navigator

First of all, what we need to do is to define our Navigator class, this class will be the one that will handle the backstack of our current navigation and will

The Navigator that we are gonna use can be found on the OWL example of the official Jetpack Compose example repo.

First of all this Navigator takes 2 arguments, first one is the Initial destination, we are gonna call this destination the Home destination, is the first screen of our app, and then it takes a backDispatcher, this one is in charge of handling the back button press and handle the poping of the views of the backstack.

With all that said, there is no much more about this class, what it does is it takes navigations and add them to a backstack with the navigate() method, then it pops off the backstack on backButton press or calling the back() method.

After we define this, what we need to do is to create a NavGraph, something similar as we use to do with Jetpack Navigation but without XML.

NavGraph

The NavGraph is responsible of knowing where we want to go, in this example, we just define a sealed class named Destination, this class will contain our current Destinations, in this case, I have 2, the Home destination and the CreatePoll destination (the one I need to go after pressing the Create poll button on the home screen)

After that, there is a Actions class which takes a Navigator initialized with the Destinations that we wrote before, this Actions class will be responsible of defining the actions we will do to navigate to a certain screen in our app.

This is actually 80% of the work done, what we need to add next is one more thing that will help us measure the insets of the screen to populate with the new composable that will be called as the other screen.

DisplayInsets

As the Documentation it provides

Applies any [WindowInsetsCompat] values to [InsetsAmbient], which are then available * within [content]. *@param consumeWindowInsets Whether to consume any [WindowInsetsCompat]s which are dispatched to * the host view. Defaults to `true`.

There is no much to say about this class, it will only measure the current composables that we are navigating and will take only composables in the ProvideDisplayInsets Composable

Let's create the Navigation

Now that we already have all the classes we need for navigation and defining the destinations in the NavGraph file we are ready to start implementing it into our current code

First of all, in our MainActivity we will need to launch our Composable with the on onBackPressedDispatcher which will know when we do a backPress, passing this dispatcher lets us handle the backPress button and apply logic into it.

MainActivity

Now that we already passed the onBackPressedDispatcher , now we need to create the Navigation into our PollMain composable.

PollMain

This is the most important step, here we will define our Navigator, as you can see the first line of code defines the Navigator with the Destinations we provided on the NavGraph, then we use the rememberSavedInstanceState, and this is really great because it survives process death !!

Then the Navigator defines the saver, and here is what I was talking about before, we pass the onBackPressedDispatcher that will be configured to handle poping of the backstack.

Now, we need to initialize our Navigator, we will initialize it with the Home destination, so the first screen that pops up is the Home screen

We then create an action variable that will remember our actions (current navigated screen) and will be responsible to push our new destinations into our screen.

Lastly, we provide the BackStackDispatcherAmbient to handle the backstack and then the ProvideDisplayInsets we talked about before, this function just takes Composables as it child.

Here, CrossFade will just play a nicer CrossFade animation on the navigator.current which represents the new navigations that we are going

And lastly, we already have our destinations !! we use a when() expression to inflate one or the other, and this inflation will be caused with the action we created before, now, for example, my Home screen has the Create button, what I just need to do is that when I press that button to execute the actions.createPoll that we defined in our NavGraph, that's all.

PollHome

Lastly, we just propagate the click to the CreatePollComponent which holds the Create button that we need to handle interaction with

CreatePollComponent

And thats all ! , when we press Create we will navigate to our CreatePollScreen(), which only shows a Text for now

CreatePollScreen

Output

All of this code can be found in my repository, I will keep it up to date, also you can find a lot of Jetpack Compose example components there, the repo is here

--

--