Caching a Rive File
Under most circumstances a .riv
file should load quickly and managing the RiveFile
yourself is not necessary. But if you intend to use the same .riv
file in multiple parts of your application, or even on the same screen, it might be advantageous to load the file once and keep it in memory.
Example Usage
The following is a basic example to illustrate how to preload a Rive file, and pass the data to multiple Rive instances:
const rive = require("@rive-app/canvas"); let riveInstances = []; function loadRiveFile(src, onSuccess, onError) { const file = new rive.RiveFile({ src: src, onLoad: () => onSuccess(file), onLoadError: onError, }); // Remember to call init() to trigger the load; file.init().catch(onError); } function setupRiveInstance(loadedRiveFile, canvasId) { const canvas = document.getElementById(canvasId); if (!canvas) return; const riveInstance = new rive.Rive({ riveFile: loadedRiveFile, // Be sure to specify the correct state machine (or animation) name stateMachines: "Motion", // Name of the State Machine to play canvas: canvas, layout: new rive.Layout({ fit: rive.Fit.FitWidth, alignment: rive.Alignment.Center, }), autoplay: true, onLoad: () => { // Prevent a blurry canvas by using the device pixel ratio riveInstance.resizeDrawingSurfaceToCanvas(); }, }); riveInstances.push(riveInstance); } // Loads the .riv file and initializes multiple Rive instances using the same loaded RiveFile in memory loadRiveFile( "clean_the_car.riv", (file) => { setupRiveInstance(file, "rive-canvas-1"); setupRiveInstance(file, "rive-canvas-2"); // You could also store a reference to the loaded RiveFile here so you're able to initialize other Rive instances later. }, (error) => { console.error("Failed to load Rive file:", error); } ); // Resize the drawing surface for all instances if the window resizes window.addEventListener( "resize", () => { riveInstances.forEach((instance) => { if (instance) { instance.resizeDrawingSurfaceToCanvas(); } }); }, false );
Not yet supported
The following is a basic example to illustrate how to preload a Rive file, and pass the data directly to the RiveAnimation.direct()
constructor:
class PreloadRive extends StatefulWidget { const PreloadRive({super.key}); @override State<PreloadRive> createState() => _PreloadRiveState(); } class _PreloadRiveState extends State<PreloadRive> { RiveFile? _file; // You can maintain this reference to have a cached version @override void initState() { super.initState(); preload(); } Future<void> preload() async { rootBundle.load('assets/little_machine.riv').then( (data) async { // Load the RiveFile from the binary data. setState(() { _file = RiveFile.import(data); }); }, ); } @override Widget build(BuildContext context) { return (_file == null) ? const SizedBox.shrink() : RiveAnimation.direct(_file!); } }
Other Considerations
Managing State
How the RiveFile
is kept alive in state and shared to other widgets is up to you and your preferred state management solution. One approach can be to wait for the Rive file to load in main
, or during the startup screen, and using the Provider package to expose the data to the whole application.
Or if the animation is only needed in a nested section of your application, then it might be preferable to delay loading the animation until necessary.
Memory
By managing the RiveFile yourself you can have finer control over the memory used in your application. This will be especially beneficial if the same Rive file is being used in multiple parts of your application, or used simultaneously in multiple RiveAnimation
widgets.
You can make use of Flutter DevTools’ memory tooling for additional investigation if needed or desired.
Network Assets
The easiest way to load a Rive animation over the Internet is by using RiveAnimation.network(url)
. However, similar considerations apply to a network asset regarding memory and sharing a Rive file across multiple widgets/pages.
The following can be used to load a Rive file over the network:
final riveFile = await RiveFile.network('YOUR:URL');
The reference can then be kept alive in memory and the riveFile
can be passed to RiveAnimation.direct
.
Here’s a simplified example showing how to integrate the useRiveFile
hook to reuse a RiveFile
across components
import React, { useState } from 'react'; import { useRiveFile } from '@rive-app/react-canvas'; // Custom Wrapper component to display the Rive animation const RiveAnimation = ({ riveFile }) => { const { RiveComponent } = useRive({ riveFile, autoplay: true }); return <RiveComponent/>; }; function App() { const { riveFile, status } = useRiveFile({ src: 'https://cdn.rive.app/animations/myrivefile.riv', }); const [instanceCount] = useState(5); // Number of RiveAnimation components to render if (status === 'loading') { return <div>Loading...</div>; } if (status === 'failed') { return <div>Failed to load Rive file.</div>; } // Each RiveAnimation component uses the RiveFile we loaded earlier, so it is only fetched and initialized once return ( <div className="App"> <header className="App-header">Rive Instances</header> <div className="rive-list"> {Array.from({ length: instanceCount }, (_, index) => ( <RiveAnimation key={`rive-instance-${index}`} riveFile={riveFile} /> ))} </div> </div> ); } export default App;