Skip to content

Commit c855710

Browse files
Fix memory leaks in Hero widget (#147303)
1 parent 439c03f commit c855710

File tree

2 files changed

+20
-4
lines changed

2 files changed

+20
-4
lines changed

packages/flutter/lib/src/widgets/heroes.dart

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,6 @@ class _HeroState extends State<Hero> {
425425
}
426426

427427
// Everything known about a hero flight that's to be started or diverted.
428-
@immutable
429428
class _HeroFlightManifest {
430429
_HeroFlightManifest({
431430
required this.type,
@@ -455,8 +454,10 @@ class _HeroFlightManifest {
455454

456455
Object get tag => fromHero.widget.tag;
457456

457+
CurvedAnimation? _animation;
458+
458459
Animation<double> get animation {
459-
return CurvedAnimation(
460+
return _animation ??= CurvedAnimation(
460461
parent: (type == HeroFlightDirection.push) ? toRoute.animation! : fromRoute.animation!,
461462
curve: Curves.fastOutSlowIn,
462463
reverseCurve: isDiverted ? null : Curves.fastOutSlowIn.flipped,
@@ -505,6 +506,11 @@ class _HeroFlightManifest {
505506
return '_HeroFlightManifest($type tag: $tag from route: ${fromRoute.settings} '
506507
'to route: ${toRoute.settings} with hero: $fromHero to $toHero)${isValid ? '' : ', INVALID'}';
507508
}
509+
510+
@mustCallSuper
511+
void dispose() {
512+
_animation?.dispose();
513+
}
508514
}
509515

510516
// Builds the in-flight hero widget.
@@ -531,7 +537,13 @@ class _HeroFlight {
531537
late ProxyAnimation _proxyAnimation;
532538
// The manifest will be available once `start` is called, throughout the
533539
// flight's lifecycle.
534-
late _HeroFlightManifest manifest;
540+
_HeroFlightManifest? _manifest;
541+
_HeroFlightManifest get manifest => _manifest!;
542+
set manifest (_HeroFlightManifest value) {
543+
_manifest?.dispose();
544+
_manifest = value;
545+
}
546+
535547
OverlayEntry? overlayEntry;
536548
bool _aborted = false;
537549

@@ -634,6 +646,7 @@ class _HeroFlight {
634646
_proxyAnimation.removeListener(onTick);
635647
_proxyAnimation.removeStatusListener(_handleAnimationUpdate);
636648
}
649+
_manifest?.dispose();
637650
}
638651

639652
void onTick() {

packages/flutter/test/widgets/heroes_test.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,10 @@ Future<void> main() async {
301301
expect(find.byKey(thirdKey), isInCard);
302302
});
303303

304-
testWidgets('Heroes still animate after hero controller is swapped.', (WidgetTester tester) async {
304+
testWidgets('Heroes still animate after hero controller is swapped.',
305+
// TODO(polina-c): remove when fixed https://github.com/flutter/flutter/issues/145600 [leak-tracking-opt-in]
306+
experimentalLeakTesting: LeakTesting.settings.withTracked(classes: const <String>['CurvedAnimation']),
307+
(WidgetTester tester) async {
305308
final GlobalKey<NavigatorState> key = GlobalKey<NavigatorState>();
306309
final UniqueKey heroKey = UniqueKey();
307310
final HeroController controller1 = HeroController();

0 commit comments

Comments
 (0)