animation_controller_test.dart 36.7 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'package:flutter/foundation.dart';
6
import 'package:flutter/physics.dart';
7
import 'package:flutter/scheduler.dart';
8
import 'package:flutter/semantics.dart';
9
import 'package:flutter/widgets.dart';
10
import 'package:flutter_test/flutter_test.dart';
11
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
12

13 14
import '../scheduler/scheduler_tester.dart';

15
void main() {
16
  setUp(() {
17
    WidgetsFlutterBinding.ensureInitialized();
18 19 20 21
    WidgetsBinding.instance
        ..resetEpoch()
        ..platformDispatcher.onBeginFrame = null
        ..platformDispatcher.onDrawFrame = null;
22 23
  });

24 25 26 27 28 29 30 31 32 33 34 35 36
  test('AnimationController dispatches memory events', () async {
    await expectLater(
      await memoryEvents(
        () => AnimationController(
          duration: const Duration(milliseconds: 100),
          vsync: const TestVSync(),
        ).dispose(),
        AnimationController,
      ),
      areCreateAndDispose,
    );
  });

37
  test('Can set value during status callback', () {
38
    final AnimationController controller = AnimationController(
39 40
      duration: const Duration(milliseconds: 100),
      vsync: const TestVSync(),
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
    );
    bool didComplete = false;
    bool didDismiss = false;
    controller.addStatusListener((AnimationStatus status) {
      if (status == AnimationStatus.completed) {
        didComplete = true;
        controller.value = 0.0;
        controller.forward();
      } else if (status == AnimationStatus.dismissed) {
        didDismiss = true;
        controller.value = 0.0;
        controller.forward();
      }
    });

    controller.forward();
    expect(didComplete, isFalse);
    expect(didDismiss, isFalse);
59
    tick(const Duration(seconds: 1));
60 61
    expect(didComplete, isFalse);
    expect(didDismiss, isFalse);
62
    tick(const Duration(seconds: 2));
63 64
    expect(didComplete, isTrue);
    expect(didDismiss, isTrue);
65 66

    controller.stop();
67
    controller.dispose();
68 69
  });

70
  test('Receives status callbacks for forward and reverse', () {
71
    final AnimationController controller = AnimationController(
72 73
      duration: const Duration(milliseconds: 100),
      vsync: const TestVSync(),
74
    );
75 76
    final List<double> valueLog = <double>[];
    final List<AnimationStatus> log = <AnimationStatus>[];
77
    controller
78
      ..addStatusListener(log.add)
79 80 81 82
      ..addListener(() {
        valueLog.add(controller.value);
      });

83 84
    expect(log, equals(<AnimationStatus>[]));
    expect(valueLog, equals(<AnimationStatus>[]));
85 86 87

    controller.forward();

88 89
    expect(log, equals(<AnimationStatus>[AnimationStatus.forward]));
    expect(valueLog, equals(<AnimationStatus>[]));
90 91 92

    controller.reverse();

93 94
    expect(log, equals(<AnimationStatus>[AnimationStatus.forward, AnimationStatus.dismissed]));
    expect(valueLog, equals(<AnimationStatus>[]));
95 96 97

    controller.reverse();

98 99
    expect(log, equals(<AnimationStatus>[AnimationStatus.forward, AnimationStatus.dismissed]));
    expect(valueLog, equals(<AnimationStatus>[]));
100 101 102 103

    log.clear();
    controller.forward();

104 105
    expect(log, equals(<AnimationStatus>[AnimationStatus.forward]));
    expect(valueLog, equals(<AnimationStatus>[]));
106 107 108

    controller.forward();

109 110
    expect(log, equals(<AnimationStatus>[AnimationStatus.forward]));
    expect(valueLog, equals(<AnimationStatus>[]));
111 112 113 114

    controller.reverse();
    log.clear();

115
    tick(const Duration(seconds: 10));
116 117
    expect(log, equals(<AnimationStatus>[]));
    expect(valueLog, equals(<AnimationStatus>[]));
118
    tick(const Duration(seconds: 20));
119 120
    expect(log, equals(<AnimationStatus>[]));
    expect(valueLog, equals(<AnimationStatus>[]));
121
    tick(const Duration(seconds: 30));
122 123
    expect(log, equals(<AnimationStatus>[]));
    expect(valueLog, equals(<AnimationStatus>[]));
124
    tick(const Duration(seconds: 40));
125 126
    expect(log, equals(<AnimationStatus>[]));
    expect(valueLog, equals(<AnimationStatus>[]));
127 128

    controller.stop();
129
    controller.dispose();
130
  });
131

132
  test('Forward and reverse from values', () {
133
    final AnimationController controller = AnimationController(
134 135
      duration: const Duration(milliseconds: 100),
      vsync: const TestVSync(),
136
    );
137 138
    final List<double> valueLog = <double>[];
    final List<AnimationStatus> statusLog = <AnimationStatus>[];
139
    controller
140
      ..addStatusListener(statusLog.add)
141 142 143 144 145
      ..addListener(() {
        valueLog.add(controller.value);
      });

    controller.reverse(from: 0.2);
146 147
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.reverse ]));
    expect(valueLog, equals(<double>[ 0.2 ]));
