Careeroza
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> ──► snackbarCode
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}