StateFlow

what is Stateflow?

StateFlow is an interface, which contains read-only and always return the updated value.The update value is passed through implemented flow like MutableStateFlow.

StateFlow return only when the value is updated if same value mean it doesnt return

StateFlow is observed using collect inside corroutine scope like below

lifecycleScope.launch {
viewModel.countState.collect { value ->
binding.countTextView.text = "$value"
}
}

where countState is a StateFlow variable inside viewmodel

private val _countState = MutableStateFlow(0)
val countState: StateFlow<Int> = _countState

where collect is a suspend inline function so only we are calling inside coroutine scope

def coroutines_version = '1.3.7'def lifecycle_version = '2.2.0'// Coroutines
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
// ViewModel & Lifecycles
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"

Sample Example below

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.lifecycleScope
import com.example.kotlinstateflow.databinding.ActivityMainBinding
import com.example.kotlinstateflow.viewmodel.MainViewModel
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import org.koin.android.viewmodel.ext.android.viewModel
import timber.log.Timber

@ExperimentalCoroutinesApi
class MainActivity : AppCompatActivity() {

private lateinit var binding: ActivityMainBinding

private val viewModel: MainViewModel by viewModel()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

setCountObserver()
setButtons()
}

private fun setCountObserver() {
lifecycleScope.launch {
viewModel.countState.collect { value ->
binding.countTextView.text = "$value"
Timber.d("Collect StateFlow value: $value")
}
}
}

private fun setButtons() {
binding.plusButton.setOnClickListener { viewModel.incrementCount() }
binding.minusButton.setOnClickListener { viewModel.decrementCount() }
}
}

ViewModel class

import androidx.lifecycle.ViewModel
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import timber.log.Timber

@ExperimentalCoroutinesApi
class MainViewModel : ViewModel() {

private val _countState = MutableStateFlow(0)
val countState: StateFlow<Int> = _countState






fun incrementCount() {
_countState.value++
Timber.d("Set StateFlow value: ${_countState.value}")
}

fun decrementCount() {
_countState.value--
Timber.d("Set StateFlow value: ${_countState.value}")
}
}

Followed by layout class

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.main.MainActivity">

<TextView
android:id="@+id/countTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:textSize="30sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="0" />

<Button
android:id="@+id/plusButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="4dp"
android:text="@string/plus"
app:layout_constraintEnd_toStartOf="@+id/minusButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/countTextView" />

<Button
android:id="@+id/minusButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="8dp"
android:text="@string/minus"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/plusButton"
app:layout_constraintTop_toBottomOf="@+id/countTextView" />

</androidx.constraintlayout.widget.ConstraintLayout>