Step #2: Emitting View States

Then, replace getItems() in RosterMotor with:

  val states = repo.items()
    .map { RosterViewState(it) }
    .stateIn(viewModelScope, SharingStarted.Eagerly, RosterViewState())

Flow has a map() operator for converting data between types. Here, we map() the list of ToDoModel objects into a RosterViewState.

We then use an stateIn() extension function. This converts a Flow into a StateFlow, ready to be consumed by our RosterListFragment.

You can learn more about StateFlow in the "Opting Into SharedFlow and StateFlow" chapter of Elements of Kotlin Coroutines!

A StateFlow is a flow of states. It holds onto a current state and gives that to any new observer once it starts observing. And, it emits new states to current observers if it is handed a new state.

stateIn() takes three parameters:

For the CoroutineScope, we use viewModelScope. viewModelScope is an extension function supplied by lifecycle-viewmodel-ktx, to give us a CoroutineScope associated with our ViewModel. The major feature of viewModelScope is that it is aware of the viewmodel’s lifecycle. When the viewmodel is cleared (after the user exits the fragment), any outstanding coroutines being run in the context of the viewModelScope get canceled.

We use a property (states), rather than a function. For the view-state pattern, it works best if you have a stable stream of states. That will make our viewmodels a bit more complicated in the future, but it means that our fragments are simpler: just subscribe to the one source of view-states and use them. In fact, we will do just that in the next step.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.