TimePicker moves to minute mode after hour selection (#31566)
Adds a feature of the native Android Time Picker to our Material Time Picker. When the user selects an hour, it automatically switches to minute mode.
This is a merging of two pull requests:
Code changes from @sdolski #24677
Tests from @lucaslcode #29876
Thanks to both of you for your contributions!
diff --git a/packages/flutter/lib/src/material/time_picker.dart b/packages/flutter/lib/src/material/time_picker.dart
index d012519..05d9c7d 100644
--- a/packages/flutter/lib/src/material/time_picker.dart
+++ b/packages/flutter/lib/src/material/time_picker.dart
@@ -1000,12 +1000,16 @@
@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<TimeOfDay> onChanged;
+ final VoidCallback onHourSelected;
@override
_DialState createState() => _DialState();
@@ -1169,6 +1173,11 @@
_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 @@
} else {
_announceToAccessibility(context, localizations.formatDecimal(newTime.hourOfPeriod));
}
+ if (widget.onHourSelected != null) {
+ widget.onHourSelected();
+ }
} else {
_announceToAccessibility(context, localizations.formatDecimal(newTime.minute));
}
@@ -1522,6 +1534,12 @@
});
}
+ void _handleHourSelected() {
+ setState(() {
+ _mode = _TimePickerMode.minute;
+ });
+ }
+
void _handleCancel() {
Navigator.pop(context);
}
@@ -1547,6 +1565,7 @@
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 93f4ab2..198a892 100644
--- a/packages/flutter/test/material/time_picker_test.dart
+++ b/packages/flutter/test/material/time_picker_test.dart
@@ -134,6 +134,39 @@
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);