Note that certain Rive features may not be supported yet for a particular runtime, or may require using the Rive Renderer.For more details, refer to the feature support and choosing a renderer pages.

Overview

This guide documents how to use the Rive Flutter runtime to easily integrate Rive graphics in your Flutter apps.
The latest version of Rive Flutter is currently published as a dev release 0.14.0-dev.x. This means that while the package is stable and ready for production use, we are still actively developing new features and improvements. We recommend using the latest dev version to take advantage of the newest features and fixes.
Already using Rive Flutter? See our Migration Guide for information on adopting the latest 0.14.x version.

Quick start

See our example app.

Getting started

Follow the steps below to integrate Rive into your Flutter apps.
1

Add the Rive package dependency

Check out Rive’s pub.dev page to get the latest version.
# pubspec.yaml
dependencies:
  rive: ^0.14.0-dev.6 # or latest version
2

Import the Rive package

Import the Rive runtime library in the file you’re looking to integrate Rive animations into.
import 'package:rive/rive.dart';
Consider doing a named import to avoid conflicts with other libraries:
import 'package:rive/rive.dart' as rive;
3

Initialize Rive

You’re encouraged to call await RiveNative.init() at the start of your app, or before you use Rive. For example, in main.dart. This will automatically be called the first time you load a Rive file, but if you want to ensure Rive is loaded before showing your first graphic, call it manually.
import 'package:rive/rive.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // Call init before using Rive.
  await RiveNative.init();
  runApp(const MyApp());
}
4

Add a Rive widget

There are several ways to render Rive graphics in Flutter. We recommend using the RiveWidget, and optionally the RiveWidgetBuilder, or RivePanel.
  • RiveWidget is responsible for rendering the graphic and exposing common view configuration.
  • RiveWidgetBuilder handles file loading, error states, and resource management automatically.
  • RivePanel is a higher-level inherited widget that creates a shared texture to paint multiple RiveWidgets to. Only usable when using the Rive Renderer (Factory.rive). This can drastically improve performance when showing many Rive graphics at once by reducing the number of textures and avoiding WebGL context limitations on the web.
class ExampleRiveBuilder extends StatefulWidget {
  const ExampleRiveBuilder({super.key});

  @override
  State<ExampleRiveBuilder> createState() => _ExampleRiveBuilderState();
}

class _ExampleRiveBuilderState extends State<ExampleRiveBuilder> {
  late final fileLoader = FileLoader.fromAsset("assets/vehicles.riv", riveFactory: Factory.rive);

  @override
  void dispose() {
    fileLoader.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return RiveWidgetBuilder(
      fileLoader: fileLoader,
      builder: (context, state) => switch (state) {
        RiveLoading() => const Center(child: CircularProgressIndicator()),
        RiveFailed() => ErrorWidget.withDetails(
            message: state.error.toString(),
            error: FlutterError(state.error.toString()),
          ),
        RiveLoaded() => RiveWidget(
            controller: state.controller,
            fit: Fit.cover,
          )
      },
    );
  }
}
5

Loading from different sources

From Asset Bundle:Make sure you add the Rive files to your asset bundle and reference them in pubspec.yaml:
# pubspec.yaml
assets:
    - assets/vehicles.riv
// Using FileLoader (with RiveWidgetBuilder)
final fileLoader = FileLoader.fromAsset("assets/vehicles.riv", riveFactory: Factory.rive);

// Using File directly
final file = await File.asset("assets/vehicles.riv", riveFactory: Factory.rive);
From URL:
// Using FileLoader (with RiveWidgetBuilder)
final fileLoader = FileLoader.fromUrl("https://cdn.rive.app/animations/vehicles.riv", riveFactory: Factory.rive);

// Using File directly
final file = await File.url("https://cdn.rive.app/animations/vehicles.riv", riveFactory: Factory.rive);
From Rive File:
// Using FileLoader (with RiveWidgetBuilder)
final fileLoader = FileLoader.fromFile(existingFile, riveFactory: Factory.rive);

Key components

RiveWidget

RiveWidget is responsible for displaying Rive graphics. Properties:
  • controller [required]: The RiveWidgetController that manages the Rive graphic
  • fit: How the artboard should fit within the widget (default: contain)
  • alignment: How the artboard should be aligned within the widget (default: center)
  • hitTestBehavior: How pointer events should be handled (default: opaque)
  • cursor: The cursor to display when hovering over the widget (default: defer)
  • layoutScaleFactor: Scale factor when using Fit.layout (default: 1.0)
  • useSharedTexture: Whether to use a shared texture (RivePanel) to draw the artboard to. Defaults to false. When set to true, it draws to nearest inherited widget of type RivePanel.
  • drawOrder: The draw order of the artboard. This is only used when useSharedTexture is true when drawing to a RivePanel, and using Factory.rive. Defaults to 1.

RiveWidgetBuilder

RiveWidgetBuilder is a higher-level widget that handles file loading, error states, and resource management automatically. Properties:
  • fileLoader [required]: The FileLoader for loading the Rive file
  • builder [required]: Function that builds the widget based on state
  • artboardSelector: Which artboard to use (default: ArtboardDefault())
  • stateMachineSelector: Which state machine to use (default: StateMachineDefault())
  • dataBind: How to bind view model data (optional)
  • controller: Optional custom controller builder
  • onLoaded: Callback when Rive state is loaded
  • onFailed: Callback when Rive state fails to load

RivePanel

RivePanel is a widget that creates a shared texture to paint multiple RiveWidget s to. This is useful when using Factory.rive and can significantly improve performance under certain conditions. When to use RivePanel:
  • When displaying multiple RiveWidgets in your app and they can be drawn to the same texture
  • When you want to programatically composit a scene that includes multiple Rive graphics (from multiple Rive files/artboards)
  • When using Factory.rive (will report errors with Factory.flutter) and want to improve performance
  • When you want to reduce the number of textures being drawn to
  • When targeting web platforms to avoid WebGL context limitations through Factory.rive
