Kotlin Flow and Channels Tutorial: Cold and Hot Streams, Flow, StateFlow, SharedFlow, and Channels with Real Projects


This Kotlin Flow and Channels tutorial explains reactive and concurrent data streams using Flow, StateFlow, SharedFlow, and Channels. It covers cold vs hot streams, stream operators, real-world use cases, and best practices. The chapter also includes mini projects such as an API-based weather app, concurrent file downloader, and chat simulation system to build production-ready Kotlin skills.

Flow and Channels in Kotlin (Complete Tutorial)

Cold and Hot Streams

Cold Streams

  1. Data is produced only when collected
  2. Each collector gets its own data stream
  3. Example: Flow

Hot Streams

  1. Data is produced regardless of collectors
  2. Shared among multiple collectors
  3. Examples: StateFlow, SharedFlow, Channels

Flow

Flow is a cold asynchronous stream that emits multiple values sequentially.

Basic Flow Example


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

fun simpleFlow(): Flow<Int> = flow {
for (i in 1..5) {
delay(500)
emit(i)
}
}

fun main() = runBlocking {
simpleFlow().collect { value ->
println(value)
}
}

Best Practices

  1. Use Flow for continuous data streams
  2. Keep emission logic lightweight
  3. Use operators like map, filter, catch

Flow Operators Example


simpleFlow()
.filter { it % 2 == 0 }
.map { it * 10 }
.collect { println(it) }

StateFlow

StateFlow is a hot stream that holds and emits the latest state.

Use Case

  1. UI state management
  2. Configuration or app state tracking

Example


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

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

launch {
stateFlow.collect { println("Collector 1: $it") }
}

stateFlow.value = 1
stateFlow.value = 2
}

Best Practices

  1. Always provide an initial value
  2. Use for state, not events

SharedFlow

SharedFlow is a hot stream used for broadcasting events.

Use Case

  1. UI events
  2. Notifications
  3. One-time actions

Example


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

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

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

sharedFlow.emit("Event 1")
}

Best Practices

  1. Use SharedFlow for events
  2. Configure replay carefully

Channels

Channels allow communication between coroutines using send/receive.

Example


import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*

fun main() = runBlocking {
val channel = Channel<Int>()

launch {
for (x in 1..5) channel.send(x)
channel.close()
}

for (y in channel) {
println(y)
}
}

Best Practices

  1. Close channels after use
  2. Prefer Flow for streams
  3. Use Channels for low-level communication

Flow vs Channel

FeatureFlowChannel
TypeCold / HotHot
BackpressureAutomaticManual
Use caseData streamsCoroutine messaging

Mini Projects

API-Based Weather Application

Concepts Used: Flow, Coroutines, StateFlow

  1. Fetch weather data periodically
  2. Emit updates using Flow
  3. Maintain UI state using StateFlow

Concurrent File Downloader

Concepts Used: Channels, async, Dispatchers.IO

  1. Download multiple files concurrently
  2. Use Channels to track progress
  3. Handle failures gracefully

Chat Simulation System

Concepts Used: SharedFlow, Channels

  1. Simulate multiple users
  2. Broadcast messages using SharedFlow
  3. Manage message delivery using Channels

Best Practices Summary

  1. Use Flow for continuous data streams
  2. Use StateFlow for state
  3. Use SharedFlow for events
  4. Prefer Flow over Channels when possible
  5. Follow structured concurrency

Chapter Summary

This chapter covered Kotlin Flow and Channels, explaining cold and hot streams, Flow, StateFlow, SharedFlow, and Channels with real-world examples and mini projects. These concepts are essential for building reactive, scalable, and concurrent Kotlin applications.