【Flutter】ウィジェットのドラッグ&ドロップ

Flutter

Flutter でウィジェットをドラッグ&ドロップしたい場合、DraggableDragTarget と言う便利なウィジェットが用意されています。今回はこのドラッグ&ドロップ用の便利なウィジェットについて解説します。

スポンサーリンク

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');
},

ちなみにDraggableウィジェットの長押しバージョン「LongPressDraggable」もあります。長押しからドラッグをする以外はほとんど同じです。

DragTarget ウィジェット

Draggableウィジェットを受け入れるためのウィジェットです。

DragTarget<String>(
  builder: (context, accepted, rejected) {
    return Container(
      width: 200,
      height: 200,
      color: Colors.grey,
    );
  },
),

DraggableウィジェットをDragTargetに対してドラッグすると各種のイベントが発生します。イベント発生時に builder: が呼ばれるので、適切なウィジェットを返すようにします。

上の例では DragTarget<String> となっていますが、ここはDraggableウィジェットの data: に設定したデータと同じ型を指定します。型が違うとイベントが発生しないので注意しましょう。

ハンドルできるイベント

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;
              },
            ),
          ],
        ),
      ),
    );
  }
}

その他のジェスチャーに関する記事も参考にしてみてください。

コメント

  1. ずーやん より:

    参考になる記事をありがとうございます。
    上記のDraggableを使用して移動させたwidgetが様々な色情報を持つpng画像内に入った時にwidgetが重なる色によって表示させる名前を変える方法はありますか?

タイトルとURLをコピーしました