parent
03e7ebe67d
commit
4b7e3494dd
@ -94,7 +94,8 @@ class Draggable<T> extends StatefulWidget {
|
|||||||
this.affinity,
|
this.affinity,
|
||||||
this.maxSimultaneousDrags,
|
this.maxSimultaneousDrags,
|
||||||
this.onDragStarted,
|
this.onDragStarted,
|
||||||
this.onDraggableCanceled
|
this.onDraggableCanceled,
|
||||||
|
this.onDragCompleted,
|
||||||
}) : assert(child != null),
|
}) : assert(child != null),
|
||||||
assert(feedback != null),
|
assert(feedback != null),
|
||||||
assert(maxSimultaneousDrags == null || maxSimultaneousDrags >= 0),
|
assert(maxSimultaneousDrags == null || maxSimultaneousDrags >= 0),
|
||||||
@ -182,6 +183,16 @@ class Draggable<T> extends StatefulWidget {
|
|||||||
/// callback is still in the tree.
|
/// callback is still in the tree.
|
||||||
final DraggableCanceledCallback onDraggableCanceled;
|
final DraggableCanceledCallback onDraggableCanceled;
|
||||||
|
|
||||||
|
/// Called when the draggable is dropped and accepted by a [DragTarget].
|
||||||
|
///
|
||||||
|
/// This function might be called after this widget has been removed from the
|
||||||
|
/// tree. For example, if a drag was in progress when this widget was removed
|
||||||
|
/// from the tree and the drag ended up completing, this callback will
|
||||||
|
/// still be called. For this reason, implementations of this callback might
|
||||||
|
/// need to check [State.mounted] to check whether the state receiving the
|
||||||
|
/// callback is still in the tree.
|
||||||
|
final VoidCallback onDragCompleted;
|
||||||
|
|
||||||
/// 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
|
||||||
@ -313,6 +324,8 @@ class _DraggableState<T> extends State<Draggable<T>> {
|
|||||||
_activeCount -= 1;
|
_activeCount -= 1;
|
||||||
_disposeRecognizerIfInactive();
|
_disposeRecognizerIfInactive();
|
||||||
}
|
}
|
||||||
|
if (wasAccepted && widget.onDragCompleted != null)
|
||||||
|
widget.onDragCompleted();
|
||||||
if (!wasAccepted && widget.onDraggableCanceled != null)
|
if (!wasAccepted && widget.onDraggableCanceled != null)
|
||||||
widget.onDraggableCanceled(velocity, offset);
|
widget.onDraggableCanceled(velocity, offset);
|
||||||
}
|
}
|
||||||
|
@ -518,7 +518,7 @@ void main() {
|
|||||||
events.clear();
|
events.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Drag and drop - onDraggableDropped 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>[];
|
||||||
bool onDraggableCanceledCalled = false;
|
bool onDraggableCanceledCalled = false;
|
||||||
|
|
||||||
@ -579,7 +579,7 @@ void main() {
|
|||||||
expect(onDraggableCanceledCalled, isFalse);
|
expect(onDraggableCanceledCalled, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Drag and drop - onDraggableDropped called if dropped on non-accepting target', (WidgetTester tester) async {
|
testWidgets('Drag and drop - onDraggableCanceled called if dropped on non-accepting target', (WidgetTester tester) async {
|
||||||
final List<int> accepted = <int>[];
|
final List<int> accepted = <int>[];
|
||||||
bool onDraggableCanceledCalled = false;
|
bool onDraggableCanceledCalled = false;
|
||||||
Velocity onDraggableCanceledVelocity;
|
Velocity onDraggableCanceledVelocity;
|
||||||
@ -649,7 +649,7 @@ void main() {
|
|||||||
expect(onDraggableCanceledOffset, equals(new Offset(secondLocation.dx, secondLocation.dy)));
|
expect(onDraggableCanceledOffset, equals(new Offset(secondLocation.dx, secondLocation.dy)));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Drag and drop - onDraggableDropped called if dropped on non-accepting target with correct velocity', (WidgetTester tester) async {
|
testWidgets('Drag and drop - onDraggableCanceled called if dropped on non-accepting target with correct velocity', (WidgetTester tester) async {
|
||||||
final List<int> accepted = <int>[];
|
final List<int> accepted = <int>[];
|
||||||
bool onDraggableCanceledCalled = false;
|
bool onDraggableCanceledCalled = false;
|
||||||
Velocity onDraggableCanceledVelocity;
|
Velocity onDraggableCanceledVelocity;
|
||||||
@ -699,6 +699,131 @@ void main() {
|
|||||||
expect(onDraggableCanceledOffset, equals(new Offset(flingStart.dx, flingStart.dy) + const Offset(0.0, 100.0)));
|
expect(onDraggableCanceledOffset, equals(new Offset(flingStart.dx, flingStart.dy) + const Offset(0.0, 100.0)));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Drag and drop - onDragCompleted not called if dropped on non-accepting target', (WidgetTester tester) async {
|
||||||
|
final List<int> accepted = <int>[];
|
||||||
|
bool onDragCompletedCalled = false;
|
||||||
|
|
||||||
|
await tester.pumpWidget(new MaterialApp(
|
||||||
|
home: new Column(
|
||||||
|
children: <Widget>[
|
||||||
|
new Draggable<int>(
|
||||||
|
data: 1,
|
||||||
|
child: const Text('Source'),
|
||||||
|
feedback: const Text('Dragging'),
|
||||||
|
onDragCompleted: () {
|
||||||
|
onDragCompletedCalled = true;
|
||||||
|
}
|
||||||
|
),
|
||||||
|
new DragTarget<int>(
|
||||||
|
builder: (BuildContext context, List<int> data, List<dynamic> rejects) {
|
||||||
|
return new 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(onDragCompletedCalled, 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(onDragCompletedCalled, 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(onDragCompletedCalled, 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(onDragCompletedCalled, isFalse);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Drag and drop - onDragCompleted called if dropped on accepting target', (WidgetTester tester) async {
|
||||||
|
final List<int> accepted = <int>[];
|
||||||
|
bool onDragCompletedCalled = false;
|
||||||
|
|
||||||
|
await tester.pumpWidget(new MaterialApp(
|
||||||
|
home: new Column(
|
||||||
|
children: <Widget>[
|
||||||
|
new Draggable<int>(
|
||||||
|
data: 1,
|
||||||
|
child: const Text('Source'),
|
||||||
|
feedback: const Text('Dragging'),
|
||||||
|
onDragCompleted: () {
|
||||||
|
onDragCompletedCalled = true;
|
||||||
|
}
|
||||||
|
),
|
||||||
|
new DragTarget<int>(
|
||||||
|
builder: (BuildContext context, List<int> data, List<dynamic> rejects) {
|
||||||
|
return new 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(onDragCompletedCalled, 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(onDragCompletedCalled, 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(onDragCompletedCalled, isFalse);
|
||||||
|
|
||||||
|
await gesture.up();
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(accepted, equals(<int>[1]));
|
||||||
|
expect(find.text('Source'), findsOneWidget);
|
||||||
|
expect(find.text('Dragging'), findsNothing);
|
||||||
|
expect(find.text('Target'), findsOneWidget);
|
||||||
|
expect(onDragCompletedCalled, isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('Drag and drop - allow pass thru of unaccepted data test', (WidgetTester tester) async {
|
testWidgets('Drag and drop - allow pass thru of unaccepted data test', (WidgetTester tester) async {
|
||||||
final List<int> acceptedInts = <int>[];
|
final List<int> acceptedInts = <int>[];
|
||||||
final List<double> acceptedDoubles = <double>[];
|
final List<double> acceptedDoubles = <double>[];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user