【问题标题】:Flutter Draggable - How to Reset Draggable to Starting Position?Flutter Draggable - 如何将 Draggable 重置为起始位置?
【发布时间】:2020-08-13 03:40:09
【问题描述】:

更新:

在底部查看最新更新。

原创

我有抖动的 Draggable 和 DragTarget 小部件正在工作,除了在我放置 DragTarget 后,我​​遇到了一种特殊情况,我想重建 DragTarget 并将 Draggable 移回其原始位置。

当我检查 Flutter 检查器时,它会显示我的小部件,但当我调用我的 Provider 以获取下一组信息时,Draggables 保持在它们的放置位置而不是返回到它们的起始位置。

如图像的最后一帧所示,三个目标帧仍然包含对可拖动小部件的引用,但我在每次点击红色 IconButton 时重建 Draggable 和 DragTarget 小部件。

观察

  • 即使我正在包装 Draggables 仍处于掉落位置 带有 Consumer 的父小部件中的磁贴。
  • Consumer 正在传递更改的数据值。
  • 使用 Flutter 在我的小部件树中可以看到 DragTargets
    检查员。
  • 当我参加一个拥有三个以上 Draggable 的活动时,
    当 Tiles 和 DropTargets 用于 其他小部件按预期显示。

期望:

当我重置我的消费者数据时,我希望使用新数据在其原始位置重新绘制磁贴和目标。

拖动目标

@override
  Widget build(BuildContext context) {
    return DragTarget(
      builder:
          (context, List<String> candidateData, List<dynamic> rejectedData) {
        return (isSuccessful) //&& !widget.isPuzzleComplete
            ? Tile(
                title: widget.data,
              )
            : TargetPlaceholder(
                widget: widget,
              );
      },
      onWillAccept: (data) {
        return widget.data == data;
      },
      onAccept: (data) {
        setState(() {
          Provider.of<GameController>(
            context,
            listen: false,
          ).checkPuzzleSolved();
          //activate the animation, may not require setState
          isSuccessful = !widget.isPuzzleComplete;
        });
      },
      onLeave: (data) {
        print('Draggable object left the target area containing data of $data');
      },
    );
  }
}

可拖动

@override
  Widget build(BuildContext context) {
    return Draggable(
      data: widget.title,
//      dragAnchor: DragAnchor.pointer,
      child: isSuccessful ? Container() : TileContent(widget: widget),
      childWhenDragging: Container(),
      feedback: Opacity(
        opacity: 0.7,
        child: TileContent(
          widget: widget,
        ),
      ),
      onDragCompleted: () {
        setState(() {
          Transform(
              transform: Matrix4.translation(_shake()),
              child: TileContent(widget: widget));
        });
      },
      onDragEnd: (details) {
        setState(() {
          isSuccessful = details.wasAccepted;
        });
      },
    );
  }
}

父小部件

 Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              for (var item in targetPattern)
                Consumer<GameController>(builder: (context, gc, _) {
                  return Target(
                    data: item,
                    isPuzzleComplete: gc.isPuzzleComplete,
                  );
                })
            ],
          ),
          SizedBox(
            height: 36,
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              for (var data in scrambledPattern)
                Consumer<GameController>(builder: (context, gc, _) {
                  return Tile(
                    title: data,
                    isPuzzleComplete: gc.isPuzzleComplete,
                  );
                })
            ],
          ),

更新的行为

请参阅下面的注释和第二个动画 gif,以将代码更改和新行为与上面的原始行为进行比较和对比。

在向目标和瓷砖添加唯一键后 - 瓷砖 错误地显示在其原始位置,目标是 正确显示。

我想做的是:

  • 让瓷砖显示在目标上的放置位置
  • 在不同的点击事件后,将磁贴和目标重新绘制到其原始位置。

注意 - 将以下代码应用于 Tile 和 Target 小部件。

key: (gc.isPuzzleComplete == true) ? UniqueKey() : null,

解决方案

感谢@LoVe 关于使用 UniqueKey 的提醒以及对 Flutter 团队关于 Keys 的视频的回顾;我能够通过有条件地将 UniqueKey 添加到包含 Tiles 和 Target 的封闭行来解决该问题。

在这些小部件中,我可以使用(isSuccessful || widget.isPuzzleComplete) 更新小部件的外观。我还能够从 Tile/Target 小部件和 setState 调用中删除与获取 isPuzzleComplete 逻辑相关的消费者逻辑。

目前,该行为是 100% 正确的。

//mod to parent row of Tiles
Row(
  key: widget.isPuzzleComplete ? UniqueKey() : null,
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: <Widget>[
   for (var data in scrambledPattern)
     Tile( title: data,
           isPuzzleComplete: widget.isPuzzleComplete,
          ), 
        ],
      ),

//mod to draggable Tile
Draggable(
  key: (widget.isPuzzleComplete == true) ? UniqueKey() : null,
  data: widget.title,
  child: (isSuccessful || widget.isPuzzleComplete)
      ? Container()
      : TileContent(widget: widget),

//mod to drag target 
return DragTarget(
      key: UniqueKey(),
      builder:
          (context, List<String> candidateData, List<dynamic> rejectedData) {
        return (widget.isPuzzleComplete || isSuccessful)
            ? Tile(
                title: widget.data,

参考文献

关于密钥使用的 Flutter 团队视频 (link)

【问题讨论】:

  • 你是怎么做到的,你的应用中一次只能拖动 1 个可拖动对象?
  • @Sniperduel17 - 除了将瓷砖和目标嵌入到两个不同的包装小部件中之外,我什么也没做。这些行为类似于行小部件,只是它们处理溢出。在那一点上,我得到了各种动画行为,没有任何努力。仅供参考 - 我也可以将多个图块拖动到多个目标。我没有设置任何选项来允许单次拖动,一切都是开箱即用的。

标签: flutter draggable provider consumer dragtarget


【解决方案1】:

你必须使用钥匙,像这样:

Tile( key:UniqueKey(), title: data, isPuzzleComplete: gc.isPuzzleComplete, ); }

当您使用 RowColumnList 的相同类型的小部件并且您经常更新它们时,您必须使用 Keys 以便框架知道何时该何时不更新同类型的widget,

更多关于使用密钥here

【讨论】:

    猜你喜欢
    • 2020-09-10
    • 2013-09-16
    • 2016-10-20
    • 2021-02-22
    • 1970-01-01
    • 1970-01-01
    • 2021-08-08
    • 1970-01-01
    • 2015-02-13
    相关资源
    最近更新 更多