mirror of
https://github.com/flutter/flutter
synced 2024-10-03 23:14:12 +00:00
Add optional DragAnchorStrategy to Draggable (#73143)
This commit is contained in:
parent
4807f806e2
commit
4b150b135f
|
@ -116,6 +116,7 @@ class ExampleDragSource extends StatelessWidget {
|
|||
|
||||
Offset feedbackOffset;
|
||||
DragAnchor anchor;
|
||||
DragAnchorStrategy dragAnchorStrategy;
|
||||
if (!under) {
|
||||
feedback = Transform(
|
||||
transform: Matrix4.identity()
|
||||
|
@ -124,9 +125,11 @@ class ExampleDragSource extends StatelessWidget {
|
|||
);
|
||||
feedbackOffset = const Offset(0.0, -kFingerSize);
|
||||
anchor = DragAnchor.pointer;
|
||||
dragAnchorStrategy = pointerDragAnchorStrategy;
|
||||
} else {
|
||||
feedbackOffset = Offset.zero;
|
||||
anchor = DragAnchor.child;
|
||||
dragAnchorStrategy = childDragAnchorStrategy;
|
||||
}
|
||||
|
||||
if (heavy) {
|
||||
|
@ -143,7 +146,7 @@ class ExampleDragSource extends StatelessWidget {
|
|||
child: contents,
|
||||
feedback: feedback,
|
||||
feedbackOffset: feedbackOffset,
|
||||
dragAnchor: anchor,
|
||||
dragAnchorStrategy: dragAnchorStrategy,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,24 @@ typedef DragTargetLeave<T> = void Function(T? data);
|
|||
/// Used by [DragTarget.onMove].
|
||||
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.
|
||||
enum DragAnchor {
|
||||
/// Display the feedback anchored at the position of the original child. If
|
||||
|
@ -191,7 +209,12 @@ class Draggable<T extends Object> extends StatefulWidget {
|
|||
this.axis,
|
||||
this.childWhenDragging,
|
||||
this.feedbackOffset = Offset.zero,
|
||||
@Deprecated(
|
||||
'Use dragAnchorStrategy instead. '
|
||||
'This feature was deprecated after v2.1.0-10.0.pre.'
|
||||
)
|
||||
this.dragAnchor = DragAnchor.child,
|
||||
this.dragAnchorStrategy,
|
||||
this.affinity,
|
||||
this.maxSimultaneousDrags,
|
||||
this.onDragStarted,
|
||||
|
@ -263,8 +286,23 @@ class Draggable<T extends Object> extends StatefulWidget {
|
|||
final Offset feedbackOffset;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// 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
|
||||
/// the semantics tree.
|
||||
///
|
||||
|
@ -308,7 +346,7 @@ class Draggable<T extends Object> extends StatefulWidget {
|
|||
/// Called when the draggable starts being dragged.
|
||||
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
|
||||
/// 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>> {
|
|||
if (widget.maxSimultaneousDrags != null && _activeCount >= widget.maxSimultaneousDrags!)
|
||||
return null;
|
||||
final Offset dragStartPoint;
|
||||
switch (widget.dragAnchor) {
|
||||
case DragAnchor.child:
|
||||
final RenderBox renderObject = context.findRenderObject()! as RenderBox;
|
||||
dragStartPoint = renderObject.globalToLocal(position);
|
||||
break;
|
||||
case DragAnchor.pointer:
|
||||
dragStartPoint = Offset.zero;
|
||||
break;
|
||||
if (widget.dragAnchorStrategy == null) {
|
||||
switch (widget.dragAnchor) {
|
||||
case DragAnchor.child:
|
||||
dragStartPoint = childDragAnchorStrategy(widget, context, position);
|
||||
break;
|
||||
case DragAnchor.pointer:
|
||||
dragStartPoint = pointerDragAnchorStrategy(widget, context, position);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
dragStartPoint = widget.dragAnchorStrategy!(widget, context, position);
|
||||
}
|
||||
setState(() {
|
||||
_activeCount += 1;
|
||||
|
|
|
@ -2540,7 +2540,6 @@ void main() {
|
|||
expect(find.text('Target'), findsOneWidget);
|
||||
expect(onDragCompletedCalled, isFalse);
|
||||
|
||||
|
||||
final Offset secondLocation = tester.getCenter(find.text('Target'));
|
||||
await gesture.moveTo(secondLocation);
|
||||
await tester.pump();
|
||||
|
@ -3019,6 +3018,30 @@ void main() {
|
|||
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 {
|
||||
const HitTestBehavior hitTestBehavior = HitTestBehavior.deferToChild;
|
||||
|
||||
|
|
Loading…
Reference in a new issue