148 149 150 151 152
    expect(controller.value, equals(0.2));
    statusLog.clear();
    valueLog.clear();

    controller.forward(from: 0.0);
153 154
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.dismissed, AnimationStatus.forward ]));
    expect(valueLog, equals(<double>[ 0.0 ]));
155
    expect(controller.value, equals(0.0));
156
    controller.dispose();
157
  });
158

159 160 161 162 163 164 165 166 167 168
  test('Forward and reverse with different durations', () {
    AnimationController controller = AnimationController(
      duration: const Duration(milliseconds: 100),
      reverseDuration: const Duration(milliseconds: 50),
      vsync: const TestVSync(),
    );

    controller.forward();
    tick(const Duration(milliseconds: 10));
    tick(const Duration(milliseconds: 30));
169
    expect(controller.value, moreOrLessEquals(0.2));
170
    tick(const Duration(milliseconds: 60));
171
    expect(controller.value, moreOrLessEquals(0.5));
172
    tick(const Duration(milliseconds: 90));
173
    expect(controller.value, moreOrLessEquals(0.8));
174
    tick(const Duration(milliseconds: 120));
175
    expect(controller.value, moreOrLessEquals(1.0));
176 177 178 179 180
    controller.stop();

    controller.reverse();
    tick(const Duration(milliseconds: 210));
    tick(const Duration(milliseconds: 220));
181
    expect(controller.value, moreOrLessEquals(0.8));
182
    tick(const Duration(milliseconds: 230));
183
    expect(controller.value, moreOrLessEquals(0.6));
184
    tick(const Duration(milliseconds: 240));
185
    expect(controller.value, moreOrLessEquals(0.4));
186
    tick(const Duration(milliseconds: 260));
187
    expect(controller.value, moreOrLessEquals(0.0));
188 189 190 191 192 193 194 195 196 197 198 199
    controller.stop();

    // Swap which duration is longer.
    controller = AnimationController(
      duration: const Duration(milliseconds: 50),
      reverseDuration: const Duration(milliseconds: 100),
      vsync: const TestVSync(),
    );

    controller.forward();
    tick(const Duration(milliseconds: 10));
    tick(const Duration(milliseconds: 30));
200
    expect(controller.value, moreOrLessEquals(0.4));
201
    tick(const Duration(milliseconds: 60));
202
    expect(controller.value, moreOrLessEquals(1.0));
203
    tick(const Duration(milliseconds: 90));
204
    expect(controller.value, moreOrLessEquals(1.0));
205 206 207 208 209
    controller.stop();

    controller.reverse();
    tick(const Duration(milliseconds: 210));
    tick(const Duration(milliseconds: 220));
210
    expect(controller.value, moreOrLessEquals(0.9));
211
    tick(const Duration(milliseconds: 230));
212
    expect(controller.value, moreOrLessEquals(0.8));
213
    tick(const Duration(milliseconds: 240));
214
    expect(controller.value, moreOrLessEquals(0.7));
215
    tick(const Duration(milliseconds: 260));
216
    expect(controller.value, moreOrLessEquals(0.5));
217
    tick(const Duration(milliseconds: 310));
218
    expect(controller.value, moreOrLessEquals(0.0));
219
    controller.stop();
220
    controller.dispose();
221 222
  });

223
  test('Forward only from value', () {
224
    final AnimationController controller = AnimationController(
225 226
      duration: const Duration(milliseconds: 100),
      vsync: const TestVSync(),
227
    );
228 229
    final List<double> valueLog = <double>[];
    final List<AnimationStatus> statusLog = <AnimationStatus>[];
230
    controller
231
      ..addStatusListener(statusLog.add)
232 233 234 235 236 237 238 239
      ..addListener(() {
        valueLog.add(controller.value);
      });

    controller.forward(from: 0.2);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward ]));
    expect(valueLog, equals(<double>[ 0.2 ]));
    expect(controller.value, equals(0.2));
240
    controller.dispose();
241 242
  });

243
  test('Can fling to upper and lower bounds', () {
244
    final AnimationController controller = AnimationController(
245 246
      duration: const Duration(milliseconds: 100),
      vsync: const TestVSync(),
247 248 249
    );

    controller.fling();
250 251
    tick(const Duration(seconds: 1));
    tick(const Duration(seconds: 2));
252 253 254
    expect(controller.value, 1.0);
    controller.stop();

255
    final AnimationController largeRangeController = AnimationController(
256 257
      duration: const Duration(milliseconds: 100),
      lowerBound: -30.0,
258 259
      upperBound: 45.0,
      vsync: const TestVSync(),
260 261 262
    );

    largeRangeController.fling();
263 264
    tick(const Duration(seconds: 3));
    tick(const Duration(seconds: 4));
265 266
    expect(largeRangeController.value, 45.0);
    largeRangeController.fling(velocity: -1.0);
267 268
    tick(const Duration(seconds: 5));
    tick(const Duration(seconds: 6));
269 270
    expect(largeRangeController.value, -30.0);
    largeRangeController.stop();
271 272
    controller.dispose();
    largeRangeController.dispose();
273
  });
