From 7b07b85d919e184ab8deb62141e222b47eb2bcfe Mon Sep 17 00:00:00 2001 From: Callum Moffat Date: Thu, 21 Jul 2022 16:15:05 -0400 Subject: [PATCH] Add supportedDevices parameter to GestureDetector (#107312) --- .../lib/src/widgets/gesture_detector.dart | 22 +++++---- .../test/widgets/gesture_detector_test.dart | 47 +++++++++++++++++++ 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/packages/flutter/lib/src/widgets/gesture_detector.dart b/packages/flutter/lib/src/widgets/gesture_detector.dart index 23453645c7..6ac0650832 100644 --- a/packages/flutter/lib/src/widgets/gesture_detector.dart +++ b/packages/flutter/lib/src/widgets/gesture_detector.dart @@ -288,6 +288,7 @@ class GestureDetector extends StatelessWidget { this.behavior, this.excludeFromSemantics = false, this.dragStartBehavior = DragStartBehavior.start, + this.supportedDevices, }) : assert(excludeFromSemantics != null), assert(dragStartBehavior != null), assert(() { @@ -1004,6 +1005,11 @@ class GestureDetector extends StatelessWidget { /// * [DragGestureRecognizer.dragStartBehavior], which gives an example for the different behaviors. final DragStartBehavior dragStartBehavior; + /// The kind of devices that are allowed to be recognized. + /// + /// If set to null, events from all device types will be recognized. Defaults to null. + final Set? supportedDevices; + @override Widget build(BuildContext context) { final Map gestures = {}; @@ -1022,7 +1028,7 @@ class GestureDetector extends StatelessWidget { onTertiaryTapCancel != null ) { gestures[TapGestureRecognizer] = GestureRecognizerFactoryWithHandlers( - () => TapGestureRecognizer(debugOwner: this), + () => TapGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices), (TapGestureRecognizer instance) { instance ..onTapDown = onTapDown @@ -1043,7 +1049,7 @@ class GestureDetector extends StatelessWidget { if (onDoubleTap != null) { gestures[DoubleTapGestureRecognizer] = GestureRecognizerFactoryWithHandlers( - () => DoubleTapGestureRecognizer(debugOwner: this), + () => DoubleTapGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices), (DoubleTapGestureRecognizer instance) { instance ..onDoubleTapDown = onDoubleTapDown @@ -1076,7 +1082,7 @@ class GestureDetector extends StatelessWidget { onTertiaryLongPressUp != null || onTertiaryLongPressEnd != null) { gestures[LongPressGestureRecognizer] = GestureRecognizerFactoryWithHandlers( - () => LongPressGestureRecognizer(debugOwner: this), + () => LongPressGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices), (LongPressGestureRecognizer instance) { instance ..onLongPressDown = onLongPressDown @@ -1111,7 +1117,7 @@ class GestureDetector extends StatelessWidget { onVerticalDragEnd != null || onVerticalDragCancel != null) { gestures[VerticalDragGestureRecognizer] = GestureRecognizerFactoryWithHandlers( - () => VerticalDragGestureRecognizer(debugOwner: this), + () => VerticalDragGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices), (VerticalDragGestureRecognizer instance) { instance ..onDown = onVerticalDragDown @@ -1131,7 +1137,7 @@ class GestureDetector extends StatelessWidget { onHorizontalDragEnd != null || onHorizontalDragCancel != null) { gestures[HorizontalDragGestureRecognizer] = GestureRecognizerFactoryWithHandlers( - () => HorizontalDragGestureRecognizer(debugOwner: this), + () => HorizontalDragGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices), (HorizontalDragGestureRecognizer instance) { instance ..onDown = onHorizontalDragDown @@ -1151,7 +1157,7 @@ class GestureDetector extends StatelessWidget { onPanEnd != null || onPanCancel != null) { gestures[PanGestureRecognizer] = GestureRecognizerFactoryWithHandlers( - () => PanGestureRecognizer(debugOwner: this), + () => PanGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices), (PanGestureRecognizer instance) { instance ..onDown = onPanDown @@ -1167,7 +1173,7 @@ class GestureDetector extends StatelessWidget { if (onScaleStart != null || onScaleUpdate != null || onScaleEnd != null) { gestures[ScaleGestureRecognizer] = GestureRecognizerFactoryWithHandlers( - () => ScaleGestureRecognizer(debugOwner: this), + () => ScaleGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices), (ScaleGestureRecognizer instance) { instance ..onStart = onScaleStart @@ -1184,7 +1190,7 @@ class GestureDetector extends StatelessWidget { onForcePressUpdate != null || onForcePressEnd != null) { gestures[ForcePressGestureRecognizer] = GestureRecognizerFactoryWithHandlers( - () => ForcePressGestureRecognizer(debugOwner: this), + () => ForcePressGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices), (ForcePressGestureRecognizer instance) { instance ..onStart = onForcePressStart diff --git a/packages/flutter/test/widgets/gesture_detector_test.dart b/packages/flutter/test/widgets/gesture_detector_test.dart index 6ba520e86a..1463e24860 100644 --- a/packages/flutter/test/widgets/gesture_detector_test.dart +++ b/packages/flutter/test/widgets/gesture_detector_test.dart @@ -875,6 +875,53 @@ void main() { }); }); }); + + testWidgets('supportedDevices is respected', (WidgetTester tester) async { + bool didStartPan = false; + Offset? panDelta; + bool didEndPan = false; + + await tester.pumpWidget( + GestureDetector( + onPanStart: (DragStartDetails details) { + didStartPan = true; + }, + onPanUpdate: (DragUpdateDetails details) { + panDelta = (panDelta ?? Offset.zero) + details.delta; + }, + onPanEnd: (DragEndDetails details) { + didEndPan = true; + }, + supportedDevices: const {PointerDeviceKind.mouse}, + child: Container( + color: const Color(0xFF00FF00), + ) + ), + ); + + expect(didStartPan, isFalse); + expect(panDelta, isNull); + expect(didEndPan, isFalse); + + await tester.dragFrom(const Offset(10.0, 10.0), const Offset(20.0, 30.0), kind: PointerDeviceKind.mouse); + + // Matching device should allow gesture. + expect(didStartPan, isTrue); + expect(panDelta!.dx, 20.0); + expect(panDelta!.dy, 30.0); + expect(didEndPan, isTrue); + + didStartPan = false; + panDelta = null; + didEndPan = false; + + await tester.dragFrom(const Offset(10.0, 10.0), const Offset(20.0, 30.0), kind: PointerDeviceKind.stylus); + + // Non-matching device should not lead to any callbacks. + expect(didStartPan, isFalse); + expect(panDelta, isNull); + expect(didEndPan, isFalse); + }); } class _EmptySemanticsGestureDelegate extends SemanticsGestureDelegate {