ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Follow publication

Creating Stateful Architecture with Android Jetpack’s Navigation Component

Amanda Hinchman
ProAndroidDev
Published in
8 min readMay 5, 2020

Space Coast, Florida has one of the world’s most unique environments in the world. The St. John’s River is one of 3 rivers in the world to flow north, and for Brevard County, the Indian River and the Banana River are estuaries, which are warm part-salt/part-fresh water bodies, giving home to unique denizens like bottlenose dolphins, pink shrimp, horseshoe crab, manatees, comb jellies, and more. Trying to keep the unique ecosystems healthy in Brevard County is a challenging responsibility to hold from giving fire to trees to preserve scrub jays, netting and protecting turtle nests, and enforcing manatee wake zones.

Like many unique systems, it also struggles with invasive species like the Brazilian pepper tree, or Schinus terebinthifolius. Brazilian pepper trees are widespread and known to be invasive for many subtropical regions with moderate-to-high rainfall.

Let’s create an application a user can easily navigate different fragments within the same activity using a navigation drawer for the user to easily switch between features.

This application will contain two features:

  • Brazilian Pepper Tree Spotter (Part 2) — If an app user spots a pepper tree, they can upload the photo to a database and tag the tree’s location.
  • Invasive Species Map (Part 3) — We can pull up a map of pepper trees spotted in Brevard county. This requires Google maps and a basic firebase setup to save some location points. We’ll render an error page for now.
  • Connecting the Dots (Part 4) — Clicking on a spot in Google maps should render information of the picture and where the image was captured

I couldn’t wait to get my hands dirty with more modern Android components and libraries, one of them being Navigation Component!

There’s quite a bit to unpack in this post, so for convenience, I’ve added the GitHub repo to follow along with! https://github.com/ahinchman1/Brazilian-PepperTree-Tracker

Resources:

What is the Navigation Component?

Android Jetpack’s Navigation component helps developers to implement navigation, or the way an Android application moves from one screen to the next. Some benefits of using this feature includes:

  • Hooking up navigation behavior seamlessly with Android UI components
  • Navigation components handling the backstack
  • Navigation components handling fragment transactions

Additionally, using Navigation is kind of a freebie in the sense that we are receiving the standard of navigation according to the Android set of principles by ensuring consistency across the board for user experience.

A Brief History of Navigating Fragments

We can take advantage of the SingleActivityPattern by using the activity to manage communication between multiple fragments, meaning we would be responsible for handling SupportFragmentTransactions, which is responsible for handling all fragments within the stack.

Brazilian Pepper Tree Tracker App

We’ll be demonstrating how one might marry a very simple ViewModel with the Navigation UI to create a more stateful navigation within the application.

build.gradle setup

You will need both the Android Jetpack Navigation components as well as the Android Lifecycle components needed to handle ViewModels as well as the coroutines needed to run threading. There are also some helper libraries I like using an any Android application — Jake Wharton’s Timber for logging and Coil-kt for loading images with no fuss.

ViewModels require your android app to operate with inline JVM target bytecode at 1.8. You will need to have this specified in your build.gradle (app):

android { 
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
}

The SingleFragmentActivity Pattern

The SingleFragmentActivity pattern, coined famously by the Big Nerd Ranch Guide, is a pattern that involves a Fragment living as a singleton sitting on top of a base Activity. Using the SingleFragmentActivity pattern allows you to take advantage of Fragment runtime composability, modularity, and allows for easily passing data and actions within the context of a shared base Activity.

The BaseActivity would be responsible for managing navigation

We’re going to use the SingleFragmentActivity pattern so that the actions of navigation can live in the main activity and all the menu options located in the drawer will be hooked in with a navigation graph where every fragment is inflated through this pattern. We would normally have to manage fragment transactions within the SingleFragmentActivity but with the navigation component, we don’t need much for set up:

abstract class SingleFragmentActivity : AppCompatActivity()  {

abstract val layoutResId: Int

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(layoutResId)
}
}

We’ll also a couple seconds to set up the MainActivity along with the first inflated fragment:

class MainActivity : SingleFragmentActivity() {

override val layoutResId: Int
@LayoutRes
get() = R.layout.activity_main
}

Now that we have a base, let’s set up our Navigation component and ViewModels for our MainActivity and the respective fragments found in our navigation graph.

Setting up Navigation UI

The navigation graph defines what is possible for a fragment to be able to navigate to, otherwise known as destinations, as well as possible paths your user can take through the app and rests in the /navigation package of your resources.

Actions within a fragment represents all the logical connections that users can take from one destination to another.

