Fix memory leaks in Hero widget (#147303)

This commit is contained in:
Valentin Vignal
2024-04-25 23:55:23 +08:00
committed by GitHub
parent 439c03fd3a
commit c855710af8
2 changed files with 20 additions and 4 deletions

View File

@@ -425,7 +425,6 @@ class _HeroState extends State<Hero> {
}
// Everything known about a hero flight that's to be started or diverted.
@immutable
class _HeroFlightManifest {
_HeroFlightManifest({
required this.type,
@@ -455,8 +454,10 @@ class _HeroFlightManifest {
Object get tag => fromHero.widget.tag;
CurvedAnimation? _animation;
Animation<double> get animation {
return CurvedAnimation(
return _animation ??= CurvedAnimation(
parent: (type == HeroFlightDirection.push) ? toRoute.animation! : fromRoute.animation!,
curve: Curves.fastOutSlowIn,
reverseCurve: isDiverted ? null : Curves.fastOutSlowIn.flipped,
@@ -505,6 +506,11 @@ class _HeroFlightManifest {
return '_HeroFlightManifest($type tag: $tag from route: ${fromRoute.settings} '
'to route: ${toRoute.settings} with hero: $fromHero to $toHero)${isValid ? '' : ', INVALID'}';
}
@mustCallSuper
void dispose() {
_animation?.dispose();
}
}
// Builds the in-flight hero widget.
@@ -531,7 +537,13 @@ class _HeroFlight {
late ProxyAnimation _proxyAnimation;
// The manifest will be available once `start` is called, throughout the
// flight's lifecycle.
late _HeroFlightManifest manifest;
_HeroFlightManifest? _manifest;
_HeroFlightManifest get manifest => _manifest!;
set manifest (_HeroFlightManifest value) {
_manifest?.dispose();
_manifest = value;
}
OverlayEntry? overlayEntry;
bool _aborted = false;
@@ -634,6 +646,7 @@ class _HeroFlight {
_proxyAnimation.removeListener(onTick);
_proxyAnimation.removeStatusListener(_handleAnimationUpdate);
}
_manifest?.dispose();
}
void onTick() {

View File

@@ -301,7 +301,10 @@ Future<void> main() async {
expect(find.byKey(thirdKey), isInCard);
});
testWidgets('Heroes still animate after hero controller is swapped.', (WidgetTester tester) async {
testWidgets('Heroes still animate after hero controller is swapped.',
// TODO(polina-c): remove when fixed https://github.com/flutter/flutter/issues/145600 [leak-tracking-opt-in]
experimentalLeakTesting: LeakTesting.settings.withTracked(classes: const <String>['CurvedAnimation']),
(WidgetTester tester) async {
final GlobalKey<NavigatorState> key = GlobalKey<NavigatorState>();
final UniqueKey heroKey = UniqueKey();
final HeroController controller1 = HeroController();