From 72b69f944915e8aae2af6a5475d4d6d4f4a4e4af Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Fri, 15 Sep 2023 08:55:50 -0700 Subject: [PATCH] Date picker dialog state should dispose members. (#134804) --- .../flutter/lib/src/material/date_picker.dart | 8 + .../test/material/date_picker_test.dart | 182 +++++++++--------- 2 files changed, 103 insertions(+), 87 deletions(-) diff --git a/packages/flutter/lib/src/material/date_picker.dart b/packages/flutter/lib/src/material/date_picker.dart index 88614ec1bdfad..f1b86a3cf3e08 100644 --- a/packages/flutter/lib/src/material/date_picker.dart +++ b/packages/flutter/lib/src/material/date_picker.dart @@ -416,6 +416,14 @@ class _DatePickerDialogState extends State with RestorationMix late final _RestorableDatePickerEntryMode _entryMode = _RestorableDatePickerEntryMode(widget.initialEntryMode); final _RestorableAutovalidateMode _autovalidateMode = _RestorableAutovalidateMode(AutovalidateMode.disabled); + @override + void dispose() { + _selectedDate.dispose(); + _entryMode.dispose(); + _autovalidateMode.dispose(); + super.dispose(); + } + @override String? get restorationId => widget.restorationId; diff --git a/packages/flutter/test/material/date_picker_test.dart b/packages/flutter/test/material/date_picker_test.dart index 66cddc20c9138..10c2a74f602e5 100644 --- a/packages/flutter/test/material/date_picker_test.dart +++ b/packages/flutter/test/material/date_picker_test.dart @@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -122,7 +123,7 @@ void main() { } group('showDatePicker Dialog', () { - testWidgets('Default dialog size', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Default dialog size', (WidgetTester tester) async { Future showPicker(WidgetTester tester, Size size) async { tester.view.physicalSize = size; tester.view.devicePixelRatio = 1.0; @@ -149,7 +150,7 @@ void main() { expect(dialogContainerSize, calendarPortraitDialogSizeM3); }); - testWidgets('Default dialog properties', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Default dialog properties', (WidgetTester tester) async { final ThemeData theme = ThemeData(useMaterial3: true); await prepareDatePicker(tester, (Future date) async { final Material dialogMaterial = tester.widget( @@ -172,13 +173,13 @@ void main() { }, useMaterial3: theme.useMaterial3); }); - testWidgets('Material3 uses sentence case labels', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Material3 uses sentence case labels', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { expect(find.text('Select date'), findsOneWidget); }, useMaterial3: true); }); - testWidgets('Cancel, confirm, and help text is used', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Cancel, confirm, and help text is used', (WidgetTester tester) async { cancelText = 'nope'; confirmText = 'yep'; helpText = 'help'; @@ -189,21 +190,21 @@ void main() { }); }); - testWidgets('Initial date is the default', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Initial date is the default', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { await tester.tap(find.text('OK')); expect(await date, DateTime(2016, DateTime.january, 15)); }); }); - testWidgets('Can cancel', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can cancel', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { await tester.tap(find.text('CANCEL')); expect(await date, isNull); }); }); - testWidgets('Can switch from calendar to input entry mode', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can switch from calendar to input entry mode', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { expect(find.byType(TextField), findsNothing); await tester.tap(find.byIcon(Icons.edit)); @@ -212,7 +213,7 @@ void main() { }); }); - testWidgets('Can switch from input to calendar entry mode', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can switch from input to calendar entry mode', (WidgetTester tester) async { initialEntryMode = DatePickerEntryMode.input; await prepareDatePicker(tester, (Future date) async { expect(find.byType(TextField), findsOneWidget); @@ -222,7 +223,7 @@ void main() { }); }); - testWidgets('Can not switch out of calendarOnly mode', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can not switch out of calendarOnly mode', (WidgetTester tester) async { initialEntryMode = DatePickerEntryMode.calendarOnly; await prepareDatePicker(tester, (Future date) async { expect(find.byType(TextField), findsNothing); @@ -230,7 +231,7 @@ void main() { }); }); - testWidgets('Can not switch out of inputOnly mode', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can not switch out of inputOnly mode', (WidgetTester tester) async { initialEntryMode = DatePickerEntryMode.inputOnly; await prepareDatePicker(tester, (Future date) async { expect(find.byType(TextField), findsOneWidget); @@ -238,7 +239,7 @@ void main() { }); }); - testWidgets('Switching to input mode keeps selected date', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switching to input mode keeps selected date', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { await tester.tap(find.text('12')); await tester.tap(find.byIcon(Icons.edit)); @@ -248,7 +249,7 @@ void main() { }); }); - testWidgets('Input only mode should validate date', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Input only mode should validate date', (WidgetTester tester) async { initialEntryMode = DatePickerEntryMode.inputOnly; await prepareDatePicker(tester, (Future date) async { // Enter text input mode and type an invalid date to get error. @@ -259,7 +260,7 @@ void main() { }); }); - testWidgets('Switching to input mode resets input error state', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Switching to input mode resets input error state', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { // Enter text input mode and type an invalid date to get error. await tester.tap(find.byIcon(Icons.edit)); @@ -283,7 +284,7 @@ void main() { }); }); - testWidgets('builder parameter', (WidgetTester tester) async { + testWidgetsWithLeakTracking('builder parameter', (WidgetTester tester) async { Widget buildFrame(TextDirection textDirection) { return MaterialApp( home: Material( @@ -340,7 +341,7 @@ void main() { rootObserver = _DatePickerObserver(); }); - 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], @@ -378,7 +379,7 @@ void main() { expect(rootObserver.datePickerCount, 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], @@ -418,7 +419,7 @@ void main() { }); }); - testWidgets('Barrier color', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Barrier color', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: Material( @@ -484,7 +485,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( @@ -517,7 +518,7 @@ void main() { expect(tester.widget(find.byType(ModalBarrier).last).semanticsLabel, 'Custom Label'); }); - testWidgets('uses nested navigator if useRootNavigator is false', (WidgetTester tester) async { + testWidgetsWithLeakTracking('uses nested navigator if useRootNavigator is false', (WidgetTester tester) async { final _DatePickerObserver rootObserver = _DatePickerObserver(); final _DatePickerObserver nestedObserver = _DatePickerObserver(); @@ -554,7 +555,7 @@ void main() { expect(nestedObserver.datePickerCount, 1); }); - testWidgets('honors DialogTheme for shape and elevation', (WidgetTester tester) async { + testWidgetsWithLeakTracking('honors DialogTheme for shape and elevation', (WidgetTester tester) async { // Test that the defaults work const DialogTheme datePickerDefaultDialogTheme = DialogTheme( shape: RoundedRectangleBorder( @@ -627,7 +628,7 @@ void main() { expect(themeDialogMaterial.elevation, customDialogTheme.elevation); }); - testWidgets('OK Cancel button layout', (WidgetTester tester) async { + testWidgetsWithLeakTracking('OK Cancel button layout', (WidgetTester tester) async { Widget buildFrame(TextDirection textDirection) { return MaterialApp( theme: ThemeData(useMaterial3: false), @@ -703,7 +704,7 @@ void main() { await tester.pumpAndSettle(); }); - testWidgets('honors switchToInputEntryModeIcon', (WidgetTester tester) async { + testWidgetsWithLeakTracking('honors switchToInputEntryModeIcon', (WidgetTester tester) async { Widget buildApp({bool? useMaterial3, Icon? switchToInputEntryModeIcon}) { return MaterialApp( theme: ThemeData( @@ -759,7 +760,7 @@ void main() { await tester.pumpAndSettle(); }); - testWidgets('honors switchToCalendarEntryModeIcon', (WidgetTester tester) async { + testWidgetsWithLeakTracking('honors switchToCalendarEntryModeIcon', (WidgetTester tester) async { Widget buildApp({bool? useMaterial3, Icon? switchToCalendarEntryModeIcon}) { return MaterialApp( theme: ThemeData( @@ -818,7 +819,7 @@ void main() { }); group('Calendar mode', () { - testWidgets('Default Calendar mode layout (Landscape)', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Default Calendar mode layout (Landscape)', (WidgetTester tester) async { final Finder helpText = find.text('Select date'); final Finder headerText = find.text('Fri, Jan 15'); final Finder subHeaderText = find.text('January 2016'); @@ -917,7 +918,7 @@ void main() { expect(cancelButtonTopRight.dx, okButtonTopLeft.dx - 8); }); - testWidgets('Default Calendar mode layout (Portrait)', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Default Calendar mode layout (Portrait)', (WidgetTester tester) async { final Finder helpText = find.text('Select date'); final Finder headerText = find.text('Fri, Jan 15'); final Finder subHeaderText = find.text('January 2016'); @@ -1016,7 +1017,7 @@ void main() { expect(cancelButtonTopRight.dx, okButtonTopLeft.dx - 8); }); - testWidgets('Can select a day', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can select a day', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { await tester.tap(find.text('12')); await tester.tap(find.text('OK')); @@ -1024,7 +1025,7 @@ void main() { }); }); - testWidgets('Can select a month', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can select a month', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { await tester.tap(previousMonthIcon); await tester.pumpAndSettle(const Duration(seconds: 1)); @@ -1034,7 +1035,7 @@ void main() { }); }); - testWidgets('Can select a year', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can select a year', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { await tester.tap(find.text('January 2016')); // Switch to year mode. await tester.pump(); @@ -1044,7 +1045,7 @@ void main() { }); }); - testWidgets('Can select a day with no initial date', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can select a day with no initial date', (WidgetTester tester) async { initialDate = null; await prepareDatePicker(tester, (Future date) async { await tester.tap(find.text('12')); @@ -1053,7 +1054,7 @@ void main() { }); }); - testWidgets('Can select a month with no initial date', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can select a month with no initial date', (WidgetTester tester) async { initialDate = null; await prepareDatePicker(tester, (Future date) async { await tester.tap(previousMonthIcon); @@ -1064,7 +1065,7 @@ void main() { }); }); - testWidgets('Can select a year with no initial date', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can select a year with no initial date', (WidgetTester tester) async { initialDate = null; await prepareDatePicker(tester, (Future date) async { await tester.tap(find.text('January 2016')); // Switch to year mode. @@ -1075,7 +1076,7 @@ void main() { }); }); - testWidgets('Selecting date does not change displayed month', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Selecting date does not change displayed month', (WidgetTester tester) async { initialDate = DateTime(2020, DateTime.march, 15); await prepareDatePicker(tester, (Future date) async { await tester.tap(nextMonthIcon); @@ -1089,7 +1090,7 @@ void main() { }); }); - testWidgets('Changing year does change selected date', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Changing year does change selected date', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { await tester.tap(find.text('January 2016')); await tester.pump(); @@ -1100,7 +1101,7 @@ void main() { }); }); - testWidgets('Changing year does not change the month', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Changing year does not change the month', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { await tester.tap(nextMonthIcon); await tester.pumpAndSettle(); @@ -1114,7 +1115,7 @@ void main() { }); }); - testWidgets('Can select a year and then a day', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can select a year and then a day', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { await tester.tap(find.text('January 2016')); // Switch to year mode. await tester.pump(); @@ -1126,7 +1127,7 @@ void main() { }); }); - testWidgets('Current year is visible in year picker', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Current year is visible in year picker', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { await tester.tap(find.text('January 2016')); // Switch to year mode. await tester.pump(); @@ -1134,7 +1135,7 @@ void main() { }); }); - testWidgets('Cannot select a day outside bounds', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Cannot select a day outside bounds', (WidgetTester tester) async { initialDate = DateTime(2017, DateTime.january, 15); firstDate = initialDate!; lastDate = initialDate!; @@ -1149,7 +1150,7 @@ void main() { }); }); - testWidgets('Cannot select a month past last date', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Cannot select a month past last date', (WidgetTester tester) async { initialDate = DateTime(2017, DateTime.january, 15); firstDate = initialDate!; lastDate = DateTime(2017, DateTime.february, 20); @@ -1161,7 +1162,7 @@ void main() { }); }); - testWidgets('Cannot select a month before first date', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Cannot select a month before first date', (WidgetTester tester) async { initialDate = DateTime(2017, DateTime.january, 15); firstDate = DateTime(2016, DateTime.december, 10); lastDate = initialDate!; @@ -1173,7 +1174,7 @@ void main() { }); }); - testWidgets('Cannot select disabled year', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Cannot select disabled year', (WidgetTester tester) async { initialDate = DateTime(2018, DateTime.july, 4); firstDate = DateTime(2018, DateTime.june, 9); lastDate = DateTime(2018, DateTime.december, 15); @@ -1188,7 +1189,7 @@ void main() { }); }); - testWidgets('Selecting firstDate year respects firstDate', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Selecting firstDate year respects firstDate', (WidgetTester tester) async { initialDate = DateTime(2018, DateTime.may, 4); firstDate = DateTime(2016, DateTime.june, 9); lastDate = DateTime(2019, DateTime.january, 15); @@ -1202,7 +1203,7 @@ void main() { }); }); - testWidgets('Selecting lastDate year respects lastDate', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Selecting lastDate year respects lastDate', (WidgetTester tester) async { initialDate = DateTime(2018, DateTime.may, 4); firstDate = DateTime(2016, DateTime.june, 9); lastDate = DateTime(2019, DateTime.january, 15); @@ -1216,7 +1217,7 @@ void main() { }); }); - testWidgets('Only predicate days are selectable', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Only predicate days are selectable', (WidgetTester tester) async { initialDate = DateTime(2017, DateTime.january, 16); firstDate = DateTime(2017, DateTime.january, 10); lastDate = DateTime(2017, DateTime.january, 20); @@ -1230,7 +1231,7 @@ void main() { }); }); - testWidgets('Can select initial calendar picker mode', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can select initial calendar picker mode', (WidgetTester tester) async { initialDate = DateTime(2014, DateTime.january, 15); initialCalendarMode = DatePickerMode.year; await prepareDatePicker(tester, (Future date) async { @@ -1243,7 +1244,7 @@ void main() { }); }); - testWidgets('currentDate is highlighted', (WidgetTester tester) async { + testWidgetsWithLeakTracking('currentDate is highlighted', (WidgetTester tester) async { today = DateTime(2016, 1, 2); await prepareDatePicker(tester, (Future date) async { await tester.pump(); @@ -1256,7 +1257,7 @@ void main() { }); }); - testWidgets('Date picker dayOverlayColor resolves pressed state', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Date picker dayOverlayColor resolves pressed state', (WidgetTester tester) async { today = DateTime(2023, 5, 4); final ThemeData theme = ThemeData(); final bool material3 = theme.useMaterial3; @@ -1288,7 +1289,7 @@ void main() { }, theme: theme); }); - testWidgets('Selecting date does not switch picker to year selection', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Selecting date does not switch picker to year selection', (WidgetTester tester) async { initialDate = DateTime(2020, DateTime.may, 10); initialCalendarMode = DatePickerMode.year; await prepareDatePicker(tester, (Future date) async { @@ -1312,7 +1313,7 @@ void main() { initialEntryMode = DatePickerEntryMode.input; }); - testWidgets('Default InputDecoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Default InputDecoration', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { final InputDecoration decoration = tester.widget( find.byType(TextField)).decoration!; @@ -1324,13 +1325,13 @@ void main() { }, useMaterial3: true); }); - testWidgets('Initial entry mode is used', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Initial entry mode is used', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { expect(find.byType(TextField), findsOneWidget); }); }); - testWidgets('Hint, label, and help text is used', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Hint, label, and help text is used', (WidgetTester tester) async { cancelText = 'nope'; confirmText = 'yep'; fieldHintText = 'hint'; @@ -1345,7 +1346,7 @@ void main() { }); }); - testWidgets('KeyboardType is used', (WidgetTester tester) async { + testWidgetsWithLeakTracking('KeyboardType is used', (WidgetTester tester) async { keyboardType = TextInputType.text; await prepareDatePicker(tester, (Future date) async { final TextField field = textField(tester); @@ -1353,14 +1354,14 @@ void main() { }); }); - testWidgets('Initial date is the default', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Initial date is the default', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { await tester.tap(find.text('OK')); expect(await date, DateTime(2016, DateTime.january, 15)); }); }); - testWidgets('Can toggle to calendar entry mode', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can toggle to calendar entry mode', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { expect(find.byType(TextField), findsOneWidget); await tester.tap(find.byIcon(Icons.calendar_today)); @@ -1369,7 +1370,7 @@ void main() { }); }); - testWidgets('Toggle to calendar mode keeps selected date', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Toggle to calendar mode keeps selected date', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { final TextField field = textField(tester); field.controller!.clear(); @@ -1382,7 +1383,7 @@ void main() { }); }); - testWidgets('Entered text returns date', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Entered text returns date', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { final TextField field = textField(tester); field.controller!.clear(); @@ -1393,7 +1394,7 @@ void main() { }); }); - testWidgets('Too short entered text shows error', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Too short entered text shows error', (WidgetTester tester) async { errorFormatText = 'oops'; await prepareDatePicker(tester, (Future date) async { final TextField field = textField(tester); @@ -1409,7 +1410,7 @@ void main() { }); }); - testWidgets('Bad format entered text shows error', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Bad format entered text shows error', (WidgetTester tester) async { errorFormatText = 'oops'; await prepareDatePicker(tester, (Future date) async { final TextField field = textField(tester); @@ -1426,7 +1427,7 @@ void main() { }); }); - testWidgets('Invalid entered text shows error', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Invalid entered text shows error', (WidgetTester tester) async { errorInvalidText = 'oops'; await prepareDatePicker(tester, (Future date) async { final TextField field = textField(tester); @@ -1442,7 +1443,7 @@ void main() { }); }); - testWidgets('Invalid entered text shows error on autovalidate', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Invalid entered text shows error on autovalidate', (WidgetTester tester) async { // This is a regression test for https://github.com/flutter/flutter/issues/126397. await prepareDatePicker(tester, (Future date) async { final TextField field = textField(tester); @@ -1471,7 +1472,7 @@ void main() { }); // This is a regression test for https://github.com/flutter/flutter/issues/131989. - testWidgets('Dialog contents do not overflow when resized from landscape to portrait', + testWidgetsWithLeakTracking('Dialog contents do not overflow when resized from landscape to portrait', (WidgetTester tester) async { addTearDown(tester.view.reset); // Initial window size is wide for landscape mode. @@ -1488,7 +1489,7 @@ void main() { }); group('Semantics', () { - testWidgets('calendar mode', (WidgetTester tester) async { + testWidgetsWithLeakTracking('calendar mode', (WidgetTester tester) async { final SemanticsHandle semantics = tester.ensureSemantics(); await prepareDatePicker(tester, (Future date) async { @@ -1537,7 +1538,7 @@ void main() { semantics.dispose(); }); - testWidgets('input mode', (WidgetTester tester) async { + testWidgetsWithLeakTracking('input mode', (WidgetTester tester) async { final SemanticsHandle semantics = tester.ensureSemantics(); initialEntryMode = DatePickerEntryMode.input; @@ -1582,7 +1583,7 @@ void main() { }); group('Keyboard navigation', () { - testWidgets('Can toggle to calendar entry mode', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can toggle to calendar entry mode', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { expect(find.byType(TextField), findsNothing); // Navigate to the entry toggle button and activate it @@ -1598,7 +1599,7 @@ void main() { }); }); - testWidgets('Can toggle to year mode', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can toggle to year mode', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { expect(find.text('2016'), findsNothing); // Navigate to the year selector and activate it @@ -1610,7 +1611,7 @@ void main() { }); }); - testWidgets('Can navigate next/previous months', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can navigate next/previous months', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { expect(find.text('January 2016'), findsOneWidget); // Navigate to the previous month button and activate it twice @@ -1638,7 +1639,7 @@ void main() { }); }); - testWidgets('Can navigate date grid with arrow keys', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can navigate date grid with arrow keys', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { // Navigate to the grid await tester.sendKeyEvent(LogicalKeyboardKey.tab); @@ -1674,7 +1675,7 @@ void main() { }); }); - testWidgets('Navigating with arrow keys scrolls months', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Navigating with arrow keys scrolls months', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { // Navigate to the grid await tester.sendKeyEvent(LogicalKeyboardKey.tab); @@ -1722,7 +1723,7 @@ void main() { }); }); - testWidgets('RTL text direction reverses the horizontal arrow key navigation', (WidgetTester tester) async { + testWidgetsWithLeakTracking('RTL text direction reverses the horizontal arrow key navigation', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { // Navigate to the grid await tester.sendKeyEvent(LogicalKeyboardKey.tab); @@ -1787,49 +1788,49 @@ void main() { await tester.pumpAndSettle(); } - testWidgets('common screen size - portrait', (WidgetTester tester) async { + testWidgetsWithLeakTracking('common screen size - portrait', (WidgetTester tester) async { await showPicker(tester, kCommonScreenSizePortrait); expect(tester.takeException(), isNull); }); - testWidgets('common screen size - landscape', (WidgetTester tester) async { + testWidgetsWithLeakTracking('common screen size - landscape', (WidgetTester tester) async { await showPicker(tester, kCommonScreenSizeLandscape); expect(tester.takeException(), isNull); }); - testWidgets('common screen size - portrait - textScale 1.3', (WidgetTester tester) async { + testWidgetsWithLeakTracking('common screen size - portrait - textScale 1.3', (WidgetTester tester) async { await showPicker(tester, kCommonScreenSizePortrait, 1.3); expect(tester.takeException(), isNull); }); - testWidgets('common screen size - landscape - textScale 1.3', (WidgetTester tester) async { + testWidgetsWithLeakTracking('common screen size - landscape - textScale 1.3', (WidgetTester tester) async { await showPicker(tester, kCommonScreenSizeLandscape, 1.3); expect(tester.takeException(), isNull); }); - testWidgets('small screen size - portrait', (WidgetTester tester) async { + testWidgetsWithLeakTracking('small screen size - portrait', (WidgetTester tester) async { await showPicker(tester, kSmallScreenSizePortrait); expect(tester.takeException(), isNull); }); - testWidgets('small screen size - landscape', (WidgetTester tester) async { + testWidgetsWithLeakTracking('small screen size - landscape', (WidgetTester tester) async { await showPicker(tester, kSmallScreenSizeLandscape); expect(tester.takeException(), isNull); }); - testWidgets('small screen size - portrait -textScale 1.3', (WidgetTester tester) async { + testWidgetsWithLeakTracking('small screen size - portrait -textScale 1.3', (WidgetTester tester) async { await showPicker(tester, kSmallScreenSizePortrait, 1.3); expect(tester.takeException(), isNull); }); - testWidgets('small screen size - landscape - textScale 1.3', (WidgetTester tester) async { + testWidgetsWithLeakTracking('small screen size - landscape - textScale 1.3', (WidgetTester tester) async { await showPicker(tester, kSmallScreenSizeLandscape, 1.3); expect(tester.takeException(), isNull); }); }); group('showDatePicker 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) { @@ -1867,7 +1868,7 @@ void main() { expect(tester.getBottomRight(find.byType(DatePickerDialog)), const Offset(800.0, 600.0)); }); - testWidgets('positioning with Directionality', (WidgetTester tester) async { + testWidgetsWithLeakTracking('positioning with Directionality', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( builder: (BuildContext context, Widget? child) { @@ -1907,7 +1908,7 @@ void main() { expect(tester.getBottomRight(find.byType(DatePickerDialog)), const Offset(800.0, 600.0)); }); - testWidgets('positioning with defaults', (WidgetTester tester) async { + testWidgetsWithLeakTracking('positioning with defaults', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( builder: (BuildContext context, Widget? child) { @@ -1945,7 +1946,7 @@ void main() { }); }); - testWidgets('DatePickerDialog is state restorable', (WidgetTester tester) async { + testWidgetsWithLeakTracking('DatePickerDialog is state restorable', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp( restorationScopeId: 'app', @@ -1998,7 +1999,7 @@ void main() { expect(find.text('30/7/2021'), findsOneWidget); }, skip: isBrowser); // https://github.com/flutter/flutter/issues/33615 - testWidgets('DatePickerDialog state restoration - DatePickerEntryMode', (WidgetTester tester) async { + testWidgetsWithLeakTracking('DatePickerDialog state restoration - DatePickerEntryMode', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp( restorationScopeId: 'app', @@ -2047,7 +2048,7 @@ void main() { expect(find.byIcon(Icons.edit), findsNothing); }, skip: isBrowser); // https://github.com/flutter/flutter/issues/33615 - testWidgets('Test Callback on Toggle of DatePicker Mode', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Test Callback on Toggle of DatePicker Mode', (WidgetTester tester) async { prepareDatePicker(tester, (Future date) async { await tester.tap(find.byIcon(Icons.edit)); expect(currentMode, DatePickerEntryMode.input); @@ -2075,14 +2076,14 @@ void main() { await prepareDatePicker(tester, (Future date) async { }, useMaterial3: true); } - testWidgets('portrait', (WidgetTester tester) async { + testWidgetsWithLeakTracking('portrait', (WidgetTester tester) async { await showPicker(tester, kCommonScreenSizePortrait); expect(tester.widget(find.text('Fri, Jan 15')).style?.fontSize, 32); await tester.tap(find.text('Cancel')); await tester.pumpAndSettle(); }); - testWidgets('landscape', (WidgetTester tester) async { + testWidgetsWithLeakTracking('landscape', (WidgetTester tester) async { await showPicker(tester, kCommonScreenSizeLandscape); expect(tester.widget(find.text('Fri, Jan 15')).style?.fontSize, 24); await tester.tap(find.text('Cancel')); @@ -2096,7 +2097,7 @@ void main() { // can be deleted. group('showDatePicker Dialog', () { - testWidgets('Default dialog size', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Default dialog size', (WidgetTester tester) async { Future showPicker(WidgetTester tester, Size size) async { tester.view.physicalSize = size; tester.view.devicePixelRatio = 1.0; @@ -2125,7 +2126,7 @@ void main() { expect(dialogContainerSize, calendarPortraitDialogSizeM2); }); - testWidgets('Default dialog properties', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Default dialog properties', (WidgetTester tester) async { final ThemeData theme = ThemeData(useMaterial3: false); await prepareDatePicker(tester, (Future date) async { final Material dialogMaterial = tester.widget( @@ -2157,7 +2158,7 @@ void main() { initialEntryMode = DatePickerEntryMode.input; }); - testWidgets('Default InputDecoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Default InputDecoration', (WidgetTester tester) async { await prepareDatePicker(tester, (Future date) async { final InputDecoration decoration = tester.widget( find.byType(TextField)).decoration!; @@ -2201,6 +2202,13 @@ class _RestorableDatePickerDialogTestWidgetState extends State<_RestorableDatePi }, ); + @override + void dispose() { + _selectedDate.dispose(); + _restorableDatePickerRouteFuture.dispose(); + super.dispose(); + } + @override void restoreState(RestorationBucket? oldBucket, bool initialRestore) { registerForRestoration(_selectedDate, 'selected_date');