Performance considerations:
  • Benefits: Drawing multiple RiveWidgets to the same texture can drastically improve performance by reducing texture allocation overhead
  • Memory cost: There is a memory cost in allocating a larger texture, though this may be offset by the reduced number of individual textures
  • Rendering limitations: Drawing to the same surface means you cannot interleave Rive drawing commands with Flutter’s drawing commands
  • Benchmarking recommended: Performance characteristics vary by use case - what works for one scenario may not work for another
Usage:
RivePanel(
  backgroundColor: Colors.red, // Optional background color
  child: YourWidgetWithMultipleRiveWidgets(),
)
Important notes:
  • Only works with Factory.rive - has no effect with Factory.flutter
  • Set useSharedTexture: true in your RiveWidgets to enable shared texture rendering
  • If you need to interleave Rive content with Flutter content, consider using separate RivePanels or Factory.flutter
  • For complex scenarios, benchmark both approaches to determine the best performance strategy

RiveWidgetController

RiveWidgetController manages the graphic. Creating a Controller:
// Using default artboard and state machine
final controller = RiveWidgetController(file);

// Specifying artboard and state machine
final controller = RiveWidgetController(
  file,
  artboardSelector: ArtboardSelector.byName("MyArtboard"),
  stateMachineSelector: StateMachineSelector.byName("MyStateMachine"),
);
Data Binding:
// Auto-bind with default view model instance
final viewModelInstance = controller.dataBind(DataBind.auto());

// Bind by specific instance
final viewModelInstance = controller.dataBind(DataBind.byInstance(myInstance));

// Bind by name
final viewModelInstance = controller.dataBind(DataBind.byName("MyViewModel"));

File loading

The FileLoader class provides a unified way to load Rive files from different sources. Loading from Assets:
final fileLoader = FileLoader.fromAsset(
  "assets/vehicles.riv",
  riveFactory: Factory.rive,
);
Loading from URL:
final fileLoader = FileLoader.fromUrl(
  "https://example.com/animation.riv",
  riveFactory: Factory.rive,
);
Loading from Existing File:
final fileLoader = FileLoader.fromFile(
  existingFile,
  riveFactory: Factory.rive,
);
Or you can load files directly using the File class:
// Load from asset
final file = await File.asset("assets/vehicles.riv", riveFactory: Factory.rive);
// Load from URL
final file = await File.url("https://example.com/animation.riv", riveFactory:
Factory.rive);
// Load from path
final file = await File.path("/path/to/animation.riv", riveFactory: Factory.rive);
// Load from bytes
final file = await File.decode(bytes, riveFactory: Factory.rive);

Error handling

The Rive Flutter package provides specific exception types for different error scenarios:
  • RiveFileLoaderException: Thrown when file loading fails
  • RiveArtboardException: Thrown when artboard selection fails
  • RiveStateMachineException: Thrown when state machine selection fails
  • RiveDataBindException: Thrown when data binding fails

Resource management

Manual resource management (RiveWidget)

When using RiveWidget directly, you are responsible for managing all resources:
@override
void dispose() {
  // Dispose resources in reverse order of creation
  viewModelInstance.dispose();
  controller.dispose();
  file.dispose();
  super.dispose();
}

Automatic resource management (RiveWidgetBuilder)

When using RiveWidgetBuilder, the widget automatically manages most resources. You only need to dispose the file loader:
@override
void dispose() {
  fileLoader.dispose();
  super.dispose();
}
Because the resources are managed by the RiveWidgetBuilder, you will not be able to access the RiveWidgetController (and other state) after the widget is disposed. If you need to access the controller after the widget is disposed, consider creating the file and controller yourself.The exception to this is the FileLoader, which you control. This loader can be reused across multiple RiveWidgetBuilder instances. The underlying File will only be loaded once. The File will be disposed when the FileLoader is disposed.

Specifying a renderer

When creating a Rive File or FileLoader, you need to specify a factory to use:
  • Factory.rive for the Rive renderer
  • Factory.flutter for the Flutter renderer (Skia or Impeller)
You can use different renderers for different graphics in your app. Some considerations when choosing a renderer:
  • If you plan on showing many Rive graphics that are all drawing to different Rive widgets, consider using Factory.flutter to reduce the native overhead of allocating native render targets and textures.
  • If you are showing a complex graphic, consider using Factory.rive to take advantage of the Rive renderer’s optimizations.
  • Vector Feathering is only available with Factory.rive, so if you need that feature, use the Rive renderer.
For more information see Choosing a Renderer.

Troubleshooting

If you encounter issues with Rive in Flutter, consider the following:
  • Ensure you have called await RiveNative.init() before using any Rive features.
  • Check the console for any error messages related to Rive.
  • Make sure your Rive files are correctly referenced in pubspec.yaml and exist in the specified paths.
  • If using RiveWidgetBuilder, ensure you handle all possible states (loading, loaded, failed) in the builder function.

Build errors

If you encounter build errors related to Rive, ensure that:
  • You have the correct version of the Rive package in your pubspec.yaml.
  • You have run flutter pub get to fetch the latest dependencies.
If you’re still having issues, please see the Troubleshooting section in the Rive Native documentation.

Manually building Rive native libraries

Rive automatically downloads the native libraries for you as part of the rive_native plugin. However, if you need to manually build the native libraries, see the build section in the Rive Native documentation.

Next steps

Now that you have Rive integrated into your Flutter app, you can explore more advanced features like:

Resources

Rive Flutter: Rive Native: