Jetpack Compose

Oct 11 2022 · Kotlin 1.7.10, Android 13, Android Studio Chipmunk

Part 3: Manage State with Compose

15. Use the MVVM Pattern

Episode complete

Play next episode

Next
About this episode

Leave a rating/review

See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 14. Add Animations to Compose Next episode: 16. Move Operations to ViewModels

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Notes: 15. Use the MVVM Pattern

The student materials have been reviewed and are updated as of September 2022.

Transcript: 15. Use the MVVM Pattern

Intro

[Slide 1 - Declarative patterns]

There have been other declarative frameworks similar to Compose, such as Flutter, React Native, Swift UI and much more.

And all of these frameworks always present the same challenge - how do you connect your declarative UI to the rest of the architecture you used in your app.

Good thing about Jetpack Compose is that you can connect the UI directly to observable state, such as LiveData, and have the UI update itself automatically!

[Slide 2 - LiveData]

Within Compose, there are special functions that let you turn LiveData constructs into State, allowing you to connect the state to the UI and update it whenever your database changes, or a new piece of information comes from the API.

[Slide 3 - UI Coupling 1/3 - Declarative UI]

It’s also important to know how to couple or decouple your UI from the rest of the application and architecture.

So the philosophy in Compose, when it comes to the Model-View-ViewModel pattern, is that your UI is fully declarative. It doesn’t know much about the rest of the architecture, other than it has to communicate user actions to the ViewModel.

[Slide 4 - UI Coupling 2/3 - ViewModel communication]

Then when the ViewModel receives those actions, it knows that it needs to either fetch some data, or change the state and in doing so, it updates the state, reflecting those changes to the UI.

[Slide 5 - UI Coupling 3/3 - Data Updates]

And any time you update the data from the Database for example, the database will emit new state, reactively updating the UI.

This is great because you don’t have to think about writing extra logic to change each specific part of the UI or update just one View. Compose does it for you!

Let’s see how to implement the MVVM pattern, and slowly start migrating the logic away from the UI.

[Slide 6 - Three step process]

Now before you dive into code, you need to understand that the MVVM pattern implementation is a three-step process.

You first have to build all the ViewModels, and set up the state you want to handle within them.

Then you need to move all the operations to ViewModels and change how they update the state from within.

And finally, you need to connect the state to Compose UI, to show the data, and react to data changes.

You’ll do all this over the course of the next three episodes, with some pre-built code in each of the episodes.

Let’s get to it! :]

Demo

Most of the ViewModels are already predefined for you, but there’s one that you’ll build yourself. Create a new file called BookReviewDetailsViewModel, and add the following code:

@HiltViewModel
class BookReviewDetailsViewModel @Inject constructor(
  private val repository: LibrarianRepository
) : ViewModel() {

}

Each ViewModel will communicate with the repository, which is why you added it to the constructor parameters.

You also used the @Inject and @HiltViewModel annotations to inject the dependency through Hilt, a new DI library for Android! That’s all less important now, as your focus is to set up the state that will update the UI as you use the app.

Add the following code, to represent the state:

private val _bookReviewDetailsState = MutableLiveData<BookReview>()
val bookReviewDetailsState: LiveData<BookReview> = _bookReviewDetailsState

private val _genreState = MutableLiveData<Genre>()
val genreState: LiveData<Genre> = _genreState

private val _deleteEntryState = MutableLiveData<ReadingEntry>()
val deleteEntryState: LiveData<ReadingEntry> = _deleteEntryState

private val _isShowingAddEntryState = MutableLiveData<Boolean>(false)
val isShowingAddEntryState: LiveData<Boolean> = _isShowingAddEntryState

private val _screenAnimationState = MutableLiveData<BookReviewDetailsScreenState>()
val screenAnimationState: LiveData<BookReviewDetailsScreenState> = _screenAnimationState

To represent the state within the ViewModel, you’re using a common pattern of a backing mutable state and an exposed immutable state.

The properties with an underscore prefix are mutable, and you’ll change them from within the ViewModel. And the non-underscore properties are used to expose the data to the UI, and are immutable.

Notice how you’re using LiveData again, instead of the State type from Compose. This is because there’s a super cool way to turn LiveData into State, using helper functions. But more on that later!

Now add the following code to add a way to set the BookReview:

fun setReview(bookReview: BookReview) {
  _bookReviewDetailsState.value = bookReview

  viewModelScope.launch {
    _genreState.value = repository.getGenreById(bookReview.book.genreId)
  }
}

Whenever you set the BookReview, you update the state, and that will in turn update the UI, once you connect the LiveData in the next few episodes. And finally, you also have to fetch the Genre by ID, to show that on the UI.

One last thing before you start moving the operations to the ViewModels. Open the BookReviewDetailsActivity, and add the following code to prepare the ViewModel dependency for the activity:

private val bookReviewDetailsViewModel by viewModels<BookReviewDetailsViewModel>()

This will let Hilt inject the ViewModel and let you use it for state.

Now build & Run the app, just to check that everything works correctly.

[Build & Run]

This is the first step out of the three you need to do. In the next episode, you’ll move all the data operations to the ViewModel, and then in the third episode, you’ll connect the state to the UI.

See you there! :]