11import 'package:flutter/widgets.dart' ;
22import 'package:hyper_effects/hyper_effects.dart' ;
33
4+ /// A callback that returns whether an animation should be allowed
5+ /// to follow through with its animation or be skipped completely,
6+ /// even when explicitly triggered.
7+ typedef BooleanCallback = bool Function ();
8+
49/// Represents the different ways that can trigger an animation.
510enum AnimationTriggerType {
611 /// The animation is triggered by a [toggle] parameter.
@@ -41,6 +46,7 @@ extension AnimatedEffectExt on Widget {
4146 bool reverse = false ,
4247 Duration delay = Duration .zero,
4348 VoidCallback ? onEnd,
49+ BooleanCallback ? playIf,
4450 }) {
4551 return AnimatedEffect (
4652 triggerType: AnimationTriggerType .toggle,
@@ -51,6 +57,7 @@ extension AnimatedEffectExt on Widget {
5157 reverse: reverse,
5258 delay: delay,
5359 onEnd: onEnd,
60+ playIf: playIf,
5461 child: this ,
5562 );
5663 }
@@ -64,7 +71,7 @@ extension AnimatedEffectExt on Widget {
6471 bool reverse = false ,
6572 Duration delay = Duration .zero,
6673 VoidCallback ? onEnd,
67- }) {
74+ BooleanCallback ? playIf }) {
6875 return AnimatedEffect (
6976 triggerType: AnimationTriggerType .afterLast,
7077 toggle: false ,
@@ -74,6 +81,7 @@ extension AnimatedEffectExt on Widget {
7481 reverse: reverse,
7582 delay: delay,
7683 onEnd: onEnd,
84+ playIf: playIf,
7785 child: this ,
7886 );
7987 }
@@ -99,7 +107,7 @@ extension AnimatedEffectExt on Widget {
99107 bool reverse = false ,
100108 Duration delay = Duration .zero,
101109 VoidCallback ? onEnd,
102- }) {
110+ BooleanCallback ? playIf }) {
103111 return AnimatedEffect (
104112 triggerType: AnimationTriggerType .oneShot,
105113 duration: duration,
@@ -108,6 +116,7 @@ extension AnimatedEffectExt on Widget {
108116 repeat: repeat,
109117 reverse: reverse,
110118 delay: delay,
119+ playIf: playIf,
111120 child: this ,
112121 );
113122 }
@@ -147,6 +156,11 @@ class AnimatedEffect extends StatefulWidget {
147156 /// A delay before the animation starts.
148157 final Duration delay;
149158
159+ /// A callback that returns whether the animation should be played
160+ /// or skipped. If the callback returns false, the animation will
161+ /// be skipped, even when it is explicitly triggered.
162+ final BooleanCallback ? playIf;
163+
150164 /// Creates [AnimatedEffect] widget.
151165 const AnimatedEffect ({
152166 super .key,
@@ -159,6 +173,7 @@ class AnimatedEffect extends StatefulWidget {
159173 this .repeat = 0 ,
160174 this .reverse = false ,
161175 this .delay = Duration .zero,
176+ this .playIf,
162177 });
163178
164179 @override
@@ -182,11 +197,15 @@ class _AnimatedEffectState extends State<AnimatedEffect>
182197 duration: widget.duration,
183198 );
184199
200+ bool get shouldPlay => widget.playIf? .call () ?? true ;
201+
185202 @override
186203 void initState () {
187204 super .initState ();
188205
189- if (widget.triggerType == AnimationTriggerType .oneShot) drive ();
206+ if (widget.triggerType == AnimationTriggerType .oneShot) {
207+ drive ();
208+ }
190209 }
191210
192211 @override
@@ -221,8 +240,8 @@ class _AnimatedEffectState extends State<AnimatedEffect>
221240 final parentState =
222241 context.findAncestorStateOfType <_AnimatedEffectState >();
223242 if (parentState != null ) {
224- if ( parentState.widget.triggerType ==
225- AnimationTriggerType .afterLast) {
243+ final triggerType = parentState.widget.triggerType;
244+ if (triggerType == AnimationTriggerType .afterLast) {
226245 await parentState.drive ();
227246 }
228247 } else {
@@ -242,13 +261,15 @@ class _AnimatedEffectState extends State<AnimatedEffect>
242261 Future <void > drive () async {
243262 return ensureDelay (() async {
244263 if (! mounted) return ;
264+ if (! shouldPlay) return ;
245265 if (widget.reverse && shouldReverse) {
246266 shouldReverse = false ;
247267 await _controller.reverse ().orCancel;
248268 } else {
249269 shouldReverse = widget.reverse;
250270 await _controller.forward (from: 0 ).orCancel;
251271 }
272+
252273 return onAnimationStatusChanged ();
253274 });
254275 }
0 commit comments