Unverified Commit aeb500a0 authored by Kostia Sokolovskyi's avatar Kostia Sokolovskyi Committed by GitHub

AnimationController should dispatch creation in constructor. (#134839)

parent d1e24a49
......@@ -20,6 +20,8 @@ export 'package:flutter/scheduler.dart' show TickerFuture, TickerProvider;
export 'animation.dart' show Animation, AnimationStatus;
export 'curves.dart' show Curve;
const String _flutterAnimationLibrary = 'package:flutter/animation.dart';
// Examples can assume:
// late AnimationController _controller, fadeAnimationController, sizeAnimationController;
// late bool dismissed;
......@@ -244,6 +246,7 @@ class AnimationController extends Animation<double>
required TickerProvider vsync,
}) : assert(upperBound >= lowerBound),
_direction = _AnimationDirection.forward {
_maybeDispatchObjectCreation();
_ticker = vsync.createTicker(_tick);
_internalSetValue(value ?? lowerBound);
}
......@@ -275,10 +278,22 @@ class AnimationController extends Animation<double>
}) : lowerBound = double.negativeInfinity,
upperBound = double.infinity,
_direction = _AnimationDirection.forward {
_maybeDispatchObjectCreation();
_ticker = vsync.createTicker(_tick);
_internalSetValue(value);
}
/// Dispatches event of object creation to [MemoryAllocations.instance].
void _maybeDispatchObjectCreation() {
if (kFlutterMemoryAllocationsEnabled) {
MemoryAllocations.instance.dispatchObjectCreated(
library: _flutterAnimationLibrary,
className: '$AnimationController',
object: this,
);
}
}
/// The value at which this animation is deemed to be dismissed.
final double lowerBound;
......@@ -800,6 +815,9 @@ class AnimationController extends Animation<double>
}
return true;
}());
if (kFlutterMemoryAllocationsEnabled) {
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
}
_ticker!.dispose();
_ticker = null;
clearStatusListeners();
......
......@@ -597,4 +597,11 @@ class _RenderCupertinoSlider extends RenderConstrainedBox implements MouseTracke
@override
bool get validForMouseTracker => false;
@override
void dispose() {
_drag.dispose();
_position.dispose();
super.dispose();
}
}
......@@ -154,6 +154,14 @@ class _AnimationTuple {
final CurvedAnimation endAnimation;
final CurvedAnimation gapAnimation;
double gapStart = 0.0;
@mustCallSuper
void dispose() {
controller.dispose();
startAnimation.dispose();
endAnimation.dispose();
gapAnimation.dispose();
}
}
class _MergeableMaterialState extends State<MergeableMaterial> with TickerProviderStateMixin {
......@@ -208,7 +216,7 @@ class _MergeableMaterialState extends State<MergeableMaterial> with TickerProvid
void dispose() {
for (final MergeableMaterialItem child in _children) {
if (child is MaterialGap) {
_animationTuples[child.key]!.controller.dispose();
_animationTuples[child.key]!.dispose();
}
}
super.dispose();
......@@ -258,6 +266,7 @@ class _MergeableMaterialState extends State<MergeableMaterial> with TickerProvid
final MergeableMaterialItem child = _children.removeAt(index);
if (child is MaterialGap) {
_animationTuples[child.key]!.dispose();
_animationTuples[child.key] = null;
}
}
......
......@@ -522,6 +522,12 @@ class _CheckedPopupMenuItemState<T> extends PopupMenuItemState<T, CheckedPopupMe
..addListener(() => setState(() { /* animation changed */ }));
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
void handleTap() {
// This fades the checkmark in or out when tapped.
......
......@@ -594,6 +594,7 @@ class ScaffoldMessengerState extends State<ScaffoldMessenger> with TickerProvide
@override
void dispose() {
_materialBannerController?.dispose();
_snackBarController?.dispose();
_snackBarTimer?.cancel();
_snackBarTimer = null;
......
......@@ -8,6 +8,7 @@ import 'package:flutter/scheduler.dart';
import 'package:flutter/semantics.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
import '../scheduler/scheduler_tester.dart';
......@@ -20,6 +21,19 @@ void main() {
..platformDispatcher.onDrawFrame = null;
});
test('AnimationController dispatches memory events', () async {
await expectLater(
await memoryEvents(
() => AnimationController(
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
).dispose(),
AnimationController,
),
areCreateAndDispose,
);
});
test('Can set value during status callback', () {
final AnimationController controller = AnimationController(
duration: const Duration(milliseconds: 100),
......
......@@ -13,14 +13,17 @@ void main() {
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
);
addTearDown(controller1.dispose);
final AnimationController controller2 = AnimationController(
duration: const Duration(milliseconds: 600),
vsync: const TestVSync(),
);
addTearDown(controller2.dispose);
final AnimationController controller3 = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: const TestVSync(),
);
addTearDown(controller3.dispose);
final List<String> log = <String>[];
Future<void> runTest() async {
log.add('a'); // t=0
......@@ -62,14 +65,17 @@ void main() {
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
);
addTearDown(controller1.dispose);
final AnimationController controller2 = AnimationController(
duration: const Duration(milliseconds: 600),
vsync: const TestVSync(),
);
addTearDown(controller2.dispose);
final AnimationController controller3 = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: const TestVSync(),
);
addTearDown(controller3.dispose);
final List<String> log = <String>[];
Future<void> runTest() async {
log.add('a'); // t=0
......@@ -139,6 +145,7 @@ void main() {
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
);
addTearDown(controller1.dispose);
final TickerFuture f = controller1.forward();
await tester.pump(); // start ticker
await tester.pump(const Duration(milliseconds: 200)); // end ticker
......@@ -152,6 +159,7 @@ void main() {
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
);
addTearDown(controller1.dispose);
final TickerFuture f = controller1.forward();
await tester.pump(); // start ticker
controller1.stop(); // cancel ticker
......@@ -169,6 +177,7 @@ void main() {
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
);
addTearDown(controller1.dispose);
final TickerFuture f = controller1.forward();
await tester.pump(); // start ticker
await tester.pump(const Duration(milliseconds: 200)); // end ticker
......
......@@ -85,6 +85,7 @@ void main() {
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
);
addTearDown(controller.dispose);
final List<String> log = <String>[];
void listener1() { log.add('listener1'); }
......
......@@ -25,7 +25,15 @@ Future<void> testExecutable(FutureOr<void> Function() testMain) {
WidgetController.hitTestWarningShouldBeFatal = true;
LeakTracking.warnForUnsupportedPlatforms = false;
LeakTesting.settings = LeakTesting.settings.withTrackedAll().withIgnored(allNotGCed: true);
// TODO(polina-c): clean up leaks and stop ignoring them.
// https://github.com/flutter/flutter/issues/137311
LeakTesting.settings = LeakTesting
.settings
.withTrackedAll()
.withIgnored(
allNotGCed: true,
);
// Enable golden file testing using Skia Gold.
return flutter_goldens.testExecutable(testMain);
......
......@@ -1345,6 +1345,13 @@ void main() {
testWidgetsWithLeakTracking('Verify showModalBottomSheet use AnimationController if provided.', (WidgetTester tester) async {
const Key tapTarget = Key('tap-target');
final AnimationController controller = AnimationController(
vsync: const TestVSync(),
duration: const Duration(seconds: 2),
reverseDuration: const Duration(seconds: 2),
);
addTearDown(controller.dispose);
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: Builder(
......@@ -1355,11 +1362,7 @@ void main() {
showModalBottomSheet<void>(
context: context,
// The default duration and reverseDuration is 1 second
transitionAnimationController: AnimationController(
vsync: const TestVSync(),
duration: const Duration(seconds: 2),
reverseDuration: const Duration(seconds: 2),
),
transitionAnimationController: controller,
builder: (BuildContext context) {
return const Text('BottomSheet');
},
......@@ -1465,6 +1468,13 @@ void main() {
testWidgetsWithLeakTracking('Verify persistence BottomSheet use AnimationController if provided.', (WidgetTester tester) async {
const Key tapTarget = Key('tap-target');
const Key tapTargetToClose = Key('tap-target-to-close');
final AnimationController controller = AnimationController(
vsync: const TestVSync(),
duration: const Duration(seconds: 2),
reverseDuration: const Duration(seconds: 2),
);
addTearDown(controller.dispose);
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: Builder(
......@@ -1475,11 +1485,7 @@ void main() {
showBottomSheet<void>(
context: context,
// The default duration and reverseDuration is 1 second
transitionAnimationController: AnimationController(
vsync: const TestVSync(),
duration: const Duration(seconds: 2),
reverseDuration: const Duration(seconds: 2),
),
transitionAnimationController: controller,
builder: (BuildContext context) {
return ElevatedButton(
key: tapTargetToClose,
......
......@@ -1083,6 +1083,7 @@ void main() {
Future<void> testColor(Color color) async {
final AnimationController positionController = AnimationController(vsync: const TestVSync());
addTearDown(positionController.dispose);
// Correspond to [_setupColorTween].
final Animation<Color?> valueColorAnimation = positionController.drive(
ColorTween(
......
......@@ -11,6 +11,7 @@ void main() {
testWidgetsWithLeakTracking('RenderAnimatedOpacityMixin does not drop layer when animating to 1', (WidgetTester tester) async {
RenderTestObject.paintCount = 0;
final AnimationController controller = AnimationController(vsync: const TestVSync(), duration: const Duration(seconds: 1));
addTearDown(controller.dispose);
final Tween<double> opacityTween = Tween<double>(begin: 0, end: 1);
await tester.pumpWidget(
ColoredBox(
......@@ -44,6 +45,7 @@ void main() {
testWidgetsWithLeakTracking('RenderAnimatedOpacityMixin avoids repainting child as it animates', (WidgetTester tester) async {
RenderTestObject.paintCount = 0;
final AnimationController controller = AnimationController(vsync: const TestVSync(), duration: const Duration(seconds: 1));
addTearDown(controller.dispose);
final Tween<double> opacityTween = Tween<double>(begin: 0, end: 0.99); // Layer is dropped at 1
await tester.pumpWidget(
ColoredBox(
......@@ -77,9 +79,8 @@ void main() {
testWidgetsWithLeakTracking('RenderAnimatedOpacityMixin allows opacity layer to be disposed when animating to 0 opacity', (WidgetTester tester) async {
RenderTestObject.paintCount = 0;
final AnimationController controller = AnimationController(vsync: const TestVSync(), duration: const Duration(seconds: 1));
final Tween<double> opacityTween = Tween<double>(begin: 0.99, end: 0);
addTearDown(controller.dispose);
final Tween<double> opacityTween = Tween<double>(begin: 0.99, end: 0);
await tester.pumpWidget(
ColoredBox(
......
......@@ -19,6 +19,7 @@ void main() {
vsync: const TestVSync(),
duration: const Duration(seconds: 2),
);
addTearDown(controller.dispose);
await tester.pumpWidget(FadeTransition(
opacity: controller,
child: const Placeholder(),
......
......@@ -169,6 +169,7 @@ void main() {
vsync: const TestVSync(),
duration: const Duration(milliseconds: 200),
);
addTearDown(controller.dispose);
// The overall height of the frame is (as ever) 600
Widget buildFrame() {
......
......@@ -254,6 +254,7 @@ void main() {
await runFakeAsync((FakeAsync async) async {
final _MockAnimationController animationController =
_MockAnimationController();
addTearDown(animationController.dispose);
const RawMagnifier testMagnifier = RawMagnifier(
size: Size(100, 100),
......
......@@ -64,6 +64,7 @@ void main() {
duration: const Duration(seconds: 1),
vsync: const TestVSync(),
);
addTearDown(anotherController.dispose);
await tester.pumpWidget(TestStatusTransitionWidget(
animation: anotherController,
......
......@@ -97,7 +97,7 @@ void main() {
));
});
testWidgetsWithLeakTracking('SingleTickerProviderStateMixin dispose while active', (WidgetTester tester) async {
testWidgets('SingleTickerProviderStateMixin dispose while active', (WidgetTester tester) async {
final GlobalKey<_SingleTickerTestState> key = GlobalKey<_SingleTickerTestState>();
final Widget widget = _SingleTickerTest(key: key);
await tester.pumpWidget(widget);
......@@ -137,7 +137,7 @@ void main() {
}
});
testWidgetsWithLeakTracking('SingleTickerProviderStateMixin dispose while active', (WidgetTester tester) async {
testWidgets('SingleTickerProviderStateMixin dispose while active', (WidgetTester tester) async {
final GlobalKey<_SingleTickerTestState> key = GlobalKey<_SingleTickerTestState>();
final Widget widget = _SingleTickerTest(key: key);
await tester.pumpWidget(widget);
......@@ -177,7 +177,7 @@ void main() {
}
});
testWidgetsWithLeakTracking('TickerProviderStateMixin dispose while any ticker is active', (WidgetTester tester) async {
testWidgets('TickerProviderStateMixin dispose while any ticker is active', (WidgetTester tester) async {
final GlobalKey<_MultipleTickerTestState> key = GlobalKey<_MultipleTickerTestState>();
final Widget widget = _MultipleTickerTest(key: key);
await tester.pumpWidget(widget);
......@@ -217,12 +217,12 @@ void main() {
});
});
testWidgetsWithLeakTracking('SingleTickerProviderStateMixin does not call State.toString', (WidgetTester tester) async {
testWidgets('SingleTickerProviderStateMixin does not call State.toString', (WidgetTester tester) async {
await tester.pumpWidget(const _SingleTickerTest());
expect(tester.state<_SingleTickerTestState>(find.byType(_SingleTickerTest)).toStringCount, 0);
});
testWidgetsWithLeakTracking('TickerProviderStateMixin does not call State.toString', (WidgetTester tester) async {
testWidgets('TickerProviderStateMixin does not call State.toString', (WidgetTester tester) async {
await tester.pumpWidget(const _MultipleTickerTest());
expect(tester.state<_MultipleTickerTestState>(find.byType(_MultipleTickerTest)).toStringCount, 0);
});
......
......@@ -147,6 +147,7 @@ void main() {
testWidgetsWithLeakTracking('AlignTransition animates', (WidgetTester tester) async {
final AnimationController controller = AnimationController(vsync: const TestVSync());
addTearDown(controller.dispose);
final Animation<Alignment> alignmentTween = AlignmentTween(
begin: Alignment.centerLeft,
end: Alignment.bottomRight,
......@@ -171,6 +172,7 @@ void main() {
testWidgetsWithLeakTracking('RelativePositionedTransition animates', (WidgetTester tester) async {
final AnimationController controller = AnimationController(vsync: const TestVSync());
addTearDown(controller.dispose);
final Animation<Rect?> rectTween = RectTween(
begin: const Rect.fromLTWH(0, 0, 30, 40),
end: const Rect.fromLTWH(100, 200, 100, 200),
......@@ -217,6 +219,7 @@ void main() {
testWidgetsWithLeakTracking('AlignTransition keeps width and height factors', (WidgetTester tester) async {
final AnimationController controller = AnimationController(vsync: const TestVSync());
addTearDown(controller.dispose);
final Animation<Alignment> alignmentTween = AlignmentTween(
begin: Alignment.centerLeft,
end: Alignment.bottomRight,
......@@ -238,6 +241,7 @@ void main() {
testWidgetsWithLeakTracking('SizeTransition clamps negative size factors - vertical axis', (WidgetTester tester) async {
final AnimationController controller = AnimationController(vsync: const TestVSync());
addTearDown(controller.dispose);
final Animation<double> animation = Tween<double>(begin: -1.0, end: 1.0).animate(controller);
final Widget widget = Directionality(
......@@ -273,6 +277,7 @@ void main() {
testWidgetsWithLeakTracking('SizeTransition clamps negative size factors - horizontal axis', (WidgetTester tester) async {
final AnimationController controller = AnimationController(vsync: const TestVSync());
addTearDown(controller.dispose);
final Animation<double> animation = Tween<double>(begin: -1.0, end: 1.0).animate(controller);
final Widget widget = Directionality(
......@@ -309,6 +314,7 @@ void main() {
testWidgetsWithLeakTracking('SizeTransition with fixedCrossAxisSizeFactor should size its cross axis from its children - vertical axis', (WidgetTester tester) async {
final AnimationController controller = AnimationController(vsync: const TestVSync());
addTearDown(controller.dispose);
final Animation<double> animation = Tween<double>(begin: 0, end: 1.0).animate(controller);
const Key key = Key('key');
......@@ -367,6 +373,7 @@ void main() {
testWidgetsWithLeakTracking('SizeTransition with fixedCrossAxisSizeFactor should size its cross axis from its children - horizontal axis', (WidgetTester tester) async {
final AnimationController controller = AnimationController(vsync: const TestVSync());
addTearDown(controller.dispose);
final Animation<double> animation = Tween<double>(begin: 0.0, end: 1.0).animate(controller);
const Key key = Key('key');
......@@ -426,6 +433,7 @@ void main() {
testWidgetsWithLeakTracking('MatrixTransition animates', (WidgetTester tester) async {
final AnimationController controller = AnimationController(vsync: const TestVSync());
addTearDown(controller.dispose);
final Widget widget = MatrixTransition(
alignment: Alignment.topRight,
onTransform: (double value) => Matrix4.translationValues(value, value, value),
......@@ -466,6 +474,7 @@ void main() {
testWidgetsWithLeakTracking('MatrixTransition maintains chosen alignment during animation', (WidgetTester tester) async {
final AnimationController controller = AnimationController(vsync: const TestVSync());
addTearDown(controller.dispose);
final Widget widget = MatrixTransition(
alignment: Alignment.topRight,
onTransform: (double value) => Matrix4.identity(),
......@@ -487,6 +496,7 @@ void main() {
testWidgetsWithLeakTracking('RotationTransition animates', (WidgetTester tester) async {
final AnimationController controller = AnimationController(vsync: const TestVSync());
addTearDown(controller.dispose);
final Widget widget = RotationTransition(
alignment: Alignment.topRight,
turns: controller,
......@@ -526,6 +536,7 @@ void main() {
testWidgetsWithLeakTracking('RotationTransition maintains chosen alignment during animation', (WidgetTester tester) async {
final AnimationController controller = AnimationController(vsync: const TestVSync());
addTearDown(controller.dispose);
final Widget widget = RotationTransition(
alignment: Alignment.topRight,
turns: controller,
......@@ -556,6 +567,7 @@ void main() {
}
testWidgetsWithLeakTracking('animates', (WidgetTester tester) async {
final AnimationController controller = AnimationController(vsync: const TestVSync());
addTearDown(controller.dispose);
final Animation<double> animation = Tween<double>(begin: 0.0, end: 1.0).animate(controller);
final Widget widget = Directionality(
textDirection: TextDirection.ltr,
......@@ -599,6 +611,7 @@ void main() {
}
testWidgetsWithLeakTracking('animates', (WidgetTester tester) async {
final AnimationController controller = AnimationController(vsync: const TestVSync());
addTearDown(controller.dispose);
final Animation<double> animation = Tween<double>(begin: 0.0, end: 1.0).animate(controller);
final Widget widget = Localizations(
locale: const Locale('en', 'us'),
......@@ -649,6 +662,7 @@ void main() {
group('MatrixTransition', () {
testWidgetsWithLeakTracking('uses ImageFilter when provided with FilterQuality argument', (WidgetTester tester) async {
final AnimationController controller = AnimationController(vsync: const TestVSync());
addTearDown(controller.dispose);
final Animation<double> animation = Tween<double>(begin: 0.0, end: 1.0).animate(controller);
final Widget widget = Directionality(
textDirection: TextDirection.ltr,
......@@ -712,6 +726,7 @@ void main() {
group('ScaleTransition', () {
testWidgetsWithLeakTracking('uses ImageFilter when provided with FilterQuality argument', (WidgetTester tester) async {
final AnimationController controller = AnimationController(vsync: const TestVSync());
addTearDown(controller.dispose);
final Animation<double> animation = Tween<double>(begin: 0.0, end: 1.0).animate(controller);
final Widget widget = Directionality(
textDirection: TextDirection.ltr,
......@@ -765,6 +780,7 @@ void main() {
group('RotationTransition', () {
testWidgetsWithLeakTracking('uses ImageFilter when provided with FilterQuality argument', (WidgetTester tester) async {
final AnimationController controller = AnimationController(vsync: const TestVSync());
addTearDown(controller.dispose);
final Animation<double> animation = Tween<double>(begin: 0.0, end: 1.0).animate(controller);
final Widget widget = Directionality(
textDirection: TextDirection.ltr,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment