Create the cart MVI — 4 files (data → presentation)
This step creates the cart's four files. They're in the code column as tabs, ordered data → presentation — build them top to bottom, because each depends on the one before it. Create the ViewModel first and you get four red compile errors and no idea why.
1. CartItem.kt (data) — one cart line: a pizza plus a quantity. Nothing else.
2. CartEvent.kt (presentation) — everything the user can do, as a sealed interface:
sealed interface CartEvent {
data class AddItem(val menuItem: MenuItem) : CartEvent
data class IncreaseQuantity(val menuItem: MenuItem) : CartEvent
// …one case per action
}
3. CartUiState.kt (presentation) — the whole state. The key idea: the total is derived, not stored:
val totalPrice: Double get() = items.sumOf { it.menuItem.price * it.quantity }
It's computed from items on every read, so it can never drift out of sync with the real cart.
4. CartViewModel.kt (presentation) — the logic. onEvent(...) takes every event and produces a new state via _uiState.update { it.copy(...) }. The MutableStateFlow is private (_uiState); we expose only a read-only uiState.
Verify. The project compiles with no errors. Check the logic in your head: AddItem(Margherita) twice → items has one CartItem with quantity = 2, totalPrice = 17.00.
Gotcha. MVI state is immutable. Don't hold a
MutableListand mutate it in place:// WRONG — Compose will NOT recompose: _uiState.value.items.add(CartItem(item, 1)) // mutating the same listA
StateFlowonly notifies when the value changes (a newcopy). Mutate the old object in place and the reference is unchanged → the UI never notices, the total won't update. Always produce a new state:_uiState.update { it.copy(items = newItems) }.