Our Pain Point

We commonly implement a MVVM pattern for new screens. Generally, we create a ViewModel class and use Data Binding to bind our ViewModel data to the UI.

This has worked well for us.

Over time, we began to notice several common patterns in each screen:

show initial loading indicator

handle failure with a common error screen

provide a meaningful empty state in absence of data

display the final, loaded data

It became apparent that we could benefit from some standard mechanism with which to model and display these states.

Our Solution

Modeling UI States With Sealed Classes

Because we typically want to represent, and transition through, a small number of common states we turned to sealed classes.

We created the following class hierarchy:

gist available here: https://gist.github.com/n8ebel/93bac696a18127394c272095f450c2fb#file-uistates-kt

We can then transition through these states within our ViewModels’ lifecycle.

Exposing UI States

To expose this state to our Binding object, or any other necessary observers, we rely on an ObservableField .

Once our data has been loaded, or a failure has occurred, we transition states by setting the appropriate value to our bound Observable.

Depending on how our data flow is modeled, we may be able to handle these state transitions in a single place such as in the subscribe block of an rxjava observable chain, or we may have to handle multiple points of failure.

In most instances, we are able to handle these state transitions in a very limited set of locations and lean closely towards a single stream of data & transformations.

Binding UI States

Once the UI state is updated, we are ready update the actual view elements based on the exposed UiState .

To simplify and unify this process, we’ve created a small set of BindingAdapters to cover the majority of our common uses cases.

gist available here: https://gist.github.com/n8ebel/93bac696a18127394c272095f450c2fb#file-uistatebindingadapters-kt

With these BindingAdapters in place, connecting our individual View elements to our UiState becomes simple and consistent across screens.

For many views, it might simply require adding an adapter to the parent ViewGroup or perhaps adding the same BindingAdapter expression to multiple Views .