This is a significant update for Rive Flutter. We’ve completely removed all of the Dart code that was used for the Rive runtime and replaced it with our underlying C++ Runtime. See the Rive Native for Flutter page for more details.This has resulted in a number of changes to the underlying API, and a large portion of the code base that was previously accessible through Dart is now implemented in C++ through FFI.
The provided Factory determines the renderer that will be used. Use Factory.rive for the Rive renderer or Factory.flutter for the shipped Flutter renderer (Skia or Impeller).
Vector Feathering only works with the Rive Renderer.
Key Changes:
Creating a Rive File now requires a factory (Factory.rive or Factory.flutter)
Replace RiveFile.import with File.decode() which returns a Future<File>
This functionality is deprecated. We strongly encourage playing and blending
animations through a state machine.
In the previous version you were able to play an animation directly by passing animations: ['myAnimation'] to RiveAnimation.To achieve the same in the new version, use a SingleAnimationPainter and RiveArtboardWidget instead of RiveWidgetController and RiveWidget.
Single animation example
Copy
Ask AI
import 'package:flutter/material.dart';import 'package:rive/rive.dart';import 'package:rive_example/main.dart' show RiveExampleApp;/// This is an alternative controller (painter) to use instead of the/// [RiveWidgetController].////// This painter is used to paint/advance a state machine. Functionally it's/// very similar to the [RiveWidgetController], which we recommend using for/// most use cases.class ExampleSingleAnimationPainter extends StatefulWidget { const ExampleSingleAnimationPainter({super.key}); @override State<ExampleSingleAnimationPainter> createState() => _ExampleSingleAnimationPainterState();}class _ExampleSingleAnimationPainterState extends State<ExampleSingleAnimationPainter> { late File file; Artboard? artboard; late SingleAnimationPainter painter; @override void initState() { super.initState(); init(); } void init() async { file = (await File.asset( 'assets/off_road_car.riv', riveFactory: RiveExampleApp.getCurrentFactory, ))!; painter = SingleAnimationPainter('idle'); artboard = file.defaultArtboard(); setState(() {}); } @override void dispose() { painter.dispose(); artboard?.dispose(); file.dispose(); super.dispose(); } @override Widget build(BuildContext context) { if (artboard == null) { return const Center(child: CircularProgressIndicator()); } return RiveArtboardWidget( artboard: artboard!, painter: painter, ); }}
To play and mix multiple animations, you need to create your own painter. See
the implementation of SingleAnimationPainter and extend it to create and
advance multiple animations.
Consider using Data Binding instead of events
for more advanced use cases.
RiveEvent has been removed and replaced with Event. Event is a sealed class with two options:
OpenUrlEvent
GeneralEvent
Registering an event listener:
New API
Old API
Rive Events: New API
Copy
Ask AI
// New APIfinal controller = RiveWidgetController(_riveFile!);controller?.stateMachine.addEventListener(_onRiveEvent);void _onRiveEvent(Event event) { // Do something with the event}
Rive Events: Old API
Copy
Ask AI
// Old APIfinal controller = StateMachineController.fromArtboard(artboard, 'State Machine 1')!;controller.addEventListener(_onRiveEvent);void _onRiveEvent(RiveEvent event) { // Do something with the event}
Accessing properties returns Map<String, CustomProperty>. CustomProperty is also a sealed class with options:
CustomNumberProperty
CustomBooleanProperty
CustomStringProperty
All of these have a value field. On the Event class, there are convenient accessors:
Copy
Ask AI
// Convenient accessorsevent.property(name); // Returns a CustomPropertyevent.numberProperty(name); // Returns a CustomNumberPropertyevent.booleanProperty(name); // Returns a CustomBooleanPropertyevent.stringProperty(name); // Returns a CustomStringProperty
The FileAssetLoader class and all its subclasses have been removed:
CDNAssetLoader
LocalAssetLoader
CallbackAssetLoader
FallbackAssetLoader
Out-of-band Asset Loading
New API
Old API
Asset types: FontAsset, ImageAsset, and AudioAsset.See this example that demonstrates loading random fonts.
Out-of-band assets: New API
Copy
Ask AI
// New APIfinal fontFile = await File.asset( 'assets/acqua_text_out_of_band.riv', riveFactory: Factory.rive, assetLoader: (asset, bytes) { // Replace font assets that are not embedded in the rive file if (asset is FontAsset && bytes == null) { final urls = [ 'https://cdn.rive.app/runtime/flutter/IndieFlower-Regular.ttf', 'https://cdn.rive.app/runtime/flutter/comic-neue.ttf', 'https://cdn.rive.app/runtime/flutter/inter.ttf', 'https://cdn.rive.app/runtime/flutter/inter-tight.ttf', 'https://cdn.rive.app/runtime/flutter/josefin-sans.ttf', 'https://cdn.rive.app/runtime/flutter/send-flowers.ttf', ]; // pick a random url from the list of fonts http.get(Uri.parse(urls[Random().nextInt(urls.length)])).then((res) { if (mounted) { asset.decode( Uint8List.view(res.bodyBytes.buffer), ); setState(() { // force rebuild in case the Rive graphic is no longer advancing }); } }); return true; // Tell the runtime not to load the asset automatically } else { return false; // Tell the runtime to proceed with loading the asset if it exists } },);
You can also create the asset resource types manually and set them. This is useful if you want to preload the resources:
Copy
Ask AI
Future<void?> updateImageAsset(ImageAsset asset, Uint8List bytes) async { final renderImage = await Factory.rive.decodeImage(bytes); if (renderImage != null) { asset.renderImage(renderImage); }}Future<void?> updateFontAsset(FontAsset asset, Uint8List bytes) async { final font = await Factory.rive.decodeFont(bytes); if (font != null) { asset.font(font); }}Future<void?> updateAudioAsset(AudioAsset asset, Uint8List bytes) async { final audioSource = await Factory.rive.decodeAudio(bytes); if (audioSource != null) { asset.audio(audioSource); }}
Out-of-band Asset Loading: Old API
Copy
Ask AI
// Old APIassetLoader: (asset, bytes) async { /* async work */ final someImage = await ImageAsset.parseBytes(bytes) asset.image = someImage;}
Key Changes:
assetLoader can no longer be an asynchronous lambda
ImageAsset.parseBytes(bytes) → riveFactory.decodeImage(bytes) or asset.decode(bytes)
FontAsset.parseBytes(bytes) → riveFactory.decodeFont(bytes) or asset.decode(bytes)
AudioAsset.parseBytes(bytes) → riveFactory.decodeAudio(bytes) or asset.decode(bytes)
ImageAsset.image = value → ImageAsset.renderImage(value) (returns boolean)
FontAsset.font = value → FontAsset.font(value) (returns boolean)
AudioAsset.audio = value → AudioAsset.audio(value) (returns boolean)
We recommend using Data Binding instead to
update text at runtime.
It’s no longer possible to access a TextValueRun object directly. Use these methods instead to access the String value:
Get/Set Text Run Value
Copy
Ask AI
final controller = RiveWidgetController(riveFile);final artboard = controller.artboard;// Get a text run valueartboard.getText('textRunName')artboard.getText('textRunName', path: 'nested/path')// Set a text run valueartboard.setText('textRunName', 'new value')artboard.setText('textRunName', 'new value', path: 'nested/path')