274

275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
  test('Custom springDescription can be applied', () {
    final AnimationController controller = AnimationController(
      vsync: const TestVSync(),
    );
    final AnimationController customSpringController = AnimationController(
      vsync: const TestVSync(),
    );

    controller.fling();
    // Will produce longer and smoother animation than the default.
    customSpringController.fling(
      springDescription: SpringDescription.withDampingRatio(
        mass: 0.01,
        stiffness: 10.0,
        ratio: 2.0,
      ),
    );
292
    tick(Duration.zero);
293 294 295
    tick(const Duration(milliseconds: 50));

    expect(customSpringController.value < controller.value, true);
296 297
    controller.dispose();
    customSpringController.dispose();
298 299
  });

300
  test('lastElapsedDuration control test', () {
301
    final AnimationController controller = AnimationController(
302 303
      duration: const Duration(milliseconds: 100),
      vsync: const TestVSync(),
304 305
    );
    controller.forward();
306 307 308
    tick(const Duration(milliseconds: 20));
    tick(const Duration(milliseconds: 30));
    tick(const Duration(milliseconds: 40));
309 310
    expect(controller.lastElapsedDuration, equals(const Duration(milliseconds: 20)));
    controller.stop();
311
    controller.dispose();
312
  });
313 314

  test('toString control test', () {
315
    final AnimationController controller = AnimationController(
316 317
      duration: const Duration(milliseconds: 100),
      vsync: const TestVSync(),
318
    );
319
    expect(controller, hasOneLineDescription);
320
    controller.forward();
321 322
    tick(const Duration(milliseconds: 10));
    tick(const Duration(milliseconds: 20));
323
    expect(controller, hasOneLineDescription);
324
    tick(const Duration(milliseconds: 30));
325
    expect(controller, hasOneLineDescription);
326
    controller.reverse();
327 328
    tick(const Duration(milliseconds: 40));
    tick(const Duration(milliseconds: 50));
329
    expect(controller, hasOneLineDescription);
330
    controller.stop();
331
    controller.dispose();
332
  });
333 334

  test('velocity test - linear', () {
335
    final AnimationController controller = AnimationController(
336 337 338 339 340 341
      duration: const Duration(milliseconds: 1000),
      vsync: const TestVSync(),
    );

    // mid-flight
    controller.forward();
342
    tick(Duration.zero);
343
    tick(const Duration(milliseconds: 500));
344 345 346 347 348
    expect(controller.velocity, inInclusiveRange(0.9, 1.1));

    // edges
    controller.forward();
    expect(controller.velocity, inInclusiveRange(0.4, 0.6));
349
    tick(Duration.zero);
350
    expect(controller.velocity, inInclusiveRange(0.4, 0.6));
351
    tick(const Duration(milliseconds: 5));
352 353 354 355
    expect(controller.velocity, inInclusiveRange(0.9, 1.1));

    controller.forward(from: 0.5);
    expect(controller.velocity, inInclusiveRange(0.4, 0.6));
356
    tick(Duration.zero);
357
    expect(controller.velocity, inInclusiveRange(0.4, 0.6));
358
    tick(const Duration(milliseconds: 5));
359 360 361 362 363
    expect(controller.velocity, inInclusiveRange(0.9, 1.1));

    // stopped
    controller.forward(from: 1.0);
    expect(controller.velocity, 0.0);
364
    tick(Duration.zero);
365
    expect(controller.velocity, 0.0);
366
    tick(const Duration(milliseconds: 500));
367 368 369
    expect(controller.velocity, 0.0);

    controller.forward();
370
    tick(Duration.zero);
371
    tick(const Duration(milliseconds: 1000));
372 373 374
    expect(controller.velocity, 0.0);

    controller.stop();
375
    controller.dispose();
376
  });
377 378

  test('Disposed AnimationController toString works', () {
379
    final AnimationController controller = AnimationController(
380 381 382 383 384 385
      duration: const Duration(milliseconds: 100),
      vsync: const TestVSync(),
    );
    controller.dispose();
    expect(controller, hasOneLineDescription);
  });
