Unverified Commit 4b150b13 authored by Johannes von Bargen's avatar Johannes von Bargen Committed by GitHub

Add optional DragAnchorStrategy to Draggable (#73143)

parent 4807f806
...@@ -116,6 +116,7 @@ class ExampleDragSource extends StatelessWidget { ...@@ -116,6 +116,7 @@ class ExampleDragSource extends StatelessWidget {
Offset feedbackOffset; Offset feedbackOffset;
DragAnchor anchor; DragAnchor anchor;
DragAnchorStrategy dragAnchorStrategy;
if (!under) { if (!under) {
feedback = Transform( feedback = Transform(
transform: Matrix4.identity() transform: Matrix4.identity()
...@@ -124,9 +125,11 @@ class ExampleDragSource extends StatelessWidget { ...@@ -124,9 +125,11 @@ class ExampleDragSource extends StatelessWidget {
); );
feedbackOffset = const Offset(0.0, -kFingerSize); feedbackOffset = const Offset(0.0, -kFingerSize);
anchor = DragAnchor.pointer; anchor = DragAnchor.pointer;
dragAnchorStrategy = pointerDragAnchorStrategy;
} else { } else {
feedbackOffset = Offset.zero; feedbackOffset = Offset.zero;
anchor = DragAnchor.child; anchor = DragAnchor.child;
dragAnchorStrategy = childDragAnchorStrategy;
} }
if (heavy) { if (heavy) {
...@@ -143,7 +146,7 @@ class ExampleDragSource extends StatelessWidget { ...@@ -143,7 +146,7 @@ class ExampleDragSource extends StatelessWidget {
child: contents, child: contents,
feedback: feedback, feedback: feedback,
feedbackOffset: feedbackOffset, feedbackOffset: feedbackOffset,
dragAnchor: anchor, dragAnchorStrategy: dragAnchorStrategy,
); );
} }
} }
......
...@@ -66,6 +66,24 @@ typedef DragTargetLeave<T> = void Function(T? data); ...@@ -66,6 +66,24 @@ typedef DragTargetLeave<T> = void Function(T? data);
/// Used by [DragTarget.onMove]. /// Used by [DragTarget.onMove].
typedef DragTargetMove<T> = void Function(DragTargetDetails<T> details); typedef DragTargetMove<T> = void Function(DragTargetDetails<T> details);
/// Signature for the strategy that determines the drag start point.
///
/// Used for the built-in strategies switched via [DragAnchor] and the optinally
/// injectable [Draggable.dragAnchorStrategy]
typedef DragAnchorStrategy = Offset Function(Draggable<Object> draggable, BuildContext context, Offset position);
/// The default [DragAnchorStrategy] used when [Draggable.dragAnchor] is not set
/// or set to [DragAnchor.child]
Offset childDragAnchorStrategy(Draggable<Object> draggable, BuildContext context, Offset position) {
final RenderBox renderObject = context.findRenderObject()! as RenderBox;
return renderObject.globalToLocal(position);
}
/// The [DragAnchorStrategy] used when [Draggable.dragAnchor] set to
/// [DragAnchor.pointer]
Offset pointerDragAnchorStrategy(Draggable<Object> draggable, BuildContext context, Offset position) {
return Offset.zero;
}
/// Where the [Draggable] should be anchored during a drag. /// Where the [Draggable] should be anchored during a drag.
enum DragAnchor { enum DragAnchor {
/// Display the feedback anchored at the position of the original child. If /// Display the feedback anchored at the position of the original child. If
...@@ -191,7 +209,12 @@ class Draggable<T extends Object> extends StatefulWidget { ...@@ -191,7 +209,12 @@ class Draggable<T extends Object> extends StatefulWidget {
this.axis, this.axis,
this.childWhenDragging, this.childWhenDragging,
this.feedbackOffset = Offset.zero, this.feedbackOffset = Offset.zero,
@Deprecated(
'Use dragAnchorStrategy instead. '
'This feature was deprecated after v2.1.0-10.0.pre.'
)
this.dragAnchor = DragAnchor.child, this.dragAnchor = DragAnchor.child,
this.dragAnchorStrategy,
this.affinity, this.affinity,
this.maxSimultaneousDrags, this.maxSimultaneousDrags,
this.onDragStarted, this.onDragStarted,
...@@ -263,8 +286,23 @@ class Draggable<T extends Object> extends StatefulWidget { ...@@ -263,8 +286,23 @@ class Draggable<T extends Object> extends StatefulWidget {
final Offset feedbackOffset; final Offset feedbackOffset;
/// Where this widget should be anchored during a drag. /// Where this widget should be anchored during a drag.
///
/// This property is overridden by the [dragAnchorStrategy] if the latter is provided.
///
/// Defaults to [DragAnchor.child].
@Deprecated(
'Use dragAnchorStrategy instead. '
'This feature was deprecated after v2.1.0-10.0.pre.'
)
final DragAnchor dragAnchor; final DragAnchor dragAnchor;
/// A strategy that is used by this draggable to get the the anchor offset when it is dragged.
///
/// The anchor offset refers to the distance between the users' fingers and the [feedback] widget when this draggable is dragged.
///
/// Defaults to [childDragAnchorStrategy] if the [dragAnchor] is set to [DragAnchor.child] or [pointerDragAnchorStrategy] if the [dragAnchor] is set to [DragAnchor.pointer].
final DragAnchorStrategy? dragAnchorStrategy;
/// Whether the semantics of the [feedback] widget is ignored when building /// Whether the semantics of the [feedback] widget is ignored when building
/// the semantics tree. /// the semantics tree.
/// ///
...@@ -308,7 +346,7 @@ class Draggable<T extends Object> extends StatefulWidget { ...@@ -308,7 +346,7 @@ class Draggable<T extends Object> extends StatefulWidget {
/// Called when the draggable starts being dragged. /// Called when the draggable starts being dragged.
final VoidCallback? onDragStarted; final VoidCallback? onDragStarted;
/// Called when the draggable is being dragged. /// Called when the draggable is dragged.
/// ///
/// This function will only be called while this widget is still mounted to /// This function will only be called while this widget is still mounted to
/// the tree (i.e. [State.mounted] is true), and if this widget has actually moved. /// the tree (i.e. [State.mounted] is true), and if this widget has actually moved.
...@@ -487,14 +525,17 @@ class _DraggableState<T extends Object> extends State<Draggable<T>> { ...@@ -487,14 +525,17 @@ class _DraggableState<T extends Object> extends State<Draggable<T>> {
if (widget.maxSimultaneousDrags != null && _activeCount >= widget.maxSimultaneousDrags!) if (widget.maxSimultaneousDrags != null && _activeCount >= widget.maxSimultaneousDrags!)
return null; return null;
final Offset dragStartPoint; final Offset dragStartPoint;
switch (widget.dragAnchor) { if (widget.dragAnchorStrategy == null) {
case DragAnchor.child: switch (widget.dragAnchor) {
final RenderBox renderObject = context.findRenderObject()! as RenderBox; case DragAnchor.child:
dragStartPoint = renderObject.globalToLocal(position); dragStartPoint = childDragAnchorStrategy(widget, context, position);
break; break;
case DragAnchor.pointer: case DragAnchor.pointer:
dragStartPoint = Offset.zero; dragStartPoint = pointerDragAnchorStrategy(widget, context, position);
break; break;
}
} else {
dragStartPoint = widget.dragAnchorStrategy!(widget, context, position);
} }
setState(() { setState(() {
_activeCount += 1; _activeCount += 1;
......
...@@ -2540,7 +2540,6 @@ void main() { ...@@ -2540,7 +2540,6 @@ void main() {
expect(find.text('Target'), findsOneWidget); expect(find.text('Target'), findsOneWidget);
expect(onDragCompletedCalled, isFalse); expect(onDragCompletedCalled, isFalse);
final Offset secondLocation = tester.getCenter(find.text('Target')); final Offset secondLocation = tester.getCenter(find.text('Target'));
await gesture.moveTo(secondLocation); await gesture.moveTo(secondLocation);
await tester.pump(); await tester.pump();
...@@ -3019,6 +3018,30 @@ void main() { ...@@ -3019,6 +3018,30 @@ void main() {
semantics.dispose(); semantics.dispose();
}); });
testWidgets('Drag and drop - when a dragAnchorStrategy is provided it gets called', (WidgetTester tester) async {
bool dragAnchorStrategyCalled = false;
await tester.pumpWidget(MaterialApp(
home: Column(
children: <Widget>[
Draggable<int>(
child: const Text('Source'),
feedback: const Text('Feedback'),
dragAnchorStrategy: (Draggable<Object> widget, BuildContext context, Offset position) {
dragAnchorStrategyCalled = true;
return const Offset(0, 0);
}
)
],
),
));
final Offset location = tester.getCenter(find.text('Source'));
await tester.startGesture(location, pointer: 7);
expect(dragAnchorStrategyCalled, true);
});
testWidgets('configurable Draggable hit test behavior', (WidgetTester tester) async { testWidgets('configurable Draggable 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