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 ...@@ -36,6 +36,11 @@ typedef DragTargetAcceptWithDetails<T> = void Function(DragTargetDetails<T> deta
/// Used by [DragTarget.builder]. /// Used by [DragTarget.builder].
typedef DragTargetBuilder<T> = Widget Function(BuildContext context, List<T?> candidateData, List<dynamic> rejectedData); 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]. /// Signature for when a [Draggable] is dropped without being accepted by a [DragTarget].
/// ///
/// Used by [Draggable.onDraggableCanceled]. /// Used by [Draggable.onDraggableCanceled].
...@@ -187,6 +192,7 @@ class Draggable<T extends Object> extends StatefulWidget { ...@@ -187,6 +192,7 @@ class Draggable<T extends Object> extends StatefulWidget {
this.affinity, this.affinity,
this.maxSimultaneousDrags, this.maxSimultaneousDrags,
this.onDragStarted, this.onDragStarted,
this.onDragUpdate,
this.onDraggableCanceled, this.onDraggableCanceled,
this.onDragEnd, this.onDragEnd,
this.onDragCompleted, this.onDragCompleted,
...@@ -298,6 +304,12 @@ class Draggable<T extends Object> extends StatefulWidget { ...@@ -298,6 +304,12 @@ 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.
///
/// 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]. /// 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 /// 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> { ...@@ -374,6 +386,7 @@ class LongPressDraggable<T extends Object> extends Draggable<T> {
DragAnchor dragAnchor = DragAnchor.child, DragAnchor dragAnchor = DragAnchor.child,
int? maxSimultaneousDrags, int? maxSimultaneousDrags,
VoidCallback? onDragStarted, VoidCallback? onDragStarted,
DragUpdateCallback? onDragUpdate,
DraggableCanceledCallback? onDraggableCanceled, DraggableCanceledCallback? onDraggableCanceled,
DragEndCallback? onDragEnd, DragEndCallback? onDragEnd,
VoidCallback? onDragCompleted, VoidCallback? onDragCompleted,
...@@ -390,6 +403,7 @@ class LongPressDraggable<T extends Object> extends Draggable<T> { ...@@ -390,6 +403,7 @@ class LongPressDraggable<T extends Object> extends Draggable<T> {
dragAnchor: dragAnchor, dragAnchor: dragAnchor,
maxSimultaneousDrags: maxSimultaneousDrags, maxSimultaneousDrags: maxSimultaneousDrags,
onDragStarted: onDragStarted, onDragStarted: onDragStarted,
onDragUpdate: onDragUpdate,
onDraggableCanceled: onDraggableCanceled, onDraggableCanceled: onDraggableCanceled,
onDragEnd: onDragEnd, onDragEnd: onDragEnd,
onDragCompleted: onDragCompleted, onDragCompleted: onDragCompleted,
...@@ -474,6 +488,11 @@ class _DraggableState<T extends Object> extends State<Draggable<T>> { ...@@ -474,6 +488,11 @@ 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,
onDragUpdate: (DragUpdateDetails details) {
if (mounted && widget.onDragUpdate != null) {
widget.onDragUpdate!(details);
}
},
onDragEnd: (Velocity velocity, Offset offset, bool wasAccepted) { onDragEnd: (Velocity velocity, Offset offset, bool wasAccepted) {
if (mounted) { if (mounted) {
setState(() { setState(() {
...@@ -708,6 +727,7 @@ class _DragAvatar<T extends Object> extends Drag { ...@@ -708,6 +727,7 @@ class _DragAvatar<T extends Object> extends Drag {
this.dragStartPoint = Offset.zero, this.dragStartPoint = Offset.zero,
this.feedback, this.feedback,
this.feedbackOffset = Offset.zero, this.feedbackOffset = Offset.zero,
this.onDragUpdate,
this.onDragEnd, this.onDragEnd,
required this.ignoringFeedbackSemantics, required this.ignoringFeedbackSemantics,
}) : assert(overlayState != null), }) : assert(overlayState != null),
...@@ -725,6 +745,7 @@ class _DragAvatar<T extends Object> extends Drag { ...@@ -725,6 +745,7 @@ class _DragAvatar<T extends Object> extends Drag {
final Offset dragStartPoint; final Offset dragStartPoint;
final Widget? feedback; final Widget? feedback;
final Offset feedbackOffset; final Offset feedbackOffset;
final DragUpdateCallback? onDragUpdate;
final _OnDragEnd? onDragEnd; final _OnDragEnd? onDragEnd;
final OverlayState overlayState; final OverlayState overlayState;
final bool ignoringFeedbackSemantics; final bool ignoringFeedbackSemantics;
...@@ -737,8 +758,12 @@ class _DragAvatar<T extends Object> extends Drag { ...@@ -737,8 +758,12 @@ class _DragAvatar<T extends Object> extends Drag {
@override @override
void update(DragUpdateDetails details) { void update(DragUpdateDetails details) {
final Offset oldPosition = _position;
_position += _restrictAxis(details.delta); _position += _restrictAxis(details.delta);
updateDrag(_position); updateDrag(_position);
if (onDragUpdate != null && _position != oldPosition) {
onDragUpdate!(details);
}
} }
@override @override
......
...@@ -840,6 +840,158 @@ void main() { ...@@ -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 { testWidgets('Drag and drop - onDraggableCanceled not called if dropped on accepting target', (WidgetTester tester) async {
final List<int> accepted = <int>[]; 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