Skip to content

Commit

Permalink
Fix memory leaks in Hero widget (#147303)
Browse files Browse the repository at this point in the history
  • Loading branch information
ValentinVignal authored Apr 25, 2024
1 parent 439c03f commit c855710
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 4 deletions.
19 changes: 16 additions & 3 deletions packages/flutter/lib/src/widgets/heroes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand All @@ -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;

Expand Down Expand Up @@ -634,6 +646,7 @@ class _HeroFlight {
_proxyAnimation.removeListener(onTick);
_proxyAnimation.removeStatusListener(_handleAnimationUpdate);
}
_manifest?.dispose();
}

void onTick() {
Expand Down
5 changes: 4 additions & 1 deletion packages/flutter/test/widgets/heroes_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit c855710

Please sign in to comment.