From c2934d6b4f5651c1210ab2f5b14e892374c34142 Mon Sep 17 00:00:00 2001 From: Stefano Rodriguez Date: Sat, 17 Apr 2021 22:18:13 +0200 Subject: [PATCH 1/5] feat: add more type safety --- analysis_options.yaml | 4 + .../same_variable_multiple_animations.dart | 64 ++++---- example/lib/sequence_page.dart | 10 +- .../lib/staggered_animation_replication.dart | 31 ++-- lib/flutter_sequence_animation.dart | 149 ++++++++++++------ test/animation_sequence_tests.dart | 72 ++++----- 6 files changed, 186 insertions(+), 144 deletions(-) create mode 100644 analysis_options.yaml diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..4135297 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,4 @@ +analyzer: + strong-mode: + implicit-casts: false + implicit-dynamic: false diff --git a/example/lib/same_variable_multiple_animations.dart b/example/lib/same_variable_multiple_animations.dart index 59739b4..4a00daf 100755 --- a/example/lib/same_variable_multiple_animations.dart +++ b/example/lib/same_variable_multiple_animations.dart @@ -5,57 +5,58 @@ import 'package:flutter_sequence_animation/flutter_sequence_animation.dart'; class SameVariableAnimationPage extends StatefulWidget { @override - _SameVariableAnimationPageState createState() => new _SameVariableAnimationPageState(); + _SameVariableAnimationPageState createState() => + new _SameVariableAnimationPageState(); } -class _SameVariableAnimationPageState extends State with SingleTickerProviderStateMixin{ - +class _SameVariableAnimationPageState extends State + with SingleTickerProviderStateMixin { + static const colorTag = SequenceAnimationTag("color"); + static const widthTag = SequenceAnimationTag("width"); + static const heightTag = SequenceAnimationTag("height"); late AnimationController controller; late SequenceAnimation sequenceAnimation; - @override void initState() { super.initState(); - controller = new AnimationController(vsync: this, duration: const Duration(seconds: 5)); + controller = new AnimationController( + vsync: this, duration: const Duration(seconds: 5)); sequenceAnimation = new SequenceAnimationBuilder() - .addAnimatable( + .addAnimatable( animatable: new ColorTween(begin: Colors.red, end: Colors.yellow), - from: const Duration(seconds: 0), + from: const Duration(seconds: 0), to: const Duration(seconds: 4), - tag: "color" - ).addAnimatable( + tag: colorTag) + .addAnimatable( animatable: new Tween(begin: 50.0, end: 300.0), - from: const Duration(seconds: 0), + from: const Duration(seconds: 0), to: const Duration(milliseconds: 3000), - tag: "width", - curve: Curves.easeIn - ).addAnimatable( + tag: widthTag, + curve: Curves.easeIn) + .addAnimatable( animatable: new Tween(begin: 300.0, end: 100.0), - from: const Duration(milliseconds: 3000), + from: const Duration(milliseconds: 3000), to: const Duration(milliseconds: 3700), - tag: "width", - curve: Curves.decelerate - ).addAnimatable( + tag: widthTag, + curve: Curves.decelerate) + .addAnimatable( animatable: new Tween(begin: 50.0, end: 300.0), - from: const Duration(seconds: 0), + from: const Duration(seconds: 0), to: const Duration(milliseconds: 3000), - tag: "height", - curve: Curves.ease - ).addAnimatable( + tag: heightTag, + curve: Curves.ease) + .addAnimatable( animatable: new Tween(begin: 300.0, end: 450.0), - from: const Duration(milliseconds: 3000), + from: const Duration(milliseconds: 3000), to: const Duration(milliseconds: 3800), - tag: "height", - curve: Curves.decelerate - ).animate(controller); - - + tag: heightTag, + curve: Curves.decelerate) + .animate(controller); } - Future _playAnimation() async { try { await controller.forward().orCancel; @@ -86,9 +87,9 @@ class _SameVariableAnimationPageState extends State w builder: (context, child) { return new Center( child: new Container( - color: sequenceAnimation["color"].value, - height: sequenceAnimation["height"].value, - width: sequenceAnimation["width"].value, + color: sequenceAnimation.get(colorTag).value, + height: sequenceAnimation.get(heightTag).value, + width: sequenceAnimation.get(widthTag).value, ), ); }, @@ -97,5 +98,4 @@ class _SameVariableAnimationPageState extends State w ), ); } - } diff --git a/example/lib/sequence_page.dart b/example/lib/sequence_page.dart index 3893cff..ea1d7d1 100755 --- a/example/lib/sequence_page.dart +++ b/example/lib/sequence_page.dart @@ -9,7 +9,7 @@ class SequencePage extends StatefulWidget { } class _SequencePageState extends State with SingleTickerProviderStateMixin{ - + static const colorTag = SequenceAnimationTag("color"); late AnimationController controller; late SequenceAnimation sequenceAnimation; @@ -25,19 +25,19 @@ class _SequencePageState extends State with SingleTickerProviderSt animatable: new ColorTween(begin: Colors.red, end: Colors.yellow), from: const Duration(seconds: 0), to: const Duration(seconds: 2), - tag: "color" + tag: colorTag ).addAnimatable( animatable: new ColorTween(begin: Colors.yellow, end: Colors.blueAccent), from: const Duration(seconds: 2), to: const Duration(seconds: 4), - tag: "color", + tag: colorTag, curve: Curves.easeOut ).addAnimatable( animatable: new ColorTween(begin: Colors.blueAccent, end: Colors.pink), // animatable: new Tween(begin: 200.0, end: 40.0), from: const Duration(seconds: 5), to: const Duration(seconds: 6), - tag: "color", + tag: colorTag, curve: Curves.fastOutSlowIn ).animate(controller); @@ -75,7 +75,7 @@ class _SequencePageState extends State with SingleTickerProviderSt builder: (context, child) { return new Center( child: new Container( - color: sequenceAnimation["color"].value, + color: sequenceAnimation.get(colorTag).value, height: 200.0, width: 200.0, ), diff --git a/example/lib/staggered_animation_replication.dart b/example/lib/staggered_animation_replication.dart index 26c4d1f..693bd02 100755 --- a/example/lib/staggered_animation_replication.dart +++ b/example/lib/staggered_animation_replication.dart @@ -10,6 +10,13 @@ class StaggeredAnimationReplication extends StatefulWidget { class _StaggeredAnimationReplicationState extends State with SingleTickerProviderStateMixin{ + static const opacityKey = SequenceAnimationTag("opacity"); + static const widthKey = SequenceAnimationTag("width"); + static const heightKey = SequenceAnimationTag("height"); + static const paddingKey = SequenceAnimationTag("padding"); + static const borderRadiusKey = SequenceAnimationTag("borderRadius"); + static const colorKey = SequenceAnimationTag("color"); + late AnimationController controller; late SequenceAnimation sequenceAnimation; @@ -24,37 +31,37 @@ class _StaggeredAnimationReplicationState extends State(begin: 50.0, end: 150.0), from: const Duration(milliseconds: 250), to: const Duration(milliseconds: 500), curve: Curves.ease, - tag: "width" + tag: widthKey ).addAnimatable( animatable: new Tween(begin: 50.0, end: 150.0), from: const Duration(milliseconds: 500), to: const Duration(milliseconds: 750), curve: Curves.ease, - tag: "height" + tag: heightKey ).addAnimatable( animatable: new EdgeInsetsTween(begin: const EdgeInsets.only(bottom: 16.0), end: const EdgeInsets.only(bottom: 75.0),), from: const Duration(milliseconds: 500), to: const Duration(milliseconds: 750), curve: Curves.ease, - tag: "padding" + tag: paddingKey ).addAnimatable( animatable: new BorderRadiusTween(begin: new BorderRadius.circular(4.0), end: new BorderRadius.circular(75.0),), from: const Duration(milliseconds: 750), to: const Duration(milliseconds: 1000), curve: Curves.ease, - tag: "borderRadius" + tag: borderRadiusKey ).addAnimatable( animatable: new ColorTween(begin: Colors.indigo[100], end: Colors.orange[400],), from: const Duration(milliseconds: 1000), to: const Duration(milliseconds: 1500), curve: Curves.ease, - tag: "color" + tag: colorKey ).animate(controller); } @@ -66,20 +73,20 @@ class _StaggeredAnimationReplicationState extends State> { _AnimationInformation({ required this.animatable, required this.from, @@ -10,23 +9,49 @@ class _AnimationInformation { required this.tag, }); - final Animatable animatable; + final A animatable; final Duration from; final Duration to; final Curve curve; - final Object tag; + final SequenceAnimationTag tag; + + IntervalAnimatable createIntervalAnimatable({ + required Animatable animatable, + required Animatable defaultAnimatable, + required double begin, + required double end, + }) => + IntervalAnimatable( + animatable: animatable, + defaultAnimatable: defaultAnimatable, + begin: begin, + end: end, + ); +} + +class SequenceAnimationTag { + const SequenceAnimationTag(this.value); + final Object value; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is SequenceAnimationTag && + runtimeType == other.runtimeType && + value == other.value; + + @override + int get hashCode => value.hashCode; } class SequenceAnimationBuilder { List<_AnimationInformation> _animations = []; - // Returns the duration of the current animation chain Duration getCurrentDuration() { return Duration(microseconds: _currentLengthInMicroSeconds()); } - /// Convenient wrapper to add an animatable after the last one with a specific tag finished is finished /// /// The tags must be comparable! Strings, enums work, when using objects, be sure to override the == method @@ -53,19 +78,29 @@ class SequenceAnimationBuilder { /// /// The animation with tag "animation" will start at second 3 and run until second 4. /// - SequenceAnimationBuilder addAnimatableAfterLastOneWithTag({ + SequenceAnimationBuilder addAnimatableAfterLastOneWithTag>({ required Object lastTag, - required Animatable animatable, + required A animatable, Duration delay: Duration.zero, required Duration duration, Curve curve: Curves.linear, - required Object tag, + required SequenceAnimationTag tag, }) { - assert(_animations.isNotEmpty, "Can not add animatable after last one if there is no animatable yet"); - var start = _animations.cast<_AnimationInformation?>().lastWhere((it) => it?.tag == lastTag, orElse: () => null)?.to; - assert(start != null, "Animation with tag $lastTag can not be found before $tag"); + assert(_animations.isNotEmpty, + "Can not add animatable after last one if there is no animatable yet"); + var start = _animations + .cast<_AnimationInformation?>() + .lastWhere((it) => it?.tag == lastTag, orElse: () => null) + ?.to; + assert(start != null, + "Animation with tag $lastTag can not be found before $tag"); start!; - return addAnimatable(animatable: animatable, from: start + delay, to: start + delay + duration, tag: tag, curve: curve); + return addAnimatable( + animatable: animatable, + from: start + delay, + to: start + delay + duration, + tag: tag, + curve: curve); } /// Convenient wrapper to add an animatable after the last one is finished @@ -91,29 +126,40 @@ class SequenceAnimationBuilder { /// /// The animation with tag "animation" will start at second 3 and run until second 4. /// - SequenceAnimationBuilder addAnimatableAfterLastOne({ - required Animatable animatable, + SequenceAnimationBuilder addAnimatableAfterLastOne>({ + required A animatable, Duration delay: Duration.zero, required Duration duration, Curve curve: Curves.linear, - required Object tag, + required SequenceAnimationTag tag, }) { - assert(_animations.isNotEmpty, "Can not add animatable after last one if there is no animatable yet"); + assert(_animations.isNotEmpty, + "Can not add animatable after last one if there is no animatable yet"); var start = _animations.last.to; - return addAnimatable(animatable: animatable, from: start + delay, to: start + delay + duration, tag: tag, curve: curve); + return addAnimatable( + animatable: animatable, + from: start + delay, + to: start + delay + duration, + tag: tag, + curve: curve); } /// Convenient wrapper around to specify an animatable using a duration instead of end point /// /// Instead of specifying from and to, you specify start and duration - SequenceAnimationBuilder addAnimatableUsingDuration({ - required Animatable animatable, + SequenceAnimationBuilder addAnimatableUsingDuration>({ + required A animatable, required Duration start, required Duration duration, Curve curve: Curves.linear, - required Object tag, - }) { - return addAnimatable(animatable: animatable, from: start, to: start + duration, tag: tag, curve: curve); + required SequenceAnimationTag tag, + }) { + return addAnimatable( + animatable: animatable, + from: start, + to: start + duration, + tag: tag, + curve: curve); } /// Adds an [Animatable] to the sequence, in the most cases this would be a [Tween]. @@ -137,15 +183,16 @@ class SequenceAnimationBuilder { /// .animate(controller); /// ``` /// - SequenceAnimationBuilder addAnimatable({ - required Animatable animatable, + SequenceAnimationBuilder addAnimatable>({ + required A animatable, required Duration from, required Duration to, Curve curve: Curves.linear, - required Object tag, + required SequenceAnimationTag tag, }) { + assert(T.toString() != 'Object'); assert(to >= from); - _animations.add(new _AnimationInformation( + _animations.add(new _AnimationInformation( animatable: animatable, from: from, to: to, curve: curve, tag: tag)); return this; } @@ -167,9 +214,9 @@ class SequenceAnimationBuilder { // Sets the duration of the controller controller.duration = new Duration(microseconds: longestTimeMicro); - Map animatables = {}; - Map begins = {}; - Map ends = {}; + Map animatables = {}; + Map begins = {}; + Map ends = {}; _animations.forEach((info) { assert(info.to.inMicroseconds <= longestTimeMicro); @@ -178,8 +225,7 @@ class SequenceAnimationBuilder { double end = info.to.inMicroseconds / longestTimeMicro; Interval intervalCurve = new Interval(begin, end, curve: info.curve); if (animatables[info.tag] == null) { - animatables[info.tag] = - IntervalAnimatable.chainCurve(info.animatable, intervalCurve); + animatables[info.tag] = info.animatable.chainCurve(intervalCurve); begins[info.tag] = begin; ends[info.tag] = end; } else { @@ -188,12 +234,10 @@ class SequenceAnimationBuilder { "When animating the same property you need to: \n" "a) Have them not overlap \n" "b) Add them in an ordered fashion\n" - "Animation with tag ${info.tag} ends at ${ends[info.tag]} but also begins at $begin" - ); - animatables[info.tag] = new IntervalAnimatable( + "Animation with tag ${info.tag} ends at ${ends[info.tag]} but also begins at $begin"); + animatables[info.tag] = info.createIntervalAnimatable( animatable: animatables[info.tag]!, - defaultAnimatable: - IntervalAnimatable.chainCurve(info.animatable, intervalCurve), + defaultAnimatable: info.animatable.chainCurve(intervalCurve), begin: begins[info.tag]!, end: ends[info.tag]!, ); @@ -201,7 +245,7 @@ class SequenceAnimationBuilder { } }); - Map result = {}; + Map result = {}; animatables.forEach((tag, animInfo) { result[tag] = animInfo.animate(controller); @@ -212,16 +256,17 @@ class SequenceAnimationBuilder { } class SequenceAnimation { - final Map _animations; + final Map _animations; /// Use the [SequenceAnimationBuilder] to construct this class. SequenceAnimation._internal(this._animations); /// Returns the animation with a given tag, this animation is tied to the controller. - Animation operator [](Object key) { - assert(_animations.containsKey(key), - "There was no animatable with the key: $key"); - return _animations[key]!; + Animation get(SequenceAnimationTag tag) { + assert(_animations.containsKey(tag), + "There was no animatable with the tag: ${tag.value}"); + + return _animations[tag]! as Animation; } } @@ -235,8 +280,8 @@ class IntervalAnimatable extends Animatable { required this.end, }); - final Animatable animatable; - final Animatable defaultAnimatable; + final Animatable animatable; + final Animatable defaultAnimatable; /// The relative begin to of [animatable] /// If your [AnimationController] is running from 0->1, this needs to be a value between those two @@ -246,12 +291,6 @@ class IntervalAnimatable extends Animatable { /// If your [AnimationController] is running from 0->1, this needs to be a value between those two final double end; - /// Chains an [Animatable] with a [CurveTween] and the given [Interval]. - /// Basically, the animation is being constrained to the given interval - static Animatable chainCurve(Animatable parent, Interval interval) { - return parent.chain(new CurveTween(curve: interval)); - } - @override T transform(double t) { if (t >= begin && t <= end) { @@ -261,3 +300,11 @@ class IntervalAnimatable extends Animatable { } } } + +extension _Chain on Animatable { + /// Chains an [Animatable] with a [CurveTween] and the given [Interval]. + /// Basically, the animation is being constrained to the given interval + Animatable chainCurve(Interval interval) { + return chain(new CurveTween(curve: interval)); + } +} diff --git a/test/animation_sequence_tests.dart b/test/animation_sequence_tests.dart index 3c3143b..9b569f3 100755 --- a/test/animation_sequence_tests.dart +++ b/test/animation_sequence_tests.dart @@ -25,7 +25,7 @@ void main() { expect(controller.duration, isNull); - String seqKey = "color"; + const seqKey = const SequenceAnimationTag("color"); SequenceAnimation sequenceAnimation = new SequenceAnimationBuilder() .addAnimatable( tag: seqKey, @@ -39,7 +39,7 @@ void main() { expect(controller.duration, isNotNull); - ValueKey key = new ValueKey("color"); + final key = new ValueKey("color"); // Build our app and trigger a frame. await tester.pumpWidget(new AnimatedBuilder(animation: controller, builder: (context, child) { @@ -47,7 +47,7 @@ void main() { key: key, width: 200.0, height: 200.0, - color: sequenceAnimation[seqKey].value, + color: sequenceAnimation.get(seqKey).value, ); })); @@ -74,10 +74,11 @@ void main() { expect(controller.duration, isNull); - String seqKey = "color"; + const seqKey = const SequenceAnimationTag("color"); SequenceAnimation sequenceAnimation = new SequenceAnimationBuilder() .addAnimatable( tag: seqKey, + //animatable: Tween(), animatable: new ColorTween(begin: Colors.red, end: Colors.blue), from: const Duration(seconds: 0), to: const Duration(seconds: 1)) @@ -103,7 +104,7 @@ void main() { expect(controller.duration, equals(const Duration(seconds: 4))); - ValueKey key = new ValueKey("color"); + final key = new ValueKey("color"); // Build our app and trigger a frame. await tester.pumpWidget(new AnimatedBuilder(animation: controller, builder: (context, child) { @@ -111,7 +112,7 @@ void main() { key: key, width: 200.0, height: 200.0, - color: sequenceAnimation[seqKey].value, + color: sequenceAnimation.get(seqKey).value, ); })); @@ -137,7 +138,7 @@ void main() { expect(controller.duration, isNull); - String seqKey = "color"; + const seqKey = const SequenceAnimationTag("color"); SequenceAnimation sequenceAnimation = new SequenceAnimationBuilder() .addAnimatable( tag: seqKey, @@ -163,7 +164,7 @@ void main() { expect(controller.duration, equals(const Duration(seconds: 4))); - ValueKey key = new ValueKey("color"); + final key = new ValueKey("color"); // Build our app and trigger a frame. await tester.pumpWidget(new AnimatedBuilder(animation: controller, builder: (context, child) { @@ -171,7 +172,7 @@ void main() { key: key, width: 200.0, height: 200.0, - color: sequenceAnimation[seqKey].value, + color: sequenceAnimation.get(seqKey).value, ); })); @@ -204,7 +205,7 @@ void main() { expect(controller.duration, equals(const Duration(seconds: 0))); try { - sequenceAnimation["doesntExit"]; + sequenceAnimation.get(SequenceAnimationTag("doesntExit")); } catch(e) { expect(e,isAssertionError); } @@ -224,8 +225,9 @@ void main() { expect(controller.duration, isNull); - String colorKey = "color"; - String widthKey = "width"; + const colorKey = const SequenceAnimationTag("color"); + const widthKey = const SequenceAnimationTag("width"); + SequenceAnimation sequenceAnimation = new SequenceAnimationBuilder() .addAnimatable( tag: colorKey, @@ -234,7 +236,7 @@ void main() { to: const Duration(seconds: 1)) .addAnimatable( tag: widthKey, - animatable: new Tween(begin: 50.0, end: 500.0), + animatable: new Tween(begin: 50.0, end: 500.0), from: const Duration(seconds: 1), to: const Duration(seconds: 5)) .addAnimatable( @@ -259,15 +261,15 @@ void main() { expect(controller.duration, equals(const Duration(seconds: 5))); - ValueKey key = new ValueKey("color"); + final key = new ValueKey("color"); // Build our app and trigger a frame. await tester.pumpWidget(new AnimatedBuilder(animation: controller, builder: (context, child) { return new Container( key: key, - width: sequenceAnimation[widthKey].value, + width: sequenceAnimation.get(widthKey).value, height: 200.0, - color: sequenceAnimation[colorKey].value, + color: sequenceAnimation.get(colorKey).value, ); })); @@ -299,15 +301,17 @@ void main() { AnimationController controller = new AnimationController(vsync: const TestVSync()); + const tag = const SequenceAnimationTag("s"); + try { new SequenceAnimationBuilder() .addAnimatable( - tag: "s", + tag: tag, animatable: new ColorTween(begin: Colors.red, end: Colors.yellow), from: const Duration(seconds: 0), to: const Duration(seconds: 2)) .addAnimatable( - tag: "s", + tag: tag, animatable: new ColorTween(begin: Colors.red, end: Colors.yellow), from: const Duration(seconds: 1), to: const Duration(seconds: 2)) @@ -320,12 +324,12 @@ void main() { try { new SequenceAnimationBuilder() .addAnimatable( - tag: "s", + tag: tag, animatable: new ColorTween(begin: Colors.red, end: Colors.yellow), from: const Duration(seconds: 0), to: const Duration(milliseconds: 2000)) .addAnimatable( - tag: "s", + tag: tag, animatable: new ColorTween(begin: Colors.red, end: Colors.yellow), from: const Duration(milliseconds: 1999), to: const Duration(milliseconds: 2001)) @@ -336,32 +340,12 @@ void main() { }); - testWidgets('Same tag but different types', (WidgetTester tester) async { - AnimationController controller = new AnimationController(vsync: const TestVSync()); - try { - new SequenceAnimationBuilder() - .addAnimatable( - tag: "s", - animatable: new ColorTween(begin: Colors.red, end: Colors.yellow), - from: const Duration(seconds: 0), - to: const Duration(seconds: 2)) - .addAnimatable( - tag: "s", - animatable: new Tween(begin: 0.0, end: 100.0), - from: const Duration(seconds: 3), - to: const Duration(seconds: 4)) - .animate(controller); - } catch(e) { - expect(e, isAssertionError); - } - }); - - testWidgets('Uses object key', (WidgetTester tester) async { AnimationController controller = new AnimationController(vsync: const TestVSync()); - Object seqKey = false; + const seqKey = const SequenceAnimationTag(false); + SequenceAnimation sequenceAnimation = new SequenceAnimationBuilder() .addAnimatable( tag: seqKey, @@ -375,7 +359,7 @@ void main() { expect(controller.duration, isNotNull); - ValueKey key = new ValueKey("color"); + final key = new ValueKey("color"); // Build our app and trigger a frame. await tester.pumpWidget(new AnimatedBuilder(animation: controller, builder: (context, child) { @@ -383,7 +367,7 @@ void main() { key: key, width: 200.0, height: 200.0, - color: sequenceAnimation[seqKey].value, + color: sequenceAnimation.get(seqKey).value, ); })); From f696dee221f0334353fe676cdef81fadd4c46846 Mon Sep 17 00:00:00 2001 From: Stefano Rodriguez Date: Sun, 18 Apr 2021 02:04:57 +0200 Subject: [PATCH 2/5] fix: simplify api --- lib/flutter_sequence_animation.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/flutter_sequence_animation.dart b/lib/flutter_sequence_animation.dart index 1ffb925..3ee0020 100755 --- a/lib/flutter_sequence_animation.dart +++ b/lib/flutter_sequence_animation.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -class _AnimationInformation> { +class _AnimationInformation { _AnimationInformation({ required this.animatable, required this.from, @@ -9,7 +9,7 @@ class _AnimationInformation> { required this.tag, }); - final A animatable; + final Animatable animatable; final Duration from; final Duration to; final Curve curve; @@ -192,7 +192,7 @@ class SequenceAnimationBuilder { }) { assert(T.toString() != 'Object'); assert(to >= from); - _animations.add(new _AnimationInformation( + _animations.add(new _AnimationInformation( animatable: animatable, from: from, to: to, curve: curve, tag: tag)); return this; } From cb290e1e23bf4b440796d2b089047c8639a936d1 Mon Sep 17 00:00:00 2001 From: Stefano Rodriguez Date: Sun, 18 Apr 2021 12:17:12 +0200 Subject: [PATCH 3/5] fix: get rid of unneeded optionals --- .../same_variable_multiple_animations.dart | 2 +- example/lib/sequence_page.dart | 2 +- .../lib/staggered_animation_replication.dart | 4 ++-- lib/flutter_sequence_animation.dart | 24 +++++++++---------- test/animation_sequence_tests.dart | 12 +++++----- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/example/lib/same_variable_multiple_animations.dart b/example/lib/same_variable_multiple_animations.dart index 4a00daf..e66d9cb 100755 --- a/example/lib/same_variable_multiple_animations.dart +++ b/example/lib/same_variable_multiple_animations.dart @@ -11,7 +11,7 @@ class SameVariableAnimationPage extends StatefulWidget { class _SameVariableAnimationPageState extends State with SingleTickerProviderStateMixin { - static const colorTag = SequenceAnimationTag("color"); + static const colorTag = SequenceAnimationTag("color"); static const widthTag = SequenceAnimationTag("width"); static const heightTag = SequenceAnimationTag("height"); diff --git a/example/lib/sequence_page.dart b/example/lib/sequence_page.dart index ea1d7d1..0e7ed65 100755 --- a/example/lib/sequence_page.dart +++ b/example/lib/sequence_page.dart @@ -9,7 +9,7 @@ class SequencePage extends StatefulWidget { } class _SequencePageState extends State with SingleTickerProviderStateMixin{ - static const colorTag = SequenceAnimationTag("color"); + static const colorTag = SequenceAnimationTag("color"); late AnimationController controller; late SequenceAnimation sequenceAnimation; diff --git a/example/lib/staggered_animation_replication.dart b/example/lib/staggered_animation_replication.dart index 693bd02..67480da 100755 --- a/example/lib/staggered_animation_replication.dart +++ b/example/lib/staggered_animation_replication.dart @@ -15,7 +15,7 @@ class _StaggeredAnimationReplicationState extends State("height"); static const paddingKey = SequenceAnimationTag("padding"); static const borderRadiusKey = SequenceAnimationTag("borderRadius"); - static const colorKey = SequenceAnimationTag("color"); + static const colorKey = SequenceAnimationTag("color"); late AnimationController controller; late SequenceAnimation sequenceAnimation; @@ -76,7 +76,7 @@ class _StaggeredAnimationReplicationState extends State { required this.tag, }); - final Animatable animatable; + final Animatable animatable; final Duration from; final Duration to; final Curve curve; final SequenceAnimationTag tag; - IntervalAnimatable createIntervalAnimatable({ - required Animatable animatable, - required Animatable defaultAnimatable, + IntervalAnimatable createIntervalAnimatable({ + required Animatable animatable, + required Animatable defaultAnimatable, required double begin, required double end, }) => - IntervalAnimatable( + IntervalAnimatable( animatable: animatable, defaultAnimatable: defaultAnimatable, begin: begin, @@ -78,7 +78,7 @@ class SequenceAnimationBuilder { /// /// The animation with tag "animation" will start at second 3 and run until second 4. /// - SequenceAnimationBuilder addAnimatableAfterLastOneWithTag>({ + SequenceAnimationBuilder addAnimatableAfterLastOneWithTag>({ required Object lastTag, required A animatable, Duration delay: Duration.zero, @@ -126,7 +126,7 @@ class SequenceAnimationBuilder { /// /// The animation with tag "animation" will start at second 3 and run until second 4. /// - SequenceAnimationBuilder addAnimatableAfterLastOne>({ + SequenceAnimationBuilder addAnimatableAfterLastOne>({ required A animatable, Duration delay: Duration.zero, required Duration duration, @@ -147,7 +147,7 @@ class SequenceAnimationBuilder { /// Convenient wrapper around to specify an animatable using a duration instead of end point /// /// Instead of specifying from and to, you specify start and duration - SequenceAnimationBuilder addAnimatableUsingDuration>({ + SequenceAnimationBuilder addAnimatableUsingDuration>({ required A animatable, required Duration start, required Duration duration, @@ -183,7 +183,7 @@ class SequenceAnimationBuilder { /// .animate(controller); /// ``` /// - SequenceAnimationBuilder addAnimatable>({ + SequenceAnimationBuilder addAnimatable>({ required A animatable, required Duration from, required Duration to, @@ -262,11 +262,11 @@ class SequenceAnimation { SequenceAnimation._internal(this._animations); /// Returns the animation with a given tag, this animation is tied to the controller. - Animation get(SequenceAnimationTag tag) { + Animation get(SequenceAnimationTag tag) { assert(_animations.containsKey(tag), "There was no animatable with the tag: ${tag.value}"); - return _animations[tag]! as Animation; + return _animations[tag]! as Animation; } } @@ -301,7 +301,7 @@ class IntervalAnimatable extends Animatable { } } -extension _Chain on Animatable { +extension Chain on Animatable { /// Chains an [Animatable] with a [CurveTween] and the given [Interval]. /// Basically, the animation is being constrained to the given interval Animatable chainCurve(Interval interval) { diff --git a/test/animation_sequence_tests.dart b/test/animation_sequence_tests.dart index 9b569f3..5ef55cf 100755 --- a/test/animation_sequence_tests.dart +++ b/test/animation_sequence_tests.dart @@ -25,7 +25,7 @@ void main() { expect(controller.duration, isNull); - const seqKey = const SequenceAnimationTag("color"); + const seqKey = const SequenceAnimationTag("color"); SequenceAnimation sequenceAnimation = new SequenceAnimationBuilder() .addAnimatable( tag: seqKey, @@ -74,7 +74,7 @@ void main() { expect(controller.duration, isNull); - const seqKey = const SequenceAnimationTag("color"); + const seqKey = const SequenceAnimationTag("color"); SequenceAnimation sequenceAnimation = new SequenceAnimationBuilder() .addAnimatable( tag: seqKey, @@ -138,7 +138,7 @@ void main() { expect(controller.duration, isNull); - const seqKey = const SequenceAnimationTag("color"); + const seqKey = const SequenceAnimationTag("color"); SequenceAnimation sequenceAnimation = new SequenceAnimationBuilder() .addAnimatable( tag: seqKey, @@ -225,7 +225,7 @@ void main() { expect(controller.duration, isNull); - const colorKey = const SequenceAnimationTag("color"); + const colorKey = const SequenceAnimationTag("color"); const widthKey = const SequenceAnimationTag("width"); SequenceAnimation sequenceAnimation = new SequenceAnimationBuilder() @@ -301,7 +301,7 @@ void main() { AnimationController controller = new AnimationController(vsync: const TestVSync()); - const tag = const SequenceAnimationTag("s"); + const tag = const SequenceAnimationTag("s"); try { new SequenceAnimationBuilder() @@ -344,7 +344,7 @@ void main() { AnimationController controller = new AnimationController(vsync: const TestVSync()); - const seqKey = const SequenceAnimationTag(false); + const seqKey = const SequenceAnimationTag(false); SequenceAnimation sequenceAnimation = new SequenceAnimationBuilder() .addAnimatable( From 74ceaa09974846f7d9e4efb430f9fc40a1b9ae53 Mon Sep 17 00:00:00 2001 From: Stefano Rodriguez Date: Sun, 18 Apr 2021 18:51:30 +0200 Subject: [PATCH 4/5] fix: get rid of values in keys --- .../same_variable_multiple_animations.dart | 6 +- example/lib/sequence_page.dart | 2 +- .../lib/staggered_animation_replication.dart | 12 ++-- lib/flutter_sequence_animation.dart | 17 +---- test/animation_sequence_tests.dart | 64 ++----------------- 5 files changed, 19 insertions(+), 82 deletions(-) diff --git a/example/lib/same_variable_multiple_animations.dart b/example/lib/same_variable_multiple_animations.dart index e66d9cb..c816761 100755 --- a/example/lib/same_variable_multiple_animations.dart +++ b/example/lib/same_variable_multiple_animations.dart @@ -11,9 +11,9 @@ class SameVariableAnimationPage extends StatefulWidget { class _SameVariableAnimationPageState extends State with SingleTickerProviderStateMixin { - static const colorTag = SequenceAnimationTag("color"); - static const widthTag = SequenceAnimationTag("width"); - static const heightTag = SequenceAnimationTag("height"); + static final colorTag = SequenceAnimationTag(); + static final widthTag = SequenceAnimationTag(); + static final heightTag = SequenceAnimationTag(); late AnimationController controller; late SequenceAnimation sequenceAnimation; diff --git a/example/lib/sequence_page.dart b/example/lib/sequence_page.dart index 0e7ed65..ec432ca 100755 --- a/example/lib/sequence_page.dart +++ b/example/lib/sequence_page.dart @@ -9,7 +9,7 @@ class SequencePage extends StatefulWidget { } class _SequencePageState extends State with SingleTickerProviderStateMixin{ - static const colorTag = SequenceAnimationTag("color"); + static final colorTag = SequenceAnimationTag(); late AnimationController controller; late SequenceAnimation sequenceAnimation; diff --git a/example/lib/staggered_animation_replication.dart b/example/lib/staggered_animation_replication.dart index 67480da..836b64e 100755 --- a/example/lib/staggered_animation_replication.dart +++ b/example/lib/staggered_animation_replication.dart @@ -10,12 +10,12 @@ class StaggeredAnimationReplication extends StatefulWidget { class _StaggeredAnimationReplicationState extends State with SingleTickerProviderStateMixin{ - static const opacityKey = SequenceAnimationTag("opacity"); - static const widthKey = SequenceAnimationTag("width"); - static const heightKey = SequenceAnimationTag("height"); - static const paddingKey = SequenceAnimationTag("padding"); - static const borderRadiusKey = SequenceAnimationTag("borderRadius"); - static const colorKey = SequenceAnimationTag("color"); + static final opacityKey = SequenceAnimationTag(); + static final widthKey = SequenceAnimationTag(); + static final heightKey = SequenceAnimationTag(); + static final paddingKey = SequenceAnimationTag(); + static final borderRadiusKey = SequenceAnimationTag(); + static final colorKey = SequenceAnimationTag(); late AnimationController controller; late SequenceAnimation sequenceAnimation; diff --git a/lib/flutter_sequence_animation.dart b/lib/flutter_sequence_animation.dart index e27f919..b40f211 100755 --- a/lib/flutter_sequence_animation.dart +++ b/lib/flutter_sequence_animation.dart @@ -29,20 +29,7 @@ class _AnimationInformation { ); } -class SequenceAnimationTag { - const SequenceAnimationTag(this.value); - final Object value; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is SequenceAnimationTag && - runtimeType == other.runtimeType && - value == other.value; - - @override - int get hashCode => value.hashCode; -} +class SequenceAnimationTag {} class SequenceAnimationBuilder { List<_AnimationInformation> _animations = []; @@ -264,7 +251,7 @@ class SequenceAnimation { /// Returns the animation with a given tag, this animation is tied to the controller. Animation get(SequenceAnimationTag tag) { assert(_animations.containsKey(tag), - "There was no animatable with the tag: ${tag.value}"); + "There was no animatable with the tag: $tag"); return _animations[tag]! as Animation; } diff --git a/test/animation_sequence_tests.dart b/test/animation_sequence_tests.dart index 5ef55cf..335e499 100755 --- a/test/animation_sequence_tests.dart +++ b/test/animation_sequence_tests.dart @@ -25,7 +25,7 @@ void main() { expect(controller.duration, isNull); - const seqKey = const SequenceAnimationTag("color"); + final seqKey = SequenceAnimationTag(); SequenceAnimation sequenceAnimation = new SequenceAnimationBuilder() .addAnimatable( tag: seqKey, @@ -74,7 +74,7 @@ void main() { expect(controller.duration, isNull); - const seqKey = const SequenceAnimationTag("color"); + final seqKey = SequenceAnimationTag(); SequenceAnimation sequenceAnimation = new SequenceAnimationBuilder() .addAnimatable( tag: seqKey, @@ -138,7 +138,7 @@ void main() { expect(controller.duration, isNull); - const seqKey = const SequenceAnimationTag("color"); + final seqKey = SequenceAnimationTag(); SequenceAnimation sequenceAnimation = new SequenceAnimationBuilder() .addAnimatable( tag: seqKey, @@ -205,7 +205,7 @@ void main() { expect(controller.duration, equals(const Duration(seconds: 0))); try { - sequenceAnimation.get(SequenceAnimationTag("doesntExit")); + sequenceAnimation.get(SequenceAnimationTag()); } catch(e) { expect(e,isAssertionError); } @@ -225,8 +225,8 @@ void main() { expect(controller.duration, isNull); - const colorKey = const SequenceAnimationTag("color"); - const widthKey = const SequenceAnimationTag("width"); + final colorKey = SequenceAnimationTag(); + final widthKey = SequenceAnimationTag(); SequenceAnimation sequenceAnimation = new SequenceAnimationBuilder() .addAnimatable( @@ -301,7 +301,7 @@ void main() { AnimationController controller = new AnimationController(vsync: const TestVSync()); - const tag = const SequenceAnimationTag("s"); + final tag = SequenceAnimationTag(); try { new SequenceAnimationBuilder() @@ -338,54 +338,4 @@ void main() { expect(e, isAssertionError); } }); - - - testWidgets('Uses object key', (WidgetTester tester) async { - - AnimationController controller = new AnimationController(vsync: const TestVSync()); - - const seqKey = const SequenceAnimationTag(false); - - SequenceAnimation sequenceAnimation = new SequenceAnimationBuilder() - .addAnimatable( - tag: seqKey, - animatable: new ColorTween(begin: Colors.red, end: Colors.yellow), - from: const Duration(seconds: 0), - to: const Duration(seconds: 1)) - .animate(controller); - - - - expect(controller.duration, isNotNull); - - - final key = new ValueKey("color"); - - // Build our app and trigger a frame. - await tester.pumpWidget(new AnimatedBuilder(animation: controller, builder: (context, child) { - return new Container( - key: key, - width: 200.0, - height: 200.0, - color: sequenceAnimation.get(seqKey).value, - ); - })); - - - expect(find.byKey(key), findsOneWidget); - Color color = tester.widget(find.byKey(key)).color!; - expect(color, Colors.red); - - - controller.forward(); - await tester.pumpAndSettle(); - - - color = tester.widget(find.byKey(key)).color!; - expect(color, Colors.yellow); - - - }); } - - From f786ada125c2ce57d04cf20ea2d2e0cb64c49373 Mon Sep 17 00:00:00 2001 From: Stefano Rodriguez Date: Wed, 26 May 2021 16:52:13 +0200 Subject: [PATCH 5/5] fix: revamp value-based tags --- lib/flutter_sequence_animation.dart | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/flutter_sequence_animation.dart b/lib/flutter_sequence_animation.dart index b40f211..12cf9c2 100755 --- a/lib/flutter_sequence_animation.dart +++ b/lib/flutter_sequence_animation.dart @@ -29,7 +29,23 @@ class _AnimationInformation { ); } -class SequenceAnimationTag {} +class SequenceAnimationTag { + SequenceAnimationTag() : id = _id++; + + const SequenceAnimationTag.id(this.id); + + static int _id = 0; + + final Object id; + @override + bool operator ==(Object other) => + identical(this, other) || + other is SequenceAnimationTag && + runtimeType == other.runtimeType && + id == other.id; + @override + int get hashCode => id.hashCode; +} class SequenceAnimationBuilder { List<_AnimationInformation> _animations = [];