web-dev-qa-db-ja.com

Flutter:グリッドでドラッグアンドドロップ

サイズの異なる複数のウィジェットを追加し、ドラッグアンドドロップの手法を使用して位置を変更できるウィジェットを作成したいと思います。水平方向と垂直方向の両方で位置を変更できるドラッグアンドドロップ付きのグリッドビューのようなもの。選択したウィジェットをドラッグしている間、他のウィジェットが動き回ってそのスペースを開きます。

誰かがどこから始めるべきか提案がありますか、または私が探しているものを実装しているいくつかの例はすでにありますか?

11

これはあなたの質問に答えないかもしれませんが、単純なドラッグアンドドロップウィジェットを探している人々、それからここに例があります。

より簡単な方法については、私の2番目の回答を参照してください

enter image description here

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Drag app"),
        ),
        body: HomePage(),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _HomePageState();
  }
}

class _HomePageState extends State<HomePage> {
  double width = 100.0, height = 100.0;
  Offset position ;

  @override
  void initState() {
    super.initState();
    position = Offset(0.0, height - 20);
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        Positioned(
          left: position.dx,
          top: position.dy - height + 20,
          child: Draggable(
            child: Container(
              width: width,
              height: height,
              color: Colors.blue,
              child: Center(child: Text("Drag", style: Theme.of(context).textTheme.headline,),),
            ),
            feedback: Container(
              child: Center(
                child: Text("Drag", style: Theme.of(context).textTheme.headline,),),
              color: Colors.blue[300],
              width: width,
              height: height,
            ),
            onDraggableCanceled: (Velocity velocity, Offset offset){
              setState(() => position = offset);
            },
          ),
        ),
      ],
    );
  }
}
6
CopsOnRoad

これをもっと簡単に試すこともできます(フィードバックは含まれていません)

enter image description here

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: Scaffold(body: HomePage()));
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  Offset offset = Offset.zero;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        Positioned(
          left: offset.dx,
          top: offset.dy,
          child: GestureDetector(
            onPanUpdate: (details) {
              setState(() {
                offset = Offset(offset.dx + details.delta.dx, offset.dy + details.delta.dy);
              });
            },
            child: Container(width: 100, height: 100, color: Colors.blue),
          ),
        ),
      ],
    );
  }
}
5
CopsOnRoad

この問題を解決する reorderables というパッケージを作成しました。ドラッグアンドドロップが完了したときに呼び出される関数をパッケージに伝える必要があるだけですonReorder(int oldIndex, int newIndex)

この例では、グリッドに9つのアイコンウィジェットがあります- Screenshot:ReorderableWrap

class _WrapExampleState extends State<WrapExample> {
  final double _iconSize = 90;
  List<Widget> _tiles;

  @override
  void initState() {
    super.initState();
    _tiles = <Widget>[
      Icon(Icons.filter_1, key: ValueKey(1), size: _iconSize),
      Icon(Icons.filter_2, key: ValueKey(2), size: _iconSize),
      Icon(Icons.filter_3, key: ValueKey(3), size: _iconSize),
      Icon(Icons.filter_4, key: ValueKey(4), size: _iconSize),
      Icon(Icons.filter_5, key: ValueKey(5), size: _iconSize),
      Icon(Icons.filter_6, key: ValueKey(6), size: _iconSize),
      Icon(Icons.filter_7, key: ValueKey(7), size: _iconSize),
      Icon(Icons.filter_8, key: ValueKey(8), size: _iconSize),
      Icon(Icons.filter_9, key: ValueKey(9), size: _iconSize),
    ];
  }

  @override
  Widget build(BuildContext context) {
    void _onReorder(int oldIndex, int newIndex) {
      setState(() {
        Widget row = _tiles.removeAt(oldIndex);
        _tiles.insert(newIndex, row);
      });
    }

    return ReorderableWrap(
      spacing: 8.0,
      runSpacing: 4.0,
      padding: const EdgeInsets.all(8),
      children: _tiles,
      onReorder: _onReorder
    );
  }
}

列の数を制限する場合は、maxMainAxisCountという名前のオプションパラメータを使用できます

3
Hansheng

私の評判のせいでコメントを書くことはできませんが、CopsOnRoadの回答のコメントからこの質問に回答したいと思います。

元のビューをドラッグしたいのではなく、フィードバックビューを表示したくない。出来ますか?

誰かがこれも探している場合は、childWhenDragging:Container()を使用できます。まだフィードバックをドラッグしていますが、元の子は非表示になります。

        ...
        child: Draggable(
                child: Container(
                  width: width,
                  height: height,
                  color: Colors.blue,
                  child: Center(child: Text("Drag", style: Theme.of(context).textTheme.headline,),),
                ),
                feedback: Container(
                  child: Center(
                    child: Text("Drag", style: Theme.of(context).textTheme.headline,),),
                  color: Colors.blue[300],
                  width: width,
                  height: height,
                ),
                childWhenDragging: Container(), // <-- so it looks like the original view is beeing dragged
                onDraggableCanceled: (Velocity velocity, Offset offset){
                  setState(() => position = offset);
                },
              ),
       ...
2
Don Joe

これはドラッグ可能なテキストの例です

class DraggableText extends StatefulWidget {
  final Offset initialOffset;
  final String text;

  DraggableText(this.text, this.initialOffset);

  @override
  _DraggableTextState createState() => new _DraggableTextState();
}

class _DraggableTextState extends State<DraggableText> {
  Offset position = new Offset(0.0, 0.0);

  @override
  void initState() {
    super.initState();
    position = widget.initialOffset;
  }

  @override
  Widget build(BuildContext context) {
    final item = new LabelBox(size: new Size.square(100.0), label: widget.text);
    final avatar = new LabelBox(
      size: new Size.square(150.0), label: widget.text, opacity: 0.4);
    final draggable = new Draggable(
      data: widget.text,
      feedback: avatar,
      child: item,
      childWhenDragging: new Opacity(opacity: 0.0, child: item),
      onDraggableCanceled: (velocity, offset) {
        print('_DragBoxState.build -> offset ${offset}');
        setState(() => position = offset);
      });
    return new Positioned(
      left: position.dx, top: position.dy, child: draggable);
  }
}

ここで完全な例とより高度な例を確認できます https://github.com/rxlabz/flutter_dropcity

1
Tree