1- import 'package:flutter/widgets.dart' ;
2- import 'package:hyper_effects/src/apple_curves.dart' ;
1+ import 'dart:math' ;
32
4- import 'effect_animation_value.dart' ;
3+ import 'package:flutter/widgets.dart' ;
4+ import 'package:hyper_effects/hyper_effects.dart' ;
55
66/// Provides extension methods for [Widget] to animate it's appearance.
77extension AnimatedEffectExt on Widget {
@@ -40,6 +40,30 @@ extension AnimatedEffectExt on Widget {
4040 );
4141 }
4242
43+ Widget animateAfter ({
44+ Duration duration = const Duration (milliseconds: 350 ),
45+ Curve curve = appleEaseInOut,
46+ int repeat = 0 ,
47+ bool reverse = false ,
48+ Duration delay = Duration .zero,
49+ VoidCallback ? onEnd,
50+ }) {
51+ return AnimatedEffect (
52+ shouldTriggerAfterLast: true ,
53+ startImmediately: false ,
54+ toggle: false ,
55+ duration: duration,
56+ curve: curve,
57+ repeat: repeat,
58+ reverse: reverse,
59+ delay: delay,
60+ onEnd: onEnd,
61+ child: this ,
62+ );
63+ }
64+
65+ Widget resetAll () => ResetEffect (child: this );
66+
4367 /// Animate the effects applied to this widget.
4468 ///
4569 /// Unlike [animate] , this method triggers the animation immediately without
@@ -54,7 +78,7 @@ extension AnimatedEffectExt on Widget {
5478 ///
5579 /// The [repeat] parameter is used to determine how the animation should be
5680 /// repeated.
57- Widget oneShot ({
81+ AnimatedEffect oneShot ({
5882 Duration duration = const Duration (milliseconds: 350 ),
5983 Curve curve = appleEaseInOut,
6084 int repeat = 0 ,
@@ -74,40 +98,6 @@ extension AnimatedEffectExt on Widget {
7498 );
7599 }
76100
77- /// Animates the next effects only after the previous effects have finished.
78- Widget then (
79- BuildContext context, {
80- Duration duration = const Duration (milliseconds: 350 ),
81- Curve curve = appleEaseInOut,
82- int times = 0 ,
83- bool reverse = false ,
84- Duration delay = Duration .zero,
85- }) {
86- // if (this case AnimatedEffect widget) {
87- //
88- // return AnimatedEffect(
89- // toggle: null,
90- // duration: duration,
91- // curve: curve,
92- // startImmediately: true,
93- // repeat: times,
94- // reverse: reverse,
95- // startAfter: state?._controller,
96- // child: this,
97- // );
98- // }
99-
100- return AnimatedEffect (
101- toggle: null ,
102- duration: duration,
103- curve: curve,
104- startImmediately: true ,
105- repeat: times,
106- reverse: reverse,
107- child: this ,
108- );
109- }
110-
111101 /// Repeats the chain of preceding effects.
112102 /// [delay] is the delay between each repetition.
113103 /// [repeat] determines how the animation should be repeated.
@@ -162,7 +152,7 @@ class AnimatedEffect extends StatefulWidget {
162152 /// A delay before the animation starts.
163153 final Duration delay;
164154
165- final AnimationController ? startAfter ;
155+ final bool shouldTriggerAfterLast ;
166156
167157 /// Creates [AnimatedEffect] widget.
168158 const AnimatedEffect ({
@@ -176,7 +166,7 @@ class AnimatedEffect extends StatefulWidget {
176166 this .repeat = 0 ,
177167 this .reverse = false ,
178168 this .delay = Duration .zero,
179- this .startAfter ,
169+ this .shouldTriggerAfterLast = false ,
180170 });
181171
182172 @override
@@ -206,13 +196,7 @@ class _AnimatedEffectState extends State<AnimatedEffect>
206196
207197 _controller.addStatusListener (onAnimationStatusChanged);
208198
209- if (widget.startImmediately && widget.startAfter == null ) {
210- forward ();
211- }
212-
213- if (widget.startAfter != null ) {
214- widget.startAfter! .addStatusListener (startAfterListener);
215- }
199+ if (widget.startImmediately) forward ();
216200 }
217201
218202 @override
@@ -227,7 +211,6 @@ class _AnimatedEffectState extends State<AnimatedEffect>
227211
228212 @override
229213 void dispose () {
230- widget.startAfter? .removeStatusListener (startAfterListener);
231214 _controller.removeStatusListener (onAnimationStatusChanged);
232215 _controller.dispose ();
233216 super .dispose ();
@@ -240,15 +223,29 @@ class _AnimatedEffectState extends State<AnimatedEffect>
240223 }
241224
242225 void onAnimationStatusChanged (AnimationStatus status) {
226+ print ('onAnimationStatusChanged: $status | ${this .hashCode }' );
243227 if (status == AnimationStatus .completed ||
244228 status == AnimationStatus .dismissed) {
245229 widget.onEnd? .call ();
246230
247231 if (_repeatTimes == - 1 || _repeatTimes > 0 ) {
248232 if (_repeatTimes != - 1 && (! widget.reverse || ! shouldReverse)) {
249- _repeatTimes-- ;
233+ _repeatTimes = max ( 0 , _repeatTimes - 1 ) ;
250234 }
251235 forward ();
236+ } else if (_repeatTimes == 0 ) {
237+ // chaining animations
238+ final parentState =
239+ context.findAncestorStateOfType <_AnimatedEffectState >();
240+ if (parentState? .widget.shouldTriggerAfterLast == true ) {
241+ parentState? .forward ();
242+ }
243+ if (parentState == null ) {
244+ print ('resetting from state: ${this .hashCode }' );
245+ final resetState =
246+ context.findAncestorStateOfType <_ResetEffectState >();
247+ resetState? .reset ();
248+ }
252249 }
253250 }
254251 }
@@ -259,6 +256,7 @@ class _AnimatedEffectState extends State<AnimatedEffect>
259256 if (widget.reverse && shouldReverse) {
260257 _controller.reverse ();
261258 } else {
259+ _controller.reset ();
262260 _controller.forward (from: 0 );
263261 }
264262 shouldReverse = ! shouldReverse;
@@ -289,3 +287,40 @@ class _AnimatedEffectState extends State<AnimatedEffect>
289287 );
290288 }
291289}
290+
291+ class ResetEffect extends StatefulWidget {
292+ final Widget child;
293+ const ResetEffect ({super .key, required this .child});
294+
295+ @override
296+ State <ResetEffect > createState () => _ResetEffectState ();
297+ }
298+
299+ class _ResetEffectState extends State <ResetEffect > {
300+ @override
301+ Widget build (BuildContext context) => widget.child;
302+
303+ void reset () {
304+ print ('reset' );
305+ final state = findLeafAnimatedEffectState (context);
306+ print ('found state: ${state .hashCode }' );
307+ state? .forward ();
308+ }
309+
310+ _AnimatedEffectState ? findLeafAnimatedEffectState (BuildContext context) {
311+ _AnimatedEffectState ? result;
312+
313+ void visitor (Element element) {
314+ final Widget widget = element.widget;
315+ if (widget is AnimatedEffect ) {
316+ final StatefulElement editableTextElement = element as StatefulElement ;
317+
318+ result = editableTextElement.state as _AnimatedEffectState ;
319+ }
320+ element.visitChildren (visitor);
321+ }
322+
323+ context.visitChildElements (visitor);
324+ return result;
325+ }
326+ }
0 commit comments