Skip to content

Commit

Permalink
HeroController should dispatch creation and disposal events. (#137835)
Browse files Browse the repository at this point in the history
  • Loading branch information
ksokolovskyi authored Nov 4, 2023
1 parent 0e7ed92 commit e8d9d9b
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 3 deletions.
6 changes: 6 additions & 0 deletions packages/flutter/lib/src/cupertino/tab_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ class _CupertinoTabViewState extends State<CupertinoTabView> {
}
}

@override
void dispose() {
_heroController.dispose();
super.dispose();
}

void _updateObservers() {
_navigatorObservers =
List<NavigatorObserver>.of(widget.navigatorObservers)
Expand Down
18 changes: 17 additions & 1 deletion packages/flutter/lib/src/widgets/heroes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -784,7 +784,17 @@ class HeroController extends NavigatorObserver {
///
/// The [createRectTween] argument is optional. If null, the controller uses a
/// linear [Tween<Rect>].
HeroController({ this.createRectTween });
HeroController({ this.createRectTween }) {
// TODO(polina-c): stop duplicating code across disposables
// https://github.com/flutter/flutter/issues/137435
if (kFlutterMemoryAllocationsEnabled) {
MemoryAllocations.instance.dispatchObjectCreated(
library: 'package:flutter/widgets.dart',
className: '$HeroController',
object: this,
);
}
}

/// Used to create [RectTween]s that interpolate the position of heroes in flight.
///
Expand Down Expand Up @@ -1043,6 +1053,12 @@ class HeroController extends NavigatorObserver {
/// Releases resources.
@mustCallSuper
void dispose() {
// TODO(polina-c): stop duplicating code across disposables
// https://github.com/flutter/flutter/issues/137435
if (kFlutterMemoryAllocationsEnabled) {
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
}

for (final _HeroFlight flight in _flights.values) {
flight.dispose();
}
Expand Down
1 change: 1 addition & 0 deletions packages/flutter/test/material/app_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,7 @@ void main() {

testWidgetsWithLeakTracking('MaterialApp does create HeroController with the MaterialRectArcTween', (WidgetTester tester) async {
final HeroController controller = MaterialApp.createMaterialHeroController();
addTearDown(controller.dispose);
final Tween<Rect?> tween = controller.createRectTween!(
const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
const Rect.fromLTRB(0.0, 0.0, 20.0, 20.0),
Expand Down
19 changes: 17 additions & 2 deletions packages/flutter/test/widgets/heroes_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -304,9 +304,12 @@ Future<void> main() async {
testWidgetsWithLeakTracking('Heroes still animate after hero controller is swapped.', (WidgetTester tester) async {
final GlobalKey<NavigatorState> key = GlobalKey<NavigatorState>();
final UniqueKey heroKey = UniqueKey();
final HeroController controller1 = HeroController();
addTearDown(controller1.dispose);

await tester.pumpWidget(
HeroControllerScope(
controller: HeroController(),
controller: controller1,
child: TestDependencies(
child: Navigator(
key: key,
Expand Down Expand Up @@ -352,15 +355,19 @@ Future<void> main() async {
);
},
));

expect(find.byKey(heroKey), findsNothing);
// Begins the navigation
await tester.pump();
await tester.pump(const Duration(milliseconds: 30));
expect(find.byKey(heroKey), isOnstage);
final HeroController controller2 = HeroController();
addTearDown(controller2.dispose);

// Pumps a new hero controller.
await tester.pumpWidget(
HeroControllerScope(
controller: HeroController(),
controller: controller2,
child: TestDependencies(
child: Navigator(
key: key,
Expand Down Expand Up @@ -389,6 +396,7 @@ Future<void> main() async {
),
),
);

// The original animation still flies.
expect(find.byKey(heroKey), isOnstage);
// Waits for the animation finishes.
Expand Down Expand Up @@ -3135,6 +3143,13 @@ Future<void> main() async {
await tester.pumpAndSettle();
expect(tester.getTopLeft(find.byType(Image)).dy, moreOrLessEquals(forwardRest, epsilon: 0.1));
});

test('HeroController dispatches memory events', () async {
await expectLater(
await memoryEvents(() => HeroController().dispose(), HeroController),
areCreateAndDispose,
);
});
}

class TestDependencies extends StatelessWidget {
Expand Down
11 changes: 11 additions & 0 deletions packages/flutter/test/widgets/navigator_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2367,6 +2367,8 @@ void main() {
),
);
};
addTearDown(spy.dispose);

await tester.pumpWidget(
HeroControllerScope(
controller: spy,
Expand Down Expand Up @@ -2437,6 +2439,8 @@ void main() {
),
);
};
addTearDown(spy.dispose);

await tester.pumpWidget(
HeroControllerScope(
controller: spy,
Expand Down Expand Up @@ -2506,6 +2510,7 @@ void main() {
),
);
};
addTearDown(spy1.dispose);
final List<NavigatorObservation> observations2 = <NavigatorObservation>[];
final HeroControllerSpy spy2 = HeroControllerSpy()
..onPushed = (Route<dynamic>? route, Route<dynamic>? previousRoute) {
Expand All @@ -2517,6 +2522,8 @@ void main() {
),
);
};
addTearDown(spy2.dispose);

await tester.pumpWidget(
TestDependencies(
child: Stack(
Expand Down Expand Up @@ -2633,6 +2640,8 @@ void main() {

testWidgetsWithLeakTracking('hero controller subscribes to multiple navigators does throw', (WidgetTester tester) async {
final HeroControllerSpy spy = HeroControllerSpy();
addTearDown(spy.dispose);

await tester.pumpWidget(
HeroControllerScope(
controller: spy,
Expand Down Expand Up @@ -2671,6 +2680,8 @@ void main() {

testWidgetsWithLeakTracking('hero controller throws has correct error message', (WidgetTester tester) async {
final HeroControllerSpy spy = HeroControllerSpy();
addTearDown(spy.dispose);

await tester.pumpWidget(
HeroControllerScope(
controller: spy,
Expand Down

0 comments on commit e8d9d9b

Please sign in to comment.