Unverified Commit 0a694704 authored by Kostia Sokolovskyi's avatar Kostia Sokolovskyi Committed by GitHub

TimePickerDialog should dispose created ChangeNotifiers. (#136261)

parent e8bd5a32
...@@ -1630,6 +1630,14 @@ class _TimePickerInputState extends State<_TimePickerInput> with RestorationMixi ...@@ -1630,6 +1630,14 @@ class _TimePickerInputState extends State<_TimePickerInput> with RestorationMixi
final RestorableBool hourHasError = RestorableBool(false); final RestorableBool hourHasError = RestorableBool(false);
final RestorableBool minuteHasError = RestorableBool(false); final RestorableBool minuteHasError = RestorableBool(false);
@override
void dispose() {
_selectedTime.dispose();
hourHasError.dispose();
minuteHasError.dispose();
super.dispose();
}
@override @override
String? get restorationId => widget.restorationId; String? get restorationId => widget.restorationId;
...@@ -1988,6 +1996,14 @@ class _HourMinuteTextFieldState extends State<_HourMinuteTextField> with Restora ...@@ -1988,6 +1996,14 @@ class _HourMinuteTextFieldState extends State<_HourMinuteTextField> with Restora
} }
} }
@override
void dispose() {
controller.dispose();
controllerHasBeenSet.dispose();
focusNode.dispose();
super.dispose();
}
@override @override
String? get restorationId => widget.restorationId; String? get restorationId => widget.restorationId;
......
...@@ -66,7 +66,7 @@ void main() { ...@@ -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 TimePickerEntryMode entryMode = TimePickerEntryMode.input;
const Size timePickerInputSize = Size(312, 216); const Size timePickerInputSize = Size(312, 216);
const Size dayPeriodPortraitSize = Size(52, 80); const Size dayPeriodPortraitSize = Size(52, 80);
...@@ -102,7 +102,7 @@ void main() { ...@@ -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); await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, materialType: MaterialType.material2);
final List<String> labels00To22 = List<String>.generate(12, (int index) { final List<String> labels00To22 = List<String>.generate(12, (int index) {
...@@ -121,7 +121,7 @@ void main() { ...@@ -121,7 +121,7 @@ void main() {
expect(selectedLabels.map<String>((dynamic tp) => tp.painter.text.text as String), labels00To22); 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); addTearDown(tester.view.reset);
const Size timePickerPortraitSize = Size(310, 468); const Size timePickerPortraitSize = Size(310, 468);
...@@ -162,7 +162,7 @@ void main() { ...@@ -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); final ThemeData theme = ThemeData(useMaterial3: true);
const TimePickerEntryMode entryMode = TimePickerEntryMode.input; const TimePickerEntryMode entryMode = TimePickerEntryMode.input;
const double textScaleFactor = 1.0; const double textScaleFactor = 1.0;
...@@ -201,7 +201,7 @@ void main() { ...@@ -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); await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, materialType: MaterialType.material3);
final List<String> labels00To23 = List<String>.generate(24, (int index) { final List<String> labels00To23 = List<String>.generate(24, (int index) {
...@@ -226,7 +226,7 @@ void main() { ...@@ -226,7 +226,7 @@ void main() {
expect(selectedLabels.map<bool>((dynamic tp) => tp.inner as bool), inner0To23); 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); ThemeData theme = ThemeData(useMaterial3: true);
Widget buildTimePicker(ThemeData themeData) { Widget buildTimePicker(ThemeData themeData) {
return MaterialApp( return MaterialApp(
...@@ -292,7 +292,7 @@ void main() { ...@@ -292,7 +292,7 @@ void main() {
for (final MaterialType materialType in MaterialType.values) { for (final MaterialType materialType in MaterialType.values) {
group('Dial (${materialType.name})', () { group('Dial (${materialType.name})', () {
testWidgets('tap-select an hour', (WidgetTester tester) async { testWidgetsWithLeakTracking('tap-select an hour', (WidgetTester tester) async {
TimeOfDay? result; TimeOfDay? result;
Offset center = (await startPicker(tester, (TimeOfDay? time) { Offset center = (await startPicker(tester, (TimeOfDay? time) {
...@@ -325,7 +325,7 @@ void main() { ...@@ -325,7 +325,7 @@ void main() {
expect(result, equals(const TimeOfDay(hour: 9, minute: 0))); 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; late TimeOfDay result;
final Offset center = (await startPicker(tester, (TimeOfDay? time) { final Offset center = (await startPicker(tester, (TimeOfDay? time) {
...@@ -381,7 +381,7 @@ void main() { ...@@ -381,7 +381,7 @@ void main() {
expect(result.hour, equals(9)); 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; late TimeOfDay result;
final Offset center = (await startPicker(tester, (TimeOfDay? time) { final Offset center = (await startPicker(tester, (TimeOfDay? time) {
...@@ -397,7 +397,7 @@ void main() { ...@@ -397,7 +397,7 @@ void main() {
expect(result, equals(const TimeOfDay(hour: 6, minute: 45))); 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; late TimeOfDay result;
final Offset center = (await startPicker(tester, (TimeOfDay? time) { final Offset center = (await startPicker(tester, (TimeOfDay? time) {
...@@ -418,7 +418,7 @@ void main() { ...@@ -418,7 +418,7 @@ void main() {
expect(result, equals(const TimeOfDay(hour: 9, minute: 15))); 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; late TimeOfDay result;
final Offset center = (await startPicker(tester, (TimeOfDay? time) { final Offset center = (await startPicker(tester, (TimeOfDay? time) {
...@@ -434,7 +434,7 @@ void main() { ...@@ -434,7 +434,7 @@ void main() {
expect(result, equals(const TimeOfDay(hour: 6, minute: 45))); 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; late TimeOfDay result;
final Offset center = (await startPicker(tester, (TimeOfDay? time) { final Offset center = (await startPicker(tester, (TimeOfDay? time) {
...@@ -464,14 +464,14 @@ void main() { ...@@ -464,14 +464,14 @@ void main() {
feedback.dispose(); 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))!; final Offset center = (await startPicker(tester, (TimeOfDay? time) {}, materialType: materialType))!;
await tester.tapAt(Offset(center.dx, center.dy - 50)); await tester.tapAt(Offset(center.dx, center.dy - 50));
await finishPicker(tester); await finishPicker(tester);
expect(feedback.hapticCount, 1); 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))!; final Offset center = (await startPicker(tester, (TimeOfDay? time) {}, materialType: materialType))!;
await tester.tapAt(Offset(center.dx, center.dy - 50)); await tester.tapAt(Offset(center.dx, center.dy - 50));
await tester.pump(kFastFeedbackInterval); await tester.pump(kFastFeedbackInterval);
...@@ -480,7 +480,7 @@ void main() { ...@@ -480,7 +480,7 @@ void main() {
expect(feedback.hapticCount, 1); 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))!; final Offset center = (await startPicker(tester, (TimeOfDay? time) {}, materialType: materialType))!;
await tester.tapAt(Offset(center.dx, center.dy - 50)); await tester.tapAt(Offset(center.dx, center.dy - 50));
await tester.pump(kSlowFeedbackInterval); await tester.pump(kSlowFeedbackInterval);
...@@ -491,7 +491,7 @@ void main() { ...@@ -491,7 +491,7 @@ void main() {
expect(feedback.hapticCount, 3); 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 center = (await startPicker(tester, (TimeOfDay? time) {}, materialType: materialType))!;
final Offset hour0 = Offset(center.dx, center.dy - 50); final Offset hour0 = Offset(center.dx, center.dy - 50);
final Offset hour3 = Offset(center.dx + 50, center.dy); final Offset hour3 = Offset(center.dx + 50, center.dy);
...@@ -503,7 +503,7 @@ void main() { ...@@ -503,7 +503,7 @@ void main() {
expect(feedback.hapticCount, 1); 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 center = (await startPicker(tester, (TimeOfDay? time) {}, materialType: materialType))!;
final Offset hour0 = Offset(center.dx, center.dy - 50); final Offset hour0 = Offset(center.dx, center.dy - 50);
final Offset hour3 = Offset(center.dx + 50, center.dy); final Offset hour3 = Offset(center.dx + 50, center.dy);
...@@ -519,7 +519,7 @@ void main() { ...@@ -519,7 +519,7 @@ void main() {
expect(feedback.hapticCount, 1); 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 center = (await startPicker(tester, (TimeOfDay? time) {}, materialType: materialType))!;
final Offset hour0 = Offset(center.dx, center.dy - 50); final Offset hour0 = Offset(center.dx, center.dy - 50);
final Offset hour3 = Offset(center.dx + 50, center.dy); final Offset hour3 = Offset(center.dx + 50, center.dy);
...@@ -537,19 +537,19 @@ void main() { ...@@ -537,19 +537,19 @@ void main() {
}); });
group('Dialog (${materialType.name})', () { 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); await startPicker(tester, (TimeOfDay? time) {}, materialType: MaterialType.material2);
expect(find.text('SELECT TIME'), findsOneWidget); expect(find.text('SELECT TIME'), findsOneWidget);
expect(find.text('CANCEL'), 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); await startPicker(tester, (TimeOfDay? time) {}, materialType: MaterialType.material3);
expect(find.text('Select time'), findsOneWidget); expect(find.text('Select time'), findsOneWidget);
expect(find.text('Cancel'), 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) {}, await startPicker(tester, (TimeOfDay? time) {},
entryMode: TimePickerEntryMode.input, materialType: MaterialType.material2 entryMode: TimePickerEntryMode.input, materialType: MaterialType.material2
); );
...@@ -557,7 +557,7 @@ void main() { ...@@ -557,7 +557,7 @@ void main() {
expect(find.text('CANCEL'), findsOneWidget); 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) {}, await startPicker(tester, (TimeOfDay? time) {},
entryMode: TimePickerEntryMode.input, materialType: MaterialType.material3 entryMode: TimePickerEntryMode.input, materialType: MaterialType.material3
); );
...@@ -565,7 +565,7 @@ void main() { ...@@ -565,7 +565,7 @@ void main() {
expect(find.text('Cancel'), findsOneWidget); 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); await mediaQueryBoilerplate(tester, materialType: materialType);
const List<String> labels12To11 = <String>['12', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11']; const List<String> labels12To11 = <String>['12', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'];
...@@ -582,7 +582,7 @@ void main() { ...@@ -582,7 +582,7 @@ void main() {
expect(selectedLabels.map<String>((dynamic tp) => tp.painter.text.text as String), labels12To11); 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); addTearDown(tester.view.reset);
// portrait // portrait
...@@ -606,7 +606,7 @@ void main() { ...@@ -606,7 +606,7 @@ void main() {
expect((render as dynamic).orientation, Orientation.landscape); // ignore: avoid_dynamic_calls 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); addTearDown(tester.view.reset);
// portrait media query // portrait media query
...@@ -620,7 +620,7 @@ void main() { ...@@ -620,7 +620,7 @@ void main() {
expect((render as dynamic).orientation, Orientation.landscape); // ignore: avoid_dynamic_calls 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) { Widget buildFrame(TextDirection textDirection) {
return MaterialApp( return MaterialApp(
home: Material( home: Material(
...@@ -675,7 +675,7 @@ void main() { ...@@ -675,7 +675,7 @@ void main() {
rootObserver = PickerObserver(); 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( await tester.pumpWidget(
MaterialApp( MaterialApp(
navigatorObservers: <NavigatorObserver>[rootObserver], navigatorObservers: <NavigatorObserver>[rootObserver],
...@@ -709,7 +709,7 @@ void main() { ...@@ -709,7 +709,7 @@ void main() {
expect(rootObserver.pickerCount, 0); 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( await tester.pumpWidget(
MaterialApp( MaterialApp(
navigatorObservers: <NavigatorObserver>[rootObserver], navigatorObservers: <NavigatorObserver>[rootObserver],
...@@ -745,7 +745,7 @@ void main() { ...@@ -745,7 +745,7 @@ void main() {
}); });
}); });
testWidgets('Barrier color', (WidgetTester tester) async { testWidgetsWithLeakTracking('Barrier color', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Material( home: Material(
...@@ -801,7 +801,7 @@ void main() { ...@@ -801,7 +801,7 @@ void main() {
expect(tester.widget<ModalBarrier>(find.byType(ModalBarrier).last).color, Colors.pink); 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( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Material( home: Material(
...@@ -829,7 +829,7 @@ void main() { ...@@ -829,7 +829,7 @@ void main() {
expect(tester.widget<ModalBarrier>(find.byType(ModalBarrier).last).semanticsLabel, 'Custom Label'); 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 rootObserver = PickerObserver();
final PickerObserver nestedObserver = PickerObserver(); final PickerObserver nestedObserver = PickerObserver();
...@@ -862,7 +862,7 @@ void main() { ...@@ -862,7 +862,7 @@ void main() {
expect(nestedObserver.pickerCount, 0); 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 rootObserver = PickerObserver();
final PickerObserver nestedObserver = PickerObserver(); final PickerObserver nestedObserver = PickerObserver();
...@@ -896,7 +896,7 @@ void main() { ...@@ -896,7 +896,7 @@ void main() {
expect(nestedObserver.pickerCount, 1); 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 cancelText = 'Custom Cancel';
const String confirmText = 'Custom OK'; const String confirmText = 'Custom OK';
const String helperText = 'Custom Help'; const String helperText = 'Custom Help';
...@@ -932,7 +932,7 @@ void main() { ...@@ -932,7 +932,7 @@ void main() {
expect(find.text(helperText), findsOneWidget); 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 selectTimeString = 'SELECT TIME';
const String cancelString = 'CANCEL'; const String cancelString = 'CANCEL';
Widget buildFrame(TextDirection textDirection) { Widget buildFrame(TextDirection textDirection) {
...@@ -995,7 +995,7 @@ void main() { ...@@ -995,7 +995,7 @@ void main() {
await tester.pumpAndSettle(); 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 selectTimeString = 'Select time';
const String cancelString = 'Cancel'; const String cancelString = 'Cancel';
Widget buildFrame(TextDirection textDirection) { Widget buildFrame(TextDirection textDirection) {
...@@ -1068,7 +1068,7 @@ void main() { ...@@ -1068,7 +1068,7 @@ void main() {
await tester.pumpAndSettle(); 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( await mediaQueryBoilerplate(
tester, tester,
initialTime: const TimeOfDay(hour: 7, minute: 41), initialTime: const TimeOfDay(hour: 7, minute: 41),
...@@ -1109,7 +1109,7 @@ void main() { ...@@ -1109,7 +1109,7 @@ void main() {
}); });
group('showTimePicker avoids overlapping display features', () { group('showTimePicker avoids overlapping display features', () {
testWidgets('positioning with anchorPoint', (WidgetTester tester) async { testWidgetsWithLeakTracking('positioning with anchorPoint', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
builder: (BuildContext context, Widget? child) { builder: (BuildContext context, Widget? child) {
...@@ -1145,7 +1145,7 @@ void main() { ...@@ -1145,7 +1145,7 @@ void main() {
expect(tester.getBottomRight(find.byType(TimePickerDialog)), const Offset(800, 600)); 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( await tester.pumpWidget(
MaterialApp( MaterialApp(
builder: (BuildContext context, Widget? child) { builder: (BuildContext context, Widget? child) {
...@@ -1183,7 +1183,7 @@ void main() { ...@@ -1183,7 +1183,7 @@ void main() {
expect(tester.getBottomRight(find.byType(TimePickerDialog)), const Offset(800, 600)); 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( await tester.pumpWidget(
MaterialApp( MaterialApp(
builder: (BuildContext context, Widget? child) { builder: (BuildContext context, Widget? child) {
...@@ -1221,7 +1221,7 @@ void main() { ...@@ -1221,7 +1221,7 @@ void main() {
group('Works for various view sizes', () { group('Works for various view sizes', () {
for (final Size size in const <Size>[Size(100, 100), Size(300, 300), Size(800, 600)]) { 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; tester.view.physicalSize = size;
addTearDown(tester.view.reset); addTearDown(tester.view.reset);
...@@ -1230,7 +1230,7 @@ void main() { ...@@ -1230,7 +1230,7 @@ void main() {
expect(tester.takeException(), isNot(throwsAssertionError)); 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; tester.view.physicalSize = size;
addTearDown(tester.view.reset); addTearDown(tester.view.reset);
...@@ -1243,7 +1243,7 @@ void main() { ...@@ -1243,7 +1243,7 @@ void main() {
}); });
group('Time picker - A11y and Semantics (${materialType.name})', () { 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); final SemanticsTester semantics = SemanticsTester(tester);
await mediaQueryBoilerplate(tester, materialType: materialType); await mediaQueryBoilerplate(tester, materialType: materialType);
...@@ -1278,7 +1278,7 @@ void main() { ...@@ -1278,7 +1278,7 @@ void main() {
semantics.dispose(); 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); final SemanticsTester semantics = SemanticsTester(tester);
await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, materialType: MaterialType.material2); await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, materialType: MaterialType.material2);
...@@ -1303,7 +1303,7 @@ void main() { ...@@ -1303,7 +1303,7 @@ void main() {
semantics.dispose(); 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); final SemanticsTester semantics = SemanticsTester(tester);
await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, materialType: MaterialType.material3); await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, materialType: MaterialType.material3);
...@@ -1328,7 +1328,7 @@ void main() { ...@@ -1328,7 +1328,7 @@ void main() {
semantics.dispose(); 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); final SemanticsTester semantics = SemanticsTester(tester);
await mediaQueryBoilerplate( await mediaQueryBoilerplate(
tester, tester,
...@@ -1360,7 +1360,7 @@ void main() { ...@@ -1360,7 +1360,7 @@ void main() {
semantics.dispose(); 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); final SemanticsTester semantics = SemanticsTester(tester);
Future<void> actAndExpect({ Future<void> actAndExpect({
...@@ -1442,7 +1442,7 @@ void main() { ...@@ -1442,7 +1442,7 @@ void main() {
semantics.dispose(); 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); final SemanticsTester semantics = SemanticsTester(tester);
Future<void> actAndExpect({ Future<void> actAndExpect({
...@@ -1499,7 +1499,7 @@ void main() { ...@@ -1499,7 +1499,7 @@ void main() {
semantics.dispose(); 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. // Ensure picker is displayed in portrait mode.
tester.view.physicalSize = const Size(400, 800); tester.view.physicalSize = const Size(400, 800);
tester.view.devicePixelRatio = 1; tester.view.devicePixelRatio = 1;
...@@ -1530,7 +1530,7 @@ void main() { ...@@ -1530,7 +1530,7 @@ void main() {
}); });
group('Time picker - Input (${materialType.name})', () { 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( await mediaQueryBoilerplate(
tester, tester,
alwaysUse24HourFormat: true, alwaysUse24HourFormat: true,
...@@ -1540,7 +1540,7 @@ void main() { ...@@ -1540,7 +1540,7 @@ void main() {
expect(find.byType(TextField), findsNWidgets(2)); 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; late TimeOfDay result;
await startPicker(tester, (TimeOfDay? time) { await startPicker(tester, (TimeOfDay? time) {
result = time!; result = time!;
...@@ -1549,7 +1549,7 @@ void main() { ...@@ -1549,7 +1549,7 @@ void main() {
expect(result, equals(const TimeOfDay(hour: 7, minute: 0))); 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'; const String helpText = 'help';
await mediaQueryBoilerplate( await mediaQueryBoilerplate(
tester, tester,
...@@ -1561,7 +1561,7 @@ void main() { ...@@ -1561,7 +1561,7 @@ void main() {
expect(find.text(helpText), findsOneWidget); 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'; const String helpText = 'help';
await mediaQueryBoilerplate( await mediaQueryBoilerplate(
tester, tester,
...@@ -1573,7 +1573,7 @@ void main() { ...@@ -1573,7 +1573,7 @@ void main() {
expect(find.text(helpText), findsOneWidget); 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'; const String hourLabelText = 'Custom hour label';
await mediaQueryBoilerplate( await mediaQueryBoilerplate(
tester, tester,
...@@ -1585,7 +1585,7 @@ void main() { ...@@ -1585,7 +1585,7 @@ void main() {
expect(find.text(hourLabelText), findsOneWidget); 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'; const String minuteLabelText = 'Custom minute label';
await mediaQueryBoilerplate( await mediaQueryBoilerplate(
tester, tester,
...@@ -1597,7 +1597,7 @@ void main() { ...@@ -1597,7 +1597,7 @@ void main() {
expect(find.text(minuteLabelText), findsOneWidget); 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'; const String errorInvalidText = 'Custom validation error';
await mediaQueryBoilerplate( await mediaQueryBoilerplate(
tester, tester,
...@@ -1617,7 +1617,7 @@ void main() { ...@@ -1617,7 +1617,7 @@ void main() {
expect(find.text(errorInvalidText), findsOneWidget); 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( await mediaQueryBoilerplate(
tester, tester,
alwaysUse24HourFormat: true, alwaysUse24HourFormat: true,
...@@ -1629,14 +1629,14 @@ void main() { ...@@ -1629,14 +1629,14 @@ void main() {
expect(find.byType(TextField), findsNothing); 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 mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, materialType: materialType);
await tester.tap(find.byIcon(Icons.keyboard_outlined)); await tester.tap(find.byIcon(Icons.keyboard_outlined));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.byType(TextField), findsWidgets); 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( await mediaQueryBoilerplate(
tester, tester,
alwaysUse24HourFormat: true, alwaysUse24HourFormat: true,
...@@ -1647,7 +1647,7 @@ void main() { ...@@ -1647,7 +1647,7 @@ void main() {
expect(find.byIcon(Icons.access_time), findsNothing); 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( await mediaQueryBoilerplate(
tester, tester,
alwaysUse24HourFormat: true, alwaysUse24HourFormat: true,
...@@ -1658,7 +1658,7 @@ void main() { ...@@ -1658,7 +1658,7 @@ void main() {
expect(find.byIcon(Icons.keyboard_outlined), findsNothing); 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; bool triggeredCallback = false;
await mediaQueryBoilerplate( await mediaQueryBoilerplate(
...@@ -1678,7 +1678,7 @@ void main() { ...@@ -1678,7 +1678,7 @@ void main() {
expect(triggeredCallback, true); 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; bool triggeredCallback = false;
await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, onEntryModeChange: (TimePickerEntryMode mode) { await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, onEntryModeChange: (TimePickerEntryMode mode) {
...@@ -1692,7 +1692,7 @@ void main() { ...@@ -1692,7 +1692,7 @@ void main() {
expect(triggeredCallback, true); 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); await mediaQueryBoilerplate(tester, materialType: materialType);
final Finder hourFinder = find.ancestor( final Finder hourFinder = find.ancestor(
of: find.text('7'), of: find.text('7'),
...@@ -1710,7 +1710,7 @@ void main() { ...@@ -1710,7 +1710,7 @@ void main() {
expect(find.byType(TextField), findsWidgets); 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); await mediaQueryBoilerplate(tester, materialType: materialType);
final Finder hourFinder = find.ancestor( final Finder hourFinder = find.ancestor(
of: find.text('7'), of: find.text('7'),
...@@ -1736,7 +1736,7 @@ void main() { ...@@ -1736,7 +1736,7 @@ void main() {
expect(find.byType(TextField), findsNothing); 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); await mediaQueryBoilerplate(tester, materialType: materialType);
final Finder minuteFinder = find.ancestor( final Finder minuteFinder = find.ancestor(
of: find.text('00'), of: find.text('00'),
...@@ -1758,7 +1758,7 @@ void main() { ...@@ -1758,7 +1758,7 @@ void main() {
expect(find.byType(TextField), findsWidgets); 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); await mediaQueryBoilerplate(tester, materialType: materialType);
final Finder minuteFinder = find.ancestor( final Finder minuteFinder = find.ancestor(
of: find.text('00'), of: find.text('00'),
...@@ -1776,7 +1776,7 @@ void main() { ...@@ -1776,7 +1776,7 @@ void main() {
expect(find.byType(TextField), findsNothing); expect(find.byType(TextField), findsNothing);
}); });
testWidgets('Entered text returns time', (WidgetTester tester) async { testWidgetsWithLeakTracking('Entered text returns time', (WidgetTester tester) async {
late TimeOfDay result; late TimeOfDay result;
await startPicker(tester, (TimeOfDay? time) { await startPicker(tester, (TimeOfDay? time) {
result = time!; result = time!;
...@@ -1787,7 +1787,7 @@ void main() { ...@@ -1787,7 +1787,7 @@ void main() {
expect(result, equals(const TimeOfDay(hour: 9, minute: 12))); 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; late TimeOfDay result;
await startPicker(tester, (TimeOfDay? time) { await startPicker(tester, (TimeOfDay? time) {
result = time!; result = time!;
...@@ -1799,7 +1799,7 @@ void main() { ...@@ -1799,7 +1799,7 @@ void main() {
expect(result, equals(const TimeOfDay(hour: 8, minute: 15))); 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; TimeOfDay? result;
await startPicker(tester, (TimeOfDay? time) { await startPicker(tester, (TimeOfDay? time) {
result = time; result = time;
...@@ -1824,7 +1824,7 @@ void main() { ...@@ -1824,7 +1824,7 @@ void main() {
}); });
// Fixes regression that was reverted in https://github.com/flutter/flutter/pull/64094#pullrequestreview-469836378. // 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) {}, await startPicker(tester, (TimeOfDay? time) {},
entryMode: TimePickerEntryMode.input, materialType: materialType); entryMode: TimePickerEntryMode.input, materialType: materialType);
final double hourFieldTop = final double hourFieldTop =
...@@ -1837,7 +1837,7 @@ void main() { ...@@ -1837,7 +1837,7 @@ void main() {
expect(minuteFieldTop, separatorTop); 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) {}, await startPicker(tester, (TimeOfDay? time) {},
entryMode: TimePickerEntryMode.input, materialType: materialType); entryMode: TimePickerEntryMode.input, materialType: materialType);
...@@ -1860,7 +1860,7 @@ void main() { ...@@ -1860,7 +1860,7 @@ void main() {
}); });
group('Time picker - Restoration (${materialType.name})', () { 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; TimeOfDay? result;
final Offset center = (await startPicker( final Offset center = (await startPicker(
tester, tester,
...@@ -1893,7 +1893,7 @@ void main() { ...@@ -1893,7 +1893,7 @@ void main() {
expect(result, equals(const TimeOfDay(hour: 6, minute: 45))); 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; TimeOfDay? result;
await startPicker( await startPicker(
tester, tester,
...@@ -1927,7 +1927,7 @@ void main() { ...@@ -1927,7 +1927,7 @@ void main() {
expect(result, equals(const TimeOfDay(hour: 9, minute: 12))); 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; TimeOfDay? result;
final Offset center = (await startPicker( final Offset center = (await startPicker(
tester, tester,
...@@ -2099,6 +2099,12 @@ class _TimePickerLauncherState extends State<_TimePickerLauncher> with Restorati ...@@ -2099,6 +2099,12 @@ class _TimePickerLauncherState extends State<_TimePickerLauncher> with Restorati
}, },
); );
@override
void dispose() {
_restorableTimePickerRouteFuture.dispose();
super.dispose();
}
@pragma('vm:entry-point') @pragma('vm:entry-point')
static Route<TimeOfDay> _timePickerRoute( static Route<TimeOfDay> _timePickerRoute(
BuildContext context, BuildContext context,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment