State Machines
Playing and changing inputs in state machines
For more information on designing and building state machines in Rive, please refer to the editor's state machine section.
Rive's state machines provide a way to combine a set of animations and manage the transition between them through a series of inputs that can be programmatically controlled. Once a state machine is instantiated and playing, transitioning states can be accomplished by changing boolean or double value inputs, or firing triggers. The effects of these will be dependent on how the state machine has been configured in the editor.

Playing state machines

State machines are instantiated in much the same manner as animations: provide the state machine name to the Rive object:
Web
React
Flutter
Angular
1
const r = new rive.Rive({
2
src: 'https://cdn.rive.app/animations/vehicles.riv',
3
canvas: document.getElementById('canvas'),
4
autoplay: true,
5
stateMachines: 'bumpy',
6
fit: rive.Fit.cover,
7
});
Copied!
1
// State Machine require the useRive hook.
2
export default function Simple() {
3
const { RiveComponent } = useRive({
4
src: 'https://cdn.rive.app/animations/vehicles.riv',
5
stateMachines: "weather",
6
autoplay: true,
7
});
8
9
return <RiveComponent />;
10
}
Copied!
1
RiveAnimation.network(
2
'https://cdn.rive.app/animations/vehicles.riv',
3
fit: BoxFit.cover,
4
stateMachines: ['bumpy'],
5
);
Copied!
1
<canvas riv="vehicles" width="500" height="500" fit="cover">
2
<riv-state-machine name="bumpy" play></riv-state-machine>
3
</canvas>
Copied!
Once the Rive file is loaded and instantiated, the state machine can be queried for inputs, and these inputs can then be read from and written to, and in the case of triggers, fired.
Web
React
Flutter
Angular
The web runtime provides an onLoad callback that's run when the Rive file is loaded and ready for use. We use this here to ensure that the state machine is instantiated when we query for inputs.
1
<div id="button">
2
<canvas id="canvas" width="1000" height="500"></canvas>
3
</div>
4
<script src="/dist/rive.min.js"></script>
5
<script>
6
const button = document.getElementById('button');
7
8
const r = new rive.Rive({
9
src: 'https://cdn.rive.app/animations/vehicles.riv',
10
canvas: document.getElementById('canvas'),
11
autoplay: true,
12
stateMachines: 'bumpy',
13
fit: rive.Fit.cover,
14
onLoad: (_) => {
15
const inputs = r.stateMachineInputs('bumpy');
16
const bumpTrigger = inputs.find(i => i.name === 'bump');
17
button.onclick = () => bumpTrigger.fire();
18
},
19
});
20
</script>
Copied!
We use the stateMachineInputs function on the Rive object to retrieve the inputs. Each input will have a name and type. There are three types:
  • StateMachineInputType.Trigger which has a fire() function
  • StateMachineInputType.Number which has a value number property
  • StateMachineInputType.Boolean which has a value boolean property
1
const inputs = r.stateMachineInputs('bumpy');
2
inputs.forEach(i => {
3
const inputName = i.name;
4
const inputType = i.type;
5
switch(inputType) {
6
case rive.StateMachineInputType.Trigger:
7
i.fire();
8
break;
9
case rive.StateMachineInputType.Number:
10
i.value = 42;
11
break;
12
case rive.StateMachineInputType.Boolean:
13
i.value = true;
14
break;
15
}
16
});
Copied!
We can set a callback to determine when the state machine changes state. onStateChange provides an event parameter that gives us the name of the current state:
1
const r = new rive.Rive({
2
src: 'https://cdn.rive.app/animations/vehicles.riv',
3
canvas: document.getElementById('canvas'),
4
autoplay: true,
5
stateMachines: 'bumpy',
6
onStateChange: (event) => {
7
stateName.innerHTML = event.data[0];
8
},
9
});
Copied!
The react runtime provides the useStateMachineInput hook to make the process of retrieving a state machine input much simplier.
1
import { useRive, useStateMachineInput } from "rive-react";
2
3
export default function Simple() {
4
const { rive, RiveComponent } = useRive({
5
src: "https://cdn.rive.app/animations/vehicles.riv",
6
stateMachines: "bumpy",
7
autoplay: true,
8
});
9
10
const bumpInput = useStateMachineInput(rive, "bumpy", "bump");
11
12
return (
13
<RiveComponent
14
style={{ height: "1000px" }}
15
onClick={() => bumpInput && bumpInput.fire()}
16
/>
17
);
18
}
Copied!
The above example show a Trigger input. The three types of inputs are:
  • StateMachineInputType.Trigger which has a fire() function
  • StateMachineInputType.Number which has a value number property
  • StateMachineInputType.Boolean which has a value boolean property
