From ded49cda273bc31f94d7520bf89fb5f6241ff1d8 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 23 Jan 2019 16:30:41 -0800 Subject: [PATCH] Assert when calling a method that tries to use the ticker unsafely after dispose (#26991) --- .../src/animation/animation_controller.dart | 30 +++++++++++++++++++ .../animation/animation_controller_test.dart | 17 +++++++++++ 2 files changed, 47 insertions(+) diff --git a/packages/flutter/lib/src/animation/animation_controller.dart b/packages/flutter/lib/src/animation/animation_controller.dart index c564ed62ca..9623bdc9ca 100644 --- a/packages/flutter/lib/src/animation/animation_controller.dart +++ b/packages/flutter/lib/src/animation/animation_controller.dart @@ -436,6 +436,11 @@ class AnimationController extends Animation } return true; }()); + assert( + _ticker != null, + 'AnimationController.forward() called after AnimationController.dispose()\n' + 'AnimationController methods should not be used after calling dispose.' + ); _direction = _AnimationDirection.forward; if (from != null) value = from; @@ -464,6 +469,11 @@ class AnimationController extends Animation } return true; }()); + assert( + _ticker != null, + 'AnimationController.reverse() called after AnimationController.dispose()\n' + 'AnimationController methods should not be used after calling dispose.' + ); _direction = _AnimationDirection.reverse; if (from != null) value = from; @@ -483,6 +493,11 @@ class AnimationController extends Animation /// animation, when `target` is reached, [status] is reported as /// [AnimationStatus.completed]. TickerFuture animateTo(double target, { Duration duration, Curve curve = Curves.linear }) { + assert( + _ticker != null, + 'AnimationController.animateTo() called after AnimationController.dispose()\n' + 'AnimationController methods should not be used after calling dispose.' + ); _direction = _AnimationDirection.forward; return _animateToInternal(target, duration: duration, curve: curve); } @@ -500,6 +515,11 @@ class AnimationController extends Animation /// animation, when `target` is reached, [status] is reported as /// [AnimationStatus.dismissed]. TickerFuture animateBack(double target, { Duration duration, Curve curve = Curves.linear }) { + assert( + _ticker != null, + 'AnimationController.animateBack() called after AnimationController.dispose()\n' + 'AnimationController methods should not be used after calling dispose.' + ); _direction = _AnimationDirection.reverse; return _animateToInternal(target, duration: duration, curve: curve); } @@ -626,6 +646,11 @@ class AnimationController extends Animation /// canceled, meaning the future never completes and its [TickerFuture.orCancel] /// derivative future completes with a [TickerCanceled] error. TickerFuture animateWith(Simulation simulation) { + assert( + _ticker != null, + 'AnimationController.animateWith() called after AnimationController.dispose()\n' + 'AnimationController methods should not be used after calling dispose.' + ); stop(); return _startSimulation(simulation); } @@ -662,6 +687,11 @@ class AnimationController extends Animation /// * [forward], [reverse], [animateTo], [animateWith], [fling], and [repeat], /// which restart the animation controller. void stop({ bool canceled = true }) { + assert( + _ticker != null, + 'AnimationController.stop() called after AnimationController.dispose()\n' + 'AnimationController methods should not be used after calling dispose.' + ); _simulation = null; _lastElapsedDuration = null; _ticker.stop(canceled: canceled); diff --git a/packages/flutter/test/animation/animation_controller_test.dart b/packages/flutter/test/animation/animation_controller_test.dart index 271f71613e..a2a180783a 100644 --- a/packages/flutter/test/animation/animation_controller_test.dart +++ b/packages/flutter/test/animation/animation_controller_test.dart @@ -4,6 +4,7 @@ import 'dart:ui' as ui; +import 'package:flutter/physics.dart'; import 'package:flutter/semantics.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/animation.dart'; @@ -658,4 +659,20 @@ void main() { debugSemanticsDisableAnimations = null; }); }); + + 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); + + }); }