Interactive Animations with Rive

FlutterDartAnimationMobile
Share on LinkedIn

Our onboarding mascot was a 4 MB GIF that pixelated on tablets. Replacing it with a 120 KB Rive file gave us crisp vectors, tap-to-wave interaction, and a loading state tied to actual upload progress—design tweaked easing in Rive editor without waiting for an app store release for a static asset swap if we chose remote hosting.

Rive is a real-time interactive design tool. Flutter integrates via the rive package (and newer rive_native for improved runtime). Animations are state machines with inputs developers set from Dart.

Adding Rive to Flutter

dependencies:
  rive: ^0.13.0

Asset in pubspec.yaml:

flutter:
  assets:
    - assets/animations/mascot.riv

Basic playback

class MascotAnimation extends StatelessWidget {
  const MascotAnimation({super.key});

  @override
  Widget build(BuildContext context) {
    return const RiveAnimation.asset(
      'assets/animations/mascot.riv',
      fit: BoxFit.contain,
    );
  }
}

Simple animations play automatically. Interactivity needs RiveAnimationController or state machine API.

State machines and inputs

class InteractiveMascot extends StatefulWidget {
  @override
  State<InteractiveMascot> createState() => _InteractiveMascotState();
}

class _InteractiveMascotState extends State<InteractiveMascot> {
  SMIBool? _waveInput;
  StateMachineController? _controller;

  void _onRiveInit(Artboard artboard) {
    _controller = StateMachineController.fromArtboard(
      artboard,
      'State Machine 1',
    );
    if (_controller != null) {
      artboard.addController(_controller!);
      _waveInput = _controller!.findInput<bool>('wave') as SMIBool?;
    }
  }

  @override
  void dispose() {
    _controller?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => _waveInput?.value = true,
      child: RiveAnimation.asset(
        'assets/animations/mascot.riv',
        onInit: _onRiveInit,
      ),
    );
  }
}

Designers define wave as bool input triggering transition to waving state.

Binding progress to a number input

void updateProgress(double percent) {
  final input = _controller?.findInput<double>('progress') as SMINumber?;
  input?.value = percent.clamp(0, 100);
}

Hook to upload stream or AnimationController for synchronized feedback.

Performance considerations

Rive runs on GPU-friendly vector paths—usually cheaper than large PNG sequences. Complex artboards with many bones still cost; profile on low-end Android.

Dispose controllers to avoid ticker leaks. One artboard instance per visible widget—do not rebuild entire RiveAnimation on every progress tick if inputs update cheaply.

Layout and responsiveness

BoxFit.contain preserves aspect ratio in responsive layouts. Wrap in AspectRatio when placing in grids:

AspectRatio(
  aspectRatio: 1,
  child: RiveAnimation.asset('assets/loader.riv'),
)

Design handoff checklist

Remote updates

Load from URL with RiveAnimation.network when CDN hosting is acceptable—cache aggressively and verify signature if animations are security-sensitive (probably not, but supply-chain matters).

Artboard selection

Rive files may contain multiple artboards—specify in RiveAnimation.asset:

RiveAnimation.asset(
  'assets/loader.riv',
  artboard: 'LoaderDark',
)

Dark mode switch should swap artboard or input-driven theme state—avoid loading two full files if one artboard toggles via bool input.

Accessibility reduced motion

final reduceMotion = MediaQuery.disableAnimationsOf(context);
if (reduceMotion) {
  return const StaticIllustration();
}
return RiveAnimation.asset(...);

Respect platform settings—some users enable reduce motion for vestibular disorders.

Asset pipeline

Compress .riv in CI; track size budget per screen like PNG budgets. Remote .riv downloads need checksum verification and cache eviction policy.

Fallback when Rive fails

try/catch around load; show static SVG on failure—especially first app open before CDN warms.

State machine debugging

Rive editor preview vs Flutter runtime may differ—always test on device early. Log input changes in debug builds to verify Dart calls match designer naming (wave vs Wave case sensitivity).

Memory and disposal

Remove controller in dispose; failing to dispose causes ticker leak warnings in debug console after navigating away from animated screen repeatedly.

Network-loaded Rive

Cache .riv in path_provider directory with ETag from HTTP response—avoid re-downloading multi-MB files every app open. Verify signature or HTTPS only.

Lottie coexistence

Some screens Lottie, some Rive—unify loading/error fallback components so UX consistent when animation fails.

Artboard bounds

Clip Rive animation with `ClipRRect` if artboard larger than design—overflow touch targets confuse hit testing on adjacent buttons.

Rollout guidance

Rive asset updates CDN hosted controlled rollout separate app release when marketing updates mascot animation weekly—remote asset fetch version query param bust cache without store submission.

Team practices

Shipping Flutter Rive Interactive Animations in production taught our team that documentation beats hero demos. We wrote runbooks covering failure modes, on-call steps, and rollback triggers before the second release, not after the first outage.

When reviewing PRs touching Flutter Rive Interactive Animations, we ask for measurable outcomes: latency, crash rate, or support ticket volume—not subjective feels faster. If metrics are unavailable, the PR includes a DevTools trace or load test snippet proving the change.

Onboarding engineers paste a checklist into their first Flutter Rive Interactive Animations PR description: environment setup verified, tests added or updated, accessibility spot-checked, and release notes drafted for user-visible changes.

We keep a living FAQ in the repo wiki for Flutter Rive Interactive Animations questions repeated in Slack more than twice. FAQ entries link to source files and Slack threads, reducing tribal knowledge and repeated explanations in code review.

Cross-functional review includes design for UX-facing work, security for auth or storage, and platform for native bridges. Flutter Rive Interactive Animations spans layers; skipping reviewers recreated bugs we fixed months ago.

Staging soaks 24 hours for risky changes while dashboards watch error rates.canary cohorts internal staff first, then five percent production, then full rollout if crash-free sessions hold within baseline tolerance.

Resources

Frequently asked questions

Rive vs Lottie in Flutter?

Lottie excels at After Effects playback—linear timelines. Rive adds state machines, interactive inputs, and data binding at runtime—better for toggles, loaders that respond to progress, and gamified UI. Lottie is simpler for one-off marketing animations.

Does Rive require network at runtime?

No. Ship .riv files as assets bundled in the app. Update animations by shipping new assets or downloading .riv files if your product supports OTA asset updates.

How do designers hand off Rive files?

Export .riv from Rive editor. Developers reference asset path and state machine name in RiveAnimation widget. Document input names (boolean, number, trigger) in a shared spec so code matches designer labels.

Hiring a senior Android / Flutter engineer?

I architect and ship production mobile software — Kotlin, Jetpack Compose, Flutter — for robotics, EV infrastructure, fintech, and real-time systems. Open to remote roles in Europe and the US.

Get in touch →