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 ## [0.7.1] - 2021-04-06 16:19:04
- Fixes an issue with hold keyframes not loading properly. - 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:flutter/widgets.dart';
import 'package:rive/rive.dart'; import 'package:rive/rive.dart';
/// An example showing how to drive two boolean state machine inputs.
class ExampleStateMachine extends StatefulWidget { class ExampleStateMachine extends StatefulWidget {
const ExampleStateMachine({Key? key}) : super(key: key); const ExampleStateMachine({Key? key}) : super(key: key);
@ -16,8 +17,8 @@ class _ExampleStateMachineState extends State<ExampleStateMachine> {
Artboard? _riveArtboard; Artboard? _riveArtboard;
StateMachineController? _controller; StateMachineController? _controller;
StateMachineInput<bool>? _hoverInput; SMIInput<bool>? _hoverInput;
StateMachineInput<bool>? _pressInput; SMIInput<bool>? _pressInput;
@override @override
void initState() { void initState() {
@ -50,7 +51,7 @@ class _ExampleStateMachineState extends State<ExampleStateMachine> {
return Scaffold( return Scaffold(
backgroundColor: Colors.grey, backgroundColor: Colors.grey,
appBar: AppBar( appBar: AppBar(
title: const Text('State Machine Example'), title: const Text('Button State Machine'),
), ),
body: Center( body: Center(
child: _riveArtboard == null 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:flutter/material.dart';
import 'package:rive_example/example_animation.dart'; import 'package:rive_example/example_animation.dart';
import 'package:rive_example/example_state_machine.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( void main() => runApp(MaterialApp(
title: 'Navigation Basics', title: 'Navigation Basics',
@ -33,7 +35,7 @@ class Home extends StatelessWidget {
height: 10, height: 10,
), ),
ElevatedButton( ElevatedButton(
child: const Text('State Machine'), child: const Text('Button State Machine'),
onPressed: () { onPressed: () {
Navigator.push( Navigator.push(
context, 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_base.dart';
import 'package:rive/src/generated/animation/state_machine_bool_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_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_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_machine_trigger_base.dart';
import 'package:rive/src/generated/animation/state_transition_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_bool_condition_base.dart';
import 'package:rive/src/generated/animation/transition_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_trigger_condition_base.dart';
import 'package:rive/src/generated/animation/transition_value_condition_base.dart'; import 'package:rive/src/generated/animation/transition_value_condition_base.dart';
import 'package:rive/src/generated/artboard_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/linear_animation.dart';
import 'package:rive/src/rive_core/animation/state_machine.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_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_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_machine_trigger.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/animation/transition_bool_condition.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/animation/transition_trigger_condition.dart';
import 'package:rive/src/rive_core/artboard.dart'; import 'package:rive/src/rive_core/artboard.dart';
import 'package:rive/src/rive_core/backboard.dart'; import 'package:rive/src/rive_core/backboard.dart';
@ -129,14 +129,16 @@ class RiveCoreContext {
return AnimationState(); return AnimationState();
case KeyedObjectBase.typeKey: case KeyedObjectBase.typeKey:
return KeyedObject(); return KeyedObject();
case StateMachineNumberBase.typeKey:
return StateMachineNumber();
case TransitionTriggerConditionBase.typeKey: case TransitionTriggerConditionBase.typeKey:
return TransitionTriggerCondition(); return TransitionTriggerCondition();
case KeyedPropertyBase.typeKey: case KeyedPropertyBase.typeKey:
return KeyedProperty(); return KeyedProperty();
case StateMachineDoubleBase.typeKey:
return StateMachineDouble();
case KeyFrameIdBase.typeKey: case KeyFrameIdBase.typeKey:
return KeyFrameId(); return KeyFrameId();
case TransitionNumberConditionBase.typeKey:
return TransitionNumberCondition();
case AnyStateBase.typeKey: case AnyStateBase.typeKey:
return AnyState(); return AnyState();
case StateMachineLayerBase.typeKey: case StateMachineLayerBase.typeKey:
@ -145,8 +147,6 @@ class RiveCoreContext {
return Animation(); return Animation();
case CubicInterpolatorBase.typeKey: case CubicInterpolatorBase.typeKey:
return CubicInterpolator(); return CubicInterpolator();
case TransitionDoubleConditionBase.typeKey:
return TransitionDoubleCondition();
case StateTransitionBase.typeKey: case StateTransitionBase.typeKey:
return StateTransition(); return StateTransition();
case KeyFrameDoubleBase.typeKey: case KeyFrameDoubleBase.typeKey:
@ -262,26 +262,26 @@ class RiveCoreContext {
object.objectId = value; object.objectId = value;
} }
break; break;
case TransitionConditionBase.inputIdPropertyKey:
if (object is TransitionConditionBase && value is int) {
object.inputId = value;
}
break;
case StateMachineComponentBase.namePropertyKey: case StateMachineComponentBase.namePropertyKey:
if (object is StateMachineComponentBase && value is String) { if (object is StateMachineComponentBase && value is String) {
object.name = value; object.name = value;
} }
break; 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: case KeyedPropertyBase.propertyKeyPropertyKey:
if (object is KeyedPropertyBase && value is int) { if (object is KeyedPropertyBase && value is int) {
object.propertyKey = value; object.propertyKey = value;
} }
break; break;
case StateMachineDoubleBase.valuePropertyKey:
if (object is StateMachineDoubleBase && value is double) {
object.value = value;
}
break;
case KeyFrameBase.framePropertyKey: case KeyFrameBase.framePropertyKey:
if (object is KeyFrameBase && value is int) { if (object is KeyFrameBase && value is int) {
object.frame = value; object.frame = value;
@ -302,6 +302,16 @@ class RiveCoreContext {
object.value = value; object.value = value;
} }
break; 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: case AnimationBase.namePropertyKey:
if (object is AnimationBase && value is String) { if (object is AnimationBase && value is String) {
object.name = value; object.name = value;
@ -327,16 +337,6 @@ class RiveCoreContext {
object.y2 = value; object.y2 = value;
} }
break; 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: case StateTransitionBase.stateToIdPropertyKey:
if (object is StateTransitionBase && value is int) { if (object is StateTransitionBase && value is int) {
object.stateToId = value; object.stateToId = value;
@ -861,12 +861,12 @@ class RiveCoreContext {
case DrawRulesBase.drawTargetIdPropertyKey: case DrawRulesBase.drawTargetIdPropertyKey:
case TendonBase.boneIdPropertyKey: case TendonBase.boneIdPropertyKey:
return uintType; return uintType;
case StateMachineDoubleBase.valuePropertyKey: case StateMachineNumberBase.valuePropertyKey:
case TransitionNumberConditionBase.valuePropertyKey:
case CubicInterpolatorBase.x1PropertyKey: case CubicInterpolatorBase.x1PropertyKey:
case CubicInterpolatorBase.y1PropertyKey: case CubicInterpolatorBase.y1PropertyKey:
case CubicInterpolatorBase.x2PropertyKey: case CubicInterpolatorBase.x2PropertyKey:
case CubicInterpolatorBase.y2PropertyKey: case CubicInterpolatorBase.y2PropertyKey:
case TransitionDoubleConditionBase.valuePropertyKey:
case KeyFrameDoubleBase.valuePropertyKey: case KeyFrameDoubleBase.valuePropertyKey:
case LinearAnimationBase.speedPropertyKey: case LinearAnimationBase.speedPropertyKey:
case LinearGradientBase.startXPropertyKey: case LinearGradientBase.startXPropertyKey:
@ -1040,8 +1040,10 @@ class RiveCoreContext {
static double getDouble(Core object, int propertyKey) { static double getDouble(Core object, int propertyKey) {
switch (propertyKey) { switch (propertyKey) {
case StateMachineDoubleBase.valuePropertyKey: case StateMachineNumberBase.valuePropertyKey:
return (object as StateMachineDoubleBase).value; return (object as StateMachineNumberBase).value;
case TransitionNumberConditionBase.valuePropertyKey:
return (object as TransitionNumberConditionBase).value;
case CubicInterpolatorBase.x1PropertyKey: case CubicInterpolatorBase.x1PropertyKey:
return (object as CubicInterpolatorBase).x1; return (object as CubicInterpolatorBase).x1;
case CubicInterpolatorBase.y1PropertyKey: case CubicInterpolatorBase.y1PropertyKey:
@ -1050,8 +1052,6 @@ class RiveCoreContext {
return (object as CubicInterpolatorBase).x2; return (object as CubicInterpolatorBase).x2;
case CubicInterpolatorBase.y2PropertyKey: case CubicInterpolatorBase.y2PropertyKey:
return (object as CubicInterpolatorBase).y2; return (object as CubicInterpolatorBase).y2;
case TransitionDoubleConditionBase.valuePropertyKey:
return (object as TransitionDoubleConditionBase).value;
case KeyFrameDoubleBase.valuePropertyKey: case KeyFrameDoubleBase.valuePropertyKey:
return (object as KeyFrameDoubleBase).value; return (object as KeyFrameDoubleBase).value;
case LinearAnimationBase.speedPropertyKey: case LinearAnimationBase.speedPropertyKey:
@ -1340,8 +1340,11 @@ class RiveCoreContext {
static void setDouble(Core object, int propertyKey, double value) { static void setDouble(Core object, int propertyKey, double value) {
switch (propertyKey) { switch (propertyKey) {
case StateMachineDoubleBase.valuePropertyKey: case StateMachineNumberBase.valuePropertyKey:
(object as StateMachineDoubleBase).value = value; (object as StateMachineNumberBase).value = value;
break;
case TransitionNumberConditionBase.valuePropertyKey:
(object as TransitionNumberConditionBase).value = value;
break; break;
case CubicInterpolatorBase.x1PropertyKey: case CubicInterpolatorBase.x1PropertyKey:
(object as CubicInterpolatorBase).x1 = value; (object as CubicInterpolatorBase).x1 = value;
@ -1355,9 +1358,6 @@ class RiveCoreContext {
case CubicInterpolatorBase.y2PropertyKey: case CubicInterpolatorBase.y2PropertyKey:
(object as CubicInterpolatorBase).y2 = value; (object as CubicInterpolatorBase).y2 = value;
break; break;
case TransitionDoubleConditionBase.valuePropertyKey:
(object as TransitionDoubleConditionBase).value = value;
break;
case KeyFrameDoubleBase.valuePropertyKey: case KeyFrameDoubleBase.valuePropertyKey:
(object as KeyFrameDoubleBase).value = value; (object as KeyFrameDoubleBase).value = value;
break; 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 '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/rive_core/animation/transition_condition.dart';
import '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_double_condition_base.dart'; export 'package:rive/src/generated/animation/transition_number_condition_base.dart';
class TransitionDoubleCondition extends TransitionDoubleConditionBase { class TransitionNumberCondition extends TransitionNumberConditionBase {
@override @override
void valueChanged(double from, double to) {} void valueChanged(double from, double to) {}
@override @override
bool validate() => super.validate() && (input is StateMachineDouble); bool validate() => super.validate() && (input is StateMachineNumber);
@override @override
bool evaluate(HashMap<int, dynamic> values) { bool evaluate(HashMap<int, dynamic> values) {
if (input is! StateMachineDouble) { if (input is! StateMachineNumber) {
return true; return true;
} }
var doubleInput = input as StateMachineDouble; var doubleInput = input as StateMachineNumber;
dynamic providedValue = values[input.id]; dynamic providedValue = values[input.id];
double inputValue = double inputValue =
providedValue is double ? providedValue : doubleInput.value; 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 { abstract class ShapePaintMutator {
ShapePaintContainer? _shapePaintContainer; ShapePaintContainer? _shapePaintContainer;
late Paint _paint; Paint _paint = Paint();
ShapePaintContainer? get shapePaintContainer => _shapePaintContainer; ShapePaintContainer? get shapePaintContainer => _shapePaintContainer;
Paint get paint => _paint; Paint get paint => _paint;
double _renderOpacity = 1; double _renderOpacity = 1;

View File

@ -37,12 +37,6 @@ class LayerController {
if (_animationInstance != null) { if (_animationInstance != null) {
_animationInstance!.advance(elapsedSeconds); _animationInstance!.advance(elapsedSeconds);
} }
for (int i = 0; updateState(inputValues); i++) {
if (i == 100) {
print('StateMachineController.apply exceeded max iterations.');
return false;
}
}
if (_transition != null && if (_transition != null &&
_stateFrom != null && _stateFrom != null &&
_transition!.duration != 0) { _transition!.duration != 0) {
@ -52,22 +46,26 @@ class LayerController {
} else { } else {
_mix = 1; _mix = 1;
} }
var keepGoing = _mix != 1;
if (_animationInstanceFrom != null && _mix < 1) { if (_animationInstanceFrom != null && _mix < 1) {
if (!_holdAnimationFrom) { if (!_holdAnimationFrom) {
_animationInstanceFrom!.advance(elapsedSeconds); _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, _animationInstanceFrom!.animation.apply(_animationInstanceFrom!.time,
mix: 1 - _mix, coreContext: core); mix: 1 - _mix, coreContext: core);
} }
if (_animationInstance != null) { if (_animationInstance != null) {
_animationInstance!.animation _animationInstance!.animation
.apply(_animationInstance!.time, mix: _mix, coreContext: core); .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) { 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.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/artboard.dart';
import 'package:rive/src/rive_core/state_machine_controller.dart' as core; import 'package:rive/src/rive_core/state_machine_controller.dart' as core;
class StateMachineInput<T> { /// [StateMachine]s supports three input types. The StateMachine mostly
final int id; /// 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; final StateMachineController controller;
StateMachineInput._(this.id, this.controller); final SMIType type;
T get value => controller.inputValues[id] as T; SMIInput._(this._input, this.type, this.controller);
set value(T newValue) => change(newValue);
/// Change the value of the input, returns true if the value was changed the @protected
/// and [StateMachineController] was activated. 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) { bool change(T value) {
if (controller.inputValues[id] == value) { if (controller.inputValues[id] == value) {
return false; return false;
@ -20,15 +57,89 @@ class StateMachineInput<T> {
controller.isActive = true; controller.isActive = true;
return 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 /// An AnimationController which controls a StateMachine and provides access to
/// the inputs of the StateMachine. /// the inputs of the StateMachine.
class StateMachineController extends core.StateMachineController { 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) { StateMachineController(StateMachine stateMachine) : super(stateMachine) {
isActive = true; 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( static StateMachineController? fromArtboard(
Artboard artboard, String stateMachineName) { Artboard artboard, String stateMachineName) {
for (final animation in artboard.animations) { for (final animation in artboard.animations) {
@ -39,13 +150,21 @@ class StateMachineController extends core.StateMachineController {
return null; return null;
} }
StateMachineInput<T>? findInput<T>(String name) { /// Find an input with a specific backing type and a given name.
for (final input in stateMachine.inputs) { SMIInput<T>? findInput<T>(String name) {
if (input.name == name && input.isValidType<T>()) { for (final input in _inputs) {
inputValues[input.id] = input.controllerValue; if (input._is<T>() && input.name == name) {
return StateMachineInput<T>._(input.id, this); return input as SMIInput<T>;
} }
} }
return null; 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 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. 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 repository: https://github.com/rive-app/rive-flutter
homepage: https://rive.app homepage: https://rive.app