There are a lot of UI patterns that involve dragging, like moving addresses around in an email or moving documents from one folder to another.
メール内のアドレスを移動したり、あるフォルダから別のフォルダにドキュメントを移動したりするなど、ドラッグを伴うUIパターンはたくさんあります。
If you’ve got a widget you want the user to drag around,
try using Draggable.
ユーザーにドラッグしてもらいたいウィジェットがある場合は、
Draggableウィジェットを使用してみてください。
Draggables have a property to hold the data they represent.
Draggableウィジェットには、自身を表すデータを保持するプロパティがあります。
So it’s good to start with a type parameter.
したがって、型引数をから始めるのは良いことです。
Draggable<Color>{ }
Next, add the data itself and define how your Draggable should look.
次に、dataフィールド自体を追加し、Draggableの外観を定義します。
Draggable<Color>( data: Color(0x000000ff), )
A child property is used when the Draggable is waiting around.
Draggableが待機しているとき(ドラッグ開始前)に、childプロパティが使用されます。
Draggable<Color>( data: Color(0x000000ff), child: MyBlueBox(), )
※以下の色がついている区間は、赤文字が間違っている説明(動画の説明)、青文字が正しい説明です。
The optional childWhenDragging is substituted while the widget’s being dragged.
オプションのchildWhenDraggingパラメータは、ウィジェットのドラッグ中に置き換えられます。
↑動画の説明が間違っている。
(ドラッグ開始前 : childフィールドのウィジェット(MyBlueBox)
↓
ドラッグ中 : ドラッグ開始前の位置にあったウィジェット(MyBlueBox)が、
childWhenDraggingのウィジェット(MyRoundedBlueBox)に変化する。)
つまりドラッグ中に元の位置に停止しているウィジェットをchildWhenDraggingプロパティに設定する。
//↓動画で紹介されているコード。間違い。 Draggable<Color>( data: Color(0x000000ff), child: MyBlueBox(), childWhenDraggable: MyRoundedBlueBox(), )
And the optional feedback widget is what remains behind.
そして、オプションのfeedbackパラメータに設定するウィジェットは、ドラッグ中に元の位置に残っているもの(ウィジェット)です。
↑動画の説明が間違っている。
ドラッグ中にドラッグによって移動させたいウィジェットをfeedbackプロパティに設定する。
//↓動画で紹介されているコード。間違い。 Draggable<Color>( data: Color(0x000000ff), child: MyBlueBox(), childWhenDraggable: MyRoundedBlueBox(), feedback: MyGreyBox(), )
//動画で示しているように、ドラッグ中に停止しているものが灰色のボックス、 //ドラッグにより動くものがMyRoundedBlueBoxにしたいのなら //以下のように設定する必要がある。 Draggable<Color>( data: Color(0x000000ff), child: MyBlueBox(), childWhenDraggable: MyGreyBox(),//←ドラッグ中に元の位置に表示させたいもの feedback: MyRoundedBlueBox(),//←ドラッグ中に動かしたいもの ),
Where does the widget land after being dragged?
You might ask.
ドラッグした後、ウィジェットはどこに着地しますか?
あなたは尋ねるかもしれません。
On a drag target.(DragTarge<T>ウィジェット)
ドラッグターゲットに着地します。
Drag targets are the landing zones for Draggables.
ドラッグターゲットは、Draggableの目的地です。
DragTarget<Color>{ onWillAccept: (value) => value !=Colors.black, onAccept: (value){ //Update app state with value. //アプリのstate(状態)に受け取ったvalueをセットする処理などを書く。 }, onLeave: (value){ //Alert the user their value didn't land. //値が着地しなかった時にユーザーに警告を発する処理。 } }
They have three function properties for handling data.
DragTarge<T>ウィジェットには、データを処理するための3つの関数プロパティがあります。
onAccept is called when a valid Draggable lands.
onAcceptに設定されたコールバック(関数)は、有効なDraggableが(DragTargeに)着陸したときに呼び出されます。
And onLeave is called anytime a Draggable is dragged over the target and then leaves.
また、onLeaveは、Draggableがターゲット上にドラッグされた後ターゲットから離れる時に呼び出されます。
Use the builder property to define how your drag target is composed based on the values that are being dragged to it.
builderプロパティを使用して、ドラッグターゲットにドラッグされている値に基づいてドラッグターゲットがどのように構成されるかを定義します。
DragTarget<Color>{ onWillAccept: (value) => value !=Colors.black, onAccept: (value){ //Update app state with value. //アプリのstate(状態)を更新する処理などを書く。 }, onLeave: (value){ //Alert the user their value didn't land. //値が着地しなかった時にユーザーに警告を発する処理。 } builder:(context,candidates,rejects){ return candidates.length > 0 ? MyBigColorfulBox(candidates[0]) : MyDashedOutline(); } }
//cbtdev.net様のサンプルを少し改造。 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範囲内かどうかのフラグ int _statenumber=0; @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<String>( data: 'Red', child: Container( width: 50, height: 50, color: Colors.red, ), feedback: Container( width: 50, height: 50, color: Colors.red.withOpacity(0.5), ), ), SizedBox(width: 20,), // 緑のボックス Draggable<String>( data: 'Green', child: Container( width: 50, height: 50, color: Colors.green, ), feedback: Container( width: 50, height: 50, color: Colors.green.withOpacity(1.0), ), childWhenDragging:Container( width: 50, height: 50, color: Colors.grey.withOpacity(0.5), ), onDragStarted: ()=>print("▶️onDragStarted"), onDragCompleted:()=>print("🎉🎉onDragCompleted"), onDragEnd: (d)=>print("🔚onDragEnd,速度:${d.velocity},,位置:${d.offset}"), onDraggableCanceled: (v,o)=>print("✖️onDraggableCanceled"), ), SizedBox(width: 20,), // 青のボックス Draggable<String>( data: 'Blue', child: Container( width: 50, height: 50, color: Colors.blue, ), feedback: Container( width: 50, height: 50, color: Colors.blue.withOpacity(0.5), ), ), SizedBox(width: 20,), Draggable<int>( data: 3, child: Container( width: 50, height: 50, color: Colors.black54, child:Center(child: Text("3"),), ), feedback: Container( width: 50, height: 50, color: Colors.black54.withOpacity(0.2), child:Center(child: Text("3"),), ), ), ], ), SizedBox(height: 100,), // ドラッグを受けるボックス DragTarget<String>( builder: (context, accepted, rejected) { print("accepted:$accepted, rejected:$rejected"); return Container( decoration: BoxDecoration( border: Border.all( color: _willAccepted ? Colors.orange : Colors.grey, width: _willAccepted ? 5 : 1, ), ), width: 200, height: 200, child: rejected.isNotEmpty ? Center(child:Text("rejected😈:$rejectedはドロップできません。"),) : _willAccepted ? Center(child:Text("accepted👼:$acceptedをドロップしてください。"),) : Center(child: Text(_acceptedData, style: TextStyle(fontSize: 50))), ); }, onWillAccept: (String data) { print("🐱onWillAccept"); if(data=="Red" || data=="Green") { _willAccepted = true; _statenumber = 1; return true; }else{ return false; } }, onAccept: (data) { print("😺onAccept"); _acceptedData = data; _willAccepted = false; _statenumber=0; }, onLeave: (data) { print("🐭onLeave"); _willAccepted = false; }, ), ], ), ), ); } }
onWillAcceptコールバックがtrueを返した時は、builderコールバックのacceptedパラメータに、ドラッグされた値がセットされ、それを元に(DragTargetが)リビルドされる。
onWillAcceptコールバックがfalseを返した時は、builderコールバックのrejectedに、ドラッグされた値がセットされ、それを元に(DragTargetが)リビルドされる、という流れ、仕組み。
参考