It is possible to have finer control over your Rive animation at runtime by extending RiveRenderObject. This allows you to override low-level methods such as advance, beforeDraw, and draw to have more control and optionally perform additional operations. See below for example usage.
Note that this is a low-level API, and under most circumstances, it is preferable to make use of RiveAnimation or Rive widgets instead.
Example Code
The following is a basic example that demonstrates how to paint a Rive animation and advance a state machine using a custom RenderObject.
import'package:flutter/material.dart';import'package:rive/math.dart';import'package:rive/rive.dart';voidmain() {runApp(constMyApp());}classMyAppextendsStatelessWidget {constMyApp({super.key});@overrideWidgetbuild(BuildContext context) {returnconstMaterialApp( home:MyRiveWidget(), ); }}classMyRiveWidgetextendsStatefulWidget {constMyRiveWidget({Key? key}) : super(key: key);@overrideState<MyRiveWidget> createState() =>_MyRiveWidgetState();}class_MyRiveWidgetStateextendsState<MyRiveWidget> {Artboard? _riveArtboard;Future<void> _load() async {// You need to manage adding the controller to the artboard yourself,// unlike with the RiveAnimation widget that can handle a lot of this logic// for you by simply providing the state machine (or animation) name.final file =awaitRiveFile.asset('assets/little_machine.riv');final artboard = file.mainArtboard;final controller =StateMachineController.fromArtboard( artboard,'State Machine 1', ); artboard.addController(controller!);setState(() => _riveArtboard = artboard); }@overridevoidinitState() { super.initState();_load(); }@overrideWidgetbuild(BuildContext context) {returnScaffold( body:Center( child: _riveArtboard ==null?constSizedBox():CustomRiveRenderObjectWidget( artboard: _riveArtboard!, fit:BoxFit.contain, ), ), ); }}classCustomRiveRenderObjectWidgetextendsLeafRenderObjectWidget {finalArtboard artboard;finalBoxFit fit;finalAlignment alignment;constCustomRiveRenderObjectWidget({ super.key,required this.artboard, this.fit =BoxFit.contain, this.alignment =Alignment.center, });@overrideRenderObjectcreateRenderObject(BuildContext context) {returnCustomRiveRenderObject(artboard asRuntimeArtboard) ..artboard = artboard ..fit = fit ..alignment = alignment; }@overridevoidupdateRenderObject(BuildContext context, covariantCustomRiveRenderObject renderObject) { renderObject ..artboard = artboard ..fit = fit ..alignment = alignment; }@overridevoiddidUnmountRenderObject(covariantCustomRiveRenderObject renderObject) { renderObject.dispose(); }}classCustomRiveRenderObjectextendsRiveRenderObject {CustomRiveRenderObject(super.artboard) { _artboardReference = artboard; }latefinalRuntimeArtboard _artboardReference;@overridebooladvance(double elapsedSeconds) {// The super method will update the animation and advance the artboard.// You can either advance the artboard yourself, or call the super method.return _artboardReference.advance(elapsedSeconds, nested:true);// Example showing how to advance the artboard at twice the speed.return super.advance(elapsedSeconds *2); }@overridevoidbeforeDraw(Canvas canvas, Offset offset) {// Called before `draw`. Can be used to perform clipping, for example. super.beforeDraw(canvas, offset); }@overridevoiddraw(Canvas canvas, Mat2D viewTransform) {// Here you can tap into the draw method and perform custom operations. super.draw(canvas, viewTransform); }}
The artboard can be controlled and configured as it normally would, through a StateMachineController (or any other animation controller).
Example Usage
Dynamically update component colors at runtime - Make use of a custom Rive render object to change a shape's fill color, accessing it by name. This is helpful for when a color's opacity is animating, but the color needs to be changed at runtime.
Track a Rive component in Flutter - Track a Rive component’s position at runtime and overlay a Flutter widget or perform additional painting operations.
Additional Documenation
For additional information on RenderObjects, see the official Flutter examples: