Quick start¶
This guide walks through the four steps needed to run a state machine end to end.
1. Define your types¶
Implement State, Action, and Effect for your feature. The simplest form uses a data class
for state and sealed interfaces for actions and effects:
data class CounterState(val count: Int) : State
sealed interface CounterAction : Action {
data object Increment : CounterAction
data object Decrement : CounterAction
data object Reset : CounterAction
}
sealed interface CounterEffect : Effect {
data object Saved : CounterEffect
}
2. Build a store¶
Pass a CoroutineScope and configure states and handlers with the DSL:
val counter = store<CounterState, CounterAction, CounterEffect>(viewModelScope) {
initialState(CounterState(0))
state<CounterState> {
on<CounterAction.Increment> { transition(state.copy(count = state.count + 1)) }
on<CounterAction.Decrement> { transition(state.copy(count = state.count - 1)) }
on<CounterAction.Reset> {
transition(CounterState(0))
sideEffect(CounterEffect.Saved)
}
}
install(LoggingPlugin(tag = "Counter"))
}
state<T> registers a handler block for any state that is an instance of T. Multiple
state blocks can be registered — the most specific match (exact type or nearest supertype
via BFS) wins. See Hierarchical states for details.
3. Observe¶
Collect state and effects from any coroutine scope:
// State is a StateFlow — always holds the current value
counter.state.collect { render(it) }
// Effects are one-shot — use a dedicated collector so nothing is missed
counter.effects.collect { handle(it) }
In Compose, use the toViewStore() / handleEffects { } helpers from monaka-compose so
collection stops when the UI is backgrounded.
4. Dispatch actions¶
Actions are enqueued on an UNLIMITED channel and processed one at a time by a single
coroutine — dispatch is safe to call from any thread or coroutine.
Android — ViewModel¶
class CounterViewModel : ViewModel() {
val store = store<CounterState, CounterAction, CounterEffect>(viewModelScope) {
initialState(CounterState(0))
// …
}
}
The store is cancelled automatically when viewModelScope is cleared.
Compose Multiplatform — composition-scoped store¶
On non-Android targets (or when you want the store tied to composition lifetime rather than a
ViewModel), use rememberStore: