Rive: what it is, animation tool for Flutter and iOS

Author: IT Sectr Published: 2026-03-01 Reading time: 11 min

Rive is a tool for creating and playing interactive vector animation that combines an animation editor and runtime libraries for mobile platforms. Unlike passive formats, Rive supports a State Machine — logic for transitions between animations based on user actions. According to Rive Blog (2026), the tool is used in over 10,000 projects, including Duolingo and Bumble. Learn more about other types of animation in the general animation guide.

Key Takeaways

  • Rive — a tool for interactive vector animation with its own editor.
  • State Machine — visual logic for transitions between animations by triggers.
  • RiveRuntime — a playback library for iOS, Android, Flutter and Web.
  • RiveAnimation — a Flutter Dart widget for embedding .riv files in an app.
  • Format .riv — a binary container containing animation and State Machine.

What is Rive?

Rive (formerly Rive.app, founded in 2016) is a graphical editor and cross-platform runtime for interactive vector animation. Unlike After Effects + Bodymovin (Lottie), Rive provides a unified environment: the designer draws, animates, and configures transition logic right in the editor. The result is exported to a binary .riv file ranging from 2 to 50 KB depending on complexity.

The main difference of Rive from other solutions is the State Machine. It is a visual graph where nodes are animations (Idle, Hover, Press, Active), and transitions between them are triggered by triggers (Touch, Drag, Boolean). The developer simply sends triggers in code, and RiveRuntime decides which animation to play and how to smoothly switch between them. According to Rive Engineering (2026), the average .riv animation with State Machine takes 15 KB — 5–10 times smaller than an equivalent video file.

Rive Runtime is available for Flutter, iOS (Swift), Android (Kotlin/Java), Web (WASM) and React. Rendering is done via Skia (Flutter/Web), Core Graphics (iOS) or Canvas (Android). Rive supports shape animation (Knob, Slider, Button with custom geometry), allowing you to create fully animated UI components rather than just decorative animations.

Rive vs Lottie: comparison of approaches

Rive and Lottie solve different tasks. Lottie is a player for ready-made animation from After Effects, optimal for passive decorative effects. Rive is a tool for creating interactive animations with its own logic, where animation responds to user actions. The choice depends on the required level of interactivity.

CriterionRiveLottie
EditorOwn (Rive Editor)Adobe After Effects + Bodymovin
InteractivityState Machine, triggers, Boolean, NumberOnly play/pause/seek
Format.riv (binary).json or .lottie (protobuf)
RenderingSkia / Core Graphics / CanvasCanvas / Core Animation / Skia
Animated UIKnob, Slider, Button, Input (built-in)None (decorative layers only)

When to choose Rive: interactive buttons with press feedback, game elements (characters with states), animated switches, dynamic icons reacting to status. When to choose Lottie: preloaders, likes, welcome screens, accent animations without logic. For Flutter projects, Rive is the preferred choice due to native integration with Skia and Flutter's rendering pipeline.

Integrating Rive in Flutter (Dart)

In Flutter, RiveAnimation is the main widget for embedding .riv files. It is available through the rive package (official). RiveAnimation supports arguments: artboard (board selection), animation (animation name), fit, alignment, controllers (for State Machine). For interactive scenes, StateMachineController is used.

dart
import 'package:rive/rive.dart';

// Simple animation playback
RiveAnimation.asset(
  'assets/animations/button.riv',
  artboard: 'Button',
  animations: const ['idle'],
  fit: BoxFit.contain,
)

// StateMachineController with triggers
class InteractiveButton extends StatefulWidget {
  @override
  State<InteractiveButton> createState() => _InteractiveButtonState();
}

class _InteractiveButtonState extends State<InteractiveButton> {
  StateMachineController? _controller;
  SMIInput<bool>? _isPressed;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTapDown: (_) => _isPressed?.value = true,
      onTapUp: (_) => _isPressed?.value = false,
      child: RiveAnimation.asset(
        'assets/animations/button.riv',
        fit: BoxFit.contain,
        onInit: (artboard) {
          _controller = StateMachineController.fromArtboard(
            artboard, 'State Machine 1',
          );
          if (_controller != null) {
            artboard.addController(_controller!);
            _isPressed = _controller!.findInput<bool>('isPressed');
          }
        },
      ),
    );
  }
}

Flutter best practices: load .riv files asynchronously via RiveAnimation.asset (built-in AssetBundle). For lists (ListView), use a single StateMachineController reused between items. Rive no longer requires separate imports — rive v0.12+ includes all features. For custom animation, control RiveAnimationController directly — it provides access to animations, blending, and speed.

Integrating Rive in iOS (Swift)

On iOS, RiveRuntime is represented by the RiveView class (UIKit) and RiveViewModel (SwiftUI). The library is connected via Swift Package Manager. RiveView renders .riv files via Core Graphics and Metal, supporting 120 FPS on ProMotion displays. For State Machine, RiveStateMachineLayer is used with trigger binding.

swift
import RiveRuntime

// SwiftUI: RiveViewModel with animation
struct RiveAnimationView: View {
    @State private var isPressed = false

    var body: some View {
        RiveViewModel(fileName: "button").view()
            .frame(width: 200, height: 60)
            .gesture(
                DragGesture(minimumDistance: 0)
                    .onChanged { _ in
                        if !isPressed {
                            isPressed = true
                            // Trigger via State Machine
                        }
                    }
                    .onEnded { _ in
                        isPressed = false
                    }
            )
    }
}

// UIKit: RiveView with manual control
let riveView = RiveView()
riveView.configure(fileName: "loading", extension: "riv")
riveView.fit = .contain
riveView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(riveView)

// State Machine Management
riveView.triggerInput(named: "startAnimation")

iOS best practices: use RiveViewModel for SwiftUI — it automatically manages the lifecycle. For UIKit, subscribe to RiveViewDelegate for event tracking. Rive consumes less CPU than Lottie on iOS thanks to Core Graphics rendering. For animations in NavigationView/UICollectionView, use prefetching via RiveFile.preload().

Integrating Rive in Android (Kotlin)

On Android, RiveAnimationView is the main View for embedding .riv animations. The app.rive:rive-android library is connected via Gradle. RiveAnimationView inherits from View and renders via Canvas with Hardware Acceleration support. State Machine is managed through setTriggers and setInputs.

kotlin
// build.gradle.kts
implementation("app.rive:rive-android:0.9.0")

// XML layout
<app.rive.RiveAnimationView
    android:id="@+id/riveView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:rive_file="@raw/mascot"
    app:rive_artboard="Mascot"
    app:rive_animation="idle" />

// Kotlin: State Machine management
import app.rive.riveandroid.RiveAnimationView
import app.rive.riveandroid.StateMachineInput

val riveView = findViewById<RiveAnimationView>(R.id.riveView)
riveView.post {
    riveView.setTriggers("hover") "> Sending a trigger to the State Machine
    riveView.setInput("speed", 1.5f)
    riveView.setInput("isActive", true)
    riveView.play()
}

// Animation completion handling
riveView.addListener(object : RiveAnimationView.RiveListener() {
    override fun onAnimationCompleted(animationName: String) {
        Log.d("Rive", "Animation $animationName completed")
    }
})

Android best practices: load .riv files via res/raw (built-in resources) or assets (network loading). RiveAnimationView automatically handles the lifecycle — do not manually call play/pause in onResume/onPause. For animations in RecyclerView, use setOnScrollListener to pause playback during scrolling. According to Rive documentation (2026), .riv files up to 100 KB are parsed in 5–10 ms on mid-range devices.

State Machine: transition logic in Rive

State Machine is a key feature of Rive that distinguishes it from passive animation formats. It is a visual graph where vertices are states (animations) and edges are transitions activated by triggers. The State Machine is created in Rive Editor: the designer adds animation nodes, connects them with lines, and assigns triggers (Touch, Boolean, Number). The developer only needs to send these triggers from code.

dart
// Flutter: full control over State Machine
import 'package:rive/rive.dart';

class RiveStateMachineWidget extends StatefulWidget {
  @override
  State<RiveStateMachineWidget> createState() => _RiveStateMachineWidgetState();
}

class _RiveStateMachineWidgetState extends State<RiveStateMachineWidget> {
  Artboard? _artboard;
  StateMachineController? _controller;
  SMIInput<bool>? _isHovered;
  SMIInput<double>? _progress;

  @override
  Widget build(BuildContext context) {
    return MouseRegion(
      onEnter: (_) => _isHovered?.value = true,
      onExit: (_) => _isHovered?.value = false,
      child: RiveAnimation.asset(
        'assets/interactive_icon.riv',
        onInit: (artboard) {
          _controller = StateMachineController.fromArtboard(
            artboard, 'State Machine 1',
          );
          artboard.addController(_controller!);
          _isHovered = _controller!.findInput<bool>('isHovered');
          _progress = _controller!.findInput<double>('progress');
        },
      ),
    );
  }
}

// Sending numeric value
SMIInput<double> speedInput = controller.findInput('speed');
speedInput?.value = scrollPosition;

Trigger types: Boolean — switch (true/false), Number — numeric value (0.0–1.0 or arbitrary range), Trigger — one-time impulse (button press). In Rive Editor, you can configure transition smoothing (Transition Duration) — recommended value 100–300 ms for smoothness. For complex UI states, use Nested State Machines — each animation part is managed independently. Rive State Machine works on all platforms without changes to the .riv file — transition logic is embedded in the file, not in the application code.

Frequently Asked Questions

How is Rive different from Lottie?

Rive is interactive animation with State Machine, triggers, and its own editor. Lottie is passive playback of JSON animation from After Effects. Rive is suitable for buttons, games, and UI elements that respond to user interaction. Lottie is for decorative effects (preloaders, likes). Rive uses the .riv format, Lottie uses JSON or .lottie.

Can Rive be used without State Machine?

Yes. Rive supports simple animation playback without State Machine — specify animationName in RiveAnimation.asset or RiveView. In this case, Rive works like Lottie: looped or one-shot animation. State Machine is an optional layer for interactivity. For projects that only need decorative animation, State Machine is unnecessary.

Is Rive compatible with Android Jetpack Compose?

Yes, via AndroidView for embedding RiveAnimationView in Compose. Starting from rive-android 0.9.0, a Compose wrapper RiveAnimation compose() with modifier support is available. For State Machine management, use remember { StateMachineHolder() }. Rive also supports Compose Multiplatform on iOS via gemeinsamen Compose Runtime.

Does Rive work on Flutter Web?

Yes, Rive supports Flutter Web via Skia Wasm (CanvasKit) or HTML renderer. CanvasKit is recommended for maximum performance. Rive files are loaded via HTTP request (RiveAnimation.network) or packaged as assets. Flutter Web with Rive works correctly in all modern browsers (Chrome 90+, Safari 15+, Firefox 90+).

How to optimize Rive performance on mobile devices?

Limit the number of simultaneously active layers to 20–30. Use Flat shading instead of Gradient for simple shapes. For animations with text, use System Text (not vector text in Rive Editor). Minimize the number of Bone animations for characters — they are unnecessary for UI elements. On older devices, disable double buffering: riveView.enableDoubleBuffering = false on Android.

Summary

  • Rive — interactive vector animation with its own editor and runtime for 5+ platforms.
  • State Machine — built-in transition logic between animations by triggers (Touch, Boolean, Number).
  • RiveRuntime — libraries for Flutter, iOS, Android, Web and React with rendering via Skia/Core Graphics.
  • Format .riv — binary container weighing 2–50 KB containing animation and State Machine.
  • RiveAnimation (Flutter), RiveViewModel (SwiftUI), RiveAnimationView (Android) — main components.
  • Rive is suitable for interactive UI elements, Lottie for passive decorative animations.
  • Trigger types: Boolean (switch), Number (numeric), Trigger (impulse). Recommended transition duration — 100–300 ms.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also