Draggable
Recently, I did a Flutter project, in which there was a requirement to create three task lists that could be dragged by each other. Normally, when doing Android projects, the drag controls are basically implemented by custom View. I want to see how they are implemented on Fluter. Flutter provides a very convenient Draggable control.
Constructor for Draggable
When I was developing Flutter, when I encountered a control I had never seen before, the best way to understand its function was to click open source and observe its constructor. Let’s look at the constructor for Draggable:
const Draggable({
Key key,
@required this.child, // child need not explain@required this.feedback, // display the component when draggingthis.data, // The data carried by the control (typically supplied to DragTarget, described later)this.axis, // Restrict the direction of sliding (horizontal or vertical)thisChildWhenDragging, // Components displayed when multi-touch draggingthis.feedbackoffset = offset. zero, // Drag to display the position, default (0.0)this.dragAnchor = dragAnchor. Child, // The feedback position shown when the drag starts (the original child position or the touch position)this.affinity, // Allows Draggable to share vertical or horizontal sliding events (such as dragging while Scrollable)thisMaxSimultaneousDrags, // Maximum response time under multi-touchthis.ondragStarted, // Callback to start draggingthisOnDraggableCanceled, // a callback when not dragged to the DragTarget controlthis.ondragend, // Callback at the end of the dragthis.onDragCompleted, // callback when dragged to the DragTarget controlthis.ignoringFeedbackSemantics = true// control whether to display feedback}) : assert(child ! =null).assert(feedback ! =null).assert(ignoringFeedbackSemantics ! =null).assert(maxSimultaneousDrags == null || maxSimultaneousDrags >= 0).super(key: key);
Copy the code
As you can see, Draggable is still very powerful, and the weiget displayed when dragging is also wrapped. We only need to pass in a Weiget to automatically display, and the callback to drag events is also very rich, where the DragTarget is another powerful control with Draggable. Let’s implement a simple Draggable:
The realization of the Draggable
Container(
alignment: Alignment.center,
child: Draggable(
child: Text("I can be dragged!"),
feedback: Text("I'm being dragged!"),),),Copy the code
Isn’t that easy? We can add some other properties, like restrictions on horizontal drag
Container(
alignment: Alignment.center,
child: Draggable(
axis: Axis.vertical,
child: Text("I can be dragged!"),
feedback: Text("I'm being dragged!"),),),Copy the code
DragTarget
However, in general, our drag logic is to move a control to the specified position (or control). In the past, we used to judge by the coordinate after dragging. However, by observing the Draggable callback, we found that there is also a control DragTarget matching with the Draggable. Use DragTarget to assign the drag to the specified location to the system. Without further ado, add the code
Constructor for DragTarget
const DragTarget({
Key key,
@required this.builder, // constructorthis.onwillAccept, // Determine whether the data meets the requirementsthis.onaccept, // Receive Data callbackthis.onLeave, //
}) : super(key: key);
Copy the code
The DragTarget constructor is much simpler:
Builder B.
CandidateData is the list of data that can be received when onWillAccept callback is true. RejectedData Specifies the data to be rejected when the onWillAccept callback is false. The return value of builder is the Child of DragTarget
typedef DragTargetBuilder<T> = Widget Function(BuildContext context, List<T> candidateData, List<dynamic> rejectedData);
Copy the code
OnWillAccept: Callback when Draggable is dragged to DragTarget
If true is returned, the Data Data will be added to the candidateData list and onAccept will be called. When false, Data will be added to the rejectedData list and onAccept will not be called.
OnAccept: callback to receive Draggable Data;
OnLeave: callback when Draggable leaves;
The realization of the DragTarget
String data = "Default data";
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 200),
color: Colors.white,
child: Column(
children: [
Draggable<String>(
data: "Draggable data",,
child: Text("I can be dragged!"),
feedback: Text("I'm being dragged!"),
),
DragTarget<String>(
builder: (BuildContext context, List<dynamic> accepted, List<dynamic> rejected,) {
return Text(data);
},
onAccept: (data) {
setState(() {
this.data = data; }); },)],),); }Copy the code
In the same way, we can apply it to more complex images. Remember my initial requirement: So we can combine DragTarget and Draggable, using the item of the list as the Draggable, using each list as the DragTarget, and adding billions of details:
List<String> list1 = ["list1_1"."list1_2"."list1_3"];
List<String> list2 = ["list2_1"."list2_2"];
List<String> list3 = ["list3_1"."list3_2"];
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 200),
color: Colors.white,
child: Column(
children: [
_createListView(list1),
_createListView(list2),
_createListView(list3),
],
),
);
}
Widget _createListView(List<String> _items) {
return DragTarget<String>(
builder: (
BuildContext context,
List<dynamic> accepted,
List<dynamic> rejected,
) {
return ListView.builder(
itemCount: _items.length,
shrinkWrap: true,
padding: EdgeInsets.all(10),
itemBuilder: (context, index) {
return Draggable<String>(
onDragCompleted: () {
// Delete data after dragging to DragTarget
setState(() {
_items.removeAt(index);
});
},
feedback: Material(
child: Container(
height: 60,
width: 200,
color: Colors.blueAccent,
alignment: Alignment.center,
child: Text(
_items[index],
style: TextStyle(color: Colors.white),
),
),
),
data: _items[index],
child: Container(
height: 50,
width: 200,
color: Colors.blueAccent,
alignment: Alignment.center,
child: Text(
_items[index],
style: TextStyle(color: Colors.white, fontSize: 20),),),); }); }, onAccept: (String data) { setState(() {// Add Draggable data to list_items.add(data); }); }); }Copy the code
Done!