首页 > 解决方案 > Flutter Draggable - 如何将 Draggable 重置为起始位置?

问题描述

更新:

查看底部的最新更新。

原来的

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

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

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

观察

期望:

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

draggable_behavior

拖动目标

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

将 UniqueKey 添加到 Targets 和 Tiles 后 - Tiles 错误地显示在其原始位置,目标正确显示。

我想做的是:

注意 - 将以下代码应用于 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 Team 使用 Keys 的视频(链接

标签: flutterdraggableproviderconsumerdragtarget

解决方案


您将不得不使用密钥,如下所示:

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

每当您使用一个Row或一个Column或多个List相同类型的小部件并且您经常更新它们时,您必须使用Keys 以便框架知道何时以及何时不更新相同类型的小部件,

有关在此处使用密钥的更多信息


推荐阅读