diff --git a/examples/stocks/test/locale_test.dart b/examples/stocks/test/locale_test.dart index 98b31b8d0f..150fe97371 100644 --- a/examples/stocks/test/locale_test.dart +++ b/examples/stocks/test/locale_test.dart @@ -22,6 +22,10 @@ void main() { tester.setLocale("es", "US"); tester.pump(); expect(tab.widget.data, equals("MERCADO")); + + // TODO(abarth): We're leaking an animation. We should track down the leak + // and plug it rather than waiting for the animation to end here. + tester.pump(const Duration(seconds: 1)); }); }); } diff --git a/packages/flutter/lib/src/material/progress_indicator.dart b/packages/flutter/lib/src/material/progress_indicator.dart index 7668c68e3b..fc47e1ebac 100644 --- a/packages/flutter/lib/src/material/progress_indicator.dart +++ b/packages/flutter/lib/src/material/progress_indicator.dart @@ -92,6 +92,11 @@ class _LinearProgressIndicatorState extends State { _animation = new CurvedAnimation(parent: _controller, curve: Curves.fastOutSlowIn); } + void dispose() { + _controller.stop(); + super.dispose(); + } + Widget _buildIndicator(BuildContext context, double animationValue) { return new Container( constraints: new BoxConstraints.tightFor( @@ -209,15 +214,20 @@ final Animatable _kStepTween = new StepTween(begin: 0, end: 5); final Animatable _kRotationTween = new CurveTween(curve: new SawTooth(5)); class _CircularProgressIndicatorState extends State { - AnimationController _animationController; + AnimationController _controller; void initState() { super.initState(); - _animationController = new AnimationController( + _controller = new AnimationController( duration: const Duration(milliseconds: 6666) )..repeat(); } + void dispose() { + _controller.stop(); + super.dispose(); + } + Widget _buildIndicator(BuildContext context, double headValue, double tailValue, int stepValue, double rotationValue) { return new Container( constraints: new BoxConstraints( @@ -242,14 +252,14 @@ class _CircularProgressIndicatorState extends State { return _buildIndicator(context, 0.0, 0.0, 0, 0.0); return new AnimatedBuilder( - animation: _animationController, + animation: _controller, builder: (BuildContext context, Widget child) { return _buildIndicator( context, - _kStrokeHeadTween.evaluate(_animationController), - _kStrokeTailTween.evaluate(_animationController), - _kStepTween.evaluate(_animationController), - _kRotationTween.evaluate(_animationController) + _kStrokeHeadTween.evaluate(_controller), + _kStrokeTailTween.evaluate(_controller), + _kStepTween.evaluate(_controller), + _kRotationTween.evaluate(_controller) ); } ); diff --git a/packages/flutter/lib/src/material/scaffold.dart b/packages/flutter/lib/src/material/scaffold.dart index f606b41fe7..df98fa2e74 100644 --- a/packages/flutter/lib/src/material/scaffold.dart +++ b/packages/flutter/lib/src/material/scaffold.dart @@ -281,6 +281,7 @@ class ScaffoldState extends State { _snackBarController.status == AnimationStatus.completed); _snackBars.first._completer.complete(); _snackBarController.reverse(); + _snackBarTimer?.cancel(); _snackBarTimer = null; } diff --git a/packages/flutter/test/widget/positioned_test.dart b/packages/flutter/test/widget/positioned_test.dart index 7a03b2c14b..57c075007b 100644 --- a/packages/flutter/test/widget/positioned_test.dart +++ b/packages/flutter/test/widget/positioned_test.dart @@ -70,6 +70,7 @@ void main() { expect(sizes, equals([const Size(10.0, 10.0), const Size(10.0, 10.0), const Size(10.0, 10.0), const Size(10.0, 10.0), const Size(10.0, 10.0), const Size(10.0, 10.0)])); expect(positions, equals([const Offset(10.0, 10.0), const Offset(10.0, 10.0), const Offset(17.0, 17.0), const Offset(24.0, 24.0), const Offset(45.0, 45.0), const Offset(80.0, 80.0)])); + controller.stop(); }); }); diff --git a/packages/flutter_test/lib/src/widget_tester.dart b/packages/flutter_test/lib/src/widget_tester.dart index 50e8e30d43..07647bc5a0 100644 --- a/packages/flutter_test/lib/src/widget_tester.dart +++ b/packages/flutter_test/lib/src/widget_tester.dart @@ -78,7 +78,6 @@ class WidgetTester extends Instrumentation { super(binding: _SteppedWidgetFlutterBinding.ensureInitialized()) { timeDilation = 1.0; ui.window.onBeginFrame = null; - runApp(new Container(key: new UniqueKey())); // flush out the last build entirely } final FakeAsync async; @@ -132,6 +131,23 @@ class WidgetTester extends Instrumentation { void testWidgets(callback(WidgetTester tester)) { new FakeAsync().run((FakeAsync async) { - callback(new WidgetTester._(async)); + WidgetTester tester = new WidgetTester._(async); + runApp(new Container(key: new UniqueKey())); // Reset the tree to a known state. + callback(tester); + runApp(new Container(key: new UniqueKey())); // Unmount any remaining widgets. + async.flushMicrotasks(); + assert(() { + "An animation is still running even after the widget tree was disposed."; + return Scheduler.instance.transientCallbackCount == 0; + }); + assert(() { + "A Timer is still running even after the widget tree was disposed."; + return async.periodicTimerCount == 0; + }); + assert(() { + "A Timer is still running even after the widget tree was disposed."; + return async.nonPeriodicTimerCount == 0; + }); + assert(async.microtaskCount == 0); // Shouldn't be possible. }); }