386 387

  test('AnimationController error handling', () {
388
    final AnimationController controller = AnimationController(
389 390 391 392 393 394 395 396 397
      vsync: const TestVSync(),
    );

    expect(controller.forward, throwsFlutterError);
    expect(controller.reverse, throwsFlutterError);
    expect(() { controller.animateTo(0.5); }, throwsFlutterError);
    expect(controller.repeat, throwsFlutterError);

    controller.dispose();
398
    FlutterError? result;
399 400 401 402 403 404 405
    try {
      controller.dispose();
    } on FlutterError catch (e) {
      result = e;
    }
    expect(result, isNotNull);
    expect(
406
      result!.toStringDeep(),
407 408 409 410 411 412
      equalsIgnoringHashCodes(
        'FlutterError\n'
        '   AnimationController.dispose() called more than once.\n'
        '   A given AnimationController cannot be disposed more than once.\n'
        '   The following AnimationController object was disposed multiple\n'
        '   times:\n'
413
        '     AnimationController#00000(⏮ 0.000; paused; DISPOSED)\n',
414 415 416 417 418 419 420
      ),
    );
    final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
    result.debugFillProperties(builder);
    final DiagnosticsNode controllerProperty = builder.properties.last;
    expect(controllerProperty.name, 'The following AnimationController object was disposed multiple times');
    expect(controllerProperty.value, controller);
421
  });
422 423

  test('AnimationController repeat() throws if period is not specified', () {
424
    final AnimationController controller = AnimationController(
425 426
      vsync: const TestVSync(),
    );
427
    expect(() { controller.repeat(); }, throwsFlutterError);
428
    expect(() { controller.repeat(); }, throwsFlutterError);
429
    controller.dispose();
430 431
  });

432 433 434
  test('Do not animate if already at target', () {
    final List<AnimationStatus> statusLog = <AnimationStatus>[];

435
    final AnimationController controller = AnimationController(
436 437 438 439 440 441 442 443
      value: 0.5,
      vsync: const TestVSync(),
    )..addStatusListener(statusLog.add);

    expect(controller.value, equals(0.5));
    controller.animateTo(0.5, duration: const Duration(milliseconds: 100));
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.completed ]));
    expect(controller.value, equals(0.5));
444
    controller.dispose();
445 446 447 448 449
  });

  test('Do not animate to upperBound if already at upperBound', () {
    final List<AnimationStatus> statusLog = <AnimationStatus>[];

450
    final AnimationController controller = AnimationController(
451 452 453 454 455 456 457 458
      value: 1.0,
      vsync: const TestVSync(),
    )..addStatusListener(statusLog.add);

    expect(controller.value, equals(1.0));
    controller.animateTo(1.0, duration: const Duration(milliseconds: 100));
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.completed ]));
    expect(controller.value, equals(1.0));
459
    controller.dispose();
460 461 462 463 464
  });

  test('Do not animate to lowerBound if already at lowerBound', () {
    final List<AnimationStatus> statusLog = <AnimationStatus>[];

465
    final AnimationController controller = AnimationController(
466 467 468 469 470 471 472 473
      value: 0.0,
      vsync: const TestVSync(),
    )..addStatusListener(statusLog.add);

    expect(controller.value, equals(0.0));
    controller.animateTo(0.0, duration: const Duration(milliseconds: 100));
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.completed ]));
    expect(controller.value, equals(0.0));
474
    controller.dispose();
475 476 477 478
  });

  test('Do not animate if already at target mid-flight (forward)', () {
    final List<AnimationStatus> statusLog = <AnimationStatus>[];
479
    final AnimationController controller = AnimationController(
480 481 482 483 484 485 486 487
      value: 0.0,
      duration: const Duration(milliseconds: 1000),
      vsync: const TestVSync(),
    )..addStatusListener(statusLog.add);

    expect(controller.value, equals(0.0));

    controller.forward();
488
    tick(Duration.zero);
489 490 491 492 493 494 495 496
    tick(const Duration(milliseconds: 500));
    expect(controller.value, inInclusiveRange(0.4, 0.6));
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward ]));

    final double currentValue = controller.value;
    controller.animateTo(currentValue, duration: const Duration(milliseconds: 100));
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.completed ]));
    expect(controller.value, currentValue);
497
    controller.dispose();
498 499 500 501
  });

  test('Do not animate if already at target mid-flight (reverse)', () {
    final List<AnimationStatus> statusLog = <AnimationStatus>[];
502
    final AnimationController controller = AnimationController(
503 504 505 506 507 508 509 510
      value: 1.0,
      duration: const Duration(milliseconds: 1000),
      vsync: const TestVSync(),
    )..addStatusListener(statusLog.add);

    expect(controller.value, equals(1.0));

    controller.reverse();
511
    tick(Duration.zero);
512 513 514 515 516 517
    tick(const Duration(milliseconds: 500));
    expect(controller.value, inInclusiveRange(0.4, 0.6));
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.reverse ]));

    final double currentValue = controller.value;
    controller.animateTo(currentValue, duration: const Duration(milliseconds: 100));
518
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.reverse, AnimationStatus.completed ]));
519
    expect(controller.value, currentValue);
520
    controller.dispose();
521
  });
522

523
  test('animateTo can deal with duration == Duration.zero', () {
524
    final AnimationController controller = AnimationController(
525 526 527 528 529 530
      duration: const Duration(milliseconds: 100),
      vsync: const TestVSync(),
    );

    controller.forward(from: 0.2);
    expect(controller.value, 0.2);
531
    controller.animateTo(1.0, duration: Duration.zero);
532
    expect(SchedulerBinding.instance.transientCallbackCount, equals(0), reason: 'Expected no animation.');
533
    expect(controller.value, 1.0);
534
    controller.dispose();
535
  });
536

537
  test('resetting animation works at all phases', () {
538
    final List<AnimationStatus> statusLog = <AnimationStatus>[];
539
    final AnimationController controller = AnimationController(
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
      duration: const Duration(milliseconds: 100),
      value: 0.0,
      vsync: const TestVSync(),
    )..addStatusListener(statusLog.add);

    expect(controller.value, 0.0);
    expect(controller.status, AnimationStatus.dismissed);

    controller.reset();

    expect(controller.value, 0.0);
    expect(controller.status, AnimationStatus.dismissed);

    statusLog.clear();
    controller.forward();
555
    tick(Duration.zero);
556 557 558 559 560 561 562 563 564 565 566
    tick(const Duration(milliseconds: 50));
    expect(controller.status, AnimationStatus.forward);
    controller.reset();

    expect(controller.value, 0.0);
    expect(controller.status, AnimationStatus.dismissed);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.dismissed ]));

    controller.value = 1.0;
    statusLog.clear();
    controller.reverse();
567
    tick(Duration.zero);
568 569 570 571 572 573 574 575 576 577
    tick(const Duration(milliseconds: 50));
    expect(controller.status, AnimationStatus.reverse);
    controller.reset();

    expect(controller.value, 0.0);
    expect(controller.status, AnimationStatus.dismissed);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.reverse, AnimationStatus.dismissed ]));

    statusLog.clear();
    controller.forward();
578
    tick(Duration.zero);
579 580 581 582 583 584 585
    tick(const Duration(milliseconds: 150));
    expect(controller.status, AnimationStatus.completed);
    controller.reset();

    expect(controller.value, 0.0);
    expect(controller.status, AnimationStatus.dismissed);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.completed, AnimationStatus.dismissed ]));
586
    controller.dispose();
587 588
  });

589
  test('setting value directly sets correct status', () {
590
    final AnimationController controller = AnimationController(
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612
      value: 0.0,
      vsync: const TestVSync(),
    );

    expect(controller.value, 0.0);
    expect(controller.status, AnimationStatus.dismissed);

    controller.value = 0.5;
    expect(controller.value, 0.5);
    expect(controller.status, AnimationStatus.forward);

    controller.value = 1.0;
    expect(controller.value, 1.0);
    expect(controller.status, AnimationStatus.completed);

    controller.value = 0.5;
    expect(controller.value, 0.5);
    expect(controller.status, AnimationStatus.forward);

    controller.value = 0.0;
    expect(controller.value, 0.0);
    expect(controller.status, AnimationStatus.dismissed);
613
    controller.dispose();
614 615 616 617
  });

  test('animateTo sets correct status', () {
    final List<AnimationStatus> statusLog = <AnimationStatus>[];
618
    final AnimationController controller = AnimationController(
619 620 621 622 623 624 625 626 627 628
      duration: const Duration(milliseconds: 100),
      value: 0.0,
      vsync: const TestVSync(),
    )..addStatusListener(statusLog.add);

    expect(controller.value, 0.0);
    expect(controller.status, AnimationStatus.dismissed);

    // Animate from 0.0 to 0.5
    controller.animateTo(0.5);
629
    tick(Duration.zero);
630 631 632 633 634 635 636
    tick(const Duration(milliseconds: 150));
    expect(controller.value, 0.5);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.completed ]));
    statusLog.clear();

    // Animate from 0.5 to 1.0
    controller.animateTo(1.0);
637
    tick(Duration.zero);
638 639 640 641 642 643 644
    tick(const Duration(milliseconds: 150));
    expect(controller.value, 1.0);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.completed ]));
    statusLog.clear();

    // Animate from 1.0 to 0.5
    controller.animateTo(0.5);
645
    tick(Duration.zero);
646 647 648 649 650 651 652
    tick(const Duration(milliseconds: 150));
    expect(controller.value, 0.5);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.completed ]));
    statusLog.clear();

    // Animate from 0.5 to 1.0
    controller.animateTo(0.0);
653
    tick(Duration.zero);
654 655 656 657
    tick(const Duration(milliseconds: 150));
    expect(controller.value, 0.0);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.completed ]));
    statusLog.clear();
658
    controller.dispose();
659 660 661 662
  });

  test('after a reverse call animateTo sets correct status', () {
    final List<AnimationStatus> statusLog = <AnimationStatus>[];
663
    final AnimationController controller = AnimationController(
664 665 666 667 668 669 670 671 672
      duration: const Duration(milliseconds: 100),
      value: 1.0,
      vsync: const TestVSync(),
    )..addStatusListener(statusLog.add);

    expect(controller.value, 1.0);
    expect(controller.status, AnimationStatus.completed);

    controller.reverse();
673
    tick(Duration.zero);
674 675 676 677 678 679
    tick(const Duration(milliseconds: 150));
    expect(controller.value, 0.0);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.reverse, AnimationStatus.dismissed ]));
    statusLog.clear();

    controller.animateTo(0.5);
