Flow API in Kotlin Programming Language

we will dive deep into Flow API in Kotlin, exploring its features, advantages, and real-world use cases.

Kotlin has revolutionized modern Android and backend development with its concise syntax and powerful features. One of its most important advancements is Flow API, introduced as a part of Kotlin Coroutines to handle asynchronous and reactive programming efficiently.

In this article, we will dive deep into Flow API in Kotlin, exploring its features, advantages, and real-world use cases.


What is Flow API in Kotlin?

Flow API is a cold asynchronous stream that emits multiple values sequentially. It is designed to handle streams of data asynchronously while following Kotlin’s structured concurrency principles.

Unlike suspend functions, which return a single value asynchronously, Flow can emit multiple values over time.

Key Characteristics of Flow API:

  • Cold Stream: The flow starts running only when a collector collects the emitted values.
  • Sequential Emission: Values are emitted one after another, ensuring sequential processing.
  • Cancellation Support: Flow is cooperative and cancels execution when the collector stops collecting.
  • Backpressure Handling: Flow handles backpressure automatically, ensuring optimal data flow without overwhelming the system.

Why Use Flow API Instead of Other Reactive Approaches?

Before Flow API, developers often used LiveData, RxJava, or Callbacks to handle asynchronous operations. However, these approaches had certain drawbacks:

ApproachDrawbacks
CallbacksHard to manage in complex scenarios (callback hell)
RxJavaSteep learning curve, requires additional dependencies
LiveDataTied to Android lifecycle, not suitable for non-UI layers

Flow API solves these issues by providing a structured, lightweight, and efficient way to manage streams without extra dependencies.


How to Use Flow API in Kotlin?

Let’s explore the fundamental concepts of Flow API in Kotlin with examples.

1. Creating a Simple Flow

To create a Flow, use the flow {} builder.

import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.delay

fun simpleFlow(): Flow<Int> = flow {
    for (i in 1..5) {
        delay(1000) // Simulate a delay
        emit(i) // Emit values one by one
    }
}

fun main() = runBlocking {
    simpleFlow().collect { value ->
        println("Received: $value")
    }
}

Explanation:

  • The flow {} builder creates a Flow that emits numbers 1 to 5 with a delay of 1 second between each emission.
  • emit(value) is used to send values to the collector.
  • collect {} function is used to collect and process the emitted values.

Flow Builders in Kotlin

Besides flow {}, Kotlin provides several built-in flow builders:

Flow BuilderDescription
flowOf()Creates a flow from a fixed set of values.
asFlow()Converts a collection or sequence into a flow.
channelFlow()Provides a more flexible way to emit values using coroutines.

Example: Using flowOf() and asFlow()

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

fun main() = runBlocking {
    // Using flowOf()
    flowOf(1, 2, 3, 4, 5).collect { println(it) }

    // Using asFlow()
    listOf("A", "B", "C").asFlow().collect { println(it) }
}

Flow Operators: Transforming and Filtering Data

Flow provides powerful operators to process emitted data efficiently.

1. Transforming Data

  • map {} → Transforms each value.
  • flatMapConcat {} → Flattens nested flows sequentially.
  • flatMapMerge {} → Flattens nested flows concurrently.
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    (1..5).asFlow()
        .map { it * 2 } // Multiply each value by 2
        .collect { println(it) }
}

2. Filtering Data

  • filter {} → Filters elements based on a condition.
  • take(n) → Takes the first n elements and cancels the flow afterward.
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    (1..10).asFlow()
        .filter { it % 2 == 0 } // Take even numbers
        .collect { println(it) }
}

Handling Flow Lifecycle and Cancellation

1. Flow is Cancellable

Flows respect coroutine cancellation and automatically stop execution when the collector stops collecting.

import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeoutOrNull

fun main() = runBlocking {
    withTimeoutOrNull(2500) { // Cancel flow after 2.5 seconds
        simpleFlow().collect { println(it) }
    }
    println("Flow cancelled!")
}

2. Flow with Lifecycle Awareness (onEach and launchIn)

onEach {} executes an action for each emitted value, while launchIn(scope) collects the flow in a coroutine scope.

import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.GlobalScope

fun main() = runBlocking {
    val flow = (1..5).asFlow()
        .onEach { println("Processing $it") }
        .launchIn(GlobalScope) // Runs in a separate coroutine scope
}

StateFlow and SharedFlow: Advanced Flow Concepts

1. StateFlow: Managing State in Kotlin

StateFlow is a special type of Flow that always holds the latest value and emits updates. It is a great replacement for LiveData in non-UI layers.

import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

fun main() = runBlocking {
    val stateFlow = MutableStateFlow(0)

    launch {
        for (i in 1..5) {
            delay(500)
            stateFlow.value = i
        }
    }

    stateFlow.collect { println("Received: $it") }
}

2. SharedFlow: For Hot Streams

Unlike Flow, SharedFlow is hot, meaning it does not depend on collectors to start emitting values. It is useful for event-based scenarios.

import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.launch

fun main() = runBlocking {
    val sharedFlow = MutableSharedFlow<String>()

    launch {
        sharedFlow.emit("Hello")
        sharedFlow.emit("World")
    }

    sharedFlow.collect { println(it) }
}

Conclusion

The Flow API in Kotlin is a powerful tool for handling asynchronous streams of data efficiently. With its built-in operators, lifecycle awareness, and structured concurrency support, Flow is a great alternative to RxJava and LiveData.

Key Takeaways:

Flow is cold and starts execution only when collected.
✔ Supports cancellation, transformations, and filtering.
StateFlow and SharedFlow extend Flow’s capabilities for state management and event handling.
✔ Ideal for handling network requests, database queries, and UI updates.

By mastering Flow API, you can write efficient, reactive, and scalable Kotlin applications! 🚀


Last modified 20.02.2025: new kotlin and mint content (93a1000)