Viewing our navigation graph should give us some hints about what’s to come:

  • We will need to set up some fragments ErrorFragment and InvasiveSpeciesFragment.
  • Every fragment has the ability to navigate to ErrorFragment, and the ErrorFragment also has the ability to navigate back to InvasiveSpeciesFragment.

Normally, a FrameLayout would be responsible to holding these singleton fragments, but we’re going to start with setting up the Navigation UI components available where the Navigation View exists within DrawerLayout and inflate those singleton fragments in a FragmentContainerView, which will act as a container for the aforementioned fragments.

The neat thing about the FragmentContainerView is that it happens to be an extension FrameLayout, but executes fragment transactions for you, which is why it is not necessary to handle the fragment transactions in the SingleFragmentActivity.

It is notable to mention that this is not the only way to use navigation components in Android! You can also attach your navigation components to Toolbars, Bottom Navigation, and other variations of Action Bars. See Android docs for more details here.

To tie destinations to these menu-driven UI components, we will need to define the navigation menu in the /menu package of your resources:

While the Navigation component manages many things for you, it is also nice to possess the ability to create special configurations suited for the purposes of your application. We will need to set up the relevant configurations needed to be able to control our drawer menu with the navigation graph we’ve set up:

You may have noticed that we haven’t actually done anything to call for navigation to fragment components. This is where we put out a neat hat trick to create a Navigator. The benefit of creating a wrapper for calls for navigation in a Navigator class is the ability to make the UI logic easily testable (although that won’t be covered in this post).

Okay, we’ve created two additional classes meant to encapsulate the navigation behavior —we’ll start to see the magic in the next section when we start setting up our ViewModels.

Setting up the ViewModels

ViewModels and LiveData are very handy to ensure you always render the most fresh data even after a configuration change. Combined with viewModelScope and coroutines they ensure automatic cancelation of your background tasks when required following lifecycle changes, all these things would be items we’d need to code by ourselves otherwise.

To start our ViewModel setup, we will need to add the following to our MainActivity in order to set up our ViewModels:

Here, this file contains both the ViewState and the ViewModel, which possesses the business logic of the application. There’s isn’t too much that’s going on here other than the observation that on its own, the ViewModel flows from the Loading ViewState to the Content ViewState.

However, there is something that is kind of interesting — _viewState and viewState. Why have a version of the ViewState that is based on MutableLiveData and the other as LiveData?

This is not an original idea — using a mutable and a non-mutable data values can be seen in other places that uses data-binding like Vue.js or TornadoFX. Working with a copy of the data that is mutable means that you can avoid manual rebinding of your data as it changes and tight-coupling (having to extract the object data again just to be able to reflect changes). Furthermore, the ViewModel keeps the current state safe when the UI has been killed, like in the case of rotating an Android screen.

Hooking up Fragments to the Main Activity

Each fragment will be relatively similar since we won’t be setting up the actual features in this post — but we’ll start with just setting up the barebones Invasive Species map feature.

Each fragment will also have a very similar setup in their fragment component as well:

That way we hide and render elements between the loading state and the loaded state is very similar to the way that it is handled in the MainActivity itself!

Using ViewStates to Navigate to Errors

Setting up a map for Android is work that goes outside of the scope of this blog post — for this reason, we’re just going to default to an error state for the time being. Using our injected navigator, we can call on the behavior to navigate to the Error screen.

viewModelScope is a lovely concept where the scope of the ViewModel is tied with the lifecycle of the ViewModel. Unlike using regular coroutines, there are no worries attempting to subscribe/cancel a job in the event that the activity is killed and we need to handle garbage collection ourselves.

This article on Easy Coroutines in Android: viewModelScopes written by Manuel Vivo provides a pretty in-depth conversation about the use of coroutines and the lifecycle of the scopes, but the demonstration here is to show how easy it is to hop in-and-out of threads.

The ErrorFragment class doesn’t have anything special going on in the ViewModel, but what is more notable is how we use the navigator to navigate back to screens based on what the previous destination id was passed in to the fragment.

The result when we first render our application shows an error screen. Had we filled the content instead, we’d be seeing the textview indicated in our xml for the fragment.

Don’t worry, we’re sure to come back to filling in those fragment features in following blog posts. Android is a lot of work, so it’s always good to take breaks after a marathon of code! Stay tuned!

More to come:

  • Brazilian Pepper Tree Spotter (Part 2): Hooking our Android app to record locations and being able to add pictures.
  • Invasive Species Map (Part 3): Plotting locations on Google maps!
  • Connecting the Dots (Part 4) — Clicking on a spot in Google maps should render information of the picture and where the image was captured

Published in ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Written by Amanda Hinchman

Kotlin GDE, Android engineer & O'Reilly book author | Support my research on Patreon: patreon.com/AmandaHinchman

Responses (1)

Write a response