stateIn and shareIn

Cold to Hot

stateIn and shareIn turn a cold flow (e.g. a Room query) into a hot one that is shared by all collectors, so the database is queried once, not once per collector.

SharingStartedUpstream runs
Eagerlyimmediately, until the scope is cancelled
Lazilyfrom the first subscriber, until the scope is cancelled
WhileSubscribed(5000)while someone collects, plus 5 s after the last one leaves (survives screen rotation)

stateIn

Example

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

fun temperatureSensor(): Flow<Int> = flow {
    println("Sensor started")
    emit(28)
    delay(100)
    emit(31)
}

fun main() = runBlocking {
    val temp: StateFlow<Int> = temperatureSensor()
        .stateIn(this, SharingStarted.Eagerly, initialValue = 0)

    println("Right away: ${temp.value}")
    delay(50)
    println("After 50 ms: ${temp.value}")
    delay(100)
    println("After 150 ms: ${temp.value}")
    coroutineContext.cancelChildren()
}

Output

Right away: 0
Sensor started
After 50 ms: 28
After 150 ms: 31

shareIn: One Upstream, Many Collectors

Example

Two screens collect the same ticker, but the upstream starts only once.

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

fun main() = runBlocking {
    val ticker = flow {
        println("Upstream connected")
        repeat(3) { delay(100); emit(it) }
    }
    val shared = ticker.shareIn(this, SharingStarted.WhileSubscribed(), replay = 0)

    launch { shared.collect { println("Screen A got $it") } }
    launch { shared.collect { println("Screen B got $it") } }
    delay(450)
    coroutineContext.cancelChildren()
}

Output

Upstream connected
Screen A got 0
Screen B got 0
Screen A got 1
Screen B got 1
Screen A got 2
Screen B got 2

In a ViewModel

class ProductsViewModel(repo: ProductRepository) : ViewModel() {
    val products: StateFlow<List<Product>> = repo.observeProducts()   // Flow from Room
        .map { list -> list.sortedBy { it.price } }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5_000),
            initialValue = emptyList(),
        )
}