diff --git a/packages/flutter/lib/src/widgets/navigator.dart b/packages/flutter/lib/src/widgets/navigator.dart index e93ab22af4e6..ccf3fbfb56ad 100644 --- a/packages/flutter/lib/src/widgets/navigator.dart +++ b/packages/flutter/lib/src/widgets/navigator.dart @@ -140,7 +140,15 @@ abstract class Route { /// /// If the [settings] are not provided, an empty [RouteSettings] object is /// used instead. - Route({ RouteSettings? settings }) : _settings = settings ?? const RouteSettings(); + Route({ RouteSettings? settings }) : _settings = settings ?? const RouteSettings() { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectCreated( + library: 'package:flutter/widgets.dart', + className: '$Route<$T>', + object: this, + ); + } + } /// The navigator that the route is in, if any. NavigatorState? get navigator => _navigator; @@ -503,6 +511,9 @@ abstract class Route { void dispose() { _navigator = null; _restorationScopeId.dispose(); + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectDisposed(object: this); + } } /// Whether this route is the top-most route on the navigator. diff --git a/packages/flutter/test/material/app_test.dart b/packages/flutter/test/material/app_test.dart index 14d7f270e0e8..16bbac3557fc 100644 --- a/packages/flutter/test/material/app_test.dart +++ b/packages/flutter/test/material/app_test.dart @@ -338,7 +338,10 @@ void main() { // TODO(polina-c): remove after fixing // https://github.com/flutter/flutter/issues/133695 leakTrackingTestConfig: const LeakTrackingTestConfig( - notDisposedAllowList: {'ValueNotifier': 3}, + notDisposedAllowList: { + 'ValueNotifier': 3, + 'MaterialPageRoute': 3, + }, )); testWidgetsWithLeakTracking('Make sure initialRoute is only used the first time', (WidgetTester tester) async { diff --git a/packages/flutter/test/widgets/navigator_test.dart b/packages/flutter/test/widgets/navigator_test.dart index 52cd934893d7..886de4a71269 100644 --- a/packages/flutter/test/widgets/navigator_test.dart +++ b/packages/flutter/test/widgets/navigator_test.dart @@ -10,6 +10,7 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; import 'navigator_utils.dart'; import 'observer_tester.dart'; @@ -627,6 +628,41 @@ void main() { expect(observations[2].previous, '/A'); }); + testWidgetsWithLeakTracking('$Route dispatches memory events', (WidgetTester tester) async { + Future createAndDisposeRoute() async { + final GlobalKey nav = GlobalKey(); + await tester.pumpWidget( + MaterialApp( + navigatorKey: nav, + home: const Scaffold( + body: Text('home'), + ) + ) + ); + + nav.currentState!.push(MaterialPageRoute(builder: (_) => const Placeholder())); // This should create a route + await tester.pumpAndSettle(); + + nav.currentState!.pop(); + await tester.pumpAndSettle(); // this should dispose the route. + } + + final List events = []; + void listener(ObjectEvent event) { + if (event.object.runtimeType == MaterialPageRoute) { + events.add(event); + } + } + MemoryAllocations.instance.addListener(listener); + + await createAndDisposeRoute(); + expect(events, hasLength(2)); + expect(events.first, isA()); + expect(events.last, isA()); + + MemoryAllocations.instance.removeListener(listener); + }); + testWidgets('Route didAdd and dispose in same frame work', (WidgetTester tester) async { // Regression Test for https://github.com/flutter/flutter/issues/61346. Widget buildNavigator() {