680
    tick(Duration.zero);
681 682 683 684
    tick(const Duration(milliseconds: 150));
    expect(controller.value, 0.5);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.completed ]));
    statusLog.clear();
685
    controller.dispose();
686 687 688 689
  });

  test('after a forward call animateTo sets correct status', () {
    final List<AnimationStatus> statusLog = <AnimationStatus>[];
690
    final AnimationController controller = AnimationController(
691 692 693 694 695 696 697 698 699
      duration: const Duration(milliseconds: 100),
      value: 0.0,
      vsync: const TestVSync(),
    )..addStatusListener(statusLog.add);

    expect(controller.value, 0.0);
    expect(controller.status, AnimationStatus.dismissed);

    controller.forward();
700
    tick(Duration.zero);
701 702 703 704 705 706
    tick(const Duration(milliseconds: 150));
    expect(controller.value, 1.0);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.completed ]));
    statusLog.clear();

    controller.animateTo(0.5);
707
    tick(Duration.zero);
708 709 710 711
    tick(const Duration(milliseconds: 150));
    expect(controller.value, 0.5);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.completed ]));
    statusLog.clear();
712
    controller.dispose();
713
  });
714

715 716 717 718 719 720 721 722 723
  test(
    'calling repeat with reverse set to true makes the animation alternate '
    'between lowerBound and upperBound values on each repeat',
    () {
      final AnimationController controller = AnimationController(
        duration: const Duration(milliseconds: 100),
        value: 0.0,
        vsync: const TestVSync(),
      );
724

725
      expect(controller.value, 0.0);
726

727
      controller.repeat(reverse: true);
728
      tick(Duration.zero);
729 730
      tick(const Duration(milliseconds: 25));
      expect(controller.value, 0.25);
731

732
      tick(Duration.zero);
733 734
      tick(const Duration(milliseconds: 125));
      expect(controller.value, 0.75);
735

736 737 738
      controller.reset();
      controller.value = 1.0;
      expect(controller.value, 1.0);
739

740
      controller.repeat(reverse: true);
741
      tick(Duration.zero);
742 743
      tick(const Duration(milliseconds: 25));
      expect(controller.value, 0.75);
744

745
      tick(Duration.zero);
746 747
      tick(const Duration(milliseconds: 125));
      expect(controller.value, 0.25);
748

749 750 751
      controller.reset();
      controller.value = 0.5;
      expect(controller.value, 0.5);
752

753
      controller.repeat(reverse: true);
754
      tick(Duration.zero);
755 756
      tick(const Duration(milliseconds: 50));
      expect(controller.value, 1.0);
757

758
      tick(Duration.zero);
759 760
      tick(const Duration(milliseconds: 150));
      expect(controller.value, 0.0);
761
      controller.dispose();
762 763
    },
  );
764

765 766 767 768 769 770 771 772 773
  test(
    'calling repeat with specified min and max values makes the animation '
    'alternate between min and max values on each repeat',
    () {
      final AnimationController controller = AnimationController(
        duration: const Duration(milliseconds: 100),
        value: 0.0,
        vsync: const TestVSync(),
      );
774

775
      expect(controller.value, 0.0);
776

777
      controller.repeat(reverse: true, min: 0.5, max: 1.0);
778
      tick(Duration.zero);
779 780
      tick(const Duration(milliseconds: 50));
      expect(controller.value, 0.75);
781

782
      tick(Duration.zero);
783 784
      tick(const Duration(milliseconds: 100));
      expect(controller.value, 1.00);
785

786
      tick(Duration.zero);
787 788
      tick(const Duration(milliseconds: 200));
      expect(controller.value, 0.5);
789

790 791 792
      controller.reset();
      controller.value = 0.0;
      expect(controller.value, 0.0);
793

794
      controller.repeat(reverse: true, min: 1.0, max: 1.0);
795
      tick(Duration.zero);
796 797
      tick(const Duration(milliseconds: 25));
      expect(controller.value, 1.0);
798

799
      tick(Duration.zero);
800 801
      tick(const Duration(milliseconds: 125));
      expect(controller.value, 1.0);
802
      controller.dispose();
803 804
    },
  );
805

