Flutter Draggable - 如何将 Draggable 重置为起始位置?

Flutter Draggable - How to Reset Draggable to Starting Position?

更新:

See latest updates at the bottom.

原创

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

当我检查 Flutter 检查器时,它会在我期望的位置显示我的小部件,但是当我调用我的提供程序获取下一组信息时,可拖动对象仍处于其放下的位置,而不是返回到它们的起始位置。

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

观察

期望:

当我重置我的消费者数据时,我希望使用新数据在它们的原始位置重新绘制 Tiles 和 Targets。

DragTarget

@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 动画,将代码更改和新行为与上面的原始行为进行比较和对比。

After adding a UniqueKey to both the Targets and Tiles - The Tiles are incorrectly shown in their original location, The Targets are correctly shown.

What I am seeking to do is:

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

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

解决方案

感谢@LoVe关于使用UniqueKey的提醒和对Flutter Team关于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)

你将不得不使用密钥,像这样:

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

每当您使用 RowColumnList 相同类型的小部件并且您经常更新它们时,您必须使用 Keys所以框架知道什么时候应该和什么时候不更新相同类型的小部件,

更多关于使用键的信息here