Android JetPack: Navigation Architecture Component
Less boilerplate and easier control over your app navigation.
Last year, Google introduced Android Architecture Components, a set of components that aimed to reduce boilerplate code and accelerate development. Google IO 2018 added new components and mixed them with the existing support libraries to create what they now call Android Jetpack — a set of categorised unbundled libraries that can be added according to your application’s needs.
Navigation Architecture Component was introduced with Android Jetpack to facilitate the implementation of the navigation within your app and simultaneously enforce guidelines that will provide a consistent and predictable navigation to the end users. What are those guidelines, you ask?
- Aside from one-time setups, an app should have a fixed starting screen when started from the launcher.
- Android navigation stack should follow a LIFO structure. With the starting screen at bottom of the stack and the current on top.
- The up button should never exit your app.
- With the exception of the bottom element of the stack, the up button should function identically to the system back button.
- The navigation stack should be consistent even if it’s triggered from an outside source. This is a common mistake when using deep links to app content, where the system back button and up button have distinct destinations.
That seems promising… Tell me more!
First and foremost, we need to add all the required dependencies. Also, it’s worth mentioning that to use the Navigation Component the Android Studio 3.2 Canary 14 is required.
We will also be using Safeargs — a Navigation Component plugin created to address data exchange between transitions in a type-safe manner.
This component handles navigation as a graph, where each node represents a screen. These nodes are called destinations and are bounded by each other through actions. The set of destinations and actions compose the navigation graph.
Let’s start by the creating of our navigation graph. So, on your res directory go to New > Android resource file and select Navigation from the resource type list. On the newly created navigation directory, we can start writing our navigation graph.
So, we created our navigation graph but we aren’t hosting it anywhere. A typical use case would be to host it on your root resource.
android:name: Attribute that flags the fragment as a NavHost — A container for the navigation graph that allows destinations to be swapped as the user navigates through the app.
app:navGraph: Connects the NavHostFragment to the navigation graph resource.
app:defaultNavHost: When set to true, NavHostFragment is capable of intercepting the system’s back button presses.
Now you should be able to hop to the design editor of your navigation graph resource and see which resource holds your NavFragmentHost.
Add destinations to your navigation graph
Let’s start simple and add two nodes to our graph — A and B.
From the snippet above, it is worth mentioning the app:startDestination which defines the entry point of the navigation graph. In this case means, fragment A is the first destination of navigation stack.
Now that we have two nodes on our graph, we can start defining our navigation flow. This is done through actions. I already talked about these, remember? Actions are the Android Navigation Component equivalent to a graph edge, defined to describe the intent to navigate between destinations.
So we have established the connection with a destination, the next move is to actually 🔥 that transition, right? Now, each NavHost has a controller — NavController — that is capable of swapping destinations with one of these two methods:
- navigate(): Pushes the specified action destination to the stack.
- navigateUp(): Pops to the top destination on the stack. On this case, this method would trigger an app exit since fragment A is the last on the stack.
This wouldn’t be complete without a sweet transition, right? And the icing on the 🍰 is how easy it is to attach it to our action:
app:enterAnim: Defines the animation for fragment B while transitioning to the top of the stack.
app:popEnterAnim: Animation transition for fragment A when populating the top of the stack.
app:exitAnim: Defines the animation of fragment A transitioning to the bottom of the stack.
app:popExitAnim: Animation transition of fragment B leaving the stack.
Despite XML being my go-to, transitions can also be defined programmatically by using an instance of the NavOptions.Builder:
That was easy, right? Let’s see how did it turned out! 🚀
Let’s imagine that on our hypothetical app, there is a screen flow that is only accessible for premium users. Nested graphs come in handy when dealing with these type of situations, providing the capability to encapsulate destinations. The following b_graph example displayed below encapsulates destinations C and D allowing outside destinations to only trigger actions to the B graph entry point.
Pass data between destinations
Let’s hop again to our hypothetical app and suppose we now want to pass a specific flag from one destination to another 💭… You would probably use Bundle and, to be fair, it would be alright. Despite that, Navigation Component introduced Safe args — a type-safe mechanism to access arguments defined for destinations and actions. It’s useful to prevent the developer from sending a random bundle or accessing it with a wrong key.
First, we need to define the type of argument that we will be sending.
On origin fragment’s side, we need to access the action between the two destinations and attach the desired argument. Safe args generates a Directions class for each one of your destinations, enabling access to its actions and respective arguments.
To retrieve the information, there is also a generated class that has the defined arguments as variables.
Pretty simple, right? 😎
Navigation Component lives up to its expectations, offering a really clean solution to a long-lasting problem. I feel it is a really straightforward way to enforce correct guidelines within your app, decouple routing logic from views and reduce some redundant code in the process. There is still the deep link issue I would like to address but I can already say this contributes greatly to my android developer happiness. Big thumbs up! 👍
That’s it for this blog post! For a full reference checkout my repository: https://github.com/pedroseabra1091/Android-Navigation. I hope this is enough to leave you excited to try out this new component. If you have any suggestion feel free to leave a comment! 💬