Combining Flows

zip and combine

OperatorEmits whenUse for
zipboth flows have a new value (pairs them 1:1)matching items from two sources
combineeither flow emits (uses latest of the other)UI state from several inputs: filters + data
mergeany flow emits (same type)several event sources into one

Example

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun main() = runBlocking {
    val names = flowOf("Asha", "Ravi", "Meena")
    val marks = flowOf(91, 78, 85)
    names.zip(marks) { n, m -> "$n: $m" }.collect { println("zip -> $it") }

    val price = flow { emit(1000); delay(200); emit(1200) }
    val discount = flow { delay(100); emit(10); delay(200); emit(20) }
    combine(price, discount) { p, d -> "price=$p discount=$d% pay=${p - p * d / 100}" }
        .collect { println("combine -> $it") }

    merge(flowOf("click"), flowOf("scroll")).toList().sorted().forEach { println("merge -> $it") }
}

Output

zip -> Asha: 91
zip -> Ravi: 78
zip -> Meena: 85
combine -> price=1000 discount=10% pay=900
combine -> price=1200 discount=10% pay=1080
combine -> price=1200 discount=20% pay=960
merge -> click
merge -> scroll

Flattening: flatMapConcat and flatMapLatest

Example

flatMapConcat runs inner flows one after another. flatMapLatest cancels the previous inner flow when a new value arrives: perfect for search.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun searchApi(q: String): Flow<String> = flow {
    delay(150)
    emit("results for '$q'")
}

@OptIn(ExperimentalCoroutinesApi::class)
fun main() = runBlocking {
    flowOf(1, 2)
        .flatMapConcat { id -> flowOf("order $id: item A", "order $id: item B") }
        .collect { println(it) }

    val queries = flow {
        emit("k"); delay(100)
        emit("ko"); delay(100)
        emit("kotlin")
    }
    queries.flatMapLatest { searchApi(it) }.collect { println(it) }
}

Output

order 1: item A
order 1: item B
order 2: item A
order 2: item B
results for 'kotlin'