Merge pull request #85 from rive-app/state_machine_improvements

State machine improvements
This commit is contained in:
Luigi Rosso
2021-04-13 15:01:21 -07:00
committed by GitHub
17 changed files with 493 additions and 86 deletions

View File

@ -1,3 +1,8 @@
## [0.7.2] - 2021-04-12 16:57:54
- Breaking change! StateMachineInput has been renamed to SMIInput to follow conventions in other runtimes and clearly disambiguate between core.StateMachineInput (the backing type in Rive's core system, which is not explicitly exposed to this runtime) and the input instances which should be used by controllers in the Flutter ecosystem.
- New examples showing use of number, boolean, and trigger inputs.
## [0.7.1] - 2021-04-06 16:19:04
- Fixes an issue with hold keyframes not loading properly.

Binary file not shown.

BIN
example/assets/skills.riv Normal file

Binary file not shown.

View File

@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:rive/rive.dart';
/// An example showing how to drive two boolean state machine inputs.
class ExampleStateMachine extends StatefulWidget {
const ExampleStateMachine({Key? key}) : super(key: key);
@ -16,8 +17,8 @@ class _ExampleStateMachineState extends State<ExampleStateMachine> {
Artboard? _riveArtboard;
StateMachineController? _controller;
StateMachineInput<bool>? _hoverInput;
StateMachineInput<bool>? _pressInput;
SMIInput<bool>? _hoverInput;
SMIInput<bool>? _pressInput;
@override
void initState() {
@ -50,7 +51,7 @@ class _ExampleStateMachineState extends State<ExampleStateMachine> {
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(
title: const Text('State Machine Example'),
title: const Text('Button State Machine'),
),
body: Center(
child: _riveArtboard == null

View File

@ -0,0 +1,80 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:rive/rive.dart';
/// An example showing how to drive a StateMachine via a trigger input.
class LittleMachine extends StatefulWidget {
const LittleMachine({Key? key}) : super(key: key);
@override
_LittleMachineState createState() => _LittleMachineState();
}
class _LittleMachineState extends State<LittleMachine> {
/// Tracks if the animation is playing by whether controller is running.
bool get isPlaying => _controller?.isActive ?? false;
Artboard? _riveArtboard;
StateMachineController? _controller;
SMIInput<bool>? _trigger;
@override
void initState() {
super.initState();
// Load the animation file from the bundle, note that you could also
// download this. The RiveFile just expects a list of bytes.
rootBundle.load('assets/little_machine.riv').then(
(data) async {
// Load the RiveFile from the binary data.
final file = RiveFile.import(data);
// 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');
if (controller != null) {
artboard.addController(controller);
_trigger = controller.findInput('Trigger 1');
}
setState(() => _riveArtboard = artboard);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(
title: const Text('Little Machine'),
),
body: Center(
child: _riveArtboard == null
? const SizedBox()
: GestureDetector(
onTapDown: (_) => _trigger?.value = true,
child: Column(
children: [
const SizedBox(height: 10),
const Text(
'Press to activate!',
style: TextStyle(
fontSize: 18,
),
),
const SizedBox(height: 10),
Expanded(
child: Rive(
artboard: _riveArtboard!,
),
),
],
),
),
),
);
}
}

View File

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:rive_example/example_animation.dart';
import 'package:rive_example/example_state_machine.dart';
import 'package:rive_example/little_machine.dart';
import 'package:rive_example/state_machine_skills.dart';
void main() => runApp(MaterialApp(
title: 'Navigation Basics',
@ -33,7 +35,7 @@ class Home extends StatelessWidget {
height: 10,
),
ElevatedButton(
child: const Text('State Machine'),
child: const Text('Button State Machine'),
onPressed: () {
Navigator.push(
context,
@ -43,6 +45,34 @@ class Home extends StatelessWidget {
);
},
),
const SizedBox(
height: 10,
),
ElevatedButton(
child: const Text('Skills Machine'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => const StateMachineSkills(),
),
);
},
),
const SizedBox(
height: 10,
),
ElevatedButton(
child: const Text('Little Machine'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => const LittleMachine(),
),
);
},
),
],
),
),

View File

@ -0,0 +1,92 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:rive/rive.dart';
/// An example showing how to drive a StateMachine via one numeric input.
class StateMachineSkills extends StatefulWidget {
const StateMachineSkills({Key? key}) : super(key: key);
@override
_StateMachineSkillsState createState() => _StateMachineSkillsState();
}
class _StateMachineSkillsState extends State<StateMachineSkills> {
/// Tracks if the animation is playing by whether controller is running.
bool get isPlaying => _controller?.isActive ?? false;
Artboard? _riveArtboard;
StateMachineController? _controller;
SMIInput<double>? _levelInput;
@override
void initState() {
super.initState();
// Load the animation file from the bundle, note that you could also
// download this. The RiveFile just expects a list of bytes.
rootBundle.load('assets/skills.riv').then(
(data) async {
// Load the RiveFile from the binary data.
final file = RiveFile.import(data);
// The artboard is the root of the animation and gets drawn in the
// Rive widget.
final artboard = file.mainArtboard;
var controller =
StateMachineController.fromArtboard(artboard, 'Designer\'s Test');
if (controller != null) {
artboard.addController(controller);
_levelInput = controller.findInput('Level');
}
setState(() => _riveArtboard = artboard);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(
title: const Text('Skills Machine'),
),
body: Center(
child: _riveArtboard == null
? const SizedBox()
: Stack(
children: [
Positioned.fill(
child: Rive(
artboard: _riveArtboard!,
),
),
Positioned.fill(
bottom: 100,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
ElevatedButton(
child: const Text('Beginner'),
onPressed: () => _levelInput?.value = 0,
),
const SizedBox(width: 10),
ElevatedButton(
child: const Text('Intermediate'),
onPressed: () => _levelInput?.value = 1,
),
const SizedBox(width: 10),
ElevatedButton(
child: const Text('Expert'),
onPressed: () => _levelInput?.value = 2,
),
],
),
),
],
),
),
);
}
}

View File

@ -0,0 +1,41 @@
/// Core automatically generated
/// lib/src/generated/animation/state_machine_number_base.dart.
/// Do not modify manually.
import 'package:rive/src/generated/animation/state_machine_component_base.dart';
import 'package:rive/src/generated/animation/state_machine_input_base.dart';
import 'package:rive/src/rive_core/animation/state_machine_input.dart';
abstract class StateMachineNumberBase extends StateMachineInput {
static const int typeKey = 56;
@override
int get coreType => StateMachineNumberBase.typeKey;
@override
Set<int> get coreTypes => {
StateMachineNumberBase.typeKey,
StateMachineInputBase.typeKey,
StateMachineComponentBase.typeKey
};
/// --------------------------------------------------------------------------
/// Value field with key 140.
static const double valueInitialValue = 0;
double _value = valueInitialValue;
static const int valuePropertyKey = 140;
double get value => _value;
/// Change the [_value] field value.
/// [valueChanged] will be invoked only if the field's value has changed.
set value(double value) {
if (_value == value) {
return;
}
double from = _value;
_value = value;
if (hasValidated) {
valueChanged(from, value);
}
}
void valueChanged(double from, double to);
}

View File

@ -0,0 +1,41 @@
/// Core automatically generated
/// lib/src/generated/animation/transition_number_condition_base.dart.
/// Do not modify manually.
import 'package:rive/src/generated/animation/transition_condition_base.dart';
import 'package:rive/src/generated/animation/transition_value_condition_base.dart';
import 'package:rive/src/rive_core/animation/transition_value_condition.dart';
abstract class TransitionNumberConditionBase extends TransitionValueCondition {
static const int typeKey = 70;
@override
int get coreType => TransitionNumberConditionBase.typeKey;
@override
Set<int> get coreTypes => {
TransitionNumberConditionBase.typeKey,
TransitionValueConditionBase.typeKey,
TransitionConditionBase.typeKey
};
/// --------------------------------------------------------------------------
/// Value field with key 157.
static const double valueInitialValue = 0;
double _value = valueInitialValue;
static const int valuePropertyKey = 157;
double get value => _value;
/// Change the [_value] field value.
/// [valueChanged] will be invoked only if the field's value has changed.
set value(double value) {
if (_value == value) {
return;
}
double from = _value;
_value = value;
if (hasValidated) {
valueChanged(from, value);
}
}
void valueChanged(double from, double to);
}

View File

@ -21,13 +21,13 @@ import 'package:rive/src/generated/animation/linear_animation_base.dart';
import 'package:rive/src/generated/animation/state_machine_base.dart';
import 'package:rive/src/generated/animation/state_machine_bool_base.dart';
import 'package:rive/src/generated/animation/state_machine_component_base.dart';
import 'package:rive/src/generated/animation/state_machine_double_base.dart';
import 'package:rive/src/generated/animation/state_machine_layer_base.dart';
import 'package:rive/src/generated/animation/state_machine_number_base.dart';
import 'package:rive/src/generated/animation/state_machine_trigger_base.dart';
import 'package:rive/src/generated/animation/state_transition_base.dart';
import 'package:rive/src/generated/animation/transition_bool_condition_base.dart';
import 'package:rive/src/generated/animation/transition_condition_base.dart';
import 'package:rive/src/generated/animation/transition_double_condition_base.dart';
import 'package:rive/src/generated/animation/transition_number_condition_base.dart';
import 'package:rive/src/generated/animation/transition_trigger_condition_base.dart';
import 'package:rive/src/generated/animation/transition_value_condition_base.dart';
import 'package:rive/src/generated/artboard_base.dart';
@ -81,12 +81,12 @@ import 'package:rive/src/rive_core/animation/keyframe_id.dart';
import 'package:rive/src/rive_core/animation/linear_animation.dart';
import 'package:rive/src/rive_core/animation/state_machine.dart';
import 'package:rive/src/rive_core/animation/state_machine_bool.dart';
import 'package:rive/src/rive_core/animation/state_machine_double.dart';
import 'package:rive/src/rive_core/animation/state_machine_layer.dart';
import 'package:rive/src/rive_core/animation/state_machine_number.dart';
import 'package:rive/src/rive_core/animation/state_machine_trigger.dart';
import 'package:rive/src/rive_core/animation/state_transition.dart';
import 'package:rive/src/rive_core/animation/transition_bool_condition.dart';
import 'package:rive/src/rive_core/animation/transition_double_condition.dart';
import 'package:rive/src/rive_core/animation/transition_number_condition.dart';
import 'package:rive/src/rive_core/animation/transition_trigger_condition.dart';
import 'package:rive/src/rive_core/artboard.dart';
import 'package:rive/src/rive_core/backboard.dart';
@ -129,14 +129,16 @@ class RiveCoreContext {
return AnimationState();
case KeyedObjectBase.typeKey:
return KeyedObject();
case StateMachineNumberBase.typeKey:
return StateMachineNumber();
case TransitionTriggerConditionBase.typeKey:
return TransitionTriggerCondition();
case KeyedPropertyBase.typeKey:
return KeyedProperty();
case StateMachineDoubleBase.typeKey:
return StateMachineDouble();
case KeyFrameIdBase.typeKey:
return KeyFrameId();
case TransitionNumberConditionBase.typeKey:
return TransitionNumberCondition();
case AnyStateBase.typeKey:
return AnyState();
case StateMachineLayerBase.typeKey:
@ -145,8 +147,6 @@ class RiveCoreContext {
return Animation();
case CubicInterpolatorBase.typeKey:
return CubicInterpolator();
case TransitionDoubleConditionBase.typeKey:
return TransitionDoubleCondition();
case StateTransitionBase.typeKey:
return StateTransition();
case KeyFrameDoubleBase.typeKey:
@ -262,26 +262,26 @@ class RiveCoreContext {
object.objectId = value;
}
break;
case TransitionConditionBase.inputIdPropertyKey:
if (object is TransitionConditionBase && value is int) {
object.inputId = value;
}
break;
case StateMachineComponentBase.namePropertyKey:
if (object is StateMachineComponentBase && value is String) {
object.name = value;
}
break;
case StateMachineNumberBase.valuePropertyKey:
if (object is StateMachineNumberBase && value is double) {
object.value = value;
}
break;
case TransitionConditionBase.inputIdPropertyKey:
if (object is TransitionConditionBase && value is int) {
object.inputId = value;
}
break;
case KeyedPropertyBase.propertyKeyPropertyKey:
if (object is KeyedPropertyBase && value is int) {
object.propertyKey = value;
}
break;
case StateMachineDoubleBase.valuePropertyKey:
if (object is StateMachineDoubleBase && value is double) {
object.value = value;
}
break;
case KeyFrameBase.framePropertyKey:
if (object is KeyFrameBase && value is int) {
object.frame = value;
@ -302,6 +302,16 @@ class RiveCoreContext {
object.value = value;
}
break;
case TransitionValueConditionBase.opValuePropertyKey:
if (object is TransitionValueConditionBase && value is int) {
object.opValue = value;
}
break;
case TransitionNumberConditionBase.valuePropertyKey:
if (object is TransitionNumberConditionBase && value is double) {
object.value = value;
}
break;
case AnimationBase.namePropertyKey:
if (object is AnimationBase && value is String) {
object.name = value;
@ -327,16 +337,6 @@ class RiveCoreContext {
object.y2 = value;
}
break;
case TransitionValueConditionBase.opValuePropertyKey:
if (object is TransitionValueConditionBase && value is int) {
object.opValue = value;
}
break;
case TransitionDoubleConditionBase.valuePropertyKey:
if (object is TransitionDoubleConditionBase && value is double) {
object.value = value;
}
break;
case StateTransitionBase.stateToIdPropertyKey:
if (object is StateTransitionBase && value is int) {
object.stateToId = value;
@ -861,12 +861,12 @@ class RiveCoreContext {
case DrawRulesBase.drawTargetIdPropertyKey:
case TendonBase.boneIdPropertyKey:
return uintType;
case StateMachineDoubleBase.valuePropertyKey:
case StateMachineNumberBase.valuePropertyKey:
case TransitionNumberConditionBase.valuePropertyKey:
case CubicInterpolatorBase.x1PropertyKey:
case CubicInterpolatorBase.y1PropertyKey:
case CubicInterpolatorBase.x2PropertyKey:
case CubicInterpolatorBase.y2PropertyKey:
case TransitionDoubleConditionBase.valuePropertyKey:
case KeyFrameDoubleBase.valuePropertyKey:
case LinearAnimationBase.speedPropertyKey:
case LinearGradientBase.startXPropertyKey:
@ -1040,8 +1040,10 @@ class RiveCoreContext {
static double getDouble(Core object, int propertyKey) {
switch (propertyKey) {
case StateMachineDoubleBase.valuePropertyKey:
return (object as StateMachineDoubleBase).value;
case StateMachineNumberBase.valuePropertyKey:
return (object as StateMachineNumberBase).value;
case TransitionNumberConditionBase.valuePropertyKey:
return (object as TransitionNumberConditionBase).value;
case CubicInterpolatorBase.x1PropertyKey:
return (object as CubicInterpolatorBase).x1;
case CubicInterpolatorBase.y1PropertyKey:
@ -1050,8 +1052,6 @@ class RiveCoreContext {
return (object as CubicInterpolatorBase).x2;
case CubicInterpolatorBase.y2PropertyKey:
return (object as CubicInterpolatorBase).y2;
case TransitionDoubleConditionBase.valuePropertyKey:
return (object as TransitionDoubleConditionBase).value;
case KeyFrameDoubleBase.valuePropertyKey:
return (object as KeyFrameDoubleBase).value;
case LinearAnimationBase.speedPropertyKey:
@ -1340,8 +1340,11 @@ class RiveCoreContext {
static void setDouble(Core object, int propertyKey, double value) {
switch (propertyKey) {
case StateMachineDoubleBase.valuePropertyKey:
(object as StateMachineDoubleBase).value = value;
case StateMachineNumberBase.valuePropertyKey:
(object as StateMachineNumberBase).value = value;
break;
case TransitionNumberConditionBase.valuePropertyKey:
(object as TransitionNumberConditionBase).value = value;
break;
case CubicInterpolatorBase.x1PropertyKey:
(object as CubicInterpolatorBase).x1 = value;
@ -1355,9 +1358,6 @@ class RiveCoreContext {
case CubicInterpolatorBase.y2PropertyKey:
(object as CubicInterpolatorBase).y2 = value;
break;
case TransitionDoubleConditionBase.valuePropertyKey:
(object as TransitionDoubleConditionBase).value = value;
break;
case KeyFrameDoubleBase.valuePropertyKey:
(object as KeyFrameDoubleBase).value = value;
break;

View File

@ -1,11 +0,0 @@
import 'package:rive/src/generated/animation/state_machine_double_base.dart';
export 'package:rive/src/generated/animation/state_machine_double_base.dart';
class StateMachineDouble extends StateMachineDoubleBase {
@override
void valueChanged(double from, double to) {}
@override
bool isValidType<T>() => T == double;
@override
dynamic get controllerValue => value;
}

View File

@ -0,0 +1,11 @@
import 'package:rive/src/generated/animation/state_machine_number_base.dart';
export 'package:rive/src/generated/animation/state_machine_number_base.dart';
class StateMachineNumber extends StateMachineNumberBase {
@override
void valueChanged(double from, double to) {}
@override
bool isValidType<T>() => T == double;
@override
dynamic get controllerValue => value;
}

View File

@ -1,20 +1,20 @@
import 'dart:collection';
import 'package:rive/src/rive_core/animation/state_machine_double.dart';
import 'package:rive/src/rive_core/animation/state_machine_number.dart';
import 'package:rive/src/rive_core/animation/transition_condition.dart';
import 'package:rive/src/generated/animation/transition_double_condition_base.dart';
export 'package:rive/src/generated/animation/transition_double_condition_base.dart';
import 'package:rive/src/generated/animation/transition_number_condition_base.dart';
export 'package:rive/src/generated/animation/transition_number_condition_base.dart';
class TransitionDoubleCondition extends TransitionDoubleConditionBase {
class TransitionNumberCondition extends TransitionNumberConditionBase {
@override
void valueChanged(double from, double to) {}
@override
bool validate() => super.validate() && (input is StateMachineDouble);
bool validate() => super.validate() && (input is StateMachineNumber);
@override
bool evaluate(HashMap<int, dynamic> values) {
if (input is! StateMachineDouble) {
if (input is! StateMachineNumber) {
return true;
}
var doubleInput = input as StateMachineDouble;
var doubleInput = input as StateMachineNumber;
dynamic providedValue = values[input.id];
double inputValue =
providedValue is double ? providedValue : doubleInput.value;

View File

@ -4,7 +4,7 @@ import 'package:rive/src/rive_core/shapes/shape_paint_container.dart';
abstract class ShapePaintMutator {
ShapePaintContainer? _shapePaintContainer;
late Paint _paint;
Paint _paint = Paint();
ShapePaintContainer? get shapePaintContainer => _shapePaintContainer;
Paint get paint => _paint;
double _renderOpacity = 1;

View File

@ -37,12 +37,6 @@ class LayerController {
if (_animationInstance != null) {
_animationInstance!.advance(elapsedSeconds);
}
for (int i = 0; updateState(inputValues); i++) {
if (i == 100) {
print('StateMachineController.apply exceeded max iterations.');
return false;
}
}
if (_transition != null &&
_stateFrom != null &&
_transition!.duration != 0) {
@ -52,22 +46,26 @@ class LayerController {
} else {
_mix = 1;
}
var keepGoing = _mix != 1;
if (_animationInstanceFrom != null && _mix < 1) {
if (!_holdAnimationFrom) {
_animationInstanceFrom!.advance(elapsedSeconds);
}
}
for (int i = 0; updateState(inputValues); i++) {
if (i == 100) {
print('StateMachineController.apply exceeded max iterations.');
return false;
}
}
if (_animationInstanceFrom != null && _mix < 1) {
_animationInstanceFrom!.animation.apply(_animationInstanceFrom!.time,
mix: 1 - _mix, coreContext: core);
}
if (_animationInstance != null) {
_animationInstance!.animation
.apply(_animationInstance!.time, mix: _mix, coreContext: core);
if (_animationInstance!.keepGoing) {
keepGoing = true;
}
}
return keepGoing;
return _mix != 1 || (_animationInstance?.keepGoing ?? false);
}
bool updateState(HashMap<int, dynamic> inputValues) {

View File

@ -1,17 +1,54 @@
import 'package:flutter/foundation.dart';
import 'package:rive/src/core/core.dart';
import 'package:rive/src/generated/animation/state_machine_bool_base.dart';
import 'package:rive/src/generated/animation/state_machine_number_base.dart';
import 'package:rive/src/generated/animation/state_machine_trigger_base.dart';
import 'package:rive/src/rive_core/animation/state_machine.dart';
import 'package:rive/src/rive_core/animation/state_machine_bool.dart';
import 'package:rive/src/rive_core/animation/state_machine_input.dart' as core;
import 'package:rive/src/rive_core/animation/state_machine_number.dart';
import 'package:rive/src/rive_core/animation/state_machine_trigger.dart';
import 'package:rive/src/rive_core/artboard.dart';
import 'package:rive/src/rive_core/state_machine_controller.dart' as core;
class StateMachineInput<T> {
final int id;
/// [StateMachine]s supports three input types. The StateMachine mostly
/// abstracts types by allowing the programmer to query for an input of a
/// specific Dart backing type, mapping it to the correct StateMachine type.
/// This is the most flexible API to use to check if a type with a given name
/// exists. However, if you need to iterate inputs and query their types, this
/// enum is exposed for convenience.
enum SMIType { number, boolean, trigger }
/// SMI = StateMachineInstance
///
/// This is the abstraction of an instanced input
/// from the [StateMachine]. Whenever a [StateMachineController] is created, the
/// list of inputs in the corresponding [StateMachine] is wrapped into a set of
/// [SMIInput] objects that ensure inputs are initialized to design-time values.
/// The implementation can now change these values freely as they are decoupled
/// from the backing [StateMachine] and can safely be re-instanced by another
/// controller later.
abstract class SMIInput<T> {
final core.StateMachineInput _input;
final StateMachineController controller;
StateMachineInput._(this.id, this.controller);
final SMIType type;
T get value => controller.inputValues[id] as T;
set value(T newValue) => change(newValue);
SMIInput._(this._input, this.type, this.controller);
/// Change the value of the input, returns true if the value was changed the
/// and [StateMachineController] was activated.
@protected
void advance() {}
/// The id of the input within the context of the [StateMachine] it belongs
/// to.
int get id => _input.id;
/// The name given to this input at design time in Rive.
String get name => _input.name;
/// Convenience method for changing the backing [SMIInput.value] of the input.
/// For [SMITrigger] it's usually preferable to use the [SMITrigger.fire]
/// method to change the input value, but calling change(true) is totally
/// valid.
bool change(T value) {
if (controller.inputValues[id] == value) {
return false;
@ -20,15 +57,89 @@ class StateMachineInput<T> {
controller.isActive = true;
return true;
}
T get value => controller.inputValues[id] as T;
set value(T newValue) => change(newValue);
bool _is<K>() {
return K == T;
}
}
/// A boolean StateMachine input instance. Use the [value] property to change
/// the input which will automatically re-activate the [StateMachineController]
/// if necessary.
class SMIBool extends SMIInput<bool> {
SMIBool._(StateMachineBool input, StateMachineController controller)
: super._(
input,
SMIType.boolean,
controller,
) {
controller.inputValues[id] = input.value;
}
}
/// A numeric StateMachine input instance. Use the [value] property to change
/// the input which will automatically re-activate the [StateMachineController]
/// if necessary.
class SMINumber extends SMIInput<double> {
SMINumber._(StateMachineNumber input, StateMachineController controller)
: super._(
input,
SMIType.number,
controller,
) {
controller.inputValues[id] = input.value;
}
}
/// A trigger StateMachine input instance. Use the [fire] method to change the
/// input which will automatically re-activate the [StateMachineController] if
/// necessary.
class SMITrigger extends SMIInput<bool> {
SMITrigger._(StateMachineTrigger input, StateMachineController controller)
: super._(
input,
SMIType.trigger,
controller,
) {
controller.inputValues[id] = false;
}
void fire() => change(true);
@override
void advance() => change(false);
}
/// An AnimationController which controls a StateMachine and provides access to
/// the inputs of the StateMachine.
class StateMachineController extends core.StateMachineController {
final List<SMIInput> _inputs = <SMIInput>[];
/// A list of inputs available in the StateMachine.
Iterable<SMIInput> get inputs => _inputs;
StateMachineController(StateMachine stateMachine) : super(stateMachine) {
isActive = true;
for (final input in stateMachine.inputs) {
switch (input.coreType) {
case StateMachineNumberBase.typeKey:
_inputs.add(SMINumber._(input as StateMachineNumber, this));
break;
case StateMachineBoolBase.typeKey:
_inputs.add(SMIBool._(input as StateMachineBool, this));
break;
case StateMachineTriggerBase.typeKey:
_inputs.add(SMITrigger._(input as StateMachineTrigger, this));
break;
}
}
}
/// Instance a [StateMachineController] from an [artboard] with the given
/// [stateMachineName]. Returns the [StateMachineController] or null if no
/// [StateMachine] with [stateMachineName] is found.
static StateMachineController? fromArtboard(
Artboard artboard, String stateMachineName) {
for (final animation in artboard.animations) {
@ -39,13 +150,21 @@ class StateMachineController extends core.StateMachineController {
return null;
}
StateMachineInput<T>? findInput<T>(String name) {
for (final input in stateMachine.inputs) {
if (input.name == name && input.isValidType<T>()) {
inputValues[input.id] = input.controllerValue;
return StateMachineInput<T>._(input.id, this);
/// Find an input with a specific backing type and a given name.
SMIInput<T>? findInput<T>(String name) {
for (final input in _inputs) {
if (input._is<T>() && input.name == name) {
return input as SMIInput<T>;
}
}
return null;
}
@override
void apply(CoreContext core, double elapsedSeconds) {
super.apply(core, elapsedSeconds);
for (final input in _inputs) {
input.advance();
}
}
}

View File

@ -1,6 +1,6 @@
name: rive
description: Rive 2 Flutter Runtime. This package provides runtime functionality for playing back and interacting with animations built with the Rive editor available at https://rive.app.
version: 0.7.1
version: 0.7.2
repository: https://github.com/rive-app/rive-flutter
homepage: https://rive.app