Adds onStateChange callback

This commit is contained in:
matt Sullivan
2021-06-21 14:34:56 -07:00
parent 3ba7956f67
commit 84d99621c9
5 changed files with 81 additions and 10 deletions

View File

@ -1,8 +1,13 @@
## [0.7.21] - 2021-06-21 14:00:00
- Adds onStateChange callback to state machine controllers
## [0.7.20] - 2021-06-19 19:01:10
- Quick start fixes in README.md.
- Quick start fixes in README.md
## [0.7.19] - 2021-06-18 12:00:00
- BREAKING CHANGE: onInit callback now takes an artboard as a parameter
- Adds simple state machine example
## [0.7.18] - 2021-06-14 12:00:00
- Adds ability to pass controllers into RiveAnimation widgets
- Adds autoplay option to SimpleAnimation controller

View File

@ -8,7 +8,7 @@ Runtime docs are available in [Rive's help center](https://help.rive.app/runtime
```yaml
dependencies:
rive: ^0.7.20
rive: ^0.7.21
```
## Quick Start

View File

@ -15,6 +15,9 @@ class _LittleMachineState extends State<LittleMachine> {
/// Tracks if the animation is playing by whether controller is running.
bool get isPlaying => _controller?.isActive ?? false;
/// Message that displays when state has changed
String stateChangeMessage = '';
Artboard? _riveArtboard;
StateMachineController? _controller;
SMIInput<bool>? _trigger;
@ -33,8 +36,11 @@ class _LittleMachineState extends State<LittleMachine> {
// The artboard is the root of the animation and gets drawn in the
// Rive widget.
final artboard = file.mainArtboard;
var controller =
StateMachineController.fromArtboard(artboard, 'State Machine 1');
var controller = StateMachineController.fromArtboard(
artboard,
'State Machine 1',
onStateChange: _onStateChange,
);
if (controller != null) {
artboard.addController(controller);
_trigger = controller.findInput('Trigger 1');
@ -44,6 +50,12 @@ class _LittleMachineState extends State<LittleMachine> {
);
}
/// Do something when the state machine changes state
void _onStateChange(String stateMachineName, String stateName) => setState(
() => stateChangeMessage =
'State Changed in $stateMachineName to $stateName',
);
@override
Widget build(BuildContext context) {
return Scaffold(
@ -71,6 +83,9 @@ class _LittleMachineState extends State<LittleMachine> {
artboard: _riveArtboard!,
),
),
const SizedBox(height: 10),
Text('$stateChangeMessage'),
const SizedBox(height: 10),
],
),
),

View File

@ -119,7 +119,10 @@ class StateMachineController extends core.StateMachineController {
/// A list of inputs available in the StateMachine.
Iterable<SMIInput> get inputs => _inputs;
StateMachineController(StateMachine stateMachine) : super(stateMachine) {
StateMachineController(
StateMachine stateMachine, {
core.OnStateChange? onStateChange,
}) : super(stateMachine, onStateChange: onStateChange) {
isActive = true;
for (final input in stateMachine.inputs) {
switch (input.coreType) {
@ -140,10 +143,13 @@ class StateMachineController extends core.StateMachineController {
/// [stateMachineName]. Returns the [StateMachineController] or null if no
/// [StateMachine] with [stateMachineName] is found.
static StateMachineController? fromArtboard(
Artboard artboard, String stateMachineName) {
Artboard artboard,
String stateMachineName, {
core.OnStateChange? onStateChange,
}) {
for (final animation in artboard.animations) {
if (animation is StateMachine && animation.name == stateMachineName) {
return StateMachineController(animation);
return StateMachineController(animation, onStateChange: onStateChange);
}
}
return null;

View File

@ -1,5 +1,6 @@
import 'dart:collection';
import 'package:flutter/scheduler.dart';
import 'package:rive/src/core/core.dart';
import 'package:flutter/foundation.dart';
import 'package:rive/src/rive_core/animation/animation_state_instance.dart';
@ -12,6 +13,16 @@ import 'package:rive/src/rive_core/animation/state_machine_layer.dart';
import 'package:rive/src/rive_core/animation/state_transition.dart';
import 'package:rive/src/rive_core/rive_animation_controller.dart';
import 'animation/animation_state.dart';
import 'animation/entry_state.dart';
import 'animation/exit_state.dart';
/// Callback signature for satate machine state changes
typedef OnStateChange = void Function(String, String);
/// Callback signature for layer state changes
typedef OnLayerStateChange = void Function(LayerState);
class LayerController {
final StateMachineLayer layer;
final StateInstance anyStateInstance;
@ -23,7 +34,11 @@ class LayerController {
double _mix = 1.0;
double _mixFrom = 1.0;
LayerController(this.layer)
/// Optional callback which is called when a state changes
/// Takes the state machine name and state name
final OnLayerStateChange? onLayerStateChange;
LayerController(this.layer, {this.onLayerStateChange})
: assert(layer.anyState != null),
anyStateInstance = layer.anyState!.makeInstance() {
_changeState(layer.entryState);
@ -167,6 +182,10 @@ class LayerController {
// Make sure to reset _waitingForExit to false if we succeed at taking a
// transition.
_waitingForExit = false;
// State has changed, fire the callback if there's one
if (_currentState != null) {
onLayerStateChange?.call(_currentState!.state);
}
return true;
} else if (allowed == AllowTransition.waitingForExit) {
_waitingForExit = true;
@ -179,9 +198,14 @@ class LayerController {
class StateMachineController extends RiveAnimationController<CoreContext> {
final StateMachine stateMachine;
final inputValues = HashMap<int, dynamic>();
StateMachineController(this.stateMachine);
final layerControllers = <LayerController>[];
/// Optional callback for state changes
final OnStateChange? onStateChange;
/// Constructor that takes a state machine and optional state change callback
StateMachineController(this.stateMachine, {this.onStateChange});
void _clearLayerControllers() {
for (final layer in layerControllers) {
layer.dispose();
@ -189,12 +213,33 @@ class StateMachineController extends RiveAnimationController<CoreContext> {
layerControllers.clear();
}
/// Handles state change callbacks
void _onStateChange(LayerState layerState) =>
SchedulerBinding.instance?.addPostFrameCallback((_) {
String stateName = 'unknown';
print('Layer state type ${layerState.runtimeType}');
if (layerState is AnimationState && layerState.animation != null) {
stateName = layerState.animation!.name;
} else if (layerState is EntryState) {
stateName = 'EntryState';
} else if (layerState is AnyState) {
stateName = 'EntryState';
} else if (layerState is ExitState) {
stateName = 'ExitState';
}
onStateChange?.call(stateMachine.name, stateName);
});
@override
bool init(CoreContext core) {
_clearLayerControllers();
for (final layer in stateMachine.layers) {
layerControllers.add(LayerController(layer));
layerControllers.add(LayerController(
layer,
onLayerStateChange: _onStateChange,
));
}
// Make sure triggers are all reset.