mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Draggable: onDragUpdate callback (#68185)
This commit is contained in:
parent
d12098da8a
commit
635dfc3e4f
@ -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>[];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user