更新:
查看底部的最新更新。
原来的
我有抖动的 Draggable 和 DragTarget 小部件正在工作,除了在我放置 DragTarget 后,我遇到了一种特殊情况,我想重建 DragTarget 并将 Draggable 移回其原始位置。
当我检查 Flutter 检查器时,它会显示我的小部件,但当我调用我的 Provider 以获取下一组信息时,Draggables 保持在它们的放置位置而不是返回到它们的起始位置。
如图像的最后一帧所示,三个目标帧仍包含对可拖动小部件的引用,但我在每次单击红色 IconButton 时重建 Draggable 和 DragTarget 小部件。
观察
- 即使我使用 Consumer 将 Tiles 包装在父窗口小部件中,可拖动对象仍处于放置位置。
- 消费者正在传递更改的数据值。
使用 Flutter Inspector在我的小部件树中可以看到 DragTargets 。- 当我遇到一个拥有三个以上 Draggable 的事件时,
拖放的 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,以将代码更改和新行为与上面的原始行为进行比较和对比。
将 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 的视频(链接)