806 807
  group('AnimationBehavior', () {
    test('Default values for constructor', () {
808
      final AnimationController controller = AnimationController(vsync: const TestVSync());
809 810
      expect(controller.animationBehavior, AnimationBehavior.normal);

811
      final AnimationController repeating = AnimationController.unbounded(vsync: const TestVSync());
812
      expect(repeating.animationBehavior, AnimationBehavior.preserve);
813 814
      controller.dispose();
      repeating.dispose();
815 816
    });

817
    test('AnimationBehavior.preserve runs at normal speed when animatingTo', () {
818
      debugSemanticsDisableAnimations = true;
819
      final AnimationController controller = AnimationController(
820
        vsync: const TestVSync(),
821 822 823 824 825 826 827
        animationBehavior: AnimationBehavior.preserve,
      );

      expect(controller.value, 0.0);
      expect(controller.status, AnimationStatus.dismissed);

      controller.animateTo(1.0, duration: const Duration(milliseconds: 100));
828
      tick(Duration.zero);
829 830 831 832 833
      tick(const Duration(milliseconds: 50));

      expect(controller.value, 0.5);
      expect(controller.status, AnimationStatus.forward);

834
      tick(Duration.zero);
835 836 837 838
      tick(const Duration(milliseconds: 150));

      expect(controller.value, 1.0);
      expect(controller.status, AnimationStatus.completed);
839
      debugSemanticsDisableAnimations = false;
840
      controller.dispose();
841 842
    });

843
    test('AnimationBehavior.normal runs at 20x speed when animatingTo', () {
844
      debugSemanticsDisableAnimations = true;
845
      final AnimationController controller = AnimationController(
846
        vsync: const TestVSync(),
847 848 849 850 851 852
      );

      expect(controller.value, 0.0);
      expect(controller.status, AnimationStatus.dismissed);

      controller.animateTo(1.0, duration: const Duration(milliseconds: 100));
853
      tick(Duration.zero);
854 855 856 857 858
      tick(const Duration(microseconds: 2500));

      expect(controller.value, 0.5);
      expect(controller.status, AnimationStatus.forward);

859
      tick(Duration.zero);
860 861 862 863
      tick(const Duration(milliseconds: 5, microseconds: 1000));

      expect(controller.value, 1.0);
      expect(controller.status, AnimationStatus.completed);
864
      debugSemanticsDisableAnimations = null;
865
      controller.dispose();
866 867
    });

868
    test('AnimationBehavior.normal runs "faster" than AnimationBehavior.preserve', () {
869
      debugSemanticsDisableAnimations = true;
870
      final AnimationController controller = AnimationController(
871
        vsync: const TestVSync(),
872
      );
873
      final AnimationController fastController = AnimationController(
874
        vsync: const TestVSync(),
875 876
      );

877 878
      controller.fling(animationBehavior: AnimationBehavior.preserve);
      fastController.fling(animationBehavior: AnimationBehavior.normal);
879
      tick(Duration.zero);
880 881 882 883
      tick(const Duration(milliseconds: 50));

      // We don't assert a specific faction that normal animation.
      expect(controller.value < fastController.value, true);
884
      debugSemanticsDisableAnimations = null;
885 886
      controller.dispose();
      fastController.dispose();
887 888
    });
  });
889 890 891 892 893 894 895 896 897 898 899 900 901 902

  test('AnimationController methods assert _ticker is not null', () {
    final AnimationController controller = AnimationController(
      vsync: const TestVSync(),
    );

    controller.dispose();

    expect(() => controller.animateBack(0), throwsAssertionError);
    expect(() => controller.animateTo(0), throwsAssertionError);
    expect(() => controller.animateWith(GravitySimulation(0, 0, 0, 0)), throwsAssertionError);
    expect(() => controller.stop(), throwsAssertionError);
    expect(() => controller.forward(), throwsAssertionError);
    expect(() => controller.reverse(), throwsAssertionError);
903 904 905 906 907 908 909 910 911 912 913 914
  });

  test('Simulations run forward', () {
    final List<AnimationStatus> statuses = <AnimationStatus>[];
    final AnimationController controller = AnimationController(
      vsync: const TestVSync(),
      duration: const Duration(seconds: 1),
    )..addStatusListener((AnimationStatus status) {
      statuses.add(status);
    });

    controller.animateWith(TestSimulation());
915
    tick(Duration.zero);
916 917
    tick(const Duration(seconds: 2));
    expect(statuses, <AnimationStatus>[AnimationStatus.forward]);
918
    controller.dispose();
919 920 921 922 923 924 925 926 927 928 929
  });

  test('Simulations run forward even after a reverse run', () {
    final List<AnimationStatus> statuses = <AnimationStatus>[];
    final AnimationController controller = AnimationController(
      vsync: const TestVSync(),
      duration: const Duration(seconds: 1),
    )..addStatusListener((AnimationStatus status) {
      statuses.add(status);
    });
    controller.reverse(from: 1.0);
930
    tick(Duration.zero);
931 932 933
    tick(const Duration(seconds: 2));
    expect(statuses, <AnimationStatus>[AnimationStatus.completed, AnimationStatus.reverse, AnimationStatus.dismissed]);
    statuses.clear();
934

935
    controller.animateWith(TestSimulation());
936
    tick(Duration.zero);
937 938
    tick(const Duration(seconds: 2));
    expect(statuses, <AnimationStatus>[AnimationStatus.forward]);
939
    controller.dispose();
940
  });
