很少說,咱們確定遇到過這個需求:git
把一個控件從當前位置移動到另外一個位置。可能需求最多的就像是支付寶應用頁面的編輯:github
好比,我想把最近使用的 紅包 添加到 個人應用 當中,支付寶這裏是用的 + 號。bash
那若是產品說我就想讓它拖過去,這可咋整?markdown
不慌,Flutter 也爲咱們提供了相關的 Widget。app
Flutter 若是要實現這種效果,那麼非 Draggable 不可。函數
照例咱們查看官方文檔。ui
A widget that can be dragged from to a DragTarget.
可拖動到 DragTarget 的小部件。
複製代碼
那也就是說,除了 Draggable ,還有一個 DragTarget。this
DragTarget 是用來接收咱們拖過去的 Widget 的,咱們後面再說。spa
先說Draggable。指針
在官方文檔找了一圈沒發現Demo,那沒辦法了,直接開擼。
先看構造函數:
class Draggable<T> extends StatefulWidget {
/// Creates a widget that can be dragged to a [DragTarget].
///
/// The [child] and [feedback] arguments must not be null. If
/// [maxSimultaneousDrags] is non-null, it must be non-negative.
const Draggable({
Key key,
@required this.child,
@required this.feedback,
this.data,
this.axis,
this.childWhenDragging,
this.feedbackOffset = Offset.zero,
this.dragAnchor = DragAnchor.child,
this.affinity,
this.maxSimultaneousDrags,
this.onDragStarted,
this.onDraggableCanceled,
this.onDragEnd,
this.onDragCompleted,
this.ignoringFeedbackSemantics = true,
}) : assert(child != null),
assert(feedback != null),
assert(ignoringFeedbackSemantics != null),
assert(maxSimultaneousDrags == null || maxSimultaneousDrags >= 0),
super(key: key);
}
複製代碼
能夠看到該類還支持泛型,這個泛型是來界定 data
的,後面再說。
先來看看必須的參數,child 和 feedback。
child 應該不比多說,說一下 feedback。
點擊查看feedback 參數,上面的註釋這樣寫着:
當拖動正在進行時在指針下顯示的小部件。
既然搞懂了必傳參數,那就開擼:
Widget _createGridView(List<String> _items) {
return GridView.builder(
itemCount: _items.length,
shrinkWrap: true, // 至關於Android的 wrap_content ,也就是包裹住該 widget的高度
physics: NeverScrollableScrollPhysics(), // 不容許滑動
padding: EdgeInsets.all(10),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 5, // 設置 gridview 每一行的數量
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
itemBuilder: (context, index) {
return Draggable( // 返回一個Draggable
// 必需要一個Material,否則拖動時Text會有雙下劃線
feedback: Material(
child: Container(
height: 100,
width: 100,
color: Colors.blueAccent,
alignment: Alignment.center,
child: Text(
_items[index],
style: TextStyle(color: Colors.white),
),
),
),
child: Container(
color: Colors.blueAccent,
alignment: Alignment.center,
child: Text(
_items[index],
style: TextStyle(color: Colors.white),
),
),
);
},
);
}
複製代碼
咱們定義一個 GridView,裏面每個 item 都是一個 Draggable。
來運行一下看效果:
能夠看到咱們確實是能夠拖動了,大功已經告成一半了。
那麼咱們下面開始定義接收的部件 DragTarget。
廢話也很少說,直接看構造函數,看看什麼是必填:
class DragTarget<T> extends StatefulWidget {
/// Creates a widget that receives drags.
///
/// The [builder] argument must not be null.
const DragTarget({
Key key,
@required this.builder,
this.onWillAccept,
this.onAccept,
this.onLeave,
}) : super(key: key);
}
複製代碼
能夠看到必傳的參數也就是一個builder,用來構建咱們的頁面使用。
其餘參數看名字也都能明白:
那咱們這裏只須要一個確認已經放到該控件時的回調,來接收咱們傳過來的值。
擼碼:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('DraggablePage'),
),
body: Column(
children: <Widget>[
_createGridView(_items1),
SizedBox(height: 100,),// 佔位
DragTarget<String>( // 用來接收數據的 Widget
builder: (
BuildContext context,
List<dynamic> accepted,
List<dynamic> rejected,
) {
return _createGridView(_items2);
},
// 用來接收數據
onAccept: (String data) {
setState(() {
_items2.add(data);
});
},
)
],
),
);
}
複製代碼
這是完整的 build 代碼,可是還須要改造一下咱們的 Draggable 。
再看一下Draggable的構造函數:
class Draggable<T> extends StatefulWidget {
/// Creates a widget that can be dragged to a [DragTarget].
///
/// The [child] and [feedback] arguments must not be null. If
/// [maxSimultaneousDrags] is non-null, it must be non-negative.
const Draggable({
Key key,
@required this.child,
@required this.feedback,
this.data,
this.axis,
this.childWhenDragging,
this.feedbackOffset = Offset.zero,
this.dragAnchor = DragAnchor.child,
this.affinity,
this.maxSimultaneousDrags,
this.onDragStarted,
this.onDraggableCanceled,
this.onDragEnd,
this.onDragCompleted,
this.ignoringFeedbackSemantics = true,
}) : assert(child != null),
assert(feedback != null),
assert(ignoringFeedbackSemantics != null),
assert(maxSimultaneousDrags == null || maxSimultaneousDrags >= 0),
super(key: key);
}
複製代碼
由於若是想要傳入數據,那也就必需要有數據能夠傳,也就是咱們構造函數裏看到的 data
字段。
還須要刪除咱們的源數據,那也就是要監聽拖動結束的回調,這裏就是 onDragCompleted
。
咱們來看改造後的Draggable:
Widget _createGridView(List<String> _items) {
return GridView.builder(
itemCount: _items.length,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.all(10),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 5,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
itemBuilder: (context, index) {
return Draggable<String>(
onDragCompleted: (){
// 在拖動結束後刪除數據
setState(() {
_items.removeAt(index);
});
},
feedback: Material(
child: Container(
height: 100,
width: 100,
color: Colors.blueAccent,
alignment: Alignment.center,
child: Text(
_items[index],
style: TextStyle(color: Colors.white),
),
),
),
// 當前組件的數據
data: _items[index],
child: Container(
color: Colors.blueAccent,
alignment: Alignment.center,
child: Text(
_items[index],
style: TextStyle(color: Colors.white),
),
),
);
},
);
}
複製代碼
運行看一下效果:
能夠看到這樣就基本完成咱們的需求了,可是有人說,能不能把我拖着的源控件加個特效?
沒問題,咱們經過 childWhenDragging
參數來控制。
如,加個蒙層:
childWhenDragging: Container(
color: Colors.blueAccent,
alignment: Alignment.center,
foregroundDecoration: BoxDecoration(color: Colors.white30),
child: Text(
_items[index],
style: TextStyle(color: Colors.white),
),
),
複製代碼
添加一個foregroundDecoration
就ok了,效果以下:
經過這個小例子咱們能夠實現特別多的效果。
並且默認拖動的控件時能夠多指觸控的,也就是說咱們能夠同時拖動N個控件。
能夠經過 Draggable 的 maxSimultaneousDrags
來控制。
構造函數裏其餘的參數你們能夠自行了解一下。
不得不說 Flutter 是真的好用。
關注我,天天更新 Flutter & Dart 知識。
完整代碼已經傳至GitHub:github.com/wanglu1209/…