All you need to know about Kotlin FLOW (Part 2)
In this series, I will explain Kotlin’s flow in the easiest way to understand
Today will take a closer look at Flow operators and how to use them.
First, what are the Flow Operators ?!
The operator is a way or something to decide what will happen with a single emission of the flow.
Filter
for example, let’s say you have countDownFlow like the previous article
and you need to receive only the even values then you need to use one of the operators and for this purpose, we can use the filter operator
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
.filter { time ->
time % 2 == 0
}
.collect { time ->
print("The current time is $time")
}
}
}
}
Output :
The current time is 10
The current time is 8
The current time is 6
The current time is 4
The current time is 2
The current time is 0
Map
We have another operator called map you might know this operator from List. you can use that to map the emission to a new value.
private fun collectData() {
viewModelScope.launch {
countDownFLow
.filter { time ->
time % 2 == 0
}
.map { time ->
time + 2
}
.collect { time ->
println("The current time is $time")
}
}
}
Output :
The current time is 12
The current time is 10
The current time is 8
The current time is 6
The current time is 4
The current time is 2
onEach
Very obvious from the name of this operator it returns the emission and you can do anything you want with that emission it’s pretty much a collect
but the main difference here between collect and onEach that is
collect: makes sure that the flow has finished and doesn’t return anything
onEach: don’t worry about the Flow and return the Flow itself.
We can use it to launch the flow in a little different way
private fun collectData() {
countDownFLow.onEach { time ->
println("The current time is $time")
}.launchIn(viewModelScope)
}
Terminal operators
they called Terminal operators because they terminate the Flow
Count
This will count values that match specific conditions.
private fun collectData() {
viewModelScope.launch {
val count = countDownFLow
.filter { time ->
time % 2 == 0
}
.map { time ->
time + 2
}
.count { time ->
time % 2 == 0
}
println("The emission count is $count")
}
}
Output :
The emission count is 6
Reduce
Accumulates value starting with the first element and applying the operation to current accumulator value and each element.
private fun collectData() {
viewModelScope.launch {
val reduceResult = countDownFLow
.filter { time ->
time % 2 == 0
}
.reduce { accumulator, value ->
accumulator + value
}
println("The reduce result is $reduceResult")
}
}
Output :
The reduce result is 30
Fold
It is pretty much as Reduce but with init value
private fun collectData3() {
viewModelScope.launch {
val reduceResult = countDownFLow
.filter { time ->
time % 2 == 0
}
.fold(100) { accumulator, value ->
accumulator + value
}
println("The reduce result is $reduceResult")
}
}
Output :
The reduce result is 130
Flattening operators
What does Flattening mean?
let's say you need to return a single list of all elements from all collections in the given collection.
for example, we have [[1,2,3] [4,5]] and you need a single list from all that [1,2,3,4,5]
here is where flattening operators do an amazing job
in this demonstration we used List but Kotlin flow has something very similar
don’t forget we are flattening flows here not List
Let’s take a look
class MainViewModel : ViewModel() {
private val firstNameFlow = flow {
emit("Ahmad")
}
init {
collectData()
}
private fun collectData() {
viewModelScope.launch {
firstNameFlow
.flatMapConcat { firstName ->
flow {
emit(firstName)
emit("Kazimi")
}
}.collect {
println("My name is : $it ")
}
}
}
}
let’s break it down
1. declared firstNameFlow responsible for first name emit.
2. used flatMapConcat operator which is return Flow {}
3. inside flatMapConcat{} we declared our second Flow that emit lastNameFlow
4. Since we have 2 flows but we using flatMapConcat{} we need only one collector for both flows to collect{} the values and
There are another 2 flatten operator
flatMapLatest {}
it’s sound familiar from collect latest it’s simply dropping the emission of the inner flow in case it finishes before the first flow which is firstNameFlow finish, it’s Ignore the emission in the middle
flatMapMerge {}
we discourage its usage in a regular application-specific flows. Most likely, suspending operation in map operator will be sufficient and linear transformations are much easier to reason about.
see you in part 3
Happy coding :)
Kazimi