All you need to know about Kotlin FLOW (Part 1)

In this series, I will explain Kotlin’s flow in the easiest way to understand

Ahmad Kazimi
3 min readApr 18, 2022

what you will learn in part 1:

1. What is Kotlin’s flow.
2. How you can use it.
3. How to observe value from it.

let’s begin :)

we have to start with a little bit of theory it’s important to understand flows in Kotlin.

if you are familiar with the term Reactive Programming is basically that.
if you are not let me explain that first.

Reactive Programming: It’s all about being notified about any changes in your code and then doing something with these changes.

In a real-world example where this would be applied
let’s say you are making an API request you get the response and then you want to map the response to something else or you want to show a list in your UI

1. What is Kotlin’s flow.

it’s a Kotlin coroutine that can emit multiple values over a period of time.

A single coroutine is a single suspended function that can only return a single value however if you want to do something like a count down timer that emits a value every single second you can’t do that with a normal coroutine so that is actually where Kotlin flow comes into play.

2. How you can use it.

let’s take our above example the count down timer.

class MainViewModel : ViewModel() {val countDownFlow = flow<Int> {val startValue = 10
var currentValue = startValue
emit(currentValue)while (currentValue > 0) {
delay(1000L)
currentValue--
emit(currentValue)
}
}
}

3. How to observe value from it.

simply you can start observing the values in any part of your code but mainly let’s try to collect the values in our MainActivity.kt

class MainActivity : ComponentActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {val viewModel = viewModel<MainViewModel>()
val time = viewModel.countDownFlow
.collectAsState(initial = 10)
Box(modifier = Modifier.fillMaxSize()) {
Text(
text = "Counter: ${time.value.toString()}",
fontSize = 30.sp,
modifier = Modifier.align(Alignment.Center)
)
}
}
}
}

in the above example, we are in Compose project if you are familiar with Android Compose you should know that we need that as kind of a state to redraw our UI

val time = viewModel.countDownFlow                                .collectAsState(initial = 10)

if you don’t have viewModel<> function you need to add this dependency

implementation “androidx.lifecycle:lifecycle-viewmodel-compose:2.4.0”

try to build the app and see the magic happen.

But very often you don’t want to do that either in Compose or in your UI layer but you still want to be notified about changes that come from specific flow

  • It’s important to mention that flow is cold

Cold flow: A cold stream does not start producing values until one starts to collect them

Hot flow: A hot stream on the other hand starts producing values immediately.

Let’s now try something different

in our MainViewModel.kt

class MainViewModel : ViewModel() {
private val startValue = 10
private var currentValue = startValue
private val countDownFLow = flow<Int> {
emit(currentValue)
while (currentValue > 0) {
delay(1000L)
currentValue--
emit(currentValue)
}
}
init {
collectData()
}
private fun collectData() {
viewModelScope.launch {
countDownFLow.collect { time ->
Log.i("Flow Data:", "Time is $time")
}
}
}

}

We declared a new private function called collectData.
inside that function, you will notice viewModelScope.launch { }, we are using that to run a suspended action.

Don’t forget to init the MainViewModel in your activity.

val mainViewModel: MainViewModel by viewModels()
mainViewModel

Check your logs now :)

collect VS collectLatest

collect

Collect will collect every value, and CollectLatest will stop current work to collect the latest value,

The crucial difference from collect is that when the original flow emits a new value then the action block for the previous value is canceled.

viewModelScope.launch {
flow {
emit(1)
delay(50)
emit(2)
}.collect { value ->
println("Collecting $value")
delay(100) // Emulate work
println("$value collected")
}
}
}

Result:
Collecting 1
Collecting 2
1 collected
2 collected

CollectLatest

flow {
emit(1)
delay(50)
emit(2)
}.collectLatest { value ->
println("Collecting $value")
delay(100) // Emulate work
println("$value collected")
}

Result:
Collecting 1
Collecting 2
2 collected

So, if every update is important like state, view, preferences updates, etc , collect should be used . And if some updates can be overridden with no loss , like database updates , collectLatest should be used.

Happy coding :)

Kazimi.

--

--

Ahmad Kazimi

Turning pizza and coffee into magic, also called Android apps.