From dddb78b62fb2600f0e2bf94fbecbb4aefbf7ad2e Mon Sep 17 00:00:00 2001 From: matt Sullivan Date: Mon, 14 Jun 2021 14:41:10 -0700 Subject: [PATCH] Adds one-shot animation controller --- example/lib/main.dart | 15 ++++++ example/lib/play_one_shot_animation.dart | 52 ++++++++++++++++++++ example/lib/play_pause_animation.dart | 6 ++- lib/rive.dart | 1 + lib/src/controllers/one_shot_controller.dart | 44 +++++++++++++++++ lib/src/controllers/simple_controller.dart | 6 ++- 6 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 example/lib/play_one_shot_animation.dart create mode 100644 lib/src/controllers/one_shot_controller.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 32ee5fe..47aa0ed 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:rive_example/play_one_shot_animation.dart'; import 'package:rive_example/play_pause_animation.dart'; import 'package:rive_example/example_state_machine.dart'; import 'package:rive_example/liquid_download.dart'; @@ -50,6 +51,20 @@ class Home extends StatelessWidget { const SizedBox( height: 10, ), + ElevatedButton( + child: const Text('Play One-Shot Animation'), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const PlayOneShotAnimation(), + ), + ); + }, + ), + const SizedBox( + height: 10, + ), ElevatedButton( child: const Text('Button State Machine'), onPressed: () { diff --git a/example/lib/play_one_shot_animation.dart b/example/lib/play_one_shot_animation.dart new file mode 100644 index 0000000..a24080e --- /dev/null +++ b/example/lib/play_one_shot_animation.dart @@ -0,0 +1,52 @@ +/// Demonstrates play a one-shot animation on demand + +import 'package:flutter/material.dart'; +import 'package:rive/rive.dart'; + +class PlayOneShotAnimation extends StatefulWidget { + const PlayOneShotAnimation({Key? key}) : super(key: key); + + @override + _PlayOneShotAnimationState createState() => _PlayOneShotAnimationState(); +} + +class _PlayOneShotAnimationState extends State { + /// Controller for playback + late RiveAnimationController _controller; + + /// Is the animation currently playing? + bool _isPlaying = false; + + @override + void initState() { + super.initState(); + _controller = OnShotAnimation( + 'bounce', + autoplay: false, + onStop: () => setState(() => _isPlaying = false), + onStart: () => setState(() => _isPlaying = true), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('One-Shot Example'), + ), + body: Center( + child: RiveAnimation.network( + 'https://cdn.rive.app/vehicles.riv', + animations: const ['idle', 'curves'], + controllers: [_controller], + ), + ), + floatingActionButton: FloatingActionButton( + // disable the button while playing the animation + onPressed: () => _isPlaying ? null : _controller.isActive = true, + tooltip: 'Play', + child: const Icon(Icons.arrow_upward), + ), + ); + } +} diff --git a/example/lib/play_pause_animation.dart b/example/lib/play_pause_animation.dart index fbc2f28..9463d1e 100644 --- a/example/lib/play_pause_animation.dart +++ b/example/lib/play_pause_animation.dart @@ -1,3 +1,5 @@ +/// Demonstrates how to play and pause a looping animation + import 'package:flutter/material.dart'; import 'package:rive/rive.dart'; @@ -9,10 +11,10 @@ class PlayPauseAnimation extends StatefulWidget { } class _PlayPauseAnimationState extends State { - // Controller for playback + /// Controller for playback late RiveAnimationController _controller; - // Toggles between play and pause animation states + /// Toggles between play and pause animation states void _togglePlay() => setState(() => _controller.isActive = !_controller.isActive); diff --git a/lib/rive.dart b/lib/rive.dart index b7920ce..03d6c7d 100644 --- a/lib/rive.dart +++ b/lib/rive.dart @@ -1,6 +1,7 @@ library rive; export 'package:rive/src/controllers/simple_controller.dart'; +export 'package:rive/src/controllers/one_shot_controller.dart'; export 'package:rive/src/extensions.dart'; export 'package:rive/src/extensions.dart'; export 'package:rive/src/rive.dart'; diff --git a/lib/src/controllers/one_shot_controller.dart b/lib/src/controllers/one_shot_controller.dart new file mode 100644 index 0000000..986194c --- /dev/null +++ b/lib/src/controllers/one_shot_controller.dart @@ -0,0 +1,44 @@ +import 'dart:ui' show VoidCallback; + +import 'package:flutter/widgets.dart'; +import 'package:rive/src/controllers/simple_controller.dart'; + +/// Controller tailered for managing one-shot animations +class OnShotAnimation extends SimpleAnimation { + /// Fires when the animation stops being active + final VoidCallback? onStop; + + /// Fires when the animation starts being active + final VoidCallback? onStart; + + OnShotAnimation( + 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()); + } +} diff --git a/lib/src/controllers/simple_controller.dart b/lib/src/controllers/simple_controller.dart index d38a3f5..fd9c958 100644 --- a/lib/src/controllers/simple_controller.dart +++ b/lib/src/controllers/simple_controller.dart @@ -21,7 +21,6 @@ class SimpleAnimation extends RiveAnimationController { final String animationName; /// Stops the animation on the next apply - /// bool _stopOnNextApply = false; /// Pauses the animation when it's created @@ -71,4 +70,9 @@ class SimpleAnimation extends RiveAnimationController { // stop itself. _stopOnNextApply = false; } + + /// Resets the animation back to it's starting time position + void reset() { + _instance?.reset(); + } }