An intermediate operation transforms a Kotlin Flow into another Flow. It receives values from an upstream flow and returns a downstream flow, allowing operators such as map, filter, and onEach to form a chain. section 17.2 section 17.5
A custom rolling-average operator
fun Flow<Double>.averageOfLast(n: Int): Flow<Double> = flow { val numbers = mutableListOf<Double>() collect { if (numbers.size >= n) numbers.removeFirst() numbers.add(it) emit(numbers.average()) } } flowOf(1.0, 2.0, 30.0, 121.0) .averageOfLast(3) .collect { print("$it ") } // 1.0 1.5 11.0 51.0 The operator collects upstream values, maintains a bounded list, and emits a transformed value downstream. Wrapping collect inside flow keeps the operator cold: work begins only when collect is called on the returned Flow. section 17.3
Intermediate operations have three practical properties:
- Return another Flow rather than an immediate result. section 17.1
- Remain cold: calling an operator does not execute its behavior until a terminal operator such as collect runs, including when the upstream flow is hot. section 17.2
- Can transform values, perform side effects, manage execution context, or hook into lifecycle phases through operators such as transform, flowOn, onStart, onEach, onCompletion, and onEmpty. section 17.5 section 17.2.3
Figure 17.1 You distinguish between intermediate and terminal flow operators. Where intermediate operators return a modified flow, terminal operators return an actual result. They do so by executing the code in the flow.
Use the diagram to distinguish an intermediate operator’s modified downstream Flow from a terminal operator’s executed result. section 17.1