941 942 943 944 945 946 947 948 949 950 951

  test('Repeating animation with reverse: true report as forward and reverse', () {
    final List<AnimationStatus> statuses = <AnimationStatus>[];
    final AnimationController controller = AnimationController(
      vsync: const TestVSync(),
      duration: const Duration(seconds: 1),
    )..addStatusListener((AnimationStatus status) {
      statuses.add(status);
    });

    controller.repeat(reverse: true);
952
    tick(Duration.zero);
953 954 955 956 957
    tick(const Duration(milliseconds: 999));
    expect(statuses, <AnimationStatus>[AnimationStatus.forward]);
    statuses.clear();
    tick(const Duration(seconds: 1));
    expect(statuses, <AnimationStatus>[AnimationStatus.reverse]);
958
    controller.dispose();
959
  });
960 961 962 963 964 965 966 967 968 969 970 971 972 973

  test('AnimateBack can runs successfully with just "reverseDuration" property set', () {
    final List<AnimationStatus> statuses = <AnimationStatus>[];
    final AnimationController controller = AnimationController(
      reverseDuration: const Duration(seconds: 2),
      vsync: const TestVSync(),
    )..addStatusListener((AnimationStatus status) {
      statuses.add(status);
    });

    controller.animateBack(0.8);

    expect(statuses, <AnimationStatus>[AnimationStatus.reverse]);
    statuses.clear();
974
    tick(Duration.zero);
975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997
    tick(const Duration(seconds: 2));
    expect(statuses, <AnimationStatus>[AnimationStatus.dismissed]);

    controller.dispose();
  });

  group('AnimationController "duration" error test', () {
    test('AnimationController forward() will throw an error if there is no default duration', () {
      final AnimationController controller = AnimationController(
        vsync: const TestVSync(),
      );

      late FlutterError error;
      try {
        controller.forward();
      } on FlutterError catch (e) {
        error = e;
      }

      expect(error, isNotNull);
      expect(
        error.toStringDeep(),
        'FlutterError\n'
998 999 1000
        '   AnimationController.forward() called with no default duration.\n'
        '   The "duration" property should be set, either in the constructor\n'
        '   or later, before calling the forward() function.\n',
1001 1002 1003 1004 1005
      );

      controller.dispose();
    });

1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
    test(
      'AnimationController animateTo() will throw an error if there is no explicit duration '
      'and default duration',
      () {
        final AnimationController controller = AnimationController(
          vsync: const TestVSync(),
        );

        late FlutterError error;
        try {
          controller.animateTo(0.8);
        } on FlutterError catch (e) {
          error = e;
        }

        expect(error, isNotNull);
        expect(
          error.toStringDeep(),
          'FlutterError\n'
1025 1026 1027 1028 1029
          '   AnimationController.animateTo() called with no explicit duration\n'
          '   and no default duration.\n'
          '   Either the "duration" argument to the animateTo() method should\n'
          '   be provided, or the "duration" property should be set, either in\n'
          '   the constructor or later, before calling the animateTo()\n'
1030 1031
          '   function.\n',
        );
1032

1033 1034 1035
        controller.dispose();
      },
    );
1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052

    test('AnimationController reverse() will throw an error if there is no default duration or reverseDuration', () {
      final AnimationController controller = AnimationController(
        vsync: const TestVSync(),
      );

      late FlutterError error;
      try {
        controller.reverse();
      } on FlutterError catch (e) {
        error = e;
      }

      expect(error, isNotNull);
      expect(
        error.toStringDeep(),
        'FlutterError\n'
1053 1054 1055 1056 1057
        '   AnimationController.reverse() called with no default duration or\n'
        '   reverseDuration.\n'
        '   The "duration" or "reverseDuration" property should be set,\n'
        '   either in the constructor or later, before calling the reverse()\n'
        '   function.\n',
1058 1059 1060 1061 1062
      );

      controller.dispose();
    });

1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
    test(
      'AnimationController animateBack() will throw an error if there is no explicit duration and '
      'no default duration or reverseDuration',
      () {
        final AnimationController controller = AnimationController(
          vsync: const TestVSync(),
        );

        late FlutterError error;
        try {
          controller.animateBack(0.8);
        } on FlutterError catch (e) {
          error = e;
        }

        expect(error, isNotNull);
        expect(
          error.toStringDeep(),
          'FlutterError\n'
1082 1083 1084 1085 1086
          '   AnimationController.animateBack() called with no explicit\n'
          '   duration and no default duration or reverseDuration.\n'
          '   Either the "duration" argument to the animateBack() method should\n'
          '   be provided, or the "duration" or "reverseDuration" property\n'
          '   should be set, either in the constructor or later, before calling\n'
1087 1088
          '   the animateBack() function.\n',
        );
1089

1090 1091 1092
        controller.dispose();
      },
    );
1093 1094
  });

1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105
}

class TestSimulation extends Simulation {
  @override
  double dx(double time) => time;

  @override
  bool isDone(double time) => false;

  @override
  double x(double time) => time;
1106
}