Android Data Binding With MVVM: Using StateFlow and ViewModel
A guide to building MVVM architecture-based apps with Data Binding
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 GitHub, StateFlow comes in two variants: StateFlow and MutabaleStateFlow.
“The
StateFlow<T>interface is a read-only view that gives access to the currentvalueand implements aFlow<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:
- Unlike
LiveData,StateFlowis new. LiveDatais a part of the Jetpack libraries, so it can be used in Java code.StateFlowis 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>
Much like
LiveData, StateFlow 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 | |
| } |
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) | |
| } | |
| } |
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