diff --git a/lib/effects/effects.dart b/lib/effects/effects.dart index 591148e..fb96eb8 100644 --- a/lib/effects/effects.dart +++ b/lib/effects/effects.dart @@ -19,3 +19,4 @@ export 'then_effect.dart'; export 'tint_effect.dart'; export 'toggle_effect.dart'; export 'visibility_effect.dart'; +export 'tween_sequence_effect.dart'; diff --git a/lib/effects/tween_sequence_effect.dart b/lib/effects/tween_sequence_effect.dart new file mode 100644 index 0000000..b16b088 --- /dev/null +++ b/lib/effects/tween_sequence_effect.dart @@ -0,0 +1,64 @@ +import 'package:flutter/widgets.dart'; + +import '../flutter_animate.dart'; + +/// Provide an easy way to use Flutters TweenSequence API to express +/// complex multi-part tweens. +/// ``` +/// // fades in, out, and back in +/// foo.animate().tweenSequence( +// duration: 1.seconds, +// sequence: TweenSequence([ +// TweenSequenceItem(tween: Tween(begin: 0, end: 1), weight: .5), +// TweenSequenceItem(tween: Tween(begin: 1, end: 0), weight: .5), +// TweenSequenceItem(tween: Tween(begin: 0, end: 1), weight: .5), +// ]), +// builder: (_, double value, Widget child) { +// return Opacity(opacity: value, child: child); +// }, +// ) +/// ``` +@immutable +class TweenSequenceEffect extends Effect { + const TweenSequenceEffect( + {required this.builder, required this.sequence, Duration? delay, Duration? duration, Curve? curve}) + : super(delay: delay, duration: duration, curve: curve, begin: 0.0, end: 1.0); + + final TweenSequenceEffectBuilder builder; + final TweenSequence sequence; + + @override + Widget build( + BuildContext context, + Widget child, + AnimationController controller, + EffectEntry entry, + ) { + Animation animation = buildAnimation(controller, entry); + return getOptimizedBuilder( + animation: animation, + builder: (ctx, __) => builder(ctx, sequence.evaluate(animation), child), + ); + } +} + +extension TweenSequenceExtensions on AnimateManager { + /// Adds a [custom] extension to [AnimateManager] ([Animate] and [AnimateList]). + T tweenSequence({ + required TweenSequenceEffectBuilder builder, + required TweenSequence sequence, + Duration? delay, + Duration? duration, + Curve? curve, + double? begin, + double? end, + }) => + addEffect( + TweenSequenceEffect(builder: builder, delay: delay, duration: duration, curve: curve, sequence: sequence)); +} + +typedef TweenSequenceEffectBuilder = Widget Function( + BuildContext context, + double value, + Widget child, +); diff --git a/pubspec.lock b/pubspec.lock index 7708595..a085092 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -60,13 +60,6 @@ packages: description: flutter source: sdk version: "0.0.0" - js: - dependency: transitive - description: - name: js - url: "https://pub.dartlang.org" - source: hosted - version: "0.6.4" lints: dependency: transitive description: @@ -148,14 +141,14 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.14" + version: "0.4.12" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "2.1.2" sdks: - dart: ">=2.18.0 <3.0.0" + dart: ">=2.17.0-206.0.dev <3.0.0" flutter: ">=1.17.0" diff --git a/test/effects/tween_sequence_test.dart b/test/effects/tween_sequence_test.dart new file mode 100644 index 0000000..51eb6ca --- /dev/null +++ b/test/effects/tween_sequence_test.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter_animate/flutter_animate.dart'; + +import '../tester_extensions.dart'; + +void main() { + testWidgets('fade in, out, in ', (tester) async { + final animation = const FlutterLogo().animate().tweenSequence( + duration: 1.5.seconds, + sequence: TweenSequence([ + TweenSequenceItem(tween: Tween(begin: 0, end: 1), weight: .5), + TweenSequenceItem(tween: Tween(begin: 1, end: 0), weight: .5), + TweenSequenceItem(tween: Tween(begin: 0, end: 1), weight: .5), + ]), + builder: (_, double value, Widget child) { + return Opacity(opacity: value, child: child); + }, + ); + // check start + await tester.pumpAnimation(animation); + tester.expectWidgetWithDouble((ft) => ft.opacity, 0, 'opacity'); + // check 1/3 + await tester.pump(500.ms); + tester.expectWidgetWithDouble((ft) => ft.opacity, 1, 'opacity'); + // check 2/3 + await tester.pump(500.ms); + tester.expectWidgetWithDouble((ft) => ft.opacity, 0, 'opacity'); + // check end + await tester.pump(500.ms); + tester.expectWidgetWithDouble((ft) => ft.opacity, 1, 'opacity'); + }); +}