Haptic feedback on time changes in TimePicker (#8348)
Trigger a vibration on hour/minute changes, with an upper bound on number of feedback events per second. Note: state changes are expected to trigger haptic feedback on Android, but not on iOS time pickers.
This commit is contained in:
parent
3a0b83b1b6
commit
093afe0255
@ -22,6 +22,7 @@ const double _kTwoPi = 2 * math.PI;
|
||||
const int _kHoursPerDay = 24;
|
||||
const int _kHoursPerPeriod = 12;
|
||||
const int _kMinutesPerHour = 60;
|
||||
const Duration _kVibrateCommitDelay = const Duration(milliseconds: 100);
|
||||
|
||||
/// Whether the [TimeOfDay] is before or after noon.
|
||||
enum DayPeriod {
|
||||
@ -644,12 +645,17 @@ class _TimePickerDialogState extends State<_TimePickerDialog> {
|
||||
|
||||
_TimePickerMode _mode = _TimePickerMode.hour;
|
||||
TimeOfDay _selectedTime;
|
||||
Timer _vibrateTimer;
|
||||
|
||||
void _vibrate() {
|
||||
switch (Theme.of(context).platform) {
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia:
|
||||
HapticFeedback.vibrate();
|
||||
_vibrateTimer?.cancel();
|
||||
_vibrateTimer = new Timer(_kVibrateCommitDelay, () {
|
||||
HapticFeedback.vibrate();
|
||||
_vibrateTimer = null;
|
||||
});
|
||||
break;
|
||||
case TargetPlatform.iOS:
|
||||
break;
|
||||
@ -664,6 +670,7 @@ class _TimePickerDialogState extends State<_TimePickerDialog> {
|
||||
}
|
||||
|
||||
void _handleTimeChanged(TimeOfDay value) {
|
||||
_vibrate();
|
||||
setState(() {
|
||||
_selectedTime = value;
|
||||
});
|
||||
@ -759,6 +766,13 @@ class _TimePickerDialogState extends State<_TimePickerDialog> {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_vibrateTimer?.cancel();
|
||||
_vibrateTimer = null;
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// Shows a dialog containing a material design time picker.
|
||||
|
@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
class _TimePickerLauncher extends StatelessWidget {
|
||||
@ -110,4 +111,92 @@ void main() {
|
||||
await finishPicker(tester);
|
||||
expect(result.hour, equals(9));
|
||||
});
|
||||
|
||||
group('haptic feedback', () {
|
||||
const Duration kFastFeedbackInteral = const Duration(milliseconds: 10);
|
||||
const Duration kSlowFeedbackInteral = const Duration(milliseconds: 200);
|
||||
int hapticFeedbackCount;
|
||||
|
||||
setUpAll(() {
|
||||
PlatformMessages.setMockJSONMessageHandler('flutter/platform', (dynamic message) {
|
||||
if (message['method'] == "HapticFeedback.vibrate")
|
||||
hapticFeedbackCount++;
|
||||
});
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
hapticFeedbackCount = 0;
|
||||
});
|
||||
|
||||
testWidgets('tap-select vibrates once', (WidgetTester tester) async {
|
||||
Point center = await startPicker(tester, (TimeOfDay time) { });
|
||||
await tester.tapAt(new Point(center.x, center.y - 50.0));
|
||||
await finishPicker(tester);
|
||||
expect(hapticFeedbackCount, 1);
|
||||
});
|
||||
|
||||
testWidgets('quick successive tap-selects vibrate once', (WidgetTester tester) async {
|
||||
Point center = await startPicker(tester, (TimeOfDay time) { });
|
||||
await tester.tapAt(new Point(center.x, center.y - 50.0));
|
||||
await tester.pump(kFastFeedbackInteral);
|
||||
await tester.tapAt(new Point(center.x, center.y + 50.0));
|
||||
await finishPicker(tester);
|
||||
expect(hapticFeedbackCount, 1);
|
||||
});
|
||||
|
||||
testWidgets('slow successive tap-selects vibrate once per tap', (WidgetTester tester) async {
|
||||
Point center = await startPicker(tester, (TimeOfDay time) { });
|
||||
await tester.tapAt(new Point(center.x, center.y - 50.0));
|
||||
await tester.pump(kSlowFeedbackInteral);
|
||||
await tester.tapAt(new Point(center.x, center.y + 50.0));
|
||||
await tester.pump(kSlowFeedbackInteral);
|
||||
await tester.tapAt(new Point(center.x, center.y - 50.0));
|
||||
await finishPicker(tester);
|
||||
expect(hapticFeedbackCount, 3);
|
||||
});
|
||||
|
||||
testWidgets('drag-select vibrates once', (WidgetTester tester) async {
|
||||
Point center = await startPicker(tester, (TimeOfDay time) { });
|
||||
Point hour0 = new Point(center.x, center.y - 50.0);
|
||||
Point hour3 = new Point(center.x + 50.0, center.y);
|
||||
|
||||
TestGesture gesture = await tester.startGesture(hour3);
|
||||
await gesture.moveBy(hour0 - hour3);
|
||||
await gesture.up();
|
||||
await finishPicker(tester);
|
||||
expect(hapticFeedbackCount, 1);
|
||||
});
|
||||
|
||||
testWidgets('quick drag-select vibrates once', (WidgetTester tester) async {
|
||||
Point center = await startPicker(tester, (TimeOfDay time) { });
|
||||
Point hour0 = new Point(center.x, center.y - 50.0);
|
||||
Point hour3 = new Point(center.x + 50.0, center.y);
|
||||
|
||||
TestGesture gesture = await tester.startGesture(hour3);
|
||||
await gesture.moveBy(hour0 - hour3);
|
||||
await tester.pump(kFastFeedbackInteral);
|
||||
await gesture.moveBy(hour3 - hour0);
|
||||
await tester.pump(kFastFeedbackInteral);
|
||||
await gesture.moveBy(hour0 - hour3);
|
||||
await gesture.up();
|
||||
await finishPicker(tester);
|
||||
expect(hapticFeedbackCount, 1);
|
||||
});
|
||||
|
||||
testWidgets('slow drag-select vibrates once', (WidgetTester tester) async {
|
||||
Point center = await startPicker(tester, (TimeOfDay time) { });
|
||||
Point hour0 = new Point(center.x, center.y - 50.0);
|
||||
Point hour3 = new Point(center.x + 50.0, center.y);
|
||||
|
||||
TestGesture gesture = await tester.startGesture(hour3);
|
||||
await gesture.moveBy(hour0 - hour3);
|
||||
await tester.pump(kSlowFeedbackInteral);
|
||||
await gesture.moveBy(hour3 - hour0);
|
||||
await tester.pump(kSlowFeedbackInteral);
|
||||
await gesture.moveBy(hour0 - hour3);
|
||||
await gesture.up();
|
||||
await finishPicker(tester);
|
||||
expect(hapticFeedbackCount, 3);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user