mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[CupertinoActionSheet] Add haptic feedback (#151420)
This PR implements the behavior of native action sheets that produces haptic feedback when the user slides into a button.
This commit is contained in:
parent
83bfab5376
commit
f50feec5be
@ -14,6 +14,7 @@ import 'dart:ui' show ImageFilter, lerpDouble;
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'colors.dart';
|
||||
@ -595,7 +596,10 @@ abstract class _ActionSheetSlideTarget {
|
||||
// * The point has contacted the screen in this region. In this case, this
|
||||
// method is called as soon as the pointer down event occurs regardless of
|
||||
// whether the gesture wins the arena immediately.
|
||||
void didEnter();
|
||||
//
|
||||
// The `fromPointerDown` should be true if this callback is triggered by a
|
||||
// PointerDownEvent, i.e. the second case from the list above.
|
||||
void didEnter({required bool fromPointerDown});
|
||||
|
||||
// A pointer has exited this region.
|
||||
//
|
||||
@ -660,7 +664,10 @@ class _TargetSelectionGestureRecognizer extends GestureRecognizer {
|
||||
// Collect the `_ActionSheetSlideTarget`s that are currently hit by the
|
||||
// pointer, check whether the current target have changed, and invoke their
|
||||
// methods if necessary.
|
||||
void _updateDrag(Offset pointerPosition) {
|
||||
//
|
||||
// The `fromPointerDown` should be true if this update is triggered by a
|
||||
// PointerDownEvent.
|
||||
void _updateDrag(Offset pointerPosition, {required bool fromPointerDown}) {
|
||||
final HitTestResult result = hitTest(pointerPosition);
|
||||
|
||||
// A slide target might nest other targets, therefore multiple targets might
|
||||
@ -686,21 +693,21 @@ class _TargetSelectionGestureRecognizer extends GestureRecognizer {
|
||||
..clear()
|
||||
..addAll(foundTargets);
|
||||
for (final _ActionSheetSlideTarget target in _currentTargets) {
|
||||
target.didEnter();
|
||||
target.didEnter(fromPointerDown: fromPointerDown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _onDown(DragDownDetails details) {
|
||||
_updateDrag(details.globalPosition);
|
||||
_updateDrag(details.globalPosition, fromPointerDown: true);
|
||||
}
|
||||
|
||||
void _onUpdate(Offset globalPosition) {
|
||||
_updateDrag(globalPosition);
|
||||
_updateDrag(globalPosition, fromPointerDown: false);
|
||||
}
|
||||
|
||||
void _onEnd(Offset globalPosition) {
|
||||
_updateDrag(globalPosition);
|
||||
_updateDrag(globalPosition, fromPointerDown: false);
|
||||
for (final _ActionSheetSlideTarget target in _currentTargets) {
|
||||
target.didConfirm();
|
||||
}
|
||||
@ -1121,7 +1128,7 @@ class _CupertinoActionSheetActionState extends State<CupertinoActionSheetAction>
|
||||
implements _ActionSheetSlideTarget {
|
||||
// |_ActionSheetSlideTarget|
|
||||
@override
|
||||
void didEnter() {}
|
||||
void didEnter({required bool fromPointerDown}) {}
|
||||
|
||||
// |_ActionSheetSlideTarget|
|
||||
@override
|
||||
@ -1243,11 +1250,27 @@ class _ActionSheetButtonBackground extends StatefulWidget {
|
||||
class _ActionSheetButtonBackgroundState extends State<_ActionSheetButtonBackground> implements _ActionSheetSlideTarget {
|
||||
bool isBeingPressed = false;
|
||||
|
||||
void _emitVibration(){
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.android:
|
||||
HapticFeedback.selectionClick();
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.linux:
|
||||
case TargetPlatform.macOS:
|
||||
case TargetPlatform.windows:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// |_ActionSheetSlideTarget|
|
||||
@override
|
||||
void didEnter() {
|
||||
void didEnter({required bool fromPointerDown}) {
|
||||
setState(() { isBeingPressed = true; });
|
||||
widget.onPressStateChange?.call(true);
|
||||
if (!fromPointerDown) {
|
||||
_emitVibration();
|
||||
}
|
||||
}
|
||||
|
||||
// |_ActionSheetSlideTarget|
|
||||
|
||||
@ -12,6 +12,7 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
@ -1900,6 +1901,50 @@ void main() {
|
||||
kIsWeb ? SystemMouseCursors.click : SystemMouseCursors.basic,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Action sheets emits haptic vibration on sliding into a button', (WidgetTester tester) async {
|
||||
int vibrationCount = 0;
|
||||
|
||||
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, (MethodCall methodCall) async {
|
||||
if (methodCall.method == 'HapticFeedback.vibrate') {
|
||||
expect(methodCall.arguments, 'HapticFeedbackType.selectionClick');
|
||||
vibrationCount += 1;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
await tester.pumpWidget(
|
||||
createAppWithButtonThatLaunchesActionSheet(
|
||||
CupertinoActionSheet(
|
||||
title: const Text('The title'),
|
||||
actions: <Widget>[
|
||||
CupertinoActionSheetAction(child: const Text('One'), onPressed: () {}),
|
||||
CupertinoActionSheetAction(child: const Text('Two'), onPressed: () {}),
|
||||
CupertinoActionSheetAction(child: const Text('Three'), onPressed: () {}),
|
||||
],
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('Go'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.text('One')));
|
||||
await tester.pumpAndSettle();
|
||||
// Tapping down on a button should not emit vibration.
|
||||
expect(vibrationCount, 0);
|
||||
|
||||
await gesture.moveTo(tester.getCenter(find.text('Two')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(vibrationCount, 1);
|
||||
|
||||
await gesture.moveTo(tester.getCenter(find.text('Three')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(vibrationCount, 2);
|
||||
|
||||
await gesture.up();
|
||||
expect(vibrationCount, 2);
|
||||
}, variant: TargetPlatformVariant.only(TargetPlatform.iOS));
|
||||
}
|
||||
|
||||
RenderBox findScrollableActionsSectionRenderBox(WidgetTester tester) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user