Unverified Commit e5beafaf authored by xubaolin's avatar xubaolin Committed by GitHub

Expose `ignoringPointer` property for `Draggable` and `LongPressDraggable` (#100475)

parent 5bb6b071
...@@ -154,6 +154,11 @@ enum DragAnchor { ...@@ -154,6 +154,11 @@ enum DragAnchor {
/// user lifts their finger while on top of a [DragTarget], that target is given /// user lifts their finger while on top of a [DragTarget], that target is given
/// the opportunity to accept the [data] carried by the draggable. /// the opportunity to accept the [data] carried by the draggable.
/// ///
/// The [ignoringFeedbackPointer] defaults to true, which means that
/// the [feedback] widget ignores the pointer during hit testing. Similarly,
/// [ignoringFeedbackSemantics] defaults to true, and the [feedback] also ignores
/// semantics when building the semantics tree.
///
/// On multitouch devices, multiple drags can occur simultaneously because there /// On multitouch devices, multiple drags can occur simultaneously because there
/// can be multiple pointers in contact with the device at once. To limit the /// can be multiple pointers in contact with the device at once. To limit the
/// number of simultaneous drags, use the [maxSimultaneousDrags] property. The /// number of simultaneous drags, use the [maxSimultaneousDrags] property. The
...@@ -207,11 +212,13 @@ class Draggable<T extends Object> extends StatefulWidget { ...@@ -207,11 +212,13 @@ class Draggable<T extends Object> extends StatefulWidget {
this.onDragEnd, this.onDragEnd,
this.onDragCompleted, this.onDragCompleted,
this.ignoringFeedbackSemantics = true, this.ignoringFeedbackSemantics = true,
this.ignoringFeedbackPointer = true,
this.rootOverlay = false, this.rootOverlay = false,
this.hitTestBehavior = HitTestBehavior.deferToChild, this.hitTestBehavior = HitTestBehavior.deferToChild,
}) : assert(child != null), }) : assert(child != null),
assert(feedback != null), assert(feedback != null),
assert(ignoringFeedbackSemantics != null), assert(ignoringFeedbackSemantics != null),
assert(ignoringFeedbackPointer != null),
assert(maxSimultaneousDrags == null || maxSimultaneousDrags >= 0); assert(maxSimultaneousDrags == null || maxSimultaneousDrags >= 0);
/// The data that will be dropped by this draggable. /// The data that will be dropped by this draggable.
...@@ -310,6 +317,14 @@ class Draggable<T extends Object> extends StatefulWidget { ...@@ -310,6 +317,14 @@ class Draggable<T extends Object> extends StatefulWidget {
/// Defaults to true. /// Defaults to true.
final bool ignoringFeedbackSemantics; final bool ignoringFeedbackSemantics;
/// Whether the [feedback] widget is ignored during hit testing.
///
/// Regardless of whether this widget is ignored during hit testing, it will
/// still consume space during layout and be visible during painting.
///
/// Defaults to true.
final bool ignoringFeedbackPointer;
/// Controls how this widget competes with other gestures to initiate a drag. /// Controls how this widget competes with other gestures to initiate a drag.
/// ///
/// If affinity is null, this widget initiates a drag as soon as it recognizes /// If affinity is null, this widget initiates a drag as soon as it recognizes
...@@ -447,6 +462,7 @@ class LongPressDraggable<T extends Object> extends Draggable<T> { ...@@ -447,6 +462,7 @@ class LongPressDraggable<T extends Object> extends Draggable<T> {
super.onDragCompleted, super.onDragCompleted,
this.hapticFeedbackOnStart = true, this.hapticFeedbackOnStart = true,
super.ignoringFeedbackSemantics, super.ignoringFeedbackSemantics,
super.ignoringFeedbackPointer,
this.delay = kLongPressTimeout, this.delay = kLongPressTimeout,
}); });
...@@ -542,6 +558,7 @@ class _DraggableState<T extends Object> extends State<Draggable<T>> { ...@@ -542,6 +558,7 @@ class _DraggableState<T extends Object> extends State<Draggable<T>> {
feedback: widget.feedback, feedback: widget.feedback,
feedbackOffset: widget.feedbackOffset, feedbackOffset: widget.feedbackOffset,
ignoringFeedbackSemantics: widget.ignoringFeedbackSemantics, ignoringFeedbackSemantics: widget.ignoringFeedbackSemantics,
ignoringFeedbackPointer: widget.ignoringFeedbackPointer,
onDragUpdate: (DragUpdateDetails details) { onDragUpdate: (DragUpdateDetails details) {
if (mounted && widget.onDragUpdate != null) { if (mounted && widget.onDragUpdate != null) {
widget.onDragUpdate!(details); widget.onDragUpdate!(details);
...@@ -796,8 +813,10 @@ class _DragAvatar<T extends Object> extends Drag { ...@@ -796,8 +813,10 @@ class _DragAvatar<T extends Object> extends Drag {
this.onDragUpdate, this.onDragUpdate,
this.onDragEnd, this.onDragEnd,
required this.ignoringFeedbackSemantics, required this.ignoringFeedbackSemantics,
required this.ignoringFeedbackPointer,
}) : assert(overlayState != null), }) : assert(overlayState != null),
assert(ignoringFeedbackSemantics != null), assert(ignoringFeedbackSemantics != null),
assert(ignoringFeedbackPointer != null),
assert(dragStartPoint != null), assert(dragStartPoint != null),
assert(feedbackOffset != null), assert(feedbackOffset != null),
_position = initialPosition { _position = initialPosition {
...@@ -815,6 +834,7 @@ class _DragAvatar<T extends Object> extends Drag { ...@@ -815,6 +834,7 @@ class _DragAvatar<T extends Object> extends Drag {
final _OnDragEnd? onDragEnd; final _OnDragEnd? onDragEnd;
final OverlayState overlayState; final OverlayState overlayState;
final bool ignoringFeedbackSemantics; final bool ignoringFeedbackSemantics;
final bool ignoringFeedbackPointer;
_DragTargetState<Object>? _activeTarget; _DragTargetState<Object>? _activeTarget;
final List<_DragTargetState<Object>> _enteredTargets = <_DragTargetState<Object>>[]; final List<_DragTargetState<Object>> _enteredTargets = <_DragTargetState<Object>>[];
...@@ -937,6 +957,7 @@ class _DragAvatar<T extends Object> extends Drag { ...@@ -937,6 +957,7 @@ class _DragAvatar<T extends Object> extends Drag {
left: _lastOffset!.dx - overlayTopLeft.dx, left: _lastOffset!.dx - overlayTopLeft.dx,
top: _lastOffset!.dy - overlayTopLeft.dy, top: _lastOffset!.dy - overlayTopLeft.dy,
child: IgnorePointer( child: IgnorePointer(
ignoring: ignoringFeedbackPointer,
ignoringSemantics: ignoringFeedbackSemantics, ignoringSemantics: ignoringFeedbackSemantics,
child: feedback, child: feedback,
), ),
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/semantics.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
...@@ -3074,6 +3074,96 @@ void main() { ...@@ -3074,6 +3074,96 @@ void main() {
expect(tester.widget<Listener>(find.byType(Listener).first).behavior, hitTestBehavior); expect(tester.widget<Listener>(find.byType(Listener).first).behavior, hitTestBehavior);
}); });
// Regression test for https://github.com/flutter/flutter/issues/92083
testWidgets('feedback respect the MouseRegion cursor configure', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Column(
children: const <Widget>[
Draggable<int>(
ignoringFeedbackPointer: false,
feedback: MouseRegion(
cursor: SystemMouseCursors.grabbing,
child: SizedBox(height: 50.0, child: Text('Draggable')),
),
child: SizedBox(height: 50.0, child: Text('Target')),
),
],
),
),
);
final Offset location = tester.getCenter(find.text('Target'));
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer(location: location);
addTearDown(gesture.removePointer);
await gesture.down(location);
await tester.pump();
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.grabbing);
});
testWidgets('configurable feedback ignore pointer behavior', (WidgetTester tester) async {
bool onTap = false;
await tester.pumpWidget(
MaterialApp(
home: Column(
children: <Widget>[
Draggable<int>(
ignoringFeedbackPointer: false,
feedback: GestureDetector(
onTap: () => onTap = true,
child: const SizedBox(height: 50.0, child: Text('Draggable')),
),
child: const SizedBox(height: 50.0, child: Text('Target')),
),
],
),
),
);
final Offset location = tester.getCenter(find.text('Target'));
final TestGesture gesture = await tester.startGesture(location, pointer: 7);
final Offset secondLocation = location + const Offset(7.0, 7.0);
await gesture.moveTo(secondLocation);
await tester.pump();
await tester.tap(find.text('Draggable'));
expect(onTap, true);
});
testWidgets('configurable feedback ignore pointer behavior - LongPressDraggable', (WidgetTester tester) async {
bool onTap = false;
await tester.pumpWidget(
MaterialApp(
home: Column(
children: <Widget>[
LongPressDraggable<int>(
ignoringFeedbackPointer: false,
feedback: GestureDetector(
onTap: () => onTap = true,
child: const SizedBox(height: 50.0, child: Text('Draggable')),
),
child: const SizedBox(height: 50.0, child: Text('Target')),
),
],
),
),
);
final Offset location = tester.getCenter(find.text('Target'));
final TestGesture gesture = await tester.startGesture(location, pointer: 7);
await tester.pump(kLongPressTimeout);
final Offset secondLocation = location + const Offset(7.0, 7.0);
await gesture.moveTo(secondLocation);
await tester.pump();
await tester.tap(find.text('Draggable'));
expect(onTap, true);
});
testWidgets('configurable DragTarget hit test behavior', (WidgetTester tester) async { testWidgets('configurable DragTarget hit test behavior', (WidgetTester tester) async {
const HitTestBehavior hitTestBehavior = HitTestBehavior.deferToChild; const HitTestBehavior hitTestBehavior = HitTestBehavior.deferToChild;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment