TimePickerDialog should dispose created ChangeNotifiers. (#136261)

diff --git a/packages/flutter/lib/src/material/time_picker.dart b/packages/flutter/lib/src/material/time_picker.dart
index 52d176f..03dcaa3 100644
--- a/packages/flutter/lib/src/material/time_picker.dart
+++ b/packages/flutter/lib/src/material/time_picker.dart
@@ -1631,6 +1631,14 @@
   final RestorableBool minuteHasError = RestorableBool(false);
 
   @override
+  void dispose() {
+    _selectedTime.dispose();
+    hourHasError.dispose();
+    minuteHasError.dispose();
+    super.dispose();
+  }
+
+  @override
   String? get restorationId => widget.restorationId;
 
   @override
@@ -1989,6 +1997,14 @@
   }
 
   @override
+  void dispose() {
+    controller.dispose();
+    controllerHasBeenSet.dispose();
+    focusNode.dispose();
+    super.dispose();
+  }
+
+  @override
   String? get restorationId => widget.restorationId;
 
   @override
diff --git a/packages/flutter/test/material/time_picker_test.dart b/packages/flutter/test/material/time_picker_test.dart
index cb875d6..d313ab4 100644
--- a/packages/flutter/test/material/time_picker_test.dart
+++ b/packages/flutter/test/material/time_picker_test.dart
@@ -66,7 +66,7 @@
     );
   });
 
-  testWidgets('Material2 - Dialog size - input mode', (WidgetTester tester) async {
+  testWidgetsWithLeakTracking('Material2 - Dialog size - input mode', (WidgetTester tester) async {
     const TimePickerEntryMode entryMode = TimePickerEntryMode.input;
     const Size timePickerInputSize = Size(312, 216);
     const Size dayPeriodPortraitSize = Size(52, 80);
@@ -102,7 +102,7 @@
     );
   });
 
-  testWidgets('Material2 - respects MediaQueryData.alwaysUse24HourFormat == true', (WidgetTester tester) async {
+  testWidgetsWithLeakTracking('Material2 - respects MediaQueryData.alwaysUse24HourFormat == true', (WidgetTester tester) async {
     await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, materialType: MaterialType.material2);
 
     final List<String> labels00To22 = List<String>.generate(12, (int index) {
@@ -121,7 +121,7 @@
     expect(selectedLabels.map<String>((dynamic tp) => tp.painter.text.text as String), labels00To22);
   });
 
-  testWidgets('Material3 - Dialog size - dial mode', (WidgetTester tester) async {
+  testWidgetsWithLeakTracking('Material3 - Dialog size - dial mode', (WidgetTester tester) async {
     addTearDown(tester.view.reset);
 
     const Size timePickerPortraitSize =  Size(310, 468);
@@ -162,7 +162,7 @@
     );
   });
 
-  testWidgets('Material3 - Dialog size - input mode', (WidgetTester tester) async {
+  testWidgetsWithLeakTracking('Material3 - Dialog size - input mode', (WidgetTester tester) async {
     final ThemeData theme = ThemeData(useMaterial3: true);
     const TimePickerEntryMode entryMode = TimePickerEntryMode.input;
     const double textScaleFactor = 1.0;
@@ -201,7 +201,7 @@
     );
   });
 
-  testWidgets('Material3 - respects MediaQueryData.alwaysUse24HourFormat == true', (WidgetTester tester) async {
+  testWidgetsWithLeakTracking('Material3 - respects MediaQueryData.alwaysUse24HourFormat == true', (WidgetTester tester) async {
     await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, materialType: MaterialType.material3);
 
     final List<String> labels00To23 = List<String>.generate(24, (int index) {
@@ -226,7 +226,7 @@
     expect(selectedLabels.map<bool>((dynamic tp) => tp.inner as bool), inner0To23);
   });
 
-  testWidgets('Material3 - Dial background uses correct default color', (WidgetTester tester) async {
+  testWidgetsWithLeakTracking('Material3 - Dial background uses correct default color', (WidgetTester tester) async {
     ThemeData theme = ThemeData(useMaterial3: true);
     Widget buildTimePicker(ThemeData themeData) {
       return MaterialApp(
@@ -292,7 +292,7 @@
 
   for (final MaterialType materialType in MaterialType.values) {
     group('Dial (${materialType.name})', () {
-      testWidgets('tap-select an hour', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('tap-select an hour', (WidgetTester tester) async {
         TimeOfDay? result;
 
         Offset center = (await startPicker(tester, (TimeOfDay? time) {
@@ -325,7 +325,7 @@
         expect(result, equals(const TimeOfDay(hour: 9, minute: 0)));
       });
 
-      testWidgets('drag-select an hour', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('drag-select an hour', (WidgetTester tester) async {
         late TimeOfDay result;
 
         final Offset center = (await startPicker(tester, (TimeOfDay? time) {
@@ -381,7 +381,7 @@
         expect(result.hour, equals(9));
       });
 
-      testWidgets('tap-select switches from hour to minute', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('tap-select switches from hour to minute', (WidgetTester tester) async {
         late TimeOfDay result;
 
         final Offset center = (await startPicker(tester, (TimeOfDay? time) {
@@ -397,7 +397,7 @@
         expect(result, equals(const TimeOfDay(hour: 6, minute: 45)));
       });
 
-      testWidgets('drag-select switches from hour to minute', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('drag-select switches from hour to minute', (WidgetTester tester) async {
         late TimeOfDay result;
 
         final Offset center = (await startPicker(tester, (TimeOfDay? time) {
@@ -418,7 +418,7 @@
         expect(result, equals(const TimeOfDay(hour: 9, minute: 15)));
       });
 
-      testWidgets('tap-select rounds down to nearest 5 minute increment', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('tap-select rounds down to nearest 5 minute increment', (WidgetTester tester) async {
         late TimeOfDay result;
 
         final Offset center = (await startPicker(tester, (TimeOfDay? time) {
@@ -434,7 +434,7 @@
         expect(result, equals(const TimeOfDay(hour: 6, minute: 45)));
       });
 
-      testWidgets('tap-select rounds up to nearest 5 minute increment', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('tap-select rounds up to nearest 5 minute increment', (WidgetTester tester) async {
         late TimeOfDay result;
 
         final Offset center = (await startPicker(tester, (TimeOfDay? time) {
@@ -464,14 +464,14 @@
         feedback.dispose();
       });
 
-      testWidgets('tap-select vibrates once', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('tap-select vibrates once', (WidgetTester tester) async {
         final Offset center = (await startPicker(tester, (TimeOfDay? time) {}, materialType: materialType))!;
         await tester.tapAt(Offset(center.dx, center.dy - 50));
         await finishPicker(tester);
         expect(feedback.hapticCount, 1);
       });
 
-      testWidgets('quick successive tap-selects vibrate once', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('quick successive tap-selects vibrate once', (WidgetTester tester) async {
         final Offset center = (await startPicker(tester, (TimeOfDay? time) {}, materialType: materialType))!;
         await tester.tapAt(Offset(center.dx, center.dy - 50));
         await tester.pump(kFastFeedbackInterval);
@@ -480,7 +480,7 @@
         expect(feedback.hapticCount, 1);
       });
 
-      testWidgets('slow successive tap-selects vibrate once per tap', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('slow successive tap-selects vibrate once per tap', (WidgetTester tester) async {
         final Offset center = (await startPicker(tester, (TimeOfDay? time) {}, materialType: materialType))!;
         await tester.tapAt(Offset(center.dx, center.dy - 50));
         await tester.pump(kSlowFeedbackInterval);
@@ -491,7 +491,7 @@
         expect(feedback.hapticCount, 3);
       });
 
-      testWidgets('drag-select vibrates once', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('drag-select vibrates once', (WidgetTester tester) async {
         final Offset center = (await startPicker(tester, (TimeOfDay? time) {}, materialType: materialType))!;
         final Offset hour0 = Offset(center.dx, center.dy - 50);
         final Offset hour3 = Offset(center.dx + 50, center.dy);
@@ -503,7 +503,7 @@
         expect(feedback.hapticCount, 1);
       });
 
-      testWidgets('quick drag-select vibrates once', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('quick drag-select vibrates once', (WidgetTester tester) async {
         final Offset center = (await startPicker(tester, (TimeOfDay? time) {}, materialType: materialType))!;
         final Offset hour0 = Offset(center.dx, center.dy - 50);
         final Offset hour3 = Offset(center.dx + 50, center.dy);
@@ -519,7 +519,7 @@
         expect(feedback.hapticCount, 1);
       });
 
-      testWidgets('slow drag-select vibrates once', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('slow drag-select vibrates once', (WidgetTester tester) async {
         final Offset center = (await startPicker(tester, (TimeOfDay? time) {}, materialType: materialType))!;
         final Offset hour0 = Offset(center.dx, center.dy - 50);
         final Offset hour3 = Offset(center.dx + 50, center.dy);
@@ -537,19 +537,19 @@
     });
 
     group('Dialog (${materialType.name})', () {
-      testWidgets('Material2 - Widgets have correct label capitalization', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Material2 - Widgets have correct label capitalization', (WidgetTester tester) async {
         await startPicker(tester, (TimeOfDay? time) {}, materialType: MaterialType.material2);
         expect(find.text('SELECT TIME'), findsOneWidget);
         expect(find.text('CANCEL'), findsOneWidget);
       });
 
-      testWidgets('Material3 - Widgets have correct label capitalization', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Material3 - Widgets have correct label capitalization', (WidgetTester tester) async {
         await startPicker(tester, (TimeOfDay? time) {}, materialType: MaterialType.material3);
         expect(find.text('Select time'), findsOneWidget);
         expect(find.text('Cancel'), findsOneWidget);
       });
 
-      testWidgets('Material2 - Widgets have correct label capitalization in input mode', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Material2 - Widgets have correct label capitalization in input mode', (WidgetTester tester) async {
         await startPicker(tester, (TimeOfDay? time) {},
           entryMode: TimePickerEntryMode.input, materialType: MaterialType.material2
         );
@@ -557,7 +557,7 @@
         expect(find.text('CANCEL'), findsOneWidget);
       });
 
-      testWidgets('Material3 - Widgets have correct label capitalization in input mode', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Material3 - Widgets have correct label capitalization in input mode', (WidgetTester tester) async {
         await startPicker(tester, (TimeOfDay? time) {},
           entryMode: TimePickerEntryMode.input, materialType: MaterialType.material3
         );
@@ -565,7 +565,7 @@
         expect(find.text('Cancel'), findsOneWidget);
       });
 
-      testWidgets('respects MediaQueryData.alwaysUse24HourFormat == false', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('respects MediaQueryData.alwaysUse24HourFormat == false', (WidgetTester tester) async {
         await mediaQueryBoilerplate(tester, materialType: materialType);
         const List<String> labels12To11 = <String>['12', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'];
 
@@ -582,7 +582,7 @@
         expect(selectedLabels.map<String>((dynamic tp) => tp.painter.text.text as String), labels12To11);
       });
 
-      testWidgets('when change orientation, should reflect in render objects', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('when change orientation, should reflect in render objects', (WidgetTester tester) async {
         addTearDown(tester.view.reset);
 
         // portrait
@@ -606,7 +606,7 @@
         expect((render as dynamic).orientation, Orientation.landscape); // ignore: avoid_dynamic_calls
       });
 
-      testWidgets('setting orientation should override MediaQuery orientation', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('setting orientation should override MediaQuery orientation', (WidgetTester tester) async {
         addTearDown(tester.view.reset);
 
         // portrait media query
@@ -620,7 +620,7 @@
         expect((render as dynamic).orientation, Orientation.landscape); // ignore: avoid_dynamic_calls
       });
 
-      testWidgets('builder parameter', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('builder parameter', (WidgetTester tester) async {
         Widget buildFrame(TextDirection textDirection) {
           return MaterialApp(
             home: Material(
@@ -675,7 +675,7 @@
           rootObserver = PickerObserver();
         });
 
-        testWidgets('Barrier is dismissible with default parameter', (WidgetTester tester) async {
+        testWidgetsWithLeakTracking('Barrier is dismissible with default parameter', (WidgetTester tester) async {
           await tester.pumpWidget(
             MaterialApp(
               navigatorObservers: <NavigatorObserver>[rootObserver],
@@ -709,7 +709,7 @@
           expect(rootObserver.pickerCount, 0);
         });
 
-        testWidgets('Barrier is not dismissible with barrierDismissible is false', (WidgetTester tester) async {
+        testWidgetsWithLeakTracking('Barrier is not dismissible with barrierDismissible is false', (WidgetTester tester) async {
           await tester.pumpWidget(
             MaterialApp(
               navigatorObservers: <NavigatorObserver>[rootObserver],
@@ -745,7 +745,7 @@
         });
       });
 
-      testWidgets('Barrier color', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Barrier color', (WidgetTester tester) async {
         await tester.pumpWidget(
           MaterialApp(
             home: Material(
@@ -801,7 +801,7 @@
         expect(tester.widget<ModalBarrier>(find.byType(ModalBarrier).last).color, Colors.pink);
       });
 
-      testWidgets('Barrier Label', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Barrier Label', (WidgetTester tester) async {
         await tester.pumpWidget(
           MaterialApp(
             home: Material(
@@ -829,7 +829,7 @@
         expect(tester.widget<ModalBarrier>(find.byType(ModalBarrier).last).semanticsLabel, 'Custom Label');
       });
 
-      testWidgets('uses root navigator by default', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('uses root navigator by default', (WidgetTester tester) async {
         final PickerObserver rootObserver = PickerObserver();
         final PickerObserver nestedObserver = PickerObserver();
 
@@ -862,7 +862,7 @@
         expect(nestedObserver.pickerCount, 0);
       });
 
-      testWidgets('uses nested navigator if useRootNavigator is false', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('uses nested navigator if useRootNavigator is false', (WidgetTester tester) async {
         final PickerObserver rootObserver = PickerObserver();
         final PickerObserver nestedObserver = PickerObserver();
 
@@ -896,7 +896,7 @@
         expect(nestedObserver.pickerCount, 1);
       });
 
-      testWidgets('optional text parameters are utilized', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('optional text parameters are utilized', (WidgetTester tester) async {
         const String cancelText = 'Custom Cancel';
         const String confirmText = 'Custom OK';
         const String helperText = 'Custom Help';
@@ -932,7 +932,7 @@
         expect(find.text(helperText), findsOneWidget);
       });
 
-      testWidgets('Material2 - OK Cancel button and helpText layout', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Material2 - OK Cancel button and helpText layout', (WidgetTester tester) async {
         const String selectTimeString = 'SELECT TIME';
         const String cancelString = 'CANCEL';
         Widget buildFrame(TextDirection textDirection) {
@@ -995,7 +995,7 @@
         await tester.pumpAndSettle();
       });
 
-      testWidgets('Material3 - OK Cancel button and helpText layout', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Material3 - OK Cancel button and helpText layout', (WidgetTester tester) async {
         const String selectTimeString = 'Select time';
         const String cancelString = 'Cancel';
         Widget buildFrame(TextDirection textDirection) {
@@ -1068,7 +1068,7 @@
         await tester.pumpAndSettle();
       });
 
-      testWidgets('text scale affects certain elements and not others', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('text scale affects certain elements and not others', (WidgetTester tester) async {
         await mediaQueryBoilerplate(
           tester,
           initialTime: const TimeOfDay(hour: 7, minute: 41),
@@ -1109,7 +1109,7 @@
       });
 
       group('showTimePicker avoids overlapping display features', () {
-        testWidgets('positioning with anchorPoint', (WidgetTester tester) async {
+        testWidgetsWithLeakTracking('positioning with anchorPoint', (WidgetTester tester) async {
           await tester.pumpWidget(
             MaterialApp(
               builder: (BuildContext context, Widget? child) {
@@ -1145,7 +1145,7 @@
           expect(tester.getBottomRight(find.byType(TimePickerDialog)), const Offset(800, 600));
         });
 
-        testWidgets('positioning with Directionality', (WidgetTester tester) async {
+        testWidgetsWithLeakTracking('positioning with Directionality', (WidgetTester tester) async {
           await tester.pumpWidget(
             MaterialApp(
               builder: (BuildContext context, Widget? child) {
@@ -1183,7 +1183,7 @@
           expect(tester.getBottomRight(find.byType(TimePickerDialog)), const Offset(800, 600));
         });
 
-        testWidgets('positioning with defaults', (WidgetTester tester) async {
+        testWidgetsWithLeakTracking('positioning with defaults', (WidgetTester tester) async {
           await tester.pumpWidget(
             MaterialApp(
               builder: (BuildContext context, Widget? child) {
@@ -1221,7 +1221,7 @@
 
       group('Works for various view sizes', () {
         for (final Size size in const <Size>[Size(100, 100), Size(300, 300), Size(800, 600)]) {
-          testWidgets('Draws dial without overflows at $size', (WidgetTester tester) async {
+          testWidgetsWithLeakTracking('Draws dial without overflows at $size', (WidgetTester tester) async {
             tester.view.physicalSize = size;
             addTearDown(tester.view.reset);
 
@@ -1230,7 +1230,7 @@
             expect(tester.takeException(), isNot(throwsAssertionError));
           });
 
-          testWidgets('Draws input without overflows at $size', (WidgetTester tester) async {
+          testWidgetsWithLeakTracking('Draws input without overflows at $size', (WidgetTester tester) async {
             tester.view.physicalSize = size;
             addTearDown(tester.view.reset);
 
@@ -1243,7 +1243,7 @@
     });
 
     group('Time picker - A11y and Semantics (${materialType.name})', () {
-      testWidgets('provides semantics information for AM/PM indicator', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('provides semantics information for AM/PM indicator', (WidgetTester tester) async {
         final SemanticsTester semantics = SemanticsTester(tester);
         await mediaQueryBoilerplate(tester, materialType: materialType);
 
@@ -1278,7 +1278,7 @@
         semantics.dispose();
       });
 
-      testWidgets('Material2 - provides semantics information for header and footer', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Material2 - provides semantics information for header and footer', (WidgetTester tester) async {
         final SemanticsTester semantics = SemanticsTester(tester);
         await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, materialType: MaterialType.material2);
 
@@ -1303,7 +1303,7 @@
         semantics.dispose();
       });
 
-      testWidgets('Material3 - provides semantics information for header and footer', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Material3 - provides semantics information for header and footer', (WidgetTester tester) async {
         final SemanticsTester semantics = SemanticsTester(tester);
         await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, materialType: MaterialType.material3);
 
@@ -1328,7 +1328,7 @@
         semantics.dispose();
       });
 
-      testWidgets('provides semantics information for text fields', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('provides semantics information for text fields', (WidgetTester tester) async {
         final SemanticsTester semantics = SemanticsTester(tester);
         await mediaQueryBoilerplate(
           tester,
@@ -1360,7 +1360,7 @@
         semantics.dispose();
       });
 
-      testWidgets('can increment and decrement hours', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('can increment and decrement hours', (WidgetTester tester) async {
         final SemanticsTester semantics = SemanticsTester(tester);
 
         Future<void> actAndExpect({
@@ -1442,7 +1442,7 @@
         semantics.dispose();
       });
 
-      testWidgets('can increment and decrement minutes', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('can increment and decrement minutes', (WidgetTester tester) async {
         final SemanticsTester semantics = SemanticsTester(tester);
 
         Future<void> actAndExpect({
@@ -1499,7 +1499,7 @@
         semantics.dispose();
       });
 
-      testWidgets('header touch regions are large enough', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('header touch regions are large enough', (WidgetTester tester) async {
         // Ensure picker is displayed in portrait mode.
         tester.view.physicalSize = const Size(400, 800);
         tester.view.devicePixelRatio = 1;
@@ -1530,7 +1530,7 @@
     });
 
     group('Time picker - Input (${materialType.name})', () {
-      testWidgets('Initial entry mode is used', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Initial entry mode is used', (WidgetTester tester) async {
         await mediaQueryBoilerplate(
           tester,
           alwaysUse24HourFormat: true,
@@ -1540,7 +1540,7 @@
         expect(find.byType(TextField), findsNWidgets(2));
       });
 
-      testWidgets('Initial time is the default', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Initial time is the default', (WidgetTester tester) async {
         late TimeOfDay result;
         await startPicker(tester, (TimeOfDay? time) {
           result = time!;
@@ -1549,7 +1549,7 @@
         expect(result, equals(const TimeOfDay(hour: 7, minute: 0)));
       });
 
-      testWidgets('Help text is used - Input', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Help text is used - Input', (WidgetTester tester) async {
         const String helpText = 'help';
         await mediaQueryBoilerplate(
           tester,
@@ -1561,7 +1561,7 @@
         expect(find.text(helpText), findsOneWidget);
       });
 
-      testWidgets('Help text is used in Material3 - Input', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Help text is used in Material3 - Input', (WidgetTester tester) async {
         const String helpText = 'help';
         await mediaQueryBoilerplate(
           tester,
@@ -1573,7 +1573,7 @@
         expect(find.text(helpText), findsOneWidget);
       });
 
-      testWidgets('Hour label text is used - Input', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Hour label text is used - Input', (WidgetTester tester) async {
         const String hourLabelText = 'Custom hour label';
         await mediaQueryBoilerplate(
           tester,
@@ -1585,7 +1585,7 @@
         expect(find.text(hourLabelText), findsOneWidget);
       });
 
-      testWidgets('Minute label text is used - Input', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Minute label text is used - Input', (WidgetTester tester) async {
         const String minuteLabelText = 'Custom minute label';
         await mediaQueryBoilerplate(
           tester,
@@ -1597,7 +1597,7 @@
         expect(find.text(minuteLabelText), findsOneWidget);
       });
 
-      testWidgets('Invalid error text is used - Input', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Invalid error text is used - Input', (WidgetTester tester) async {
         const String errorInvalidText = 'Custom validation error';
         await mediaQueryBoilerplate(
           tester,
@@ -1617,7 +1617,7 @@
         expect(find.text(errorInvalidText), findsOneWidget);
       });
 
-      testWidgets('Can switch from input to dial entry mode', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Can switch from input to dial entry mode', (WidgetTester tester) async {
         await mediaQueryBoilerplate(
           tester,
           alwaysUse24HourFormat: true,
@@ -1629,14 +1629,14 @@
         expect(find.byType(TextField), findsNothing);
       });
 
-      testWidgets('Can switch from dial to input entry mode', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Can switch from dial to input entry mode', (WidgetTester tester) async {
         await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, materialType: materialType);
         await tester.tap(find.byIcon(Icons.keyboard_outlined));
         await tester.pumpAndSettle();
         expect(find.byType(TextField), findsWidgets);
       });
 
-      testWidgets('Can not switch out of inputOnly mode', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Can not switch out of inputOnly mode', (WidgetTester tester) async {
         await mediaQueryBoilerplate(
           tester,
           alwaysUse24HourFormat: true,
@@ -1647,7 +1647,7 @@
         expect(find.byIcon(Icons.access_time), findsNothing);
       });
 
-      testWidgets('Can not switch out of dialOnly mode', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Can not switch out of dialOnly mode', (WidgetTester tester) async {
         await mediaQueryBoilerplate(
           tester,
           alwaysUse24HourFormat: true,
@@ -1658,7 +1658,7 @@
         expect(find.byIcon(Icons.keyboard_outlined), findsNothing);
       });
 
-      testWidgets('Switching to dial entry mode triggers entry callback', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Switching to dial entry mode triggers entry callback', (WidgetTester tester) async {
         bool triggeredCallback = false;
 
         await mediaQueryBoilerplate(
@@ -1678,7 +1678,7 @@
         expect(triggeredCallback, true);
       });
 
-      testWidgets('Switching to input entry mode triggers entry callback', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Switching to input entry mode triggers entry callback', (WidgetTester tester) async {
         bool triggeredCallback = false;
 
         await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, onEntryModeChange: (TimePickerEntryMode mode) {
@@ -1692,7 +1692,7 @@
         expect(triggeredCallback, true);
       });
 
-      testWidgets('Can double tap hours (when selected) to enter input mode', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Can double tap hours (when selected) to enter input mode', (WidgetTester tester) async {
         await mediaQueryBoilerplate(tester, materialType: materialType);
         final Finder hourFinder = find.ancestor(
           of: find.text('7'),
@@ -1710,7 +1710,7 @@
         expect(find.byType(TextField), findsWidgets);
       });
 
-      testWidgets('Can not double tap hours (when not selected) to enter input mode', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Can not double tap hours (when not selected) to enter input mode', (WidgetTester tester) async {
         await mediaQueryBoilerplate(tester, materialType: materialType);
         final Finder hourFinder = find.ancestor(
           of: find.text('7'),
@@ -1736,7 +1736,7 @@
         expect(find.byType(TextField), findsNothing);
       });
 
-      testWidgets('Can double tap minutes (when selected) to enter input mode', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Can double tap minutes (when selected) to enter input mode', (WidgetTester tester) async {
         await mediaQueryBoilerplate(tester, materialType: materialType);
         final Finder minuteFinder = find.ancestor(
           of: find.text('00'),
@@ -1758,7 +1758,7 @@
         expect(find.byType(TextField), findsWidgets);
       });
 
-      testWidgets('Can not double tap minutes (when not selected) to enter input mode', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Can not double tap minutes (when not selected) to enter input mode', (WidgetTester tester) async {
         await mediaQueryBoilerplate(tester, materialType: materialType);
         final Finder minuteFinder = find.ancestor(
           of: find.text('00'),
@@ -1776,7 +1776,7 @@
         expect(find.byType(TextField), findsNothing);
       });
 
-      testWidgets('Entered text returns time', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Entered text returns time', (WidgetTester tester) async {
         late TimeOfDay result;
         await startPicker(tester, (TimeOfDay? time) {
           result = time!;
@@ -1787,7 +1787,7 @@
         expect(result, equals(const TimeOfDay(hour: 9, minute: 12)));
       });
 
-      testWidgets('Toggle to dial mode keeps selected time', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Toggle to dial mode keeps selected time', (WidgetTester tester) async {
         late TimeOfDay result;
         await startPicker(tester, (TimeOfDay? time) {
           result = time!;
@@ -1799,7 +1799,7 @@
         expect(result, equals(const TimeOfDay(hour: 8, minute: 15)));
       });
 
-      testWidgets('Invalid text prevents dismissing', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Invalid text prevents dismissing', (WidgetTester tester) async {
         TimeOfDay? result;
         await startPicker(tester, (TimeOfDay? time) {
           result = time;
@@ -1824,7 +1824,7 @@
       });
 
       // Fixes regression that was reverted in https://github.com/flutter/flutter/pull/64094#pullrequestreview-469836378.
-      testWidgets('Ensure hour/minute fields are top-aligned with the separator', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Ensure hour/minute fields are top-aligned with the separator', (WidgetTester tester) async {
         await startPicker(tester, (TimeOfDay? time) {},
             entryMode: TimePickerEntryMode.input, materialType: materialType);
         final double hourFieldTop =
@@ -1837,7 +1837,7 @@
         expect(minuteFieldTop, separatorTop);
       });
 
-      testWidgets('Can switch between hour/minute fields using keyboard input action', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Can switch between hour/minute fields using keyboard input action', (WidgetTester tester) async {
         await startPicker(tester, (TimeOfDay? time) {},
             entryMode: TimePickerEntryMode.input, materialType: materialType);
 
@@ -1860,7 +1860,7 @@
     });
 
     group('Time picker - Restoration (${materialType.name})', () {
-      testWidgets('Time Picker state restoration test - dial mode', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Time Picker state restoration test - dial mode', (WidgetTester tester) async {
         TimeOfDay? result;
         final Offset center = (await startPicker(
           tester,
@@ -1893,7 +1893,7 @@
         expect(result, equals(const TimeOfDay(hour: 6, minute: 45)));
       });
 
-      testWidgets('Time Picker state restoration test - input mode', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Time Picker state restoration test - input mode', (WidgetTester tester) async {
         TimeOfDay? result;
         await startPicker(
           tester,
@@ -1927,7 +1927,7 @@
         expect(result, equals(const TimeOfDay(hour: 9, minute: 12)));
       });
 
-      testWidgets('Time Picker state restoration test - switching modes', (WidgetTester tester) async {
+      testWidgetsWithLeakTracking('Time Picker state restoration test - switching modes', (WidgetTester tester) async {
         TimeOfDay? result;
         final Offset center = (await startPicker(
           tester,
@@ -2099,6 +2099,12 @@
     },
   );
 
+  @override
+  void dispose() {
+    _restorableTimePickerRouteFuture.dispose();
+    super.dispose();
+  }
+
   @pragma('vm:entry-point')
   static Route<TimeOfDay> _timePickerRoute(
     BuildContext context,