Rive Flutter: Accessing State Machine Inputs - A Guide
Hey folks! 👋 I'm here to break down a common hiccup when you're working with Rive animations in Flutter, specifically when you're trying to get ahold of those sweet, sweet state machine inputs. This is especially relevant if you're upgrading to the 0.14.0-dev.12 version of the Rive Flutter package. Let's dive in and clear up any confusion!
The Problem: Missing Inputs? 🧐
So, you've been cruising along with the stable version 0.13.20, and life was good. You could easily grab those state machine inputs and tweak their values, right? Something like this:
// In older versions
final controller = StateMachine.fromArtboard(file.mainArtboard, 'State Machine');
if (controller != null) {
artboard.addController(controller);
for (final i in controller.inputs) {
if (i is SMIInput<double>) {
_inputs[i.name] = i;
i.value = 100.0;
}
}
}
But then, you take the plunge and update to 0.14.0-dev.12. You eagerly try to access the inputs the same way, and...bam! The controller.stateMachine.inputs is nowhere to be found. 😩
// In newer versions (problematic)
final file = await File.asset('assets/animations/my-file.riv', riveFactory: Factory.rive);
final controller = RiveWidgetController(file!, stateMachineSelector: StateMachineSelector.byName('State Machine'));
final inputs = controller.stateMachine.inputs; // ----> State Machine doesn't expose inputs
What gives, right? Did you miss something in the migration guide? Or maybe there's a new, hip way to get at those inputs?
This article aims to provide a clear, concise answer to this issue. Let's make sure you're back on track, accessing your state machine inputs, and creating awesome animations!
Why the Change? Evolution of Rive's Flutter API 🤖
Alright, so here's the deal, the Rive Flutter API has been evolving. This is a good thing! It means the library is getting better, more efficient, and packed with new features. However, sometimes those changes require us to adjust how we interact with the library. In this case, the way you access state machine inputs has been streamlined.
In the newer versions, the direct access to controller.stateMachine.inputs has been, well, removed. This isn't a bug; it's a design decision. The Rive team has likely made changes to how the state machine controller is structured, probably for performance or to provide a more intuitive API.
Don't worry, the functionality is still there, but you need to approach it differently. The goal is to provide a more controlled and direct way to manage those inputs, ensuring better performance and a smoother developer experience.
The Recommended Approach: Using RiveWidgetController 🚀
So, how do you get those inputs now? The key is to leverage the RiveWidgetController and its methods to interact with your state machine.
Here's a breakdown of the suggested methods:
-
Instantiate
RiveWidgetController: You will still need to instantiate this controller, pointing it to your Rive file and the specific state machine you want to manipulate. -
Use
RiveWidgetController.inputs: Now, the controller itself provides methods to interact with the inputs directly without accessing the state machine's internal properties directly. This provides a clean interface for modifying your animation. -
Identify the Input: Use the
inputsproperty of theRiveWidgetControllerto find the input you want to modify. -
Modify the Input: Call the desired methods to set the value.
Let's get into some example code.
Code Example: Accessing and Modifying Inputs 💻
Here’s a simplified example of how you can access and modify the state machine inputs with the new API. It should help to clear up all confusion.
import 'package:flutter/material.dart';
import 'package:rive/rive.dart';
class RiveAnimationExample extends StatefulWidget {
@override
_RiveAnimationExampleState createState() => _RiveAnimationExampleState();
}
class _RiveAnimationExampleState extends State<RiveAnimationExample> {
StateMachineController? _controller;
SMITrigger? _triggerInput;
SMINumber? _numberInput;
@override
void initState() {
super.initState();
// You might load your Rive file here, or pass it in from a parent widget
}
void _onRiveInit(Artboard artboard) {
final controller = StateMachineController.fromArtboard(artboard, 'State Machine');
if (controller != null) {
artboard.addController(controller);
_controller = controller;
// Find the inputs using controller.findInput
_triggerInput = controller.findInput<bool>('MyTrigger') as SMITrigger?;
_numberInput = controller.findInput<double>('MyNumber') as SMINumber?;
}
}
void _toggleTrigger() {
_triggerInput?.fire();
}
void _changeNumber(double value) {
_numberInput?.value = value;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Rive Animation Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 300,
height: 300,
child: RiveAnimation.asset(
'assets/my_animation.riv', // Replace with your file
fit: BoxFit.cover,
onInit: _onRiveInit,
),
),
ElevatedButton(
onPressed: _toggleTrigger,
child: const Text('Toggle Trigger'),
),
Slider(
value: _numberInput?.value ?? 0.0,
min: 0.0,
max: 100.0,
onChanged: (value) {
_changeNumber(value);
},
),
],
),
),
);
}
}
Key things to notice:
-
We are using
StateMachineController.fromArtboardand initializing the controller. This is similar to the older version but now we have methods such asfindInput<T>('inputName')to access specific inputs by name. This method provides type safety, which can help prevent errors. -
You find the desired inputs using the
findInputmethod. This allows you to reference inputs and manipulate them. -
Then, you can use the appropriate methods (
fire()for triggers, direct value assignment for numbers, booleans, etc.) to modify the values of the inputs.
This approach gives you a cleaner, more organized, and generally more robust way to interact with your state machines.
Steps to Reproduce (and How to Fix) 🛠️
Let's revisit the steps to reproduce the original issue and how to adapt them to the new API:
- Update the Dependency: Make sure you have
rive: 0.14.0-dev.12or a later version in yourpubspec.yaml. - Load your
.rivfile: This part remains the same; useRiveAnimation.asset()or similar methods to load your animation. - Create an instance of
RiveWidgetController: This step is no longer the correct approach. UseStateMachineController.fromArtboardinstead. - Access Inputs: Instead of trying
controller.stateMachine.inputs, now usecontroller.findInput<Type>('inputName')to access individual inputs.
By following these steps, you'll be able to properly access and modify the state machine inputs in the latest version of the Rive Flutter package.
Troubleshooting: Common Issues and Solutions 💡
Here are some common problems and how to solve them:
- Input Not Found: Double-check the exact name of your input in the Rive design tool. Case matters! Make sure the name in your code matches the name in your
.rivfile. - Incorrect Input Type: Make sure you're using the correct type when calling
findInput. If it's a trigger, useSMITrigger?. If it's a number, useSMINumber?, and so on. - Null Safety: The inputs found via
findInputcan benullif the input isn't found, so handle null cases appropriately in your code.
Conclusion: Stay Updated and Keep Animating! 🎉
So, there you have it! The key to accessing state machine inputs in Rive Flutter 0.14.0-dev.12 and later is to shift your approach to use the findInput() method. This ensures that you have access to the inputs and allows you to seamlessly control your animations. This change may be confusing at first, but it is ultimately for the best! Remember, APIs evolve, and the Rive team consistently works to make their product better.
I hope this guide has been helpful. If you have any further questions or run into any more snags, please don't hesitate to ask in the comments below! Happy animating, everyone! ✨