diff --git a/packages/flutter/lib/src/material/time_picker.dart b/packages/flutter/lib/src/material/time_picker.dart index 52d176fa313..03dcaa37e46 100644 --- a/packages/flutter/lib/src/material/time_picker.dart +++ b/packages/flutter/lib/src/material/time_picker.dart @@ -1630,6 +1630,14 @@ class _TimePickerInputState extends State<_TimePickerInput> with RestorationMixi final RestorableBool hourHasError = RestorableBool(false); final RestorableBool minuteHasError = RestorableBool(false); + @override + void dispose() { + _selectedTime.dispose(); + hourHasError.dispose(); + minuteHasError.dispose(); + super.dispose(); + } + @override String? get restorationId => widget.restorationId; @@ -1988,6 +1996,14 @@ class _HourMinuteTextFieldState extends State<_HourMinuteTextField> with Restora } } + @override + void dispose() { + controller.dispose(); + controllerHasBeenSet.dispose(); + focusNode.dispose(); + super.dispose(); + } + @override String? get restorationId => widget.restorationId; diff --git a/packages/flutter/test/material/time_picker_test.dart b/packages/flutter/test/material/time_picker_test.dart index cb875d6fe4f..d313ab452bb 100644 --- a/packages/flutter/test/material/time_picker_test.dart +++ b/packages/flutter/test/material/time_picker_test.dart @@ -66,7 +66,7 @@ void main() { ); }); - 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 @@ void main() { ); }); - 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 labels00To22 = List.generate(12, (int index) { @@ -121,7 +121,7 @@ void main() { expect(selectedLabels.map((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 @@ void main() { ); }); - 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 @@ void main() { ); }); - 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 labels00To23 = List.generate(24, (int index) { @@ -226,7 +226,7 @@ void main() { expect(selectedLabels.map((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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { }); 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 @@ void main() { 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 @@ void main() { 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 labels12To11 = ['12', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11']; @@ -582,7 +582,7 @@ void main() { expect(selectedLabels.map((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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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: [rootObserver], @@ -709,7 +709,7 @@ void main() { 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: [rootObserver], @@ -745,7 +745,7 @@ void main() { }); }); - testWidgets('Barrier color', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Barrier color', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: Material( @@ -801,7 +801,7 @@ void main() { expect(tester.widget(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 @@ void main() { expect(tester.widget(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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { }); 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { group('Works for various view sizes', () { for (final Size size in const [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 @@ void main() { 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 @@ void main() { }); 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 actAndExpect({ @@ -1442,7 +1442,7 @@ void main() { 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 actAndExpect({ @@ -1499,7 +1499,7 @@ void main() { 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 @@ void main() { }); 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { }); // 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 @@ void main() { 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 @@ void main() { }); 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 @@ void main() { 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 @@ void main() { 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 @@ class _TimePickerLauncherState extends State<_TimePickerLauncher> with Restorati }, ); + @override + void dispose() { + _restorableTimePickerRouteFuture.dispose(); + super.dispose(); + } + @pragma('vm:entry-point') static Route _timePickerRoute( BuildContext context,