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 currentvalue
and 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
,StateFlow
is new. 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>
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