kotlinadvanced
StateFlow and SharedFlow Usage Patterns
Use hot flows for state management: MutableStateFlow for UI state, SharedFlow for events and broadcasting.
kotlinPress ⌘/Ctrl + Shift + C to copy
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
// StateFlow: holds current state, emits to new collectors
class CounterViewModel {
private val _count = MutableStateFlow(0)
val count: StateFlow<Int> = _count.asStateFlow()
private val _uiState = MutableStateFlow<UiState>(UiState.Idle)
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
fun increment() { _count.value++ }
fun decrement() { _count.value-- }
suspend fun loadData() {
_uiState.value = UiState.Loading
delay(1000) // simulate API call
_uiState.value = UiState.Success("Data loaded at ${System.currentTimeMillis()}")
}
}
sealed class UiState {
object Idle : UiState()
object Loading : UiState()
data class Success(val data: String) : UiState()
data class Error(val message: String) : UiState()
}
// SharedFlow: event broadcasting, no replay by default
class EventBus {
private val _events = MutableSharedFlow<AppEvent>(
replay = 0,
extraBufferCapacity = 64
)
val events: SharedFlow<AppEvent> = _events.asSharedFlow()
suspend fun emit(event: AppEvent) {
_events.emit(event)
}
}
sealed class AppEvent {
data class UserLoggedIn(val userId: String) : AppEvent()
data class UserLoggedOut(val userId: String) : AppEvent()
data class ItemPurchased(val itemId: String, val amount: Double) : AppEvent()
data class ErrorOccurred(val message: String) : AppEvent()
}
fun main() = runBlocking {
// StateFlow demo
val viewModel = CounterViewModel()
// Collector 1
val collector1 = launch {
viewModel.count.collect { println("Collector1: count = $it") }
}
delay(100)
viewModel.increment()
viewModel.increment()
viewModel.increment()
delay(100)
// New collector gets current value immediately
val collector2 = launch {
viewModel.count.take(1).collect { println("Collector2 (late): count = $it") }
}
delay(100)
collector1.cancel()
collector2.cancel()
// SharedFlow: event bus
val eventBus = EventBus()
val subscriber = launch {
eventBus.events.collect { event ->
when (event) {
is AppEvent.UserLoggedIn -> println("Login: ${event.userId}")
is AppEvent.UserLoggedOut -> println("Logout: ${event.userId}")
is AppEvent.ItemPurchased -> println("Purchase: ${event.itemId} for $${event.amount}")
is AppEvent.ErrorOccurred -> println("Error: ${event.message}")
}
}
}
delay(100)
eventBus.emit(AppEvent.UserLoggedIn("user-123"))
eventBus.emit(AppEvent.ItemPurchased("item-456", 29.99))
eventBus.emit(AppEvent.UserLoggedOut("user-123"))
delay(100)
subscriber.cancel()
println("Done")
}Use Cases
- UI state management in Android/KMP apps
- Event broadcasting across application layers
- Real-time data updates and subscriptions
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
kotlinadvanced
StateFlow and SharedFlow — Hot Streams
Manage state with hot flows: StateFlow for reactive state, SharedFlow for events, and MutableStateFlow.
Best for: Reactive UI state management
#kotlin#stateflow
kotlinintermediate
Coroutines — launch, async, and Structured Concurrency
Write concurrent code with Kotlin coroutines: launch, async/await, structured concurrency, and dispatchers.
Best for: Parallel API calls in backend services
#kotlin#coroutines
kotlinintermediate
Flow — Reactive Streams with Coroutines
Build reactive pipelines with Kotlin Flow: emit, collect, transform, combine, and error handling.
Best for: Streaming data processing pipelines
#kotlin#flow
kotlinadvanced
Channels — Producer-Consumer with Coroutines
Communicate between coroutines with Channels: produce, actor pattern, fan-out/fan-in, and select.
Best for: Producer-consumer patterns in coroutines
#kotlin#channels