Added support for passing in velocity and offset into Draggable.onDragCompleted (#22267)
* Added support for passing in velocity and offset into Draggable.onDragCompleted. * Fixed documentation of DragCompletedCallback. * Spun off previous onDragCompleted breaking changes into new callback called Draggable.onDragEnd. * Revert "Fixed documentation of DragCompletedCallback." This reverts commit 069051f5be0d4ec6a1f4b3f072e535ca87e5d740. * Revert "Added support for passing in velocity and offset into Draggable.onDragCompleted." This reverts commit 7ef744aa5645429b7bc92527226203ee8bff68ec. * DraggableDetails constructor is now declared first as per Flutter code style. * Draggable.onDragEnd will only call back if its widget is currently mounted to the tree. * Moved "});" in DraggableDetails constructor to new line, vertically aligned with the constructor name, as per Flutter code style. * Added space between if statement in drag_target.dart. * widget.onDragEnd call is now formated as per flutter code style. * Added more details to DraggableDetails documentation. * Added brackets to if statement block as per Flutter code style. * Fixed minor nits in DraggableDetails documentation. * Made DraggableDetails constructor public. Also added documentation for its constructor.
This commit is contained in:
parent
33f8030b32
commit
9447be7370
@ -36,6 +36,15 @@ typedef DragTargetBuilder<T> = Widget Function(BuildContext context, List<T> can
|
|||||||
/// Used by [Draggable.onDraggableCanceled].
|
/// Used by [Draggable.onDraggableCanceled].
|
||||||
typedef DraggableCanceledCallback = void Function(Velocity velocity, Offset offset);
|
typedef DraggableCanceledCallback = void Function(Velocity velocity, Offset offset);
|
||||||
|
|
||||||
|
/// Signature for when the draggable is dropped.
|
||||||
|
///
|
||||||
|
/// The velocity and offset at which the pointer was moving when the draggable
|
||||||
|
/// was dropped is available in the [DraggableDetails]. Also included in the
|
||||||
|
/// `details` is whether the draggable's [DragTarget] accepted it.
|
||||||
|
///
|
||||||
|
/// Used by [Draggable.onDragEnd]
|
||||||
|
typedef DragEndCallback = void Function(DraggableDetails details);
|
||||||
|
|
||||||
/// Signature for when a [Draggable] leaves a [DragTarget].
|
/// Signature for when a [Draggable] leaves a [DragTarget].
|
||||||
///
|
///
|
||||||
/// Used by [DragTarget.onLeave].
|
/// Used by [DragTarget.onLeave].
|
||||||
@ -100,6 +109,7 @@ class Draggable<T> extends StatefulWidget {
|
|||||||
this.maxSimultaneousDrags,
|
this.maxSimultaneousDrags,
|
||||||
this.onDragStarted,
|
this.onDragStarted,
|
||||||
this.onDraggableCanceled,
|
this.onDraggableCanceled,
|
||||||
|
this.onDragEnd,
|
||||||
this.onDragCompleted,
|
this.onDragCompleted,
|
||||||
this.ignoringFeedbackSemantics = true,
|
this.ignoringFeedbackSemantics = true,
|
||||||
}) : assert(child != null),
|
}) : assert(child != null),
|
||||||
@ -229,6 +239,16 @@ class Draggable<T> extends StatefulWidget {
|
|||||||
/// callback is still in the tree.
|
/// callback is still in the tree.
|
||||||
final VoidCallback onDragCompleted;
|
final VoidCallback onDragCompleted;
|
||||||
|
|
||||||
|
/// Called when the draggable is dropped.
|
||||||
|
///
|
||||||
|
/// The velocity and offset at which the pointer was moving when it was
|
||||||
|
/// dropped is available in the [DraggableDetails]. Also included in the
|
||||||
|
/// `details` is whether the draggable's [DragTarget] accepted it.
|
||||||
|
///
|
||||||
|
/// This function will only be called while this widget is still mounted to
|
||||||
|
/// the tree (i.e. [State.mounted] is true).
|
||||||
|
final DragEndCallback onDragEnd;
|
||||||
|
|
||||||
/// Creates a gesture recognizer that recognizes the start of the drag.
|
/// Creates a gesture recognizer that recognizes the start of the drag.
|
||||||
///
|
///
|
||||||
/// Subclasses can override this function to customize when they start
|
/// Subclasses can override this function to customize when they start
|
||||||
@ -266,6 +286,7 @@ class LongPressDraggable<T> extends Draggable<T> {
|
|||||||
int maxSimultaneousDrags,
|
int maxSimultaneousDrags,
|
||||||
VoidCallback onDragStarted,
|
VoidCallback onDragStarted,
|
||||||
DraggableCanceledCallback onDraggableCanceled,
|
DraggableCanceledCallback onDraggableCanceled,
|
||||||
|
DragEndCallback onDragEnd,
|
||||||
VoidCallback onDragCompleted,
|
VoidCallback onDragCompleted,
|
||||||
this.hapticFeedbackOnStart = true,
|
this.hapticFeedbackOnStart = true,
|
||||||
bool ignoringFeedbackSemantics = true,
|
bool ignoringFeedbackSemantics = true,
|
||||||
@ -281,6 +302,7 @@ class LongPressDraggable<T> extends Draggable<T> {
|
|||||||
maxSimultaneousDrags: maxSimultaneousDrags,
|
maxSimultaneousDrags: maxSimultaneousDrags,
|
||||||
onDragStarted: onDragStarted,
|
onDragStarted: onDragStarted,
|
||||||
onDraggableCanceled: onDraggableCanceled,
|
onDraggableCanceled: onDraggableCanceled,
|
||||||
|
onDragEnd: onDragEnd,
|
||||||
onDragCompleted: onDragCompleted,
|
onDragCompleted: onDragCompleted,
|
||||||
ignoringFeedbackSemantics: ignoringFeedbackSemantics,
|
ignoringFeedbackSemantics: ignoringFeedbackSemantics,
|
||||||
);
|
);
|
||||||
@ -372,6 +394,13 @@ class _DraggableState<T> extends State<Draggable<T>> {
|
|||||||
_activeCount -= 1;
|
_activeCount -= 1;
|
||||||
_disposeRecognizerIfInactive();
|
_disposeRecognizerIfInactive();
|
||||||
}
|
}
|
||||||
|
if (mounted && widget.onDragEnd != null) {
|
||||||
|
widget.onDragEnd(DraggableDetails(
|
||||||
|
wasAccepted: wasAccepted,
|
||||||
|
velocity: velocity,
|
||||||
|
offset: offset
|
||||||
|
));
|
||||||
|
}
|
||||||
if (wasAccepted && widget.onDragCompleted != null)
|
if (wasAccepted && widget.onDragCompleted != null)
|
||||||
widget.onDragCompleted();
|
widget.onDragCompleted();
|
||||||
if (!wasAccepted && widget.onDraggableCanceled != null)
|
if (!wasAccepted && widget.onDraggableCanceled != null)
|
||||||
@ -396,6 +425,38 @@ class _DraggableState<T> extends State<Draggable<T>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents the details when a specific pointer event occurred on
|
||||||
|
/// the [Draggable].
|
||||||
|
///
|
||||||
|
/// This includes the [Velocity] at which the pointer was moving and [Offset]
|
||||||
|
/// when the draggable event occurred, and whether its [DragTarget] accepted it.
|
||||||
|
///
|
||||||
|
/// Also, this is the details object for callbacks that use [DragEndCallback].
|
||||||
|
class DraggableDetails {
|
||||||
|
/// Creates details for a [DraggableDetails].
|
||||||
|
///
|
||||||
|
/// If [wasAccepted] is not specified, it will default to `false`.
|
||||||
|
///
|
||||||
|
/// The [velocity] or [offset] arguments must not be `null`.
|
||||||
|
DraggableDetails({
|
||||||
|
this.wasAccepted = false,
|
||||||
|
@required this.velocity,
|
||||||
|
@required this.offset
|
||||||
|
}) : assert(velocity != null),
|
||||||
|
assert(offset != null);
|
||||||
|
|
||||||
|
/// Determines whether the [DragTarget] accepted this draggable.
|
||||||
|
final bool wasAccepted;
|
||||||
|
|
||||||
|
/// The velocity at which the pointer was moving when the specific pointer
|
||||||
|
/// event occurred on the draggable.
|
||||||
|
final Velocity velocity;
|
||||||
|
|
||||||
|
/// The global position when the specific pointer event occurred on
|
||||||
|
/// the draggable.
|
||||||
|
final Offset offset;
|
||||||
|
}
|
||||||
|
|
||||||
/// A widget that receives data when a [Draggable] widget is dropped.
|
/// A widget that receives data when a [Draggable] widget is dropped.
|
||||||
///
|
///
|
||||||
/// When a draggable is dragged on top of a drag target, the drag target is
|
/// When a draggable is dragged on top of a drag target, the drag target is
|
||||||
|
@ -899,6 +899,77 @@ void main() {
|
|||||||
expect(onDraggableCanceledOffset, equals(Offset(flingStart.dx, flingStart.dy) + const Offset(0.0, 100.0)));
|
expect(onDraggableCanceledOffset, equals(Offset(flingStart.dx, flingStart.dy) + const Offset(0.0, 100.0)));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Drag and drop - onDragEnd not called if dropped on non-accepting target', (WidgetTester tester) async {
|
||||||
|
final List<int> accepted = <int>[];
|
||||||
|
bool onDragEndCalled = false;
|
||||||
|
DraggableDetails onDragEndDraggableDetails;
|
||||||
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
home: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Draggable<int>(
|
||||||
|
data: 1,
|
||||||
|
child: const Text('Source'),
|
||||||
|
feedback: const Text('Dragging'),
|
||||||
|
onDragEnd: (DraggableDetails details) {
|
||||||
|
onDragEndCalled = true;
|
||||||
|
onDragEndDraggableDetails = details;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
DragTarget<int>(
|
||||||
|
builder: (BuildContext context, List<int> data, List<dynamic> rejects) {
|
||||||
|
return Container(
|
||||||
|
height: 100.0,
|
||||||
|
child: const Text('Target'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onWillAccept: (int data) => false,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
expect(accepted, isEmpty);
|
||||||
|
expect(find.text('Source'), findsOneWidget);
|
||||||
|
expect(find.text('Dragging'), findsNothing);
|
||||||
|
expect(find.text('Target'), findsOneWidget);
|
||||||
|
expect(onDragEndCalled, isFalse);
|
||||||
|
|
||||||
|
final Offset firstLocation = tester.getTopLeft(find.text('Source'));
|
||||||
|
final TestGesture gesture = await tester.startGesture(firstLocation, pointer: 7);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(accepted, isEmpty);
|
||||||
|
expect(find.text('Source'), findsOneWidget);
|
||||||
|
expect(find.text('Dragging'), findsOneWidget);
|
||||||
|
expect(find.text('Target'), findsOneWidget);
|
||||||
|
expect(onDragEndCalled, isFalse);
|
||||||
|
|
||||||
|
final Offset secondLocation = tester.getCenter(find.text('Target'));
|
||||||
|
await gesture.moveTo(secondLocation);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(accepted, isEmpty);
|
||||||
|
expect(find.text('Source'), findsOneWidget);
|
||||||
|
expect(find.text('Dragging'), findsOneWidget);
|
||||||
|
expect(find.text('Target'), findsOneWidget);
|
||||||
|
expect(onDragEndCalled, isFalse);
|
||||||
|
|
||||||
|
await gesture.up();
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(accepted, isEmpty);
|
||||||
|
expect(find.text('Source'), findsOneWidget);
|
||||||
|
expect(find.text('Dragging'), findsNothing);
|
||||||
|
expect(find.text('Target'), findsOneWidget);
|
||||||
|
expect(onDragEndCalled, isTrue);
|
||||||
|
expect(onDragEndDraggableDetails, isNotNull);
|
||||||
|
expect(onDragEndDraggableDetails.wasAccepted, isFalse);
|
||||||
|
expect(onDragEndDraggableDetails.velocity, equals(Velocity.zero));
|
||||||
|
expect(onDragEndDraggableDetails.offset,
|
||||||
|
equals(
|
||||||
|
Offset(secondLocation.dx, secondLocation.dy - firstLocation.dy)));
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('Drag and drop - onDragCompleted not called if dropped on non-accepting target', (WidgetTester tester) async {
|
testWidgets('Drag and drop - onDragCompleted not called if dropped on non-accepting target', (WidgetTester tester) async {
|
||||||
final List<int> accepted = <int>[];
|
final List<int> accepted = <int>[];
|
||||||
bool onDragCompletedCalled = false;
|
bool onDragCompletedCalled = false;
|
||||||
@ -963,6 +1034,138 @@ void main() {
|
|||||||
expect(onDragCompletedCalled, isFalse);
|
expect(onDragCompletedCalled, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Drag and drop - onDragEnd called if dropped on accepting target', (WidgetTester tester) async {
|
||||||
|
final List<int> accepted = <int>[];
|
||||||
|
bool onDragEndCalled = false;
|
||||||
|
DraggableDetails onDragEndDraggableDetails;
|
||||||
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
home: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Draggable<int>(
|
||||||
|
data: 1,
|
||||||
|
child: const Text('Source'),
|
||||||
|
feedback: const Text('Dragging'),
|
||||||
|
onDragEnd: (DraggableDetails details) {
|
||||||
|
onDragEndCalled = true;
|
||||||
|
onDragEndDraggableDetails = details;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
DragTarget<int>(
|
||||||
|
builder: (BuildContext context, List<int> data, List<dynamic> rejects) {
|
||||||
|
return Container(height: 100.0, child: const Text('Target'));
|
||||||
|
},
|
||||||
|
onAccept: accepted.add,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
expect(accepted, isEmpty);
|
||||||
|
expect(find.text('Source'), findsOneWidget);
|
||||||
|
expect(find.text('Dragging'), findsNothing);
|
||||||
|
expect(find.text('Target'), findsOneWidget);
|
||||||
|
expect(onDragEndCalled, isFalse);
|
||||||
|
|
||||||
|
final Offset firstLocation = tester.getCenter(find.text('Source'));
|
||||||
|
final TestGesture gesture = await tester.startGesture(firstLocation, pointer: 7);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(accepted, isEmpty);
|
||||||
|
expect(find.text('Source'), findsOneWidget);
|
||||||
|
expect(find.text('Dragging'), findsOneWidget);
|
||||||
|
expect(find.text('Target'), findsOneWidget);
|
||||||
|
expect(onDragEndCalled, isFalse);
|
||||||
|
|
||||||
|
final Offset secondLocation = tester.getCenter(find.text('Target'));
|
||||||
|
await gesture.moveTo(secondLocation);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(accepted, isEmpty);
|
||||||
|
expect(find.text('Source'), findsOneWidget);
|
||||||
|
expect(find.text('Dragging'), findsOneWidget);
|
||||||
|
expect(find.text('Target'), findsOneWidget);
|
||||||
|
expect(onDragEndCalled, isFalse);
|
||||||
|
|
||||||
|
await gesture.up();
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
final Offset droppedLocation = tester.getTopLeft(find.text('Target'));
|
||||||
|
expect(accepted, equals(<int>[1]));
|
||||||
|
expect(find.text('Source'), findsOneWidget);
|
||||||
|
expect(find.text('Dragging'), findsNothing);
|
||||||
|
expect(find.text('Target'), findsOneWidget);
|
||||||
|
expect(onDragEndCalled, isTrue);
|
||||||
|
expect(onDragEndDraggableDetails, isNotNull);
|
||||||
|
expect(onDragEndDraggableDetails.wasAccepted, isTrue);
|
||||||
|
expect(onDragEndDraggableDetails.velocity, equals(Velocity.zero));
|
||||||
|
expect(onDragEndDraggableDetails.offset,
|
||||||
|
equals(
|
||||||
|
Offset(droppedLocation.dx, secondLocation.dy - firstLocation.dy)));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('DragTarget does not call onDragEnd when remove from the tree', (WidgetTester tester) async {
|
||||||
|
final List<String> events = <String>[];
|
||||||
|
Offset firstLocation, secondLocation;
|
||||||
|
int timesOnDragEndCalled = 0;
|
||||||
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
home: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Draggable<int>(
|
||||||
|
data: 1,
|
||||||
|
child: const Text('Source'),
|
||||||
|
feedback: const Text('Dragging'),
|
||||||
|
onDragEnd: (DraggableDetails details) {
|
||||||
|
timesOnDragEndCalled++;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
DragTarget<int>(
|
||||||
|
builder: (BuildContext context, List<int> data, List<dynamic> rejects) {
|
||||||
|
return const Text('Target');
|
||||||
|
},
|
||||||
|
onAccept: (int data) {
|
||||||
|
events.add('drop');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
expect(events, isEmpty);
|
||||||
|
expect(find.text('Source'), findsOneWidget);
|
||||||
|
expect(find.text('Target'), findsOneWidget);
|
||||||
|
|
||||||
|
expect(events, isEmpty);
|
||||||
|
await tester.tap(find.text('Source'));
|
||||||
|
expect(events, isEmpty);
|
||||||
|
|
||||||
|
firstLocation = tester.getCenter(find.text('Source'));
|
||||||
|
final TestGesture gesture = await tester.startGesture(firstLocation, pointer: 7);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
await tester.pump(const Duration(seconds: 20));
|
||||||
|
|
||||||
|
secondLocation = tester.getCenter(find.text('Target'));
|
||||||
|
await gesture.moveTo(secondLocation);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
home: Column(
|
||||||
|
children: const <Widget>[
|
||||||
|
Draggable<int>(
|
||||||
|
data: 1,
|
||||||
|
child: Text('Source'),
|
||||||
|
feedback: Text('Dragging')
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
expect(events, isEmpty);
|
||||||
|
expect(timesOnDragEndCalled, equals(1));
|
||||||
|
await gesture.up();
|
||||||
|
await tester.pump();
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('Drag and drop - onDragCompleted called if dropped on accepting target', (WidgetTester tester) async {
|
testWidgets('Drag and drop - onDragCompleted called if dropped on accepting target', (WidgetTester tester) async {
|
||||||
final List<int> accepted = <int>[];
|
final List<int> accepted = <int>[];
|
||||||
bool onDragCompletedCalled = false;
|
bool onDragCompletedCalled = false;
|
||||||
@ -1533,6 +1736,85 @@ void main() {
|
|||||||
expect(events, equals(<String>['tap']));
|
expect(events, equals(<String>['tap']));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('long-press draggable calls onDragEnd called if dropped on accepting target', (WidgetTester tester) async {
|
||||||
|
final List<int> accepted = <int>[];
|
||||||
|
bool onDragEndCalled = false;
|
||||||
|
DraggableDetails onDragEndDraggableDetails;
|
||||||
|
|
||||||
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
home: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
LongPressDraggable<int>(
|
||||||
|
data: 1,
|
||||||
|
child: const Text('Source'),
|
||||||
|
feedback: const Text('Dragging'),
|
||||||
|
onDragEnd: (DraggableDetails details) {
|
||||||
|
onDragEndCalled = true;
|
||||||
|
onDragEndDraggableDetails = details;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
DragTarget<int>(
|
||||||
|
builder: (BuildContext context, List<int> data, List<dynamic> rejects) {
|
||||||
|
return Container(height: 100.0, child: const Text('Target'));
|
||||||
|
},
|
||||||
|
onAccept: accepted.add,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
expect(accepted, isEmpty);
|
||||||
|
expect(find.text('Source'), findsOneWidget);
|
||||||
|
expect(find.text('Dragging'), findsNothing);
|
||||||
|
expect(find.text('Target'), findsOneWidget);
|
||||||
|
expect(onDragEndCalled, isFalse);
|
||||||
|
|
||||||
|
final Offset firstLocation = tester.getCenter(find.text('Source'));
|
||||||
|
final TestGesture gesture = await tester.startGesture(firstLocation, pointer: 7);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(accepted, isEmpty);
|
||||||
|
expect(find.text('Source'), findsOneWidget);
|
||||||
|
expect(find.text('Dragging'), findsNothing);
|
||||||
|
expect(find.text('Target'), findsOneWidget);
|
||||||
|
expect(onDragEndCalled, isFalse);
|
||||||
|
|
||||||
|
await tester.pump(kLongPressTimeout);
|
||||||
|
|
||||||
|
expect(accepted, isEmpty);
|
||||||
|
expect(find.text('Source'), findsOneWidget);
|
||||||
|
expect(find.text('Dragging'), findsOneWidget);
|
||||||
|
expect(find.text('Target'), findsOneWidget);
|
||||||
|
expect(onDragEndCalled, isFalse);
|
||||||
|
|
||||||
|
|
||||||
|
final Offset secondLocation = tester.getCenter(find.text('Target'));
|
||||||
|
await gesture.moveTo(secondLocation);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(accepted, isEmpty);
|
||||||
|
expect(find.text('Source'), findsOneWidget);
|
||||||
|
expect(find.text('Dragging'), findsOneWidget);
|
||||||
|
expect(find.text('Target'), findsOneWidget);
|
||||||
|
expect(onDragEndCalled, isFalse);
|
||||||
|
|
||||||
|
await gesture.up();
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
final Offset droppedLocation = tester.getTopLeft(find.text('Target'));
|
||||||
|
expect(accepted, equals(<int>[1]));
|
||||||
|
expect(find.text('Source'), findsOneWidget);
|
||||||
|
expect(find.text('Dragging'), findsNothing);
|
||||||
|
expect(find.text('Target'), findsOneWidget);
|
||||||
|
expect(onDragEndCalled, isTrue);
|
||||||
|
expect(onDragEndDraggableDetails, isNotNull);
|
||||||
|
expect(onDragEndDraggableDetails.wasAccepted, isTrue);
|
||||||
|
expect(onDragEndDraggableDetails.velocity, equals(Velocity.zero));
|
||||||
|
expect(onDragEndDraggableDetails.offset,
|
||||||
|
equals(
|
||||||
|
Offset(droppedLocation.dx, secondLocation.dy - firstLocation.dy)));
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('long-press draggable calls onDragCompleted called if dropped on accepting target', (WidgetTester tester) async {
|
testWidgets('long-press draggable calls onDragCompleted called if dropped on accepting target', (WidgetTester tester) async {
|
||||||
final List<int> accepted = <int>[];
|
final List<int> accepted = <int>[];
|
||||||
bool onDragCompletedCalled = false;
|
bool onDragCompletedCalled = false;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user