Add onLeave callback to DragTarget (#14103)
* Add a callback that fires when a Draggable leaves a DragTarget. This enables the DragTarget to manage its state from entry to exit. * It helps to have a null check here * Add test for onLeave callback and add verbiage to onWillAccept explaining the callback lifecycle of a DragTarget.
This commit is contained in:
parent
47e52b827f
commit
b7f6be6ff2
@ -37,6 +37,11 @@ typedef Widget DragTargetBuilder<T>(BuildContext context, List<T> candidateData,
|
|||||||
/// Used by [Draggable.onDraggableCanceled].
|
/// Used by [Draggable.onDraggableCanceled].
|
||||||
typedef void DraggableCanceledCallback(Velocity velocity, Offset offset);
|
typedef void DraggableCanceledCallback(Velocity velocity, Offset offset);
|
||||||
|
|
||||||
|
/// Signature for when a [Draggable] leaves a [DragTarget].
|
||||||
|
///
|
||||||
|
/// Used by [DragTarget.onLeave].
|
||||||
|
typedef void DragTargetLeave<T>(T data);
|
||||||
|
|
||||||
/// Where the [Draggable] should be anchored during a drag.
|
/// Where the [Draggable] should be anchored during a drag.
|
||||||
enum DragAnchor {
|
enum DragAnchor {
|
||||||
/// Display the feedback anchored at the position of the original child. If
|
/// Display the feedback anchored at the position of the original child. If
|
||||||
@ -372,7 +377,8 @@ class DragTarget<T> extends StatefulWidget {
|
|||||||
Key key,
|
Key key,
|
||||||
@required this.builder,
|
@required this.builder,
|
||||||
this.onWillAccept,
|
this.onWillAccept,
|
||||||
this.onAccept
|
this.onAccept,
|
||||||
|
this.onLeave,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// Called to build the contents of this widget.
|
/// Called to build the contents of this widget.
|
||||||
@ -383,11 +389,19 @@ class DragTarget<T> extends StatefulWidget {
|
|||||||
|
|
||||||
/// Called to determine whether this widget is interested in receiving a given
|
/// Called to determine whether this widget is interested in receiving a given
|
||||||
/// piece of data being dragged over this drag target.
|
/// piece of data being dragged over this drag target.
|
||||||
|
///
|
||||||
|
/// Called when a piece of data enters the target. This will be followed by
|
||||||
|
/// either [onAccept], if the data is dropped, or [onLeave], if the drag
|
||||||
|
/// leaves the target.
|
||||||
final DragTargetWillAccept<T> onWillAccept;
|
final DragTargetWillAccept<T> onWillAccept;
|
||||||
|
|
||||||
/// Called when an acceptable piece of data was dropped over this drag target.
|
/// Called when an acceptable piece of data was dropped over this drag target.
|
||||||
final DragTargetAccept<T> onAccept;
|
final DragTargetAccept<T> onAccept;
|
||||||
|
|
||||||
|
/// Called when a given piece of data being dragged over this target leaves
|
||||||
|
/// the target.
|
||||||
|
final DragTargetLeave<T> onLeave;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_DragTargetState<T> createState() => new _DragTargetState<T>();
|
_DragTargetState<T> createState() => new _DragTargetState<T>();
|
||||||
}
|
}
|
||||||
@ -421,6 +435,8 @@ class _DragTargetState<T> extends State<DragTarget<T>> {
|
|||||||
_candidateAvatars.remove(avatar);
|
_candidateAvatars.remove(avatar);
|
||||||
_rejectedAvatars.remove(avatar);
|
_rejectedAvatars.remove(avatar);
|
||||||
});
|
});
|
||||||
|
if (widget.onLeave != null)
|
||||||
|
widget.onLeave(avatar.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void didDrop(_DragAvatar<dynamic> avatar) {
|
void didDrop(_DragAvatar<dynamic> avatar) {
|
||||||
|
@ -68,6 +68,73 @@ void main() {
|
|||||||
expect(dragStartedCount, 1);
|
expect(dragStartedCount, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Drag and drop - onLeave callback fires correctly', (WidgetTester tester) async {
|
||||||
|
final Map<String,int> leftBehind = <String,int>{
|
||||||
|
'Target 1': 0,
|
||||||
|
'Target 2': 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
await tester.pumpWidget(new MaterialApp(
|
||||||
|
home: new Column(
|
||||||
|
children: <Widget>[
|
||||||
|
const Draggable<int>(
|
||||||
|
data: 1,
|
||||||
|
child: const Text('Source'),
|
||||||
|
feedback: const Text('Dragging'),
|
||||||
|
),
|
||||||
|
new DragTarget<int>(
|
||||||
|
builder: (BuildContext context, List<int> data, List<dynamic> rejects) {
|
||||||
|
return new Container(height: 100.0, child: const Text('Target 1'));
|
||||||
|
},
|
||||||
|
onLeave: (int data) => leftBehind['Target 1'] = leftBehind['Target 1'] + data,
|
||||||
|
),
|
||||||
|
new DragTarget<int>(
|
||||||
|
builder: (BuildContext context, List<int> data, List<dynamic> rejects) {
|
||||||
|
return new Container(height: 100.0, child: const Text('Target 2'));
|
||||||
|
},
|
||||||
|
onLeave: (int data) => leftBehind['Target 2'] = leftBehind['Target 2'] + data,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
expect(leftBehind['Target 1'], equals(0));
|
||||||
|
expect(leftBehind['Target 2'], equals(0));
|
||||||
|
|
||||||
|
final Offset firstLocation = tester.getCenter(find.text('Source'));
|
||||||
|
final TestGesture gesture = await tester.startGesture(firstLocation, pointer: 7);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(leftBehind['Target 1'], equals(0));
|
||||||
|
expect(leftBehind['Target 2'], equals(0));
|
||||||
|
|
||||||
|
final Offset secondLocation = tester.getCenter(find.text('Target 1'));
|
||||||
|
await gesture.moveTo(secondLocation);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(leftBehind['Target 1'], equals(0));
|
||||||
|
expect(leftBehind['Target 2'], equals(0));
|
||||||
|
|
||||||
|
final Offset thirdLocation = tester.getCenter(find.text('Target 2'));
|
||||||
|
await gesture.moveTo(thirdLocation);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(leftBehind['Target 1'], equals(1));
|
||||||
|
expect(leftBehind['Target 2'], equals(0));
|
||||||
|
|
||||||
|
await gesture.moveTo(secondLocation);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(leftBehind['Target 1'], equals(1));
|
||||||
|
expect(leftBehind['Target 2'], equals(1));
|
||||||
|
|
||||||
|
await gesture.up();
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(leftBehind['Target 1'], equals(1));
|
||||||
|
expect(leftBehind['Target 2'], equals(1));
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('Drag and drop - dragging over button', (WidgetTester tester) async {
|
testWidgets('Drag and drop - dragging over button', (WidgetTester tester) async {
|
||||||
final List<String> events = <String>[];
|
final List<String> events = <String>[];
|
||||||
Offset firstLocation, secondLocation;
|
Offset firstLocation, secondLocation;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user