【问题标题】:Flutter: Drag and drop with GridFlutter:使用 Grid 拖放
【发布时间】:2018-10-19 17:15:40
【问题描述】:

我想创建一个小部件,您可以在其中添加多个不同大小的小部件,并可以使用拖放技术更改它们的位置。类似于带有拖放功能的网格视图,您可以在其中水平和垂直更改位置。当您拖动选定的小部件时,其他小部件将四处移动以为其打开空间。

有没有人有任何建议从哪里开始,或者已经有一些例子可以实现我正在寻找的东西?

【问题讨论】:

标签: flutter mobile drag-and-drop flutter-layout


【解决方案1】:

你也可以试试这个更简单的(不包括反馈)

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

【讨论】:

  • 这是一个绝妙的答案,正是我所需要的!谢谢。
【解决方案2】:

虽然这可能无法回答您的问题,但人们正在寻找简单的拖放小部件,那么这里就是示例。

查看我的第二个答案以获得更简单的方法

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

【讨论】:

  • 好一个。我不想显示反馈视图而不是我想拖动原始视图。有可能吗?
  • @Addison 抱歉我没有I want to drag original view,我认为这里的示例中,原始视图被拖动了。
  • 那么干脆不包括它。我不认为它是强制性的。
  • @AkashPatel 我添加了一个不使用反馈的新答案,试一试。
  • @CopsOnRoad,您似乎没有得到我遇到的问题。你介意看看这个问题:stackoverflow.com/questions/54163639/…。谢谢
【解决方案3】:

我创建了一个名为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的可选参数

【讨论】:

【解决方案4】:

这是可拖动文本的示例

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

【讨论】:

  • 对我不起作用。错误:Incorrect use of ParentDataWidget. Positioned widgets must be placed directly inside Stack widgets. Positioned(no depth, left: 0.0, top: 0.0, dirty) has a Stack ancestor, but there are other widgets between them
  • 我们在使用Positioned时需要使用Stack。请参阅下面的工作示例。
【解决方案5】:

由于我的声誉,我不能写 cmets,但我想从 CopsOnRoad 的 cmets 的回答中回答这个问题:

我不想显示反馈视图而不是我想拖动的视图 原来的看法。有可能吗?

如果有人也在寻找这个,你可以使用: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);
                },
              ),
       ...

【讨论】:

    【解决方案6】:


    您也可以使用LongPressDraggable,为此您需要长按您的小部件,然后只有您可以拖动它。

    Offset _offset = Offset.zero;
    
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(),
        body: LayoutBuilder(
          builder: (context, constraints) {
            return Stack(
              children: [
                Positioned(
                  left: _offset.dx,
                  top: _offset.dy,
                  child: LongPressDraggable(
                    feedback: FlutterLogo(colors: Colors.orange, size: 100),
                    child: FlutterLogo(colors: Colors.green, size: 100),
                    onDragEnd: (details) {
                      setState(() {
                        final adjustment = MediaQuery.of(context).size.height - constraints.maxHeight;
                        _offset = Offset(details.offset.dx, details.offset.dy - adjustment);
                      });
                    },
                  ),
                ),
              ],
            );
          },
        ),
      );
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-17
      • 2019-04-12
      • 1970-01-01
      • 2019-06-29
      • 2011-07-05
      • 1970-01-01
      • 1970-01-01
      • 2019-05-23
      相关资源
      最近更新 更多