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

Fix memory leak in RenderAnimatedSize (#133653)

`AnimationController` and `CurvedAnimation` objects were not disposed in `RenderAnimatedSize`.

### Description
- Fixes https://github.com/flutter/flutter/issues/133903;
- Adds the missing `dispose()` calls for `AnimationController` and `CurvedAnimation` in `RenderAnimatedSize`.

### Tests
- Updates `animated_size_test.dart` to test that `AnimationController` and `CurvedAnimation` are disposed after `RenderSize` disposal.
parent 969911d1
......@@ -99,8 +99,42 @@ class RenderAnimatedSize extends RenderAligningShiftedBox {
);
}
/// When asserts are enabled, returns the animation controller that is used
/// to drive the resizing.
///
/// Otherwise, returns null.
///
/// This getter is intended for use in framework unit tests. Applications must
/// not depend on its value.
@visibleForTesting
AnimationController? get debugController {
AnimationController? controller;
assert(() {
controller = _controller;
return true;
}());
return controller;
}
/// When asserts are enabled, returns the animation that drives the resizing.
///
/// Otherwise, returns null.
///
/// This getter is intended for use in framework unit tests. Applications must
/// not depend on its value.
@visibleForTesting
CurvedAnimation? get debugAnimation {
CurvedAnimation? animation;
assert(() {
animation = _animation;
return true;
}());
return animation;
}
late final AnimationController _controller;
late final CurvedAnimation _animation;
final SizeTween _sizeTween = SizeTween();
late bool _hasVisualOverflow;
double? _lastValue;
......@@ -351,6 +385,8 @@ class RenderAnimatedSize extends RenderAligningShiftedBox {
@override
void dispose() {
_clipRectLayer.layer = null;
_controller.dispose();
_animation.dispose();
super.dispose();
}
}
......@@ -434,5 +434,42 @@ void main() {
const Size.square(150),
);
});
testWidgets('disposes animation and controller', (WidgetTester tester) async {
await tester.pumpWidget(
const Center(
child: AnimatedSize(
duration: Duration(milliseconds: 200),
child: SizedBox(
width: 100.0,
height: 100.0,
),
),
),
);
final RenderAnimatedSize box = tester.renderObject(find.byType(AnimatedSize));
await tester.pumpWidget(
const Center(),
);
expect(box.debugAnimation, isNotNull);
expect(box.debugAnimation!.isDisposed, isTrue);
expect(box.debugController, isNotNull);
expect(
() => box.debugController!.dispose(),
throwsA(isA<AssertionError>().having(
(AssertionError error) => error.message,
'message',
equalsIgnoringHashCodes(
'AnimationController.dispose() called more than once.\n'
'A given AnimationController cannot be disposed more than once.\n'
'The following AnimationController object was disposed multiple times:\n'
' AnimationController#00000(⏮ 0.000; paused; DISPOSED)',
),
)),
);
});
});
}
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