diff --git a/packages/flutter/lib/src/material/time_picker.dart b/packages/flutter/lib/src/material/time_picker.dart index d012519f32c..05d9c7d7d43 100644 --- a/packages/flutter/lib/src/material/time_picker.dart +++ b/packages/flutter/lib/src/material/time_picker.dart @@ -1000,12 +1000,16 @@ class _Dial extends StatefulWidget { @required this.mode, @required this.use24HourDials, @required this.onChanged, - }) : assert(selectedTime != null); + @required this.onHourSelected, + }) : assert(selectedTime != null), + assert(mode != null), + assert(use24HourDials != null); final TimeOfDay selectedTime; final _TimePickerMode mode; final bool use24HourDials; final ValueChanged onChanged; + final VoidCallback onHourSelected; @override _DialState createState() => _DialState(); @@ -1169,6 +1173,11 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { _position = null; _center = null; _animateTo(_getThetaForTime(widget.selectedTime)); + if (widget.mode == _TimePickerMode.hour) { + if (widget.onHourSelected != null) { + widget.onHourSelected(); + } + } } void _handleTapUp(TapUpDetails details) { @@ -1183,6 +1192,9 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { } else { _announceToAccessibility(context, localizations.formatDecimal(newTime.hourOfPeriod)); } + if (widget.onHourSelected != null) { + widget.onHourSelected(); + } } else { _announceToAccessibility(context, localizations.formatDecimal(newTime.minute)); } @@ -1522,6 +1534,12 @@ class _TimePickerDialogState extends State<_TimePickerDialog> { }); } + void _handleHourSelected() { + setState(() { + _mode = _TimePickerMode.minute; + }); + } + void _handleCancel() { Navigator.pop(context); } @@ -1547,6 +1565,7 @@ class _TimePickerDialogState extends State<_TimePickerDialog> { use24HourDials: use24HourDials, selectedTime: _selectedTime, onChanged: _handleTimeChanged, + onHourSelected: _handleHourSelected, ), ), ); diff --git a/packages/flutter/test/material/time_picker_test.dart b/packages/flutter/test/material/time_picker_test.dart index 93f4ab224a5..198a892de45 100644 --- a/packages/flutter/test/material/time_picker_test.dart +++ b/packages/flutter/test/material/time_picker_test.dart @@ -134,6 +134,39 @@ void _tests() { expect(result.hour, equals(9)); }); + testWidgets('tap-select switches from hour to minute', (WidgetTester tester) async { + TimeOfDay result; + + final Offset center = await startPicker(tester, (TimeOfDay time) { result = time; }); + final Offset hour6 = Offset(center.dx, center.dy + 50.0); // 6:00 + final Offset min45 = Offset(center.dx - 50.0, center.dy); // 45 mins (or 9:00 hours) + + await tester.tapAt(hour6); + await tester.pump(const Duration(milliseconds: 50)); + await tester.tapAt(min45); + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 6, minute: 45))); + }); + + testWidgets('drag-select switches from hour to minute', (WidgetTester tester) async { + TimeOfDay result; + + final Offset center = await startPicker(tester, (TimeOfDay time) { result = time; }); + final Offset hour3 = Offset(center.dx + 50.0, center.dy); + final Offset hour6 = Offset(center.dx, center.dy + 50.0); + final Offset hour9 = Offset(center.dx - 50.0, center.dy); + + TestGesture gesture = await tester.startGesture(hour6); + await gesture.moveBy(hour9 - hour6); + await gesture.up(); + await tester.pump(const Duration(milliseconds: 50)); + gesture = await tester.startGesture(hour6); + await gesture.moveBy(hour3 - hour6); + await gesture.up(); + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 9, minute: 15))); + }); + group('haptic feedback', () { const Duration kFastFeedbackInterval = Duration(milliseconds: 10); const Duration kSlowFeedbackInterval = Duration(milliseconds: 200);