Wednesday, May 11, 2022

 

Android Data Binding With MVVM: Using StateFlow and ViewModel

A guide to building MVVM architecture-based apps with Data Binding

View of Earth from space
Photo by NASA on Unsplash.

This is the third part of a series in which you’ll learn about Data Binding basics and how to use Data Binding with architecture components like LiveData and ViewModel. Today, you’ll learn how to use Kotlin coroutines and StateFlow to update the UI via Data Binding from a ViewModel.

Introduction

StateFlow

The Flow API in coroutines is a better way to handle a stream of data. StateFlow attempts to bring the powerful features of Flow to manage a state in your Android application.

As explained on GitHubStateFlow comes in two variants: StateFlow and MutabaleStateFlow.

“The StateFlow<T> interface is a read-only view that gives access to the current value and implements a Flow<T> to collect updates to the values.

The MutabaleStateFlow<T> interface adds a value-modification operation.”

Data Binding

Data Binding is the framework that acts as a bridge between layouts and data sources by reducing the boilerplate code that developers need to write. This will increase the productivity of the developers by providing them the time to concentrate on what matters.

StateFlow Support in Data Binding

If you read the second part of this series, we used LiveData as a data source to update the UI in the layout. Using LiveData makes the Data Binding lifecycle-aware and also updates the UI only when the screen is in the foreground.

The goal of this article is to replace the usage of LiveData with StateFlow. There was a recent announcement by the Kotlin team that we can now use StateFlow as the data source for Data Binding. To do this, you should install Android Studio Arctic Fox Canary 4 (2020.3.1.4), which is now available in the Canary and Dev channels.

Before going any further, let’s check the drawbacks:

  1. Unlike LiveDataStateFlow is new.
  2. LiveData is a part of the Jetpack libraries, so it can be used in Java code. StateFlow is part of coroutines, which can only be used in Kotlin.

Apart from this, I don’t see any potential drawbacks to using StateFlow. But I should say that StateFlow is going to change how we manage state in our architecture, as we tend to use coroutines very deeply.

With that being said, let’s get started.

Implementation

Let’s continue with the example we’ve used in previous parts of this series. In part 1, we directly imported UserModel in the Activity as a data source to the binding. In the second part, we created a ViewModel and accessed the UserModel that is wrapped by LiveData from the ViewModel instance.

Quick recap

Data class:

data class UserModel (var name: String ="" ,
var email: String = "")

ViewModel with LiveData:

class UserViewModel : ViewModel() {

var userModel = MutableLiveData<UserModel>()

}

Finally, using the LiveData as the data source to Data Binding in the XML layout:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewmodel"
type="com.sample.viewmodels.UserViewModel" />
</data>
<LinearLayout>
<TextView
android:text="@{viewmodel.userModel.email}" />
<EditText
android:text="@{viewmodel.userModel.name}" />
</LinearLayout>
</layout>
Replacing the LiveData with StateFlow
Much like LiveDataStateFlow fully supports Data Binding out of the box. Personally, I think it’s kind of a step forward regarding Kotlin’s influence in Android development. Data Binding is a Jetpack library from the Android family, whereas StateFlow is from Kotlin coroutines. It’s awesome to see how two different entities from different environments come together.
I hope StateFlow’s support of Data Binding won’t be like Kotlin synthetics, which were deprecated recently.
Thanks to the Kotlin team, we only need to update the LiveData in the ViewModel to StateFlow objects. The rest will be taken care of by Data Binding and StateFlow under the hood.
First, we need to create a private mutable StateFlow instance with an initial value. Then, we assign it to another StateFlow instance that we use in the XML layout to update the UI:
// Bfeore
class UserViewModel : ViewModel() {
var userModel = MutableLiveData<UserModel>()
}
// After
class UserViewModel : ViewModel() {
private val _userModel = MutableStateFlow(UserModel())
val userModel : StateFlow<UserModel> = _userModel
}
That’s all. We don’t need to do any changes in our view or XML layout. What happens internally is that it’ll use StateFlow with the given context in our Android component by replacing the LiveData. One of the most important things is asking the lifecycle owner to bind the object while using LiveData or StateFlow:

class UserActivity : AppCompatActivity() {
lateinit var binding: ActivityUserBinding
lateinit var userViewModel: UserViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_user)
binding.lifecycleOwner = this
userViewModel = UserViewModel()
binding.setVariable(BR.viewmodel, userViewModel)
}
}
Now you have an app that works with ViewModel, StateFlow, and Data Binding.In the first part of this series, we learned how to update UI with observable fields. In the second part, we removed all the boilerplate with LiveData and Data Binding. Today, we used one of the Kotlin core APIs to replace LiveData in order to improve the code with coroutines.

No comments:

Post a Comment