State Flow
StateFlowis a state-holder observable flow that emits the current and new state updates to its collectors.- A SharedFlow that represents a read-only state with a single updatable data value that emits updates to the value to its collectors.
- State flow is designed to be a more efficient and simplified version of Kotlin’s existing Flow library, with the key difference being that it represents a single value that can be updated over time, whereas Flow represents a sequence of values.
- State flow uses a similar syntax to Flow, but instead of emitting a stream of values, it exposes a single value that can be updated using the
valueproperty. Changes to this value are automatically propagated to all observers of the state flow. - A state flow is a hot flow because its active instance exists independently of the presence of collectors.
- A
StateFlowis always active and in memory, and it becomes eligible for garbage collection only when there are no other references to it from a garbage collection root. - A mutable state flow is created using
MutableStateFlow(value)constructor function with the initial value. - All methods of state flow are thread-safe and can be safely invoked from concurrent coroutines without external synchronization.
Let’s understand this with an example. Suppose we are fecthing users list from server and then showing that on ui.
Step 1: Create model class
data class User(val id: Int, val name: String, val email: String)
Step 2: Make api call in repo
class UserRepository {
suspend fun getUserById(userId: Int): User {
// make API call to fetch user data
val response = // make API call here
return response.toUser() // convert API response to User object
}
}
Step 3: Make this call from ViewModel
class UserViewModel(private val repository: UserRepository) : ViewModel() {
private val _user = MutableStateFlow<User?>(null)
val user: StateFlow<User?> = _user
fun getUserById(userId: Int) = viewModelScope.launch {
_user.value = repository.getUserById(userId)
}
}
Step 4: Observer into the activity
class UserFragment : Fragment() {
private val viewModel: UserViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val userId = arguments?.getInt("userId") ?: throw IllegalArgumentException("userId is required")
viewModel.getUserById(userId)
viewModel.user.onEach { user ->
userTextView.text = user?.name ?: "Loading..."
emailTextView.text = user?.email ?: ""
}.launchIn(viewLifecycleOwner.lifecycleScope)
}
}
In this example, the fragment obtains the user ID from its arguments and calls getUserById() on the view model to fetch the user data. The user state flow is then observed using the onEach() function, which updates the UI with the user's name and email when it becomes available. Note that launchIn() is used to launch the flow in the fragment's LifecycleScope, which ensures that the flow is cancelled when the fragment is destroyed.