Flutter でウィジェットをドラッグ&ドロップしたい場合、Draggable と DragTarget と言う便利なウィジェットが用意されています。今回はこのドラッグ&ドロップ用の便利なウィジェットについて解説します。
Draggable ウィジェット
ウィジェットをドラッグしたい場合は Draggable ウィジェットを使います。 (後述する DragTargetウィジェットとセットで使います)
Draggable(
data: 'Green',
child: Container(
width: 100,
height: 100,
color: Colors.green,
),
feedback: Container(
width: 100,
height: 100,
color: Colors.green.withOpacity(0.5),
),
childWhenDragging: Container(
width: 100,
height: 100,
color: Colors.green,
),
),
- data:
ここで任意のデータを持たせることができます。(上の例ではStringを渡していますがこれに限りません) - child:
ここでドラッグしたいウィジェットを指定します。 - feedback:
ここでドラッグ中の(掴んで動かす)ウィジェットを指定します。 - childWhenDragging:
ドラッグ中に child: の表示を変えたい場合は、ここでウィジェットを指定できます。(上の例では一応書いていますが、変える必要がなければ不要です)
上のコードの場合、ドラッグしたいウィジェットは「緑色のコンテナ」、ドラッグ中に表示されるのは、「緑色&半透明のコンテナ」になります。
ハンドルできるイベント
Draggableウィジェットでハンドルできるイベントは以下の4つです。
// ドラッグ開始時に呼ばれる
onDragStarted: () {
print('onDragStarted');
},
// ドラッグ終了時に呼ばれる
onDragEnd: (DraggableDetails details) {
print('onDragEnd - $details');
},
// ドラッグがDragTargetで受け入れられた時に呼ばれる
onDragCompleted: () {
print('onDragCompleted');
},
// ドラッグがキャンセルされた時に呼ばれる
onDraggableCanceled: (Velocity velocity, Offset offset) {
print('onDraggableCanceled - velocity:$velocity , offset:$offset');
},
DragTarget ウィジェット
Draggableウィジェットを受け入れるためのウィジェットです。
DragTarget<String>(
builder: (context, accepted, rejected) {
return Container(
width: 200,
height: 200,
color: Colors.grey,
);
},
),
DraggableウィジェットをDragTargetに対してドラッグすると各種のイベントが発生します。イベント発生時に builder: が呼ばれるので、適切なウィジェットを返すようにします。
ハンドルできるイベント
DragTarget ウィジェットでハンドルできるイベントは以下の3つです。
// DragTarget の範囲に入った時に呼ばれる
onWillAccept: (data) {
print('onWillAccept - $data');
// ドラッグ操作を受け入れる場合はここでtrueを返す
return true;
},
// DragTargetにドラッグされた時に呼ばれる
onAccept: (data) {
print('onAccept - $data');
},
// DragTarget の範囲から離れた時に呼ばれる
onLeave: (data) {
print('onLeave - $data');
},
サンプルコード
DraggableとDragTargetのサンプルコードです。(以下のツイートの動画と同じものです)
赤、緑、青、のコンテナを Target のコンテナにドラッグ&ドロップして、データの受け渡しを確認できます。また、Targetの範囲内にある時はTargetの枠をオレンジの太枠で強調表示しています。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
// MyApp ウィジェットクラス
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(title: 'Draggable & DragTarget'),
);
}
}
// MyHomePage ウィジェットクラス
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
// MyHomePage ステートクラス
class _MyHomePageState extends State<MyHomePage> {
String _acceptedData = 'Target'; // 受け側のデータ
bool _willAccepted = false; // Target範囲内かどうかのフラグ
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// 緑のボックス
Draggable(
data: 'Red',
child: Container(
width: 100,
height: 100,
color: Colors.red,
),
feedback: Container(
width: 100,
height: 100,
color: Colors.red.withOpacity(0.5),
),
),
SizedBox(width: 20,),
// 赤のボックス
Draggable(
data: 'Green',
child: Container(
width: 100,
height: 100,
color: Colors.green,
),
feedback: Container(
width: 100,
height: 100,
color: Colors.green.withOpacity(0.5),
),
),
SizedBox(width: 20,),
// 青のボックス
Draggable(
data: 'Blue',
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
feedback: Container(
width: 100,
height: 100,
color: Colors.blue.withOpacity(0.5),
),
),
],
),
SizedBox(height: 100,),
// ドラッグを受けるボックス
DragTarget(
builder: (context, accepted, rejected) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: _willAccepted ? Colors.orange : Colors.grey,
width: _willAccepted ? 5 : 1,
),
),
width: 200,
height: 200,
child: Center(child: Text(_acceptedData, style: TextStyle(fontSize: 50))),
);
},
onWillAccept: (String data) {
_willAccepted = true;
return true;
},
onAccept: (data) {
_acceptedData = data;
_willAccepted = false;
},
onLeave: (data) {
_willAccepted = false;
},
),
],
),
),
);
}
}
その他のジェスチャーに関する記事も参考にしてみてください。
コメント
参考になる記事をありがとうございます。
上記のDraggableを使用して移動させたwidgetが様々な色情報を持つpng画像内に入った時にwidgetが重なる色によって表示させる名前を変える方法はありますか?