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
- Data is produced only when collected
- Each collector gets its own data stream
- Example:
Flow
Hot Streams
- Data is produced regardless of collectors
- Shared among multiple collectors
- 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
- Use Flow for continuous data streams
- Keep emission logic lightweight
- 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
- UI state management
- 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
- Always provide an initial value
- Use for state, not events
SharedFlow
SharedFlow is a hot stream used for broadcasting events.
Use Case
- UI events
- Notifications
- 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
- Use
SharedFlowfor events - Configure
replaycarefully
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
- Close channels after use
- Prefer Flow for streams
- Use Channels for low-level communication
Flow vs Channel
| FeatureFlowChannel | ||
| Type | Cold / Hot | Hot |
| Backpressure | Automatic | Manual |
| Use case | Data streams | Coroutine messaging |
Mini Projects
API-Based Weather Application
Concepts Used: Flow, Coroutines, StateFlow
- Fetch weather data periodically
- Emit updates using Flow
- Maintain UI state using StateFlow
Concurrent File Downloader
Concepts Used: Channels, async, Dispatchers.IO
- Download multiple files concurrently
- Use Channels to track progress
- Handle failures gracefully
Chat Simulation System
Concepts Used: SharedFlow, Channels
- Simulate multiple users
- Broadcast messages using SharedFlow
- Manage message delivery using Channels
Best Practices Summary
- Use
Flowfor continuous data streams - Use
StateFlowfor state - Use
SharedFlowfor events - Prefer Flow over Channels when possible
- 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.