Wednesday, May 11, 2022

 

StateFlow with One- and TwoWay-DataBinding on Android

Android MVI with Kotlin Coroutines & Flow — QuickBird Studios Blog

There has been a lot of talk in the Android community related to LiveData being deprecated in favor of StateFlow. This doesn’t seem to be the case but with the canary release of Android Studio Arctic Fox | 2020.3.1 (before the naming change it would have been 4.3) the last piece of moving away from LiveData is complete — DataBinding is enabled for StateFlow.

Let’s check out how this works exactly.

From the initial glance the DataBinding works with StateFlow with minimum changes.

As the release notes state — For Kotlin apps that use coroutines, you can now use StateFlow objects as a data binding source to automatically notify the UI about changes in the data. Your data bindings will be lifecycle aware and will only be triggered when the UI is visible on the screen.

To use a StateFlow object with your binding class, you need to specify a lifecycle owner to define the scope of the StateFlow object, and in your layout, assign the properties and methods of your ViewModel component to the corresponding views using binding expressions, as shown in the following basic example:

class ViewModel() {
val username: StateFlow<String>
}
//code in xml
<TextView
android:id="@+id/name"
android:text="@{viewmodel.username}" />

This looks exactly as we would use LiveData before — so far so good! 😃

If you’re in a Kotlin app that uses AndroidX, StateFlow support is automatically included in the functionality of data binding, including the coroutines dependencies. As described in Bind layout views to Architecture Components, data binding works seamlessly with ViewModel objects.

Now as we have grasped the gist of it, let’s jump into a custom example.

Here is a sample app I have created to test out One- and TwoWay- Binding with StateFlow. The app is loading friends’ names from the data source and also uses TwoWayBinding to accept user’s input and append it to the names.

Demo of OneWayBinding to TextView and TwoWayBinding to EditText

The first step is to create the layout file that will have StateFlow field bound to TextView with OneWayBinding and another instance of StateFlow bound to TextInputEditText with TwoWayBinding .


The key lines that provide the binding are android:text=”@{viewModel.names}” and android:text=”@={viewModel.addedName}”. I am also leveraging binding to work with visibility of the TextView and bind the onClick action for the Button.
While continuing working with the View layer let’s create a Fragment. The key components to make our app work with binding are creating the binding object and assignig lifecycleOwner as you would do while using 

LiveData.

//in Fragment's onCreateView
_binding = FragmentFriendsNamesBinding.inflate(inflater, container, false)
_binding!!.lifecycleOwner = viewLifecycleOwner
_binding!!.viewModel = viewModel
return binding.root

Now when the View layer is handled let’s move to the ViewModel implementation.

The ViewModel class will look as following:

One thing to note MutableStateFlow and StateFlow require initial value. We can work around it but that does seem inconvenient in a way. For example, I had to introduce an if condition if (addedName.isNullOrEmpty()) in order not to append initial null value.

Let’s look closer at what is going on in loadFriendsNames .

As we are using datasource to provide some saved names we are querying repo’s repo.loadFriendsNames() which looks like this:

class MainRepository {

fun loadFriendsNames(): Flow<String> = flow {
emit(ApiProvider().getNamesFromRemote().joinToString())
}
}

To achieve a desired effect of combining user’s input and the data received I am using a function combine which provides a way to join values of 2 flows.

Finally, to update UI the line _names.value = it is the key.

And that’s it, the binding works as expected.😎

For some final thoughts, I was impressed how effortless it was to migrate to StateFlow with DataBinding. I can definitively say so far so good. I will for sure consider using StateFlow in my future projects to maintain this nice eco system with Kotlin coroutines. The absence of DataBinding support was a deal breaker for me in the past.

No comments:

Post a Comment