Unverified Commit 635dfc3e authored by gabber235's avatar gabber235 Committed by GitHub

Draggable: onDragUpdate callback (#68185)

parent d12098da
......@@ -36,6 +36,11 @@ typedef DragTargetAcceptWithDetails<T> = void Function(DragTargetDetails<T> deta
/// Used by [DragTarget.builder].
typedef DragTargetBuilder<T> = Widget Function(BuildContext context, List<T?> candidateData, List<dynamic> rejectedData);
/// Signature for when a [Draggable] is dragged across the screen.
///
/// Used by [Draggable.onDragUpdate].
typedef DragUpdateCallback = void Function(DragUpdateDetails details);
/// Signature for when a [Draggable] is dropped without being accepted by a [DragTarget].
///
/// Used by [Draggable.onDraggableCanceled].
......@@ -187,6 +192,7 @@ class Draggable<T extends Object> extends StatefulWidget {
this.affinity,
this.maxSimultaneousDrags,
this.onDragStarted,
this.onDragUpdate,
this.onDraggableCanceled,
this.onDragEnd,
this.onDragCompleted,
......@@ -298,6 +304,12 @@ class Draggable<T extends Object> extends StatefulWidget {
/// Called when the draggable starts being dragged.
final VoidCallback? onDragStarted;
/// Called when the draggable is being 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.
final DragUpdateCallback? onDragUpdate;
/// Called when the draggable is dropped without being accepted by a [DragTarget].
///
/// This function might be called after this widget has been removed from the
......@@ -374,6 +386,7 @@ class LongPressDraggable<T extends Object> extends Draggable<T> {
DragAnchor dragAnchor = DragAnchor.child,
int? maxSimultaneousDrags,
VoidCallback? onDragStarted,
DragUpdateCallback? onDragUpdate,
DraggableCanceledCallback? onDraggableCanceled,
DragEndCallback? onDragEnd,
VoidCallback? onDragCompleted,
......@@ -390,6 +403,7 @@ class LongPressDraggable<T extends Object> extends Draggable<T> {
dragAnchor: dragAnchor,
maxSimultaneousDrags: maxSimultaneousDrags,
onDragStarted: onDragStarted,
onDragUpdate: onDragUpdate,
onDraggableCanceled: onDraggableCanceled,
onDragEnd: onDragEnd,
onDragCompleted: onDragCompleted,
......@@ -474,6 +488,11 @@ class _DraggableState<T extends Object> extends State<Draggable<T>> {
feedback: widget.feedback,
feedbackOffset: widget.feedbackOffset,
ignoringFeedbackSemantics: widget.ignoringFeedbackSemantics,
onDragUpdate: (DragUpdateDetails details) {
if (mounted && widget.onDragUpdate != null) {
widget.onDragUpdate!(details);
}
},
onDragEnd: (Velocity velocity, Offset offset, bool wasAccepted) {
if (mounted) {
setState(() {
......@@ -708,6 +727,7 @@ class _DragAvatar<T extends Object> extends Drag {
this.dragStartPoint = Offset.zero,
this.feedback,
this.feedbackOffset = Offset.zero,
this.onDragUpdate,
this.onDragEnd,
required this.ignoringFeedbackSemantics,
}) : assert(overlayState != null),
......@@ -725,6 +745,7 @@ class _DragAvatar<T extends Object> extends Drag {
final Offset dragStartPoint;
final Widget? feedback;
final Offset feedbackOffset;
final DragUpdateCallback? onDragUpdate;
final _OnDragEnd? onDragEnd;
final OverlayState overlayState;
final bool ignoringFeedbackSemantics;
......@@ -737,8 +758,12 @@ class _DragAvatar<T extends Object> extends Drag {
@override
void update(DragUpdateDetails details) {
final Offset oldPosition = _position;
_position += _restrictAxis(details.delta);
updateDrag(_position);
if (onDragUpdate != null && _position != oldPosition) {
onDragUpdate!(details);
}
}
@override
......
......@@ -840,6 +840,158 @@ void main() {
});
});
group('Drag and drop - onDragUpdate called if draggable moves along a set axis', () {
int updated = 0;
Offset dragDelta = Offset.zero;
setUp(() {
updated = 0;
dragDelta = Offset.zero;
});
Widget build() {
return MaterialApp(
home: Column(
children: <Widget>[
Draggable<int>(
data: 1,
child: const Text('Source'),
feedback: const Text('Dragging'),
onDragUpdate: (DragUpdateDetails details) {
dragDelta += details.delta;
updated++;
},
),
Draggable<int>(
data: 2,
child: const Text('Vertical Source'),
feedback: const Text('Vertical Dragging'),
onDragUpdate: (DragUpdateDetails details) {
dragDelta += details.delta;
updated++;
},
axis: Axis.vertical,
),
Draggable<int>(
data: 3,
child: const Text('Horizontal Source'),
feedback: const Text('Horizontal Dragging'),
onDragUpdate: (DragUpdateDetails details) {
dragDelta += details.delta;
updated++;
},
axis: Axis.horizontal,
),
],
),
);
}
testWidgets('Null axis onDragUpdate called only if draggable moves in any direction', (WidgetTester tester) async {
await tester.pumpWidget(build());
expect(updated, 0);
expect(find.text('Source'), findsOneWidget);
expect(find.text('Dragging'), findsNothing);
final Offset firstLocation = tester.getCenter(find.text('Source'));
final TestGesture gesture = await tester.startGesture(firstLocation, pointer: 7);
await tester.pump();
expect(updated, 0);
expect(find.text('Source'), findsOneWidget);
expect(find.text('Dragging'), findsOneWidget);
await gesture.moveBy(const Offset(10, 10));
await tester.pump();
expect(updated, 1);
await gesture.moveBy(Offset.zero);
await tester.pump();
expect(updated, 1);
await gesture.up();
await tester.pump();
expect(updated, 1);
expect(find.text('Source'), findsOneWidget);
expect(find.text('Dragging'), findsNothing);
expect(dragDelta.dx, 10);
expect(dragDelta.dy, 10);
});
testWidgets('Vertical axis onDragUpdate only called if draggable moves vertical', (WidgetTester tester) async {
await tester.pumpWidget(build());
expect(updated, 0);
expect(find.text('Vertical Source'), findsOneWidget);
expect(find.text('Vertical Dragging'), findsNothing);
final Offset firstLocation = tester.getCenter(find.text('Vertical Source'));
final TestGesture gesture = await tester.startGesture(firstLocation, pointer: 7);
await tester.pump();
expect(updated, 0);
expect(find.text('Vertical Source'), findsOneWidget);
expect(find.text('Vertical Dragging'), findsOneWidget);
await gesture.moveBy(const Offset(0, 10));
await tester.pump();
expect(updated, 1);
await gesture.moveBy(const Offset(10 , 0));
await tester.pump();
expect(updated, 1);
await gesture.up();
await tester.pump();
expect(updated, 1);
expect(find.text('Vertical Source'), findsOneWidget);
expect(find.text('Vertical Dragging'), findsNothing);
expect(dragDelta.dx, 0);
expect(dragDelta.dy, 10);
});
testWidgets('Horizontal axis onDragUpdate only called if draggable moves horizontal', (WidgetTester tester) async {
await tester.pumpWidget(build());
expect(updated, 0);
expect(find.text('Horizontal Source'), findsOneWidget);
expect(find.text('Horizontal Dragging'), findsNothing);
final Offset firstLocation = tester.getCenter(find.text('Horizontal Source'));
final TestGesture gesture = await tester.startGesture(firstLocation, pointer: 7);
await tester.pump();
expect(updated, 0);
expect(find.text('Horizontal Source'), findsOneWidget);
expect(find.text('Horizontal Dragging'), findsOneWidget);
await gesture.moveBy(const Offset(0, 10));
await tester.pump();
expect(updated, 0);
await gesture.moveBy(const Offset(10 , 0));
await tester.pump();
expect(updated, 1);
await gesture.up();
await tester.pump();
expect(updated, 1);
expect(find.text('Horizontal Source'), findsOneWidget);
expect(find.text('Horizontal Dragging'), findsNothing);
expect(dragDelta.dx, 10);
expect(dragDelta.dy, 0);
});
});
testWidgets('Drag and drop - onDraggableCanceled not called if dropped on accepting target', (WidgetTester tester) async {
final List<int> accepted = <int>[];
......
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