We can set a callback to determine when the state machine changes.
1
import { useEffect } from 'react';
2
import { useRive, useStateMachineInput } from "rive-react";
3
4
export default function Simple() {
5
const { rive, RiveComponent } = useRive({
6
src: "https://cdn.rive.app/animations/vehicles.riv",
7
stateMachines: "bumpy",
8
autoplay: true,
9
// We can pass the call back to the `useRive` hook
10
onStateChange: (event) => {
11
console.log(event.data[0]);
12
}
13
});
14
15
const bumpInput = useStateMachineInput(rive, "bumpy", "bump");
16
17
// We can also pass the callback to the rive object once it has loaded.
18
// NOTE: If you pass the callback to the rive object, you do not need to
19
// pass it to the useRive hook as well, and vice versa.
20
useEffect(() => {
21
if (rive) {
22
rive.on('statechange', (event) => {
23
console.log(event.data[0]);
24
});
25
}
26
}, [rive]);
27
28
return (
29
<RiveComponent
30
style={{ height: "1000px" }}
31
onClick={() => bumpInput && bumpInput.fire()}
32
/>
33
);
34
}
Copied!
State machine controllers are used to retrieve a state machine's inputs which can then be used to interact with, and drive the state of, the state machine.
State machine controllers require a reference to an artboard when being instantiated. The RiveAnimation widget provides a callback onInit(Artboard artboard) that is called when the Rive file has loaded and is initialized for playback:
1
void _onRiveInit(Artboard artboard) {}
2
3
RiveAnimation.network(
4
'https://cdn.rive.app/animations/vehicles.riv',
5
fit: BoxFit.cover,
6
onInit: _onRiveInit,
7
);
Copied!
Now that we have an artboard, we can create an instance of a StateMachineController and from that, retrieve the inputs we're interested in. Specific inputs can be retrieved using findInput() or all inputs with the inputs property.
1
SMITrigger? _bump;
2
3
void _onRiveInit(Artboard artboard) {
4
final controller = StateMachineController.fromArtboard(artboard, 'bumpy');
5
artboard.addController(controller!);
6
_bump = controller.findInput<bool>('bump') as SMITrigger;
7
}
Copied!
Here we retrieve the bump input, which is an SMITrigger. This type of input has a fire() method to activate the trigger. There are two other input types: SMIBool and SMINumber. These both have a value property that can get and set the value.
1
class SimpleStateMachine extends StatefulWidget {
2
const SimpleStateMachine({Key? key}) : super(key: key);
3
4
@override
5
_SimpleStateMachineState createState() => _SimpleStateMachineState();
6
}
7
8
class _SimpleStateMachineState extends State<SimpleStateMachine> {
9
SMITrigger? _bump;
10
11
void _onRiveInit(Artboard artboard) {
12
final controller = StateMachineController.fromArtboard(artboard, 'bumpy');
13
artboard.addController(controller!);
14
_bump = controller.findInput<bool>('bump') as SMITrigger;
15
}
16
17
void _hitBump() => _bump?.fire();
18
19
@override
20
Widget build(BuildContext context) {
21
return Scaffold(
22
appBar: AppBar(
23
title: const Text('Simple Animation'),
24
),
25
body: Center(
26
child: GestureDetector(
27
child: RiveAnimation.network(
28
'https://cdn.rive.app/animations/vehicles.riv',
29
fit: BoxFit.cover,
30
onInit: _onRiveInit,
31
),
32
onTap: _hitBump,
33
),
34
),
35
);
36
}
37
}
Copied!
In the complete example above, every time the RiveAnimation is tapped, it fires the bump input trigger, and the state machine reacts appropriately, in this case mixing in a bump animation.
If you'd like to know which state a state machine is in, or when a state machine transitions to another state, you can provide a callback to StateMachineController. The callback has the name of the state machine and the name of the animation associated with the current state:
1
void _onRiveInit(Artboard artboard) {
2
final controller = StateMachineController.fromArtboard(
3
artboard,
4
'bumpy',
5
onStateChange: _onStateChange,
6
);
7
artboard.addController(controller!);
8
_bump = controller.findInput<bool>('bump') as SMITrigger;
9
}
10
11
void _onStateChange(
12
String stateMachineName,
13
String stateName,
14
) =>
15
setState(
16
() => message = 'State Changed in $stateMachineName to $stateName',
17
);
Copied!

State Machine

You can listen on event & manipulate the state machine animation as any other animation:
1
<canvas riv="vehicles" width="500" height="500" fit="cover">
2
<riv-state-machine name="bumpy" play speed="2" (load)="onload($event)"></riv-state-machine>
3
</canvas>
Copied!

Number & Boolean Input

If the input is a number or a boolean you can use the value
1
<canvas riv="vehicles" width="500" height="500" fit="cover">
2
<riv-state-machine name="bumpy" play (stateChange)="showStates($event)">
3
<riv-input name="level" [value]="value"><riv-input>
4
</riv-state-machine>
5
</canvas>
6
7
<input type="radio" formControl="level" value="0"> Car
8
<input type="radio" formControl="level" value="1"> Train
9
<input type="radio" formControl="level" value="2"> Airplane
Copied!
The stateChange output will display the list of state changed during the same frame.

Trigger Input

If the input is a trigger you can access it with the export as rivInput:
1
<canvas riv="vehicles">
2
<riv-state-machine name="bumpy" play>
3
<riv-input #trigger="rivInput" name="bump" (change)="showInput($event)"><riv-input>
4
</riv-state-machine>
5
</canvas>
6
7
<button (click)="trigger.fire()">Bump</button>
Copied!
You can listen to the change in the input with the change Ouput.
Last modified 1mo ago
Copy link