I am tying to use rive animations and I want to be able to change parameters from the animation. The purpose of the custom animation is to allow for any state machine to change based on inputs set in the rive animation builder. The rive animation is loaded into a widget and the inputs should be changed by events in the page of the project. How can I do this?
How is it possible to have action that changes the state of a stateful custom widget
Widgets & Design
Using custom classes, in custom files, as parameters for the widget to contain the attributes and methods to control the animation inputs without hassle. This doesn't work because I cant import the file for the class(es) (detailed by this issue issue on GitHub).
Moving the custom classes into the widget and use parameters to control the widget. This doesn't change the inability to access functions outside the widget.
Create actions inside the widgets as a parameter to change the inputs. This only create a action flow widget builder... I'm not sure how to use this.
Making custom actions to change the inputs. The actions are only usable if the widget it is on is the custom rive widget.
Here is the current state of the widget:
// Automatic FlutterFlow imports
import '/backend/schema/structs/index.dart';
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/widgets/index.dart'; // Imports other custom widgets
import '/custom_code/actions/index.dart'; // Imports custom actions
import '/flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom widget code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!
import 'package:rive/rive.dart';
class CustomRiveAnimation extends StatefulWidget {
const CustomRiveAnimation({
super.key,
this.width,
this.height,
required this.riveFilePath,
required this.startingStateMachineName,
});
final double? width;
final double? height;
final String riveFilePath;
final String startingStateMachineName;
@override
State<CustomRiveAnimation> createState() => CustomRiveAnimationState();
}
class CustomRiveAnimationState extends State<CustomRiveAnimation> {
StateMachineController? _controller;
RiveFile? _riveFile;
bool _isLoading = true;
String? _error;
@override
void initState() {
super.initState();
_loadRiveFile();
}
Future<void> _loadRiveFile() async {
try {
final file = await RiveFile.network(widget.riveFilePath);
final controller = StateMachineController.fromArtboard(
file.mainArtboard, widget.startingStateMachineName);
if (controller != null) {
file.mainArtboard.addController(controller);
setState(() {
_riveFile = file;
_controller = controller;
_isLoading = false;
});
} else {
setState(() {
_error =
"State machine '${widget.startingStateMachineName}' not found";
_isLoading = false;
});
}
} catch (e) {
setState(() {
_error = "Failed to load Rive file: $e";
_isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
if (_isLoading) {
return SizedBox(
width: widget.width,
height: widget.height,
child: const Center(child: CircularProgressIndicator()),
);
}
if (_error != null) {
return SizedBox(
width: widget.width,
height: widget.height,
child: Center(
child: Text(
_error!,
style: const TextStyle(color: Colors.red),
textAlign: TextAlign.center,
),
),
);
}
if (_riveFile == null) {
return SizedBox(
width: widget.width,
height: widget.height,
child: const Center(child: Text("No animation found")),
);
}
return SizedBox(
width: widget.width,
height: widget.height,
child: Rive(
artboard: _riveFile!.mainArtboard,
fit: BoxFit.contain,
),
);
}
@override
void dispose() {
_controller?.dispose();
super.dispose();
}
// Public methods that can be called from FlutterFlow pages
void updateInput(String inputName,
{double? numberValue, bool? boolValue, bool? triggerValue}) {
if (_controller == null) return;
try {
final input = _controller!.findInput<SMIInput>(inputName);
if (input == null) return;
if (input is SMINumber && numberValue != null) {
(input as SMINumber).value = numberValue;
} else if (input is SMIBool && boolValue != null) {
(input as SMIBool).value = boolValue;
} else if (input is SMITrigger && triggerValue == true) {
(input as SMITrigger).fire();
}
} catch (e) {
print("Error updating input '$inputName': $e");
}
}
void fireInput(String inputName) {
updateInput(inputName, triggerValue: true);
}
void setNumberInput(String inputName, double value) {
updateInput(inputName, numberValue: value);
}
void setBoolInput(String inputName, bool value) {
updateInput(inputName, boolValue: value);
}
Future<void> changeStateMachine(String newStateMachineName) async {
if (_riveFile == null) return;
try {
// Remove current controller
if (_controller != null) {
_riveFile!.mainArtboard.removeController(_controller!);
_controller!.dispose();
}
// Add new controller
final newController = StateMachineController.fromArtboard(
_riveFile!.mainArtboard,
newStateMachineName,
);
if (newController != null) {
_riveFile!.mainArtboard.addController(newController);
setState(() {
_controller = newController;
_error = null;
});
} else {
setState(() {
_error = "State machine '$newStateMachineName' not found";
});
}
} catch (e) {
setState(() {
_error = "Failed to change state machine: $e";
});
}
}
List<String> getInputNames() {
if (_controller == null) return [];
return _controller!.inputs.map((input) => input.name).toList();
}
bool hasInput(String inputName) {
if (_controller == null) return false;
return _controller!.findInput<SMIInput>(inputName) != null;
}
String? getCurrentStateMachine() {
return _controller?.stateMachine.name;
}
}I'm soooooooo lost.
Yes
3
1 reply