Kotlin Flow Part2 | Android

Awadhesh Singh
4 min readMar 17, 2024

--

If you haven’t read the previous post Part1 please read it first

In Part2 we will go through the Producer, intermediaries and Consumers.
builders, operators and collectors to under stand the flows and we will implement the project on flow in Part3

→Producer → Intermediaries →Consumers

Flow builders →

Flow can provide multiple values and is built on top of the coroutines. Flow is a stream of data that is computed asynchronously.
The data should be of the same type; if the flow is of type int, the values emitted by the flow should only be an integer.
Each flow needs to begin somewhere. A flow builder is an interface we use to build a flow. There are different ways in which we can build a flow. Let’s discuss them one by one:
Types of Flow Builders:
1.
flowOf
2. asFlow
3. emptyFlow
4. flow

Flow() function : flow() is most popular function to build a flow.We start this builder with a flow function call, and inside that builder, we emit the values of the flow.

fun flow_builder(): Flow<String> = flow {
emit ("A")
emit ("B")
emit ("C")
}

suspend fun main() {
flow_builder()
.collect { println(it) }
}

flowOf() function: its used to build of flow of fixed amount of values and its easiest way to create a flow.

suspend fun main() {
print("flowof() : ")
flowOf(1, 3, 9, 16)
.collect { print("${it} ") }
}

emptyFlow() function : this function is used to build an empty flow

suspend fun main() {
print("emptyFlow() : ")
emptyFlow<Int>()
.collect { print(it) }
}

asFlow() function: it is used to convert the sequence or range of values in flow

suspend fun main() {
print("asFlow() : ")
(1..10).asFlow()
.collect { print("${it} ")}

}

Flow Operators → There are 2 types of operators in Flows. Terminal and Non-Terminal operator

Operators are at the heart of Kotlin Flow, like every reactive Framework. It allows manipulating items as they go from observable to observer.
some of the operators:

1. collect: its a suspending function and used to start the collection the values from flow and it is called from coroutine scope.

val flow = flowOf(1, 2, 3, 4, 5)

flow.collect { number ->
println("Received number: $number")
}

2. toList and toSet: We can use the toList or toSet terminal operators to collect the data into a list or a set.

val flow = flowOf(1, 2, 3, 4, 5)

val list = flow.toList()
println(list)
output [1, 2, 3, 4, 5]

3. first : The first terminal operator collects the data until the first item that matches a given condition is found. Then it stops the collection and returns that item:

val flow = flowOf(1, 2, 3, 4, 5)
val number = flow.first { it % 2 == 0 }
println(number)
output 2

4. reduce: Reduce operator allows performing operation over all items of the flow to reduce them into one item.

flowOf(1, 2, 3)
.reduce { acc, value -> acc + value }

Output:
6

5. filter: The filter operator filters the elements emitted by the source flow using a predicate function and emits only the elements that satisfy the predicate.

val numbers = (1..5).asFlow()
numbers.filter { it % 2 == 0 }.collect { println(it) }

output 2 4

6. transform:

val numbers = (1..5).asFlow()
numbers.transform { value ->
emit("A$value")
emit("B$value")
}.collect { println(it) }

7. map: The map operator applies a transformation function to each element emitted by the source flow and emits the transformed element to downstream.

val numbers = (1..5).asFlow()
numbers.map { it * 2 }.collect { println(it) }

output 1, 4, 6, 8, 10

8. flatmapMerge and flatmapConcat :
flatmapMerge operator maps every item of the Flow with a new Flow provided by transform operation and then merges the items of those flows and flattens it.
flatmapConcat Similar to flatmapMerge but here flows are concatenated instead of merge. Remember flatmapMerge has better performance as it can process Flows in parallel.


val result = flow { // flow builder
for (i in 1..3) {
delay(1) // pretend we are doing something useful here
emit(i) // emit next value
}
}.flatMapmerge { it ->
flowOf{
it * 2,
it * 2 + 1
}
}.toList()

val result = flow { // flow builder
for (i in 1..3) {
delay(1) // pretend we are doing something useful here
emit(i) // emit next value
}
}.flatMapConcat { it ->
flow {
emit(it * 2) // 2, 4,6
delay(2)
emit(it * 2 + 1) // 3,5,7
}
}.toList()

9.fold : Similar to reduce but allows result of any type.

flowOf(1, 2, 3)
.fold("hello", { acc, value -> acc + value })

10. combine : This operator merges two flows with the provided operation function.Latest item of the Flows are always combined.

flowOf("1", "2")
.combine(flowOf("a", "b", "c"), {a, b -> a + b })

11. zip: Similar to combine , this also merges two Flows but it will broadcast one item when both Flow emits 1 item each.The zip operator combines two flows into a single flow of pairs. Each pair contains one element from each source flow.

val a = (1..5).asFlow()
val b = (6..10).asFlow()
a.zip(b) { x, y -> x + y }.collect { println(it) }

Terminal Operator: Terminal operators are those operators that consume the flow means it starts the flow. All terminal operators are suspended functions. Terminal operators initiate the execution of a flow and return a specific value or perform an action based on the emitted values.

Non Terminal Operator:
Non-terminal operators are used to transform, filter, or modify a flow without triggering its execution.Non-terminal operators return a new flow with the applied transformations, which can be further composed or combined with other operators.

Please follow on github and medium for demo project in next part

--

--