State machine

Page contents

Create state machine

A state machine can be created with one of those factory functions:

  • createStateMachine() suspendable version (from kstatemachine-coroutines artifact), the best choice by default.
  • createStateMachineBlocking() blocking version (from kstatemachine-coroutines artifact)
  • createStdLibStateMachine() - creates StateMachine instance without Kotlin Coroutines support (from kstatemachine artifact)
val machine = createStateMachine(
    scope,
    "Traffic lights" // Optional name is convenient for logging debugging and export
) {
    // Set up state machine ...
}

By default, factory functions start state machine. You can control it using start argument. All overloads accept optional argument CreationArguments which allows to change some options. Use buildCreationArguments() function to provide it.

Subsequent samples will use createStateMachine() function, but you can choose that one which fits your needs.

Creation arguments

Argument Default Description
autoDestroyOnStatesReuse true When a state owned by one machine is reused in another, automatically calls destroy() on the previous machine instead of throwing.
isUndoEnabled false Enables the undo transition. See Undo transitions.
doNotThrowOnMultipleTransitionsMatch false When multiple transitions match an event, selects the first one instead of throwing.
requireNonBlankNames false Throws on machine start if any state or transition has a null or blank name.
eventRecordingArguments null Enables event recording for later persistence/restoration. See Persistence.
skipCoroutineScopeValidityCheck false Skips the check that warns when a multithreaded dispatcher (e.g. Dispatchers.Default) is used. See Multithreading.
val machine = createStateMachine(
    scope,
    creationArguments = buildCreationArguments {
        isUndoEnabled = true
        requireNonBlankNames = true
    }
) {
    // ...
}

Lifecycle

A state machine can be stopped and restarted, or permanently destroyed.

machine.stop()          // suspendable; pauses the machine, transitions are not processed
machine.stopBlocking()  // blocking analog — do NOT call from a listener callback (deadlock risk)

machine.restart()       // stop() + start() in one call; optional argument passed to start()
machine.restartBlocking()

machine.destroy()       // terminal: clears all listeners, states and transitions
machine.destroyBlocking()

Relevant state properties:

Property Meaning
isRunning true while the machine is started and not stopped
isDestroyed true after destroy() — machine is unusable at this point
hasProcessedEvents true if any event beyond StartEvent has been processed

stopBlocking() must not be called from inside a listener callback when using a single-threaded CoroutineScope — it will deadlock. Use stop() (suspendable) instead.

Listeners

Convenience extension functions (onTransitionTriggered {}, onStateEntry {}, etc.) are available as shortcuts. For cases where a single object should handle all machine notifications, implement StateMachine.Listener directly:

machine.addListener(object : StateMachine.Listener {
    override suspend fun onStarted(transitionParams: TransitionParams<*>) {}
    override suspend fun onTransitionTriggered(transitionParams: TransitionParams<*>) {}
    override suspend fun onTransitionComplete(activeStates: Set<IState>, transitionParams: TransitionParams<*>) {}
    override suspend fun onStateEntry(state: IState, transitionParams: TransitionParams<*>) {}
    override suspend fun onStateExit(state: IState, transitionParams: TransitionParams<*>) {}
    override suspend fun onStateFinished(state: IState, transitionParams: TransitionParams<*>) {}
    override suspend fun onStopped() {}
    override suspend fun onDestroyed() {}
})

All methods have default empty implementations — override only the ones you need. machine.removeListener(listener) removes a previously added listener.


This site uses Just the Docs, a documentation theme for Jekyll.