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 ## [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 ## [0.7.19] - 2021-06-18 12:00:00
- BREAKING CHANGE: onInit callback now takes an artboard as a parameter - BREAKING CHANGE: onInit callback now takes an artboard as a parameter
- Adds simple state machine example - Adds simple state machine example
## [0.7.18] - 2021-06-14 12:00:00 ## [0.7.18] - 2021-06-14 12:00:00
- Adds ability to pass controllers into RiveAnimation widgets - Adds ability to pass controllers into RiveAnimation widgets
- Adds autoplay option to SimpleAnimation controller - 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 ```yaml
dependencies: dependencies:
rive: ^0.7.20 rive: ^0.7.21
``` ```
## Quick Start ## Quick Start

View File

@ -15,6 +15,9 @@ class _LittleMachineState extends State<LittleMachine> {
/// Tracks if the animation is playing by whether controller is running. /// Tracks if the animation is playing by whether controller is running.
bool get isPlaying => _controller?.isActive ?? false; bool get isPlaying => _controller?.isActive ?? false;
/// Message that displays when state has changed
String stateChangeMessage = '';
Artboard? _riveArtboard; Artboard? _riveArtboard;
StateMachineController? _controller; StateMachineController? _controller;
SMIInput<bool>? _trigger; 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 // The artboard is the root of the animation and gets drawn in the
// Rive widget. // Rive widget.
final artboard = file.mainArtboard; final artboard = file.mainArtboard;
var controller = var controller = StateMachineController.fromArtboard(
StateMachineController.fromArtboard(artboard, 'State Machine 1'); artboard,
'State Machine 1',
onStateChange: _onStateChange,
);
if (controller != null) { if (controller != null) {
artboard.addController(controller); artboard.addController(controller);
_trigger = controller.findInput('Trigger 1'); _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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -71,6 +83,9 @@ class _LittleMachineState extends State<LittleMachine> {
artboard: _riveArtboard!, 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. /// A list of inputs available in the StateMachine.
Iterable<SMIInput> get inputs => _inputs; Iterable<SMIInput> get inputs => _inputs;
StateMachineController(StateMachine stateMachine) : super(stateMachine) { StateMachineController(
StateMachine stateMachine, {
core.OnStateChange? onStateChange,
}) : super(stateMachine, onStateChange: onStateChange) {
isActive = true; isActive = true;
for (final input in stateMachine.inputs) { for (final input in stateMachine.inputs) {
switch (input.coreType) { switch (input.coreType) {
@ -140,10 +143,13 @@ class StateMachineController extends core.StateMachineController {
/// [stateMachineName]. Returns the [StateMachineController] or null if no /// [stateMachineName]. Returns the [StateMachineController] or null if no
/// [StateMachine] with [stateMachineName] is found. /// [StateMachine] with [stateMachineName] is found.
static StateMachineController? fromArtboard( static StateMachineController? fromArtboard(
Artboard artboard, String stateMachineName) { Artboard artboard,
String stateMachineName, {
core.OnStateChange? onStateChange,
}) {
for (final animation in artboard.animations) { for (final animation in artboard.animations) {
if (animation is StateMachine && animation.name == stateMachineName) { if (animation is StateMachine && animation.name == stateMachineName) {
return StateMachineController(animation); return StateMachineController(animation, onStateChange: onStateChange);
} }
} }
return null; return null;

View File

@ -1,5 +1,6 @@
import 'dart:collection'; import 'dart:collection';
import 'package:flutter/scheduler.dart';
import 'package:rive/src/core/core.dart'; import 'package:rive/src/core/core.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:rive/src/rive_core/animation/animation_state_instance.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/animation/state_transition.dart';
import 'package:rive/src/rive_core/rive_animation_controller.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 { class LayerController {
final StateMachineLayer layer; final StateMachineLayer layer;
final StateInstance anyStateInstance; final StateInstance anyStateInstance;
@ -23,7 +34,11 @@ class LayerController {
double _mix = 1.0; double _mix = 1.0;
double _mixFrom = 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), : assert(layer.anyState != null),
anyStateInstance = layer.anyState!.makeInstance() { anyStateInstance = layer.anyState!.makeInstance() {
_changeState(layer.entryState); _changeState(layer.entryState);
@ -167,6 +182,10 @@ class LayerController {
// Make sure to reset _waitingForExit to false if we succeed at taking a // Make sure to reset _waitingForExit to false if we succeed at taking a
// transition. // transition.
_waitingForExit = false; _waitingForExit = false;
// State has changed, fire the callback if there's one
if (_currentState != null) {
onLayerStateChange?.call(_currentState!.state);
}
return true; return true;
} else if (allowed == AllowTransition.waitingForExit) { } else if (allowed == AllowTransition.waitingForExit) {
_waitingForExit = true; _waitingForExit = true;
@ -179,9 +198,14 @@ class LayerController {
class StateMachineController extends RiveAnimationController<CoreContext> { class StateMachineController extends RiveAnimationController<CoreContext> {
final StateMachine stateMachine; final StateMachine stateMachine;
final inputValues = HashMap<int, dynamic>(); final inputValues = HashMap<int, dynamic>();
StateMachineController(this.stateMachine);
final layerControllers = <LayerController>[]; 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() { void _clearLayerControllers() {
for (final layer in layerControllers) { for (final layer in layerControllers) {
layer.dispose(); layer.dispose();
@ -189,12 +213,33 @@ class StateMachineController extends RiveAnimationController<CoreContext> {
layerControllers.clear(); 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 @override
bool init(CoreContext core) { bool init(CoreContext core) {
_clearLayerControllers(); _clearLayerControllers();
for (final layer in stateMachine.layers) { for (final layer in stateMachine.layers) {
layerControllers.add(LayerController(layer)); layerControllers.add(LayerController(
layer,
onLayerStateChange: _onStateChange,
));
} }
// Make sure triggers are all reset. // Make sure triggers are all reset.