2 min readNov 5, 2021
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>