Flutter: Drag and drop with Grid

为君一笑 提交于 2019-12-09 06:21:59

问题


I want to create a widget where you can add multiple widgets with different sizes and you can change their position by using the drag and drop technique. Something like a grid view with drag and drop where you can change the position both horizontally and vertically. While you are dragging the selected widget, other widgets will move around to open up space for it.

Does anyone have any suggestion where to start or are there already some examples which are implementing what I am looking for?


回答1:


Although this may not answer your question but people who are looking for simple drag and drop widget, then here is the example.

See my 2nd answer for more simpler way

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



回答2:


You can also try this easier one (It doesn't include Feedback)

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



回答3:


I've created a package called reorderables that solved this problem. You just need to tell the package your function to be called when drag and drop is done onReorder(int oldIndex, int newIndex).

This example has 9 icon widgets in a grid - 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
    );
  }
}

If you want to limit the number of columns, you can use an optional parameter named maxMainAxisCount




回答4:


Here is example of draggable text

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

You can check full example and a more advanced one here https://github.com/rxlabz/flutter_dropcity




回答5:


I cannot write comments becaus of my reputation but I wanted to answer to this question from the comments of CopsOnRoad's answer:

I don't want show feedback view instead of that I want to drag original view. Is it possible?

If someones looking for this too, you could use: childWhenDragging: Container(). You're still dragging the feedback but the original child will be hidden.

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


来源:https://stackoverflow.com/questions/50255869/flutter-drag-and-drop-with-grid

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!