Real World Example

Zepto-style Cart Store

A quick-commerce cart is shared by the product list, the cart badge and the checkout button. A CartStore keeps the cart in a StateFlow (every screen shows the same data) and sends one-time messages ("Only 2 left!") through a SharedFlow.

ProductList ──add()──►  CartStore
                         ├─ state: StateFlow<CartState>  ──► badge, cart screen, checkout
                         └─ messages: SharedFlow<String> ──► snackbar

Code

Example

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

data class Item(val name: String, val price: Int, val stock: Int)
data class CartState(val lines: Map<String, Int> = emptyMap(), val total: Int = 0) {
    val count get() = lines.values.sum()
}

class CartStore(private val catalog: Map<String, Item>) {
    private val _state = MutableStateFlow(CartState())
    val state: StateFlow<CartState> = _state.asStateFlow()
    private val _messages = MutableSharedFlow<String>(extraBufferCapacity = 8)
    val messages: SharedFlow<String> = _messages.asSharedFlow()

    fun add(name: String) {
        val item = catalog.getValue(name)
        val inCart = _state.value.lines[name] ?: 0
        if (inCart >= item.stock) {
            _messages.tryEmit("Only ${item.stock} ${item.name} left!")
            return
        }
        _state.update { s ->
            s.copy(lines = s.lines + (name to inCart + 1), total = s.total + item.price)
        }
    }
}

fun main() = runBlocking {
    val store = CartStore(mapOf(
        "milk" to Item("Milk 500ml", 30, 5),
        "bread" to Item("Bread", 45, 2),
    ))
    launch { store.state.collect { println("Badge: ${it.count} items, Rs ${it.total}") } }
    launch { store.messages.collect { println("Snackbar: $it") } }
    delay(10)

    for (name in listOf("milk", "bread", "bread", "bread")) {
        store.add(name)
        delay(10)
    }
    println("Cart lines: ${store.state.value.lines}")
    coroutineContext.cancelChildren()
}

Output

Badge: 0 items, Rs 0
Badge: 1 items, Rs 30
Badge: 2 items, Rs 75
Badge: 3 items, Rs 120
Snackbar: Only 2 Bread left!
Cart lines: {milk=1, bread=2}