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

HeroController should dispatch creation and disposal events. (#137835)

parent 0e7ed929
...@@ -156,6 +156,12 @@ class _CupertinoTabViewState extends State<CupertinoTabView> { ...@@ -156,6 +156,12 @@ class _CupertinoTabViewState extends State<CupertinoTabView> {
} }
} }
@override
void dispose() {
_heroController.dispose();
super.dispose();
}
void _updateObservers() { void _updateObservers() {
_navigatorObservers = _navigatorObservers =
List<NavigatorObserver>.of(widget.navigatorObservers) List<NavigatorObserver>.of(widget.navigatorObservers)
......
...@@ -784,7 +784,17 @@ class HeroController extends NavigatorObserver { ...@@ -784,7 +784,17 @@ class HeroController extends NavigatorObserver {
/// ///
/// The [createRectTween] argument is optional. If null, the controller uses a /// The [createRectTween] argument is optional. If null, the controller uses a
/// linear [Tween<Rect>]. /// 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. /// Used to create [RectTween]s that interpolate the position of heroes in flight.
/// ///
...@@ -1043,6 +1053,12 @@ class HeroController extends NavigatorObserver { ...@@ -1043,6 +1053,12 @@ class HeroController extends NavigatorObserver {
/// Releases resources. /// Releases resources.
@mustCallSuper @mustCallSuper
void dispose() { 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) { for (final _HeroFlight flight in _flights.values) {
flight.dispose(); flight.dispose();
} }
......
...@@ -1060,6 +1060,7 @@ void main() { ...@@ -1060,6 +1060,7 @@ void main() {
testWidgetsWithLeakTracking('MaterialApp does create HeroController with the MaterialRectArcTween', (WidgetTester tester) async { testWidgetsWithLeakTracking('MaterialApp does create HeroController with the MaterialRectArcTween', (WidgetTester tester) async {
final HeroController controller = MaterialApp.createMaterialHeroController(); final HeroController controller = MaterialApp.createMaterialHeroController();
addTearDown(controller.dispose);
final Tween<Rect?> tween = controller.createRectTween!( final Tween<Rect?> tween = controller.createRectTween!(
const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0), const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
const Rect.fromLTRB(0.0, 0.0, 20.0, 20.0), const Rect.fromLTRB(0.0, 0.0, 20.0, 20.0),
......
...@@ -304,9 +304,12 @@ Future<void> main() async { ...@@ -304,9 +304,12 @@ Future<void> main() async {
testWidgetsWithLeakTracking('Heroes still animate after hero controller is swapped.', (WidgetTester tester) async { testWidgetsWithLeakTracking('Heroes still animate after hero controller is swapped.', (WidgetTester tester) async {
final GlobalKey<NavigatorState> key = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> key = GlobalKey<NavigatorState>();
final UniqueKey heroKey = UniqueKey(); final UniqueKey heroKey = UniqueKey();
final HeroController controller1 = HeroController();
addTearDown(controller1.dispose);
await tester.pumpWidget( await tester.pumpWidget(
HeroControllerScope( HeroControllerScope(
controller: HeroController(), controller: controller1,
child: TestDependencies( child: TestDependencies(
child: Navigator( child: Navigator(
key: key, key: key,
...@@ -352,15 +355,19 @@ Future<void> main() async { ...@@ -352,15 +355,19 @@ Future<void> main() async {
); );
}, },
)); ));
expect(find.byKey(heroKey), findsNothing); expect(find.byKey(heroKey), findsNothing);
// Begins the navigation // Begins the navigation
await tester.pump(); await tester.pump();
await tester.pump(const Duration(milliseconds: 30)); await tester.pump(const Duration(milliseconds: 30));
expect(find.byKey(heroKey), isOnstage); expect(find.byKey(heroKey), isOnstage);
final HeroController controller2 = HeroController();
addTearDown(controller2.dispose);
// Pumps a new hero controller. // Pumps a new hero controller.
await tester.pumpWidget( await tester.pumpWidget(
HeroControllerScope( HeroControllerScope(
controller: HeroController(), controller: controller2,
child: TestDependencies( child: TestDependencies(
child: Navigator( child: Navigator(
key: key, key: key,
...@@ -389,6 +396,7 @@ Future<void> main() async { ...@@ -389,6 +396,7 @@ Future<void> main() async {
), ),
), ),
); );
// The original animation still flies. // The original animation still flies.
expect(find.byKey(heroKey), isOnstage); expect(find.byKey(heroKey), isOnstage);
// Waits for the animation finishes. // Waits for the animation finishes.
...@@ -3135,6 +3143,13 @@ Future<void> main() async { ...@@ -3135,6 +3143,13 @@ Future<void> main() async {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(tester.getTopLeft(find.byType(Image)).dy, moreOrLessEquals(forwardRest, epsilon: 0.1)); 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 { class TestDependencies extends StatelessWidget {
......
...@@ -2367,6 +2367,8 @@ void main() { ...@@ -2367,6 +2367,8 @@ void main() {
), ),
); );
}; };
addTearDown(spy.dispose);
await tester.pumpWidget( await tester.pumpWidget(
HeroControllerScope( HeroControllerScope(
controller: spy, controller: spy,
...@@ -2437,6 +2439,8 @@ void main() { ...@@ -2437,6 +2439,8 @@ void main() {
), ),
); );
}; };
addTearDown(spy.dispose);
await tester.pumpWidget( await tester.pumpWidget(
HeroControllerScope( HeroControllerScope(
controller: spy, controller: spy,
...@@ -2506,6 +2510,7 @@ void main() { ...@@ -2506,6 +2510,7 @@ void main() {
), ),
); );
}; };
addTearDown(spy1.dispose);
final List<NavigatorObservation> observations2 = <NavigatorObservation>[]; final List<NavigatorObservation> observations2 = <NavigatorObservation>[];
final HeroControllerSpy spy2 = HeroControllerSpy() final HeroControllerSpy spy2 = HeroControllerSpy()
..onPushed = (Route<dynamic>? route, Route<dynamic>? previousRoute) { ..onPushed = (Route<dynamic>? route, Route<dynamic>? previousRoute) {
...@@ -2517,6 +2522,8 @@ void main() { ...@@ -2517,6 +2522,8 @@ void main() {
), ),
); );
}; };
addTearDown(spy2.dispose);
await tester.pumpWidget( await tester.pumpWidget(
TestDependencies( TestDependencies(
child: Stack( child: Stack(
...@@ -2633,6 +2640,8 @@ void main() { ...@@ -2633,6 +2640,8 @@ void main() {
testWidgetsWithLeakTracking('hero controller subscribes to multiple navigators does throw', (WidgetTester tester) async { testWidgetsWithLeakTracking('hero controller subscribes to multiple navigators does throw', (WidgetTester tester) async {
final HeroControllerSpy spy = HeroControllerSpy(); final HeroControllerSpy spy = HeroControllerSpy();
addTearDown(spy.dispose);
await tester.pumpWidget( await tester.pumpWidget(
HeroControllerScope( HeroControllerScope(
controller: spy, controller: spy,
...@@ -2671,6 +2680,8 @@ void main() { ...@@ -2671,6 +2680,8 @@ void main() {
testWidgetsWithLeakTracking('hero controller throws has correct error message', (WidgetTester tester) async { testWidgetsWithLeakTracking('hero controller throws has correct error message', (WidgetTester tester) async {
final HeroControllerSpy spy = HeroControllerSpy(); final HeroControllerSpy spy = HeroControllerSpy();
addTearDown(spy.dispose);
await tester.pumpWidget( await tester.pumpWidget(
HeroControllerScope( HeroControllerScope(
controller: spy, controller: spy,
......
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