Interactive Animations with Rive
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
- State machine name documented
- Input names and types (bool, number, trigger)
- Artboard name if file contains multiple
- Fallback static PNG for accessibility reduced motion—check
MediaQuery.disableAnimations
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
- Rive Flutter runtime
- rive package on pub.dev
- Rive editor
- State machines overview (Rive docs)
- rive_native package
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 →