Interactivity powered by state machines

DotLottie Android supports interactive animations with state machines for advanced user interactions. State machines allow you to create dynamic animations that respond to user input, gestures, and other events.

Creation

Learn how to create dotLotties with state machines here.

Loading State Machines

You can load state machines from .lottie files or from JSON data:

// Load state machine by ID from .lottie file
dotLottieAnimationView.stateMachineLoad("state_machine_id")
// Or load state machine from JSON data
dotLottieAnimationView.stateMachineLoadData(stateMachineJsonData)
// Start the state machine
dotLottieAnimationView.stateMachineStart()

Usage

The following example demonstrates how to use a state machine with a Lottie animation in a Composable function. The state machine is controlled by a DotLottieController, which is used to load the state machine, start and stop it, and post events to it. The controller also provides a listener for state machine events.

Exploding Pigeon example

@Composable
fun StateMachineExample() {
// Controller for the DotLottieAnimation composable
val controller = remember { DotLottieController() }
// Listener for the state machine events
val stateListener = remember {
object : StateMachineEventListener {
// Logs the transition from one state to another
override fun onTransition(previousState: String, newState: String) {
Log.i("DotLottie", "Transition: $previousState -> $newState")
}
// Logs when a state is exited
override fun onStateExit(leavingState: String) {
Log.i("DotLottie", "Exit: $leavingState")
}
// Logs when a state is entered
override fun onStateEntered(enteringState: String) {
Log.i("DotLottie", "Enter: $enteringState")
}
}
}
// Adds the state machine event listener to the controller
LaunchedEffect(UInt) {
controller.addStateMachineEventListener(stateListener)
}
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Row {
DotLottieAnimation(
source = DotLottieSource.Asset("exploding_pigeon.lottie"),
autoplay = true,
loop = false,
controller = controller,
)
}
Row {
Column {
// Button to load and start the state machine
Button(onClick = {
val result = controller.stateMachineLoad("pigeon_fsm")
if (result) {
controller.stateMachineStart()
}
}) {
Text(text = "State Machine")
}
// Button to fire an "explosion" event to the state machine. You can also bind this to a gesture or other event.
Button(onClick = {
controller.stateMachineFireEvent("explosion")
}) {
Text(text = "Explosion")
}
// Button to stop the state machine
Button(onClick = {
controller.stateMachineStop()
}) {
Text(text = "Stop")
}
}
}
}
}

Exploding Pigeon state machine

{
"descriptor": { "id": "pigeon_fsm", "initial": 0 },
"states": [
{
"name": "pigeon",
"type": "PlaybackState",
"mode": "Forward",
"speed": 1,
"use_frame_interpolation": true,
"autoplay": true,
"loop": true,
"marker": "bird"
},
{
"name": "explosion",
"type": "PlaybackState",
"mode": "Forward",
"autoplay": true,
"speed": 0.5,
"loop": false,
"marker": "explosion"
},
{
"name": "feathers",
"type": "PlaybackState",
"autoplay": true,
"speed": 1,
"loop": false,
"marker": "feathers"
}
],
"transitions": [
{
"type": "Transition",
"from_state": 0,
"to_state": 1,
"string_event": { "value": "explosion" }
},
{
"type": "Transition",
"from_state": 1,
"to_state": 2,
"on_complete_event": {}
},
{
"type": "Transition",
"from_state": 2,
"to_state": 0,
"on_complete_event": {}
}
],
...
}

How it works

  • The state machine has three states: pigeon, explosion, and feathers.
  • The state machine starts in the pigeon state.
  • When the explosion event is fired to the state machine, it transitions to the explosion state.
  • When the explosion state completes, it transitions to the feathers state.
  • When the feathers state completes, it transitions back to the pigeon state.

State Machine Events

Monitor state machine events by implementing StateMachineEventListener:

private val stateMachineListener = object : StateMachineEventListener {
override fun onStart() {
Log.d(TAG, "State machine started")
}
override fun onStop() {
Log.d(TAG, "State machine stopped")
}
override fun onStateEntered(enteringState: String) {
Log.d(TAG, "Entered state: $enteringState")
}
override fun onStateExit(leavingState: String) {
Log.d(TAG, "Exited state: $leavingState")
}
override fun onTransition(previousState: String, newState: String) {
Log.d(TAG, "Transitioned from $previousState to $newState")
}
override fun onNumericInputValueChange(inputName: String, oldValue: Float, newValue: Float) {
Log.d(TAG, "Input $inputName changed from $oldValue to $newValue")
}
override fun onStringInputValueChange(inputName: String, oldValue: String, newValue: String) {
Log.d(TAG, "Input $inputName changed from $oldValue to $newValue")
}
override fun onBooleanInputValueChange(inputName: String, oldValue: Boolean, newValue: Boolean) {
Log.d(TAG, "Input $inputName changed from $oldValue to $newValue")
}
override fun onInputFired(inputName: String) {
Log.d(TAG, "Input fired: $inputName")
}
override fun onCustomEvent(message: String) {
Log.d(TAG, "Custom event: $message")
}
override fun onError(message: String) {
Log.e(TAG, "State machine error: $message")
}
}
// Add the listener
dotLottieAnimationView.addStateMachineEventListener(stateMachineListener)

State Machine Input Controls

State machines can have various types of inputs that you can control programmatically:

// Set input values
dotLottieAnimationView.stateMachineSetNumericInput("inputName", 42.0f)
dotLottieAnimationView.stateMachineSetStringInput("inputName", "value")
dotLottieAnimationView.stateMachineSetBooleanInput("inputName", true)
// Get input values
val numericValue = dotLottieAnimationView.stateMachineGetNumericInput("inputName")
val stringValue = dotLottieAnimationView.stateMachineGetStringInput("inputName")
val booleanValue = dotLottieAnimationView.stateMachineGetBooleanInput("inputName")
// Fire events
dotLottieAnimationView.stateMachineFireEvent("eventName")
// Get current state
val currentState = dotLottieAnimationView.stateMachineCurrentState()
// Stop state machine
dotLottieAnimationView.stateMachineStop()

Advanced State Machine Features

Gesture Events

You can post gesture events directly to the state machine:

// For traditional UI
dotLottieAnimationView.stateMachinePostEvent(Event.Click(x, y))
// For Jetpack Compose
controller.stateMachinePostEvent(Event.Click(x, y))

URL Policy

When starting a state machine, you can specify a URL policy and callback:

dotLottieAnimationView.stateMachineStart(OpenUrlPolicy, { url ->
// Handle URL opening
})