sushioutlaw@unsplash

Exploring Jetpack Compose with Hilt and ViewModels

Jaewoong Eum
ProAndroidDev
5 min readSep 23, 2020

--

Anew UI toolkit Jetpack Compose is announced by Google. Jetpack Compose is Android’s new modern toolkit for building native UI based declarative component approaches. In contrast to imperative programming, declarative programming is a programming paradigm that expresses the logic of computation or drawing UIs without describing its control flow. The declarative programming approaches in mobile are already used widely like SwiftUI and Flutter. And here is a new one; Jetpack Compose.

So what are the best benefits of Jetpack Compose?

  • Code reusability (dramatically less code)
  • Declarative UI (lacks side effects, referentially transparent)
  • Consistency of native codes without using XML files
  • Unidirectional Data Flow (state flows down and events flow up)
  • Efficient theme resources managing (dark mode, colors, typography, etc)
  • Compatibility with previous Android SDK and other Jetpacks components

In this post, we will look around the basic structure of a sample Disney project that built with Jetpack Compose and other Jetpack components like Hilt and Room database.

DisneyCompose@GitHub

Theming

One of the best benefits of Jetpack Compose is that we can give our application a consistent look and construct many kinds of styles easily by applying themes. Jetpack Compose offers an implementation of Material Design and they are built on top of Material Theming, which is a systematically customize Material Design to better reflect your product’s brand. A Material Theme is composed of color, typography, and shape attributes. And we can apply them to our content based on our customizing options like a dark theme.

We can apply our customized theme to our screen by wrapping contents like below.

Navigating

The sample app DisneyCompose has two screen flows. One is the main screen that has three kinds of different forms and switchable between them via BottmNavigation. And the other one is the details screen of a specific poster. It means there are two flows that main screen to the details screen, and back to the main screen from the details screen. To go back to the main screen from the details screen, we have to know about where the details screen come from. So we need to implement a back stack. DisneyCompose maintains the navigation back stack in the memory for navigating screen flows.

And next, we should define destination models that the screens in the application and arguments they require. In DisneyCompose, we only need two destinations and two Actions. Navigating to the details screen from Main(Home) with a poster id argument, and go back to the main screen when users press back button. So wen can implement it like below.

As the navigator’s current stack changes, the destination screen will also be changed. And by using Crossfade, we can make transitions more softly for recomposing the screens. Crossfade allows switching between two layouts with a crossfade animation.

Dependency Injection with Hilt

Google announced another much powerful DI tool, Hilt. Dependency injection is one of the popular ways to implement loose coupling between business logic and UI related logic by transferring the obligation to create an object out of the class. So we can concentrate on only our business logic or UI logic, and by removing the dependencies between classes, we could write more testable codes. If you want to get more details about the Hilt, this post will be helpful to understand the basic concepts of the Hilt.

Here is a simple NetworkModule for providing network-related dependencies using Hilt.

And here is a simple MainViewModel that requests dependencies into constructor using ViewModelInject annotation. As you can see, there is not much difference between a normal project and a Jetpack Compose project in business codes, because the Jetpack Compose is a UI toolkit.

ViewModel in Jetpack Compose

So how we use the ViewModel in our Jetpack Compose project? Let’s see the MainActivity’s code.

The MainViewModel is injected by Hilt and DisneyMain receives the instance of viewModel. It means ViewModel follows the Activity’s lifecycle and data can be maintained continuously even when the screen rotates. So let’s look into how the ViewModel be used in composable functions.

Composable functions can get an instance of ViewModels as a parameter, and observe LiveDatas in ViewModel. As we can see, we can observe LiveData in ViewModel using observeAsState and converts it into a State<T> object so Compose can react to value changes.

observeAsState observes a LiveData and returns a State object that is updated whenever the LiveData changes. It will automatically stop observing when the composable is removed from composition.

Compose can observe the business data changes from ViewModel, and reacts. The above codes make CircularProgressIndicator visible when isLoading LiveData value is true and makes it invisible when the value is false.

Network and Room in Repository

DisneyCompose has repositories for requesting poster response from the network and persisting in a local database using Room. In the network requesting, Sandwich library is used for constructing lightweight HTTP API response and handling error responses. Sandwich provides a standardized ApiResponse model and some CallAdapterFactory for converting raw data to wrap an ApiResponse model. So we can handle the response data more easily and fastly by using the response model. And also, Flow used for requesting from a network and getting/inserting data from the local database in a worker thread and emit data to ViewModel asynchronously.

Loading Images from Network

Generally, for loading images from the network, we used Glide, Fresco, or Coil. But in Jetpack Compose, here is another recommendation, Landscapist. The Landscapist is a Compose library that requesting network images and displaying them. It supports two different options Glide and Fresco. It’s quite simple to use like below.

We can customize our request options using RequestOptions and TransitionOptions for applying caching strategies, loading transformations. Furthermore, we can create our own composable functions following requesting states; loading, success, and failure.

The below example is showing a circular progress indicator while loading an image and shows the image after complete successfully. If the requesting get an error, we can show our custom text.

As we can see below, it shows a custom text when getting an error.

Conclusion

In this post, we explored the simple Disney app used Jetpack Compose, especially focused on business relates. From the past to the present, many Android developers have been suffered from the inconsistency of code that separated between XML layouts and native logic and there are some troublesome. But Jetpack Compose resolves many kinds of those previous problems and has many benefits, it is a good next-generation Android/Multiplatform UI Toolkit. And it reaches alpha release, Android developers can get a taste ahead. Looking forward to the future stable release of Jetpack Compose.

You can check the sample repository in the below link.

--

--

Senior Android Developer Advocate @ Stream 🥑 • GDE for Android • OSS engineer. Love psychology, magic tricks, and writing poems. https://github.com/skydoves