Unverified Commit a31e0a43 authored by Parker Stromberg's avatar Parker Stromberg Committed by GitHub

Added time picker entry mode callback and tests (#87595)

parent e7df5ec5
...@@ -1786,6 +1786,9 @@ class _HourMinuteTextFieldState extends State<_HourMinuteTextField> with Restora ...@@ -1786,6 +1786,9 @@ class _HourMinuteTextFieldState extends State<_HourMinuteTextField> with Restora
} }
} }
/// Signature for when the time picker entry mode is changed.
typedef EntryModeChangeCallback = void Function(TimePickerEntryMode);
/// A material design time picker designed to appear inside a popup dialog. /// A material design time picker designed to appear inside a popup dialog.
/// ///
/// Pass this widget to [showDialog]. The value returned by [showDialog] is the /// Pass this widget to [showDialog]. The value returned by [showDialog] is the
...@@ -1807,6 +1810,7 @@ class TimePickerDialog extends StatefulWidget { ...@@ -1807,6 +1810,7 @@ class TimePickerDialog extends StatefulWidget {
this.minuteLabelText, this.minuteLabelText,
this.restorationId, this.restorationId,
this.initialEntryMode = TimePickerEntryMode.dial, this.initialEntryMode = TimePickerEntryMode.dial,
this.onEntryModeChanged,
}) : assert(initialTime != null), }) : assert(initialTime != null),
super(key: key); super(key: key);
...@@ -1852,6 +1856,9 @@ class TimePickerDialog extends StatefulWidget { ...@@ -1852,6 +1856,9 @@ class TimePickerDialog extends StatefulWidget {
/// Flutter. /// Flutter.
final String? restorationId; final String? restorationId;
/// Callback called when the selected entry mode is changed.
final EntryModeChangeCallback? onEntryModeChanged;
@override @override
State<TimePickerDialog> createState() => _TimePickerDialogState(); State<TimePickerDialog> createState() => _TimePickerDialogState();
} }
...@@ -1947,6 +1954,8 @@ class _TimePickerDialogState extends State<TimePickerDialog> with RestorationMix ...@@ -1947,6 +1954,8 @@ class _TimePickerDialogState extends State<TimePickerDialog> with RestorationMix
final RestorableBoolN _autofocusMinute = RestorableBoolN(null); final RestorableBoolN _autofocusMinute = RestorableBoolN(null);
final RestorableBool _announcedInitialTime = RestorableBool(false); final RestorableBool _announcedInitialTime = RestorableBool(false);
late final VoidCallback _entryModeListener;
@override @override
void didChangeDependencies() { void didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();
...@@ -1955,6 +1964,13 @@ class _TimePickerDialogState extends State<TimePickerDialog> with RestorationMix ...@@ -1955,6 +1964,13 @@ class _TimePickerDialogState extends State<TimePickerDialog> with RestorationMix
_announceModeOnce(); _announceModeOnce();
} }
@override
void initState() {
super.initState();
_entryModeListener = () => widget.onEntryModeChanged?.call(_entryMode.value);
_entryMode.addListener(_entryModeListener);
}
@override @override
String? get restorationId => widget.restorationId; String? get restorationId => widget.restorationId;
...@@ -2288,6 +2304,7 @@ class _TimePickerDialogState extends State<TimePickerDialog> with RestorationMix ...@@ -2288,6 +2304,7 @@ class _TimePickerDialogState extends State<TimePickerDialog> with RestorationMix
void dispose() { void dispose() {
_vibrateTimer?.cancel(); _vibrateTimer?.cancel();
_vibrateTimer = null; _vibrateTimer = null;
_entryMode.removeListener(_entryModeListener);
super.dispose(); super.dispose();
} }
} }
...@@ -2380,6 +2397,7 @@ Future<TimeOfDay?> showTimePicker({ ...@@ -2380,6 +2397,7 @@ Future<TimeOfDay?> showTimePicker({
String? hourLabelText, String? hourLabelText,
String? minuteLabelText, String? minuteLabelText,
RouteSettings? routeSettings, RouteSettings? routeSettings,
EntryModeChangeCallback? onEntryModeChanged,
}) async { }) async {
assert(context != null); assert(context != null);
assert(initialTime != null); assert(initialTime != null);
...@@ -2396,6 +2414,7 @@ Future<TimeOfDay?> showTimePicker({ ...@@ -2396,6 +2414,7 @@ Future<TimeOfDay?> showTimePicker({
errorInvalidText: errorInvalidText, errorInvalidText: errorInvalidText,
hourLabelText: hourLabelText, hourLabelText: hourLabelText,
minuteLabelText: minuteLabelText, minuteLabelText: minuteLabelText,
onEntryModeChanged: onEntryModeChanged,
); );
return showDialog<TimeOfDay>( return showDialog<TimeOfDay>(
context: context, context: context,
......
...@@ -903,6 +903,34 @@ void _testsInput() { ...@@ -903,6 +903,34 @@ void _testsInput() {
expect(find.byType(TextField), findsNothing); expect(find.byType(TextField), findsNothing);
}); });
testWidgets('Switching to dial entry mode triggers entry callback', (WidgetTester tester) async {
bool triggeredCallback = false;
await mediaQueryBoilerplate(tester, true, entryMode: TimePickerEntryMode.input, onEntryModeChange: (TimePickerEntryMode mode) {
if (mode == TimePickerEntryMode.dial) {
triggeredCallback = true;
}
});
await tester.tap(find.byIcon(Icons.access_time));
await tester.pumpAndSettle();
expect(triggeredCallback, true);
});
testWidgets('Switching to input entry mode triggers entry callback', (WidgetTester tester) async {
bool triggeredCallback = false;
await mediaQueryBoilerplate(tester, true, entryMode: TimePickerEntryMode.dial, onEntryModeChange: (TimePickerEntryMode mode) {
if (mode == TimePickerEntryMode.input) {
triggeredCallback = true;
}
});
await tester.tap(find.byIcon(Icons.keyboard));
await tester.pumpAndSettle();
expect(triggeredCallback, true);
});
testWidgets('Can double tap hours (when selected) to enter input mode', (WidgetTester tester) async { testWidgets('Can double tap hours (when selected) to enter input mode', (WidgetTester tester) async {
await mediaQueryBoilerplate(tester, false, entryMode: TimePickerEntryMode.dial); await mediaQueryBoilerplate(tester, false, entryMode: TimePickerEntryMode.dial);
final Finder hourFinder = find.ancestor( final Finder hourFinder = find.ancestor(
...@@ -1163,6 +1191,7 @@ Future<void> mediaQueryBoilerplate( ...@@ -1163,6 +1191,7 @@ Future<void> mediaQueryBoilerplate(
String? minuteLabelText, String? minuteLabelText,
String? errorInvalidText, String? errorInvalidText,
bool accessibleNavigation = false, bool accessibleNavigation = false,
EntryModeChangeCallback? onEntryModeChange,
}) async { }) async {
await tester.pumpWidget( await tester.pumpWidget(
Localizations( Localizations(
...@@ -1192,7 +1221,8 @@ Future<void> mediaQueryBoilerplate( ...@@ -1192,7 +1221,8 @@ Future<void> mediaQueryBoilerplate(
helpText: helpText, helpText: helpText,
hourLabelText: hourLabelText, hourLabelText: hourLabelText,
minuteLabelText: minuteLabelText, minuteLabelText: minuteLabelText,
errorInvalidText: errorInvalidText errorInvalidText: errorInvalidText,
onEntryModeChanged: onEntryModeChange,
); );
}, },
child: const Text('X'), child: const Text('X'),
......
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