Provide a way to subscribe to the rive animation ticker
It's easy enough to extend a simple animation controller so that it could be listened to and the outside world would know that the new frame was rendered, but this is not the case for StateMachineController. Check out this example. I had to rely on a source file directly and reimplement StateMachineController and partially copy the implementation, there's no other way to subscribe to the animation ticker (at least I didn't find one).
Would it be possible to make all animation controllers (including state machine) listenable?
I want to make this implementation cleaner. Here's what it looks like:
https://user-images.githubusercontent.com/6261302/226898221-f286721a-4885-4d09-8b07-cdf051c75587.mov
Hey @lesnitsky
I tried to recreate something myself without looking at your code, and I basically got to the same result. Adding it here for transparency.
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late StateMachineController controller;
void onInit(Artboard artboard) {
controller = StateMachineOnTickController.fromArtboard(
artboard,
'State Machine 1',
onTick: () {
print('tick');
},
)!;
artboard.addController(controller);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: RiveAnimation.asset(
'assets/rotate-speed.riv',
onInit: onInit,
),
);
}
}
class StateMachineOnTickController extends StateMachineController {
StateMachineOnTickController(
super.stateMachine, {
core.OnStateChange? onStateChange,
this.onTick,
});
final VoidCallback? onTick;
static StateMachineController? fromArtboard(
Artboard artboard,
String stateMachineName, {
core.OnStateChange? onStateChange,
VoidCallback? onTick,
}) {
for (final animation in artboard.animations) {
if (animation is StateMachine && animation.name == stateMachineName) {
return StateMachineOnTickController(
animation,
onStateChange: onStateChange,
onTick: onTick,
);
}
}
return null;
}
/// Override this to be notified on each animation frame.
@override
void apply(CoreContext core, double elapsedSeconds) {
onTick?.call();
super.apply(core, elapsedSeconds);
}
}
How you're extending the State Machine Controller is completely fine.
It'll be simple enough for us to extend the API with a callback such as the one above if needed. Maybe then also supplying additional information, such as the elapsedSeconds. To make them listenable could have an impact on performance. We could potentially expose ListenableControllers alongside the regular ones - so that people can opt in to using them
I neglected to add that the above requires src imports:
import 'package:rive/src/core/core.dart';
import 'package:rive/src/rive_core/state_machine_controller.dart' as core;
Or alternatively we can create a Mixin, similar to AnimationLocalListenersMixin. Which is how Flutter's AnimationContoller works when calling addListener