0
0
KotlinHow-ToBeginner · 3 min read

How to Use flowOn in Kotlin for Coroutine Context Switching

Use flowOn in Kotlin to change the coroutine context where a Flow is executed, typically to switch to a background thread for heavy work. It is applied to a flow to specify the dispatcher, like Dispatchers.IO, ensuring upstream operations run on that dispatcher.
📐

Syntax

The flowOn operator is called on a Flow to change the coroutine context of upstream flow operations. It takes a CoroutineDispatcher as a parameter.

  • flowOn(dispatcher): Changes the context where the flow runs.
kotlin
flow {
    // emit values
}.flowOn(Dispatchers.IO)
💻

Example

This example shows a flow emitting numbers on the Dispatchers.IO thread using flowOn. The collector runs on the main thread.

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

fun main() = runBlocking {
    val flow = flow {
        println("Emitting on thread: ${Thread.currentThread().name}")
        emit(1)
        delay(100)
        emit(2)
    }.flowOn(Dispatchers.IO) // Upstream runs on IO dispatcher

    flow.collect {
        println("Collected $it on thread: ${Thread.currentThread().name}")
    }
}
Output
Emitting on thread: DefaultDispatcher-worker-1 Collected 1 on thread: main Collected 2 on thread: main
⚠️

Common Pitfalls

One common mistake is placing flowOn after terminal operators like collect, which has no effect because flowOn only affects upstream flow operations. Another pitfall is misunderstanding that flowOn changes the context only for upstream, not downstream.

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

fun main() = runBlocking {
    val flow = flow {
        println("Emitting on thread: ${Thread.currentThread().name}")
        emit(1)
    }

    // Wrong: flowOn after collect does nothing
    flow.collect {
        println("Collected $it on thread: ${Thread.currentThread().name}")
    } // .flowOn(Dispatchers.IO) This has no effect

    // Correct usage
    flow.flowOn(Dispatchers.IO).collect {
        println("Collected $it on thread: ${Thread.currentThread().name}")
    }
}
Output
Emitting on thread: main Collected 1 on thread: main Emitting on thread: DefaultDispatcher-worker-1 Collected 1 on thread: main
📊

Quick Reference

  • flowOn(dispatcher): Changes the coroutine context for upstream flow operations.
  • Use Dispatchers.IO for blocking IO tasks.
  • Use Dispatchers.Default for CPU-intensive tasks.
  • Place flowOn before terminal operators like collect.

Key Takeaways

Use flowOn to switch the coroutine context of upstream flow operations for better thread management.
Place flowOn before terminal operators like collect to have effect.
flowOn changes the context only for upstream, not downstream operations.
Common dispatchers are Dispatchers.IO for IO tasks and Dispatchers.Default for CPU tasks.
flowOn helps keep UI responsive by moving heavy work off the main thread.