1. Introduction – What is SharedFlow?

SharedFlow is a hot, broadcast-style flow in Kotlin used to send events or data to multiple collectors at the same time. Unlike cold flows, SharedFlow does not depend on a single collector. It keeps emitting values even when no one is listening.

Key Properties

2. SharedFlow vs Cold Flow

A cold flow starts producing values only when collected. But SharedFlow starts emitting immediately after creation.

Cold Flow Example


val coldFlow = flow {
    emit(1)
    emit(2)
    emit(3)
}
coldFlow.collect { println("Collector: $it") } // Starts fresh
  

SharedFlow Example


val shared = MutableSharedFlow()
shared.emit(1)
shared.emit(2)
shared.collect { println("A: $it") } // Does NOT receive past values
  

Output: (collector A receives nothing because no replay is set)

3. Basic SharedFlow Example (Hot Behavior)


val sf = MutableSharedFlow()

launch {
    delay(500)
    sf.collect { println("Collector 1: $it") }
}

launch {
    repeat(3) {
        delay(300)
        println("Emitting $it")
        sf.emit(it)
    }
}
  

Output


Emitting 0
Emitting 1
Collector 1: 1
Emitting 2
Collector 1: 2
  

Explanation: Collector starts late, so it misses older emissions.

4. Multiple Collectors on SharedFlow


val sf = MutableSharedFlow()

launch {
    sf.collect { println("Collector A: $it") }
}

launch {
    sf.collect { println("Collector B: $it") }
}

launch {
    repeat(3) {
        sf.emit(it)
    }
}
  

Output


Collector A: 0
Collector B: 0
Collector A: 1
Collector B: 1
Collector A: 2
Collector B: 2
  

Both collectors receive the same values simultaneously. SharedFlow broadcasts items.

5. Impact of Delay in One Consumer

SharedFlow does not slow down producers. If one collector is slow, the other collectors continue normally.


val sf = MutableSharedFlow()

launch {
    sf.collect { 
        delay(300)
        println("Slow Collector: $it")
    }
}

launch {
    sf.collect { 
        println("Fast Collector: $it")
    }
}

launch {
    repeat(3) {
        println("Emit $it")
        sf.emit(it)
    }
}
  

Output


Emit 0
Fast Collector: 0
Emit 1
Fast Collector: 1
Emit 2
Fast Collector: 2
Slow Collector: 0
Slow Collector: 1
Slow Collector: 2
  

The slow collector lags behind, but SharedFlow stores nothing unless replay is set. All emissions are delivered, but slow collectors may process late.

Does the Slow Collector Miss Values in SharedFlow?

No, in this case the slow collector does not miss any values. Even though MutableSharedFlow has replay = 0, it still delivers all emissions to all actively collecting collectors.

Code Example


val sf = MutableSharedFlow()

launch {
    sf.collect { 
        delay(300)
        println("Slow Collector: $it")
    }
}

launch {
    sf.collect { 
        println("Fast Collector: $it")
    }
}

launch {
    repeat(3) {
        println("Emit $it")
        sf.emit(it)
    }
}

Output

Emit 0
Fast Collector: 0
Emit 1
Fast Collector: 1
Emit 2
Fast Collector: 2
Slow Collector: 0
Slow Collector: 1
Slow Collector: 2

Explanation

Even though the slow collector processes each value with a delay, it does not lose values. A SharedFlow without replay does not store values for future collectors, but it does deliver emissions to all currently active collectors.

Because both collectors are already collecting when emissions start, both receive all values. The fast collector prints immediately, while the slow collector prints later due to its delay.

If you start collecting after the emissions, then without replay you would miss values. But in this example, both collectors are active before emission, so no value is lost.

6. SharedFlow Replay (Replaying Old Items)

You can replay the last n values using: MutableSharedFlow(replay = n)

Example – replay = 1


val sf = MutableSharedFlow(replay = 1)

sf.emit(10)
sf.emit(20)

sf.collect { println("Collector A: $it") }
  

Output


Collector A: 20
  

New collectors receive the last emission automatically.

7. Encapsulation of SharedFlow in ViewModel

SharedFlow is commonly used in MVVM for UI events such as:

ViewModels use encapsulation to expose read-only SharedFlow to the UI.

ViewModel Example


class MyViewModel : ViewModel() {

    private val _events = MutableSharedFlow(replay = 0)
    val events: SharedFlow get() = _events

    fun sendEvent(msg: String) {
        viewModelScope.launch {
            _events.emit(msg)
        }
    }
}
  

UI Collection


lifecycleScope.launch {
    viewModel.events.collect { 
        println("UI Event: $it")
    }
}

viewModel.sendEvent("Login Successful")
  

Output


UI Event: Login Successful
  

The UI cannot modify the SharedFlow directly because the setter is private. This is perfect encapsulation.

8. Summary