| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:flutter/widgets.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| void main() { |
| testWidgets('Animates forward when built', (WidgetTester tester) async { |
| final List<int> values = <int>[]; |
| int endCount = 0; |
| await tester.pumpWidget( |
| TweenAnimationBuilder<int>( |
| duration: const Duration(seconds: 1), |
| tween: IntTween(begin: 10, end: 110), |
| builder: (BuildContext context, int i, Widget? child) { |
| values.add(i); |
| return const Placeholder(); |
| }, |
| onEnd: () { |
| endCount++; |
| }, |
| ), |
| ); |
| expect(endCount, 0); |
| expect(values, <int>[10]); |
| |
| await tester.pump(const Duration(milliseconds: 500)); |
| expect(values, <int>[10, 60]); |
| |
| await tester.pump(const Duration(milliseconds: 501)); |
| expect(endCount, 1); |
| expect(values, <int>[10, 60, 110]); |
| |
| await tester.pump(const Duration(milliseconds: 500)); |
| expect(endCount, 1); |
| expect(values, <int>[10, 60, 110]); |
| }); |
| |
| testWidgets('No initial animation when begin=null', (WidgetTester tester) async { |
| final List<int> values = <int>[]; |
| int endCount = 0; |
| await tester.pumpWidget( |
| TweenAnimationBuilder<int>( |
| duration: const Duration(seconds: 1), |
| tween: IntTween(end: 100), |
| builder: (BuildContext context, int i, Widget? child) { |
| values.add(i); |
| return const Placeholder(); |
| }, |
| onEnd: () { |
| endCount++; |
| }, |
| ), |
| ); |
| expect(endCount, 0); |
| expect(values, <int>[100]); |
| await tester.pump(const Duration(seconds: 2)); |
| expect(endCount, 0); |
| expect(values, <int>[100]); |
| }); |
| |
| |
| testWidgets('No initial animation when begin=end', (WidgetTester tester) async { |
| final List<int> values = <int>[]; |
| int endCount = 0; |
| await tester.pumpWidget( |
| TweenAnimationBuilder<int>( |
| duration: const Duration(seconds: 1), |
| tween: IntTween(begin: 100, end: 100), |
| builder: (BuildContext context, int i, Widget? child) { |
| values.add(i); |
| return const Placeholder(); |
| }, |
| onEnd: () { |
| endCount++; |
| }, |
| ), |
| ); |
| expect(endCount, 0); |
| expect(values, <int>[100]); |
| await tester.pump(const Duration(seconds: 2)); |
| expect(endCount, 0); |
| expect(values, <int>[100]); |
| }); |
| |
| testWidgets('Replace tween animates new tween', (WidgetTester tester) async { |
| final List<int> values = <int>[]; |
| Widget buildWidget({required IntTween tween}) { |
| return TweenAnimationBuilder<int>( |
| duration: const Duration(seconds: 1), |
| tween: tween, |
| builder: (BuildContext context, int i, Widget? child) { |
| values.add(i); |
| return const Placeholder(); |
| }, |
| ); |
| } |
| |
| await tester.pumpWidget(buildWidget(tween: IntTween(begin: 0, end: 100))); |
| expect(values, <int>[0]); |
| await tester.pump(const Duration(seconds: 2)); // finish first animation. |
| expect(values, <int>[0, 100]); |
| |
| await tester.pumpWidget(buildWidget(tween: IntTween(begin: 100, end: 200))); |
| expect(values, <int>[0, 100, 100]); |
| |
| await tester.pump(const Duration(milliseconds: 500)); |
| expect(values, <int>[0, 100, 100, 150]); |
| |
| await tester.pump(const Duration(milliseconds: 500)); |
| expect(values, <int>[0, 100, 100, 150, 200]); |
| }); |
| |
| testWidgets('Curve is respected', (WidgetTester tester) async { |
| final List<int> values = <int>[]; |
| Widget buildWidget({required IntTween tween, required Curve curve}) { |
| return TweenAnimationBuilder<int>( |
| duration: const Duration(seconds: 1), |
| tween: tween, |
| curve: curve, |
| builder: (BuildContext context, int i, Widget? child) { |
| values.add(i); |
| return const Placeholder(); |
| }, |
| ); |
| } |
| |
| await tester.pumpWidget(buildWidget(tween: IntTween(begin: 0, end: 100), curve: Curves.easeInExpo)); |
| expect(values, <int>[0]); |
| await tester.pump(const Duration(milliseconds: 500)); |
| expect(values.last, lessThan(50)); |
| expect(values.last, greaterThan(0)); |
| |
| await tester.pump(const Duration(seconds: 2)); // finish animation. |
| |
| values.clear(); |
| // Update curve (and tween to re-trigger animation). |
| await tester.pumpWidget(buildWidget(tween: IntTween(begin: 100, end: 200), curve: Curves.linear)); |
| expect(values, <int>[100]); |
| await tester.pump(const Duration(milliseconds: 500)); |
| expect(values, <int>[100, 150]); |
| }); |
| |
| testWidgets('Duration is respected', (WidgetTester tester) async { |
| final List<int> values = <int>[]; |
| Widget buildWidget({required IntTween tween, required Duration duration}) { |
| return TweenAnimationBuilder<int>( |
| tween: tween, |
| duration: duration, |
| builder: (BuildContext context, int i, Widget? child) { |
| values.add(i); |
| return const Placeholder(); |
| }, |
| ); |
| } |
| |
| await tester.pumpWidget(buildWidget(tween: IntTween(begin: 0, end: 100), duration: const Duration(seconds: 1))); |
| expect(values, <int>[0]); |
| await tester.pump(const Duration(milliseconds: 500)); |
| expect(values, <int>[0, 50]); |
| |
| await tester.pump(const Duration(seconds: 2)); // finish animation. |
| |
| values.clear(); |
| // Update duration (and tween to re-trigger animation). |
| await tester.pumpWidget(buildWidget(tween: IntTween(begin: 100, end: 200), duration: const Duration(seconds: 2))); |
| expect(values, <int>[100]); |
| await tester.pump(const Duration(milliseconds: 500)); |
| expect(values, <int>[100, 125]); |
| }); |
| |
| testWidgets('Child is integrated into tree', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: TweenAnimationBuilder<int>( |
| tween: IntTween(begin: 0, end: 100), |
| duration: const Duration(seconds: 1), |
| builder: (BuildContext context, int i, Widget? child) { |
| return child!; |
| }, |
| child: const Text('Hello World'), |
| ), |
| ), |
| ); |
| |
| expect(find.text('Hello World'), findsOneWidget); |
| }); |
| |
| group('Change tween gapless while', () { |
| testWidgets('running forward', (WidgetTester tester) async { |
| final List<int> values = <int>[]; |
| Widget buildWidget({required IntTween tween}) { |
| return TweenAnimationBuilder<int>( |
| tween: tween, |
| duration: const Duration(seconds: 1), |
| builder: (BuildContext context, int i, Widget? child) { |
| values.add(i); |
| return const Placeholder(); |
| }, |
| ); |
| } |
| |
| await tester.pumpWidget(buildWidget( |
| tween: IntTween(begin: 0, end: 100), |
| )); |
| expect(values, <int>[0]); |
| await tester.pump(const Duration(milliseconds: 500)); |
| expect(values, <int>[0, 50]); |
| |
| // Change tween |
| await tester.pumpWidget(buildWidget( |
| tween: IntTween(begin: 200, end: 300), |
| )); |
| expect(values, <int>[0, 50, 50]); // gapless: animation continues where it left off. |
| |
| await tester.pump(const Duration(milliseconds: 500)); |
| expect(values, <int>[0, 50, 50, 175]); // 175 = halfway between 50 and new target 300. |
| |
| // Run animation to end |
| await tester.pump(const Duration(seconds: 2)); |
| expect(values, <int>[0, 50, 50, 175, 300]); |
| values.clear(); |
| }); |
| |
| testWidgets('running forward and then reverse with same tween instance', (WidgetTester tester) async { |
| final List<int> values = <int>[]; |
| Widget buildWidget({required IntTween tween}) { |
| return TweenAnimationBuilder<int>( |
| tween: tween, |
| duration: const Duration(seconds: 1), |
| builder: (BuildContext context, int i, Widget? child) { |
| values.add(i); |
| return const Placeholder(); |
| }, |
| ); |
| } |
| |
| final IntTween tween1 = IntTween(begin: 0, end: 100); |
| final IntTween tween2 = IntTween(begin: 200, end: 300); |
| |
| await tester.pumpWidget(buildWidget( |
| tween: tween1, |
| )); |
| await tester.pump(const Duration(milliseconds: 500)); |
| await tester.pumpWidget(buildWidget( |
| tween: tween2, |
| )); |
| await tester.pump(const Duration(milliseconds: 500)); |
| await tester.pump(const Duration(seconds: 2)); |
| expect(values, <int>[0, 50, 50, 175, 300]); |
| values.clear(); |
| }); |
| }); |
| |
| testWidgets('Changing tween while gapless tween change is in progress', (WidgetTester tester) async { |
| final List<int> values = <int>[]; |
| Widget buildWidget({required IntTween tween}) { |
| return TweenAnimationBuilder<int>( |
| tween: tween, |
| duration: const Duration(seconds: 1), |
| builder: (BuildContext context, int i, Widget? child) { |
| values.add(i); |
| return const Placeholder(); |
| }, |
| ); |
| } |
| |
| final IntTween tween1 = IntTween(begin: 0, end: 100); |
| final IntTween tween2 = IntTween(begin: 200, end: 300); |
| final IntTween tween3 = IntTween(begin: 400, end: 501); |
| |
| await tester.pumpWidget(buildWidget( |
| tween: tween1, |
| )); |
| await tester.pump(const Duration(milliseconds: 500)); |
| expect(values, <int>[0, 50]); |
| values.clear(); |
| |
| // Change tween |
| await tester.pumpWidget(buildWidget( |
| tween: tween2, |
| )); |
| await tester.pump(const Duration(milliseconds: 500)); |
| expect(values, <int>[50, 175]); |
| values.clear(); |
| |
| await tester.pumpWidget(buildWidget( |
| tween: tween3, |
| )); |
| await tester.pump(const Duration(milliseconds: 500)); |
| await tester.pump(const Duration(milliseconds: 500)); |
| expect(values, <int>[175, 338, 501]); |
| }); |
| |
| testWidgets('Changing curve while no animation is running does not trigger animation', (WidgetTester tester) async { |
| final List<int> values = <int>[]; |
| Widget buildWidget({required Curve curve}) { |
| return TweenAnimationBuilder<int>( |
| tween: IntTween(begin: 0, end: 100), |
| curve: curve, |
| duration: const Duration(seconds: 1), |
| builder: (BuildContext context, int i, Widget? child) { |
| values.add(i); |
| return const Placeholder(); |
| }, |
| ); |
| } |
| |
| await tester.pumpWidget(buildWidget( |
| curve: Curves.linear, |
| )); |
| await tester.pump(const Duration(seconds: 2)); |
| expect(values, <int>[0, 100]); |
| values.clear(); |
| |
| await tester.pumpWidget(buildWidget( |
| curve: Curves.easeInExpo, |
| )); |
| expect(values, <int>[100]); |
| await tester.pump(const Duration(seconds: 2)); |
| expect(values, <int>[100]); |
| }); |
| |
| testWidgets('Setting same tween and direction does not trigger animation', (WidgetTester tester) async { |
| final List<int> values = <int>[]; |
| Widget buildWidget({required IntTween tween}) { |
| return TweenAnimationBuilder<int>( |
| tween: tween, |
| duration: const Duration(seconds: 1), |
| builder: (BuildContext context, int i, Widget? child) { |
| values.add(i); |
| return const Placeholder(); |
| }, |
| ); |
| } |
| |
| await tester.pumpWidget(buildWidget( |
| tween: IntTween(begin: 0, end: 100), |
| )); |
| await tester.pump(const Duration(milliseconds: 500)); |
| await tester.pump(const Duration(milliseconds: 500)); |
| expect(values, <int>[0, 50, 100]); |
| values.clear(); |
| |
| await tester.pumpWidget(buildWidget( |
| tween: IntTween(begin: 0, end: 100), |
| )); |
| await tester.pump(const Duration(milliseconds: 500)); |
| await tester.pump(const Duration(milliseconds: 500)); |
| expect(values, everyElement(100)); |
| }); |
| |
| testWidgets('Setting same tween and direction while gapless animation is in progress works', (WidgetTester tester) async { |
| final List<int> values = <int>[]; |
| Widget buildWidget({required IntTween tween}) { |
| return TweenAnimationBuilder<int>( |
| tween: tween, |
| duration: const Duration(seconds: 1), |
| builder: (BuildContext context, int i, Widget? child) { |
| values.add(i); |
| return const Placeholder(); |
| }, |
| ); |
| } |
| |
| await tester.pumpWidget(buildWidget( |
| tween: IntTween(begin: 0, end: 100), |
| )); |
| await tester.pump(const Duration(milliseconds: 500)); |
| expect(values, <int>[0, 50]); |
| await tester.pumpWidget(buildWidget( |
| tween: IntTween(begin: 200, end: 300), |
| )); |
| await tester.pump(const Duration(milliseconds: 500)); |
| expect(values, <int>[0, 50, 50, 175]); |
| |
| await tester.pumpWidget(buildWidget( |
| tween: IntTween(begin: 200, end: 300), |
| )); |
| expect(values, <int>[0, 50, 50, 175, 175]); |
| await tester.pump(const Duration(milliseconds: 500)); |
| expect(values, <int>[0, 50, 50, 175, 175, 300]); |
| |
| values.clear(); |
| await tester.pump(const Duration(seconds: 2)); |
| expect(values, everyElement(300)); |
| }); |
| |
| testWidgets('Works with nullable tweens', (WidgetTester tester) async { |
| final List<Size?> values = <Size?>[]; |
| await tester.pumpWidget( |
| TweenAnimationBuilder<Size?>( |
| duration: const Duration(seconds: 1), |
| tween: SizeTween(end: const Size(10,10)), |
| builder: (BuildContext context, Size? s, Widget? child) { |
| values.add(s); |
| return const Placeholder(); |
| }, |
| ), |
| ); |
| expect(values, <Size>[const Size(10,10)]); |
| await tester.pump(const Duration(seconds: 2)); |
| expect(values, <Size>[const Size(10,10)]); |
| }); |
| } |