I have used your code and a sample rive's community-made animation to reproduce the issue. If I understood your needs right, here are the two solutions:
1- If you take a look at the source code of rive's controller(OneShotAnimation) that you use in your application:
one_shot_controller.dart
import 'package:flutter/widgets.dart';
import 'package:rive/src/controllers/simple_controller.dart';
/// Controller tailered for managing one-shot animations
class OneShotAnimation extends SimpleAnimation {
/// Fires when the animation stops being active
final VoidCallback? onStop;
/// Fires when the animation starts being active
final VoidCallback? onStart;
OneShotAnimation(
String animationName, {
double mix = 1,
bool autoplay = true,
this.onStop,
this.onStart,
}) : super(animationName, mix: mix, autoplay: autoplay) {
isActiveChanged.addListener(onActiveChanged);
}
/// Dispose of any callback listeners
@override
void dispose() {
super.dispose();
isActiveChanged.removeListener(onActiveChanged);
}
/// Perform tasks when the animation's active state changes
void onActiveChanged() {
// If the animation stops and it is at the end of the one-shot, reset the
// animation back to the starting time
if (!isActive) {
reset();
}
// Fire any callbacks
isActive
? onStart?.call()
// onStop can fire while widgets are still drawing
: WidgetsBinding.instance?.addPostFrameCallback((_) => onStop?.call());
}
}
As we can see in the source code, it resets the animation when it ends. So there is nothing to do if you want to use OneShotAnimation. The only way is that you can fork the source code and change the related line. And add a modified version to your project.
one_shot_controller.dart
import 'package:flutter/widgets.dart';
import 'package:rive/src/controllers/simple_controller.dart';
/// Controller tailered for managing one-shot animations
class OneShotAnimation extends SimpleAnimation {
/// Fires when the animation stops being active
final VoidCallback? onStop;
/// Fires when the animation starts being active
final VoidCallback? onStart;
OneShotAnimation(
String animationName, {
double mix = 1,
bool autoplay = true,
this.onStop,
this.onStart,
}) : super(animationName, mix: mix, autoplay: autoplay) {
isActiveChanged.addListener(onActiveChanged);
}
/// Dispose of any callback listeners
@override
void dispose() {
super.dispose();
isActiveChanged.removeListener(onActiveChanged);
}
/// Perform tasks when the animation's active state changes
void onActiveChanged() {
// Fire any callbacks
isActive
? onStart?.call()
// onStop can fire while widgets are still drawing
: WidgetsBinding.instance?.addPostFrameCallback((_) => onStop?.call());
}
}
I have already tried it out, and it works as you wanted. But running animation for the second time could be problematic. For that, please check the second solution.
2- You can use SimpleAnimation. Please check the following solution:
class Square extends StatefulWidget {
final String title;
const Square({
required this.title,
Key? key,
}) : super(key: key);
@override
State<Square> createState() => _SquareState();
}
class _SquareState extends State<Square> {
late SimpleAnimation _esamiController;
bool get isPlaying => _esamiController.isActive;
@override
void initState() {
_esamiController = SimpleAnimation('bell', autoplay: false);
super.initState();
}
void _reset() {
if (!isPlaying) {
_esamiController.reset();
}
}
Future<void> _togglePlay() async {
if (isPlaying) return;
_reset();
_esamiController.isActive = true;
await Future.delayed(
const Duration(milliseconds: 20),
);
_esamiController.isActive = true;
}
@override
void dispose() {
_esamiController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) => Scaffold(
body: Container(
alignment: Alignment.center,
child: InkWell(
onTap: _togglePlay,
child: SizedBox(
width: 192,
height: 192,
child: Card(
color: Colors.black26,
elevation: 10,
child: Center(
child: RiveAnimation.asset(
'assets/alarm.riv',
controllers: [_esamiController],
onInit: (_) => setState(() {}),
),
),
),
),
),
),
);
}
As you can see, I have changed RiveAnimationController with the SimpleAnimation to access the reset method. Because another way, once the animation runs, there is no way to run it for a second time through RiveAnimationController.
If you have further problems, please don't hesitate to write in the comments.
Note: Don't forget to modify animationName and provide a correct asset directory in RiveAnimation.asset widget.