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 小部件。
观察
- 即使我正在包装可拖动对象仍处于下降位置
带有消费者的父小部件中的图块。
- 消费者正在传递更改后的数据值。
- 使用 Flutter 在我的小部件树中可以看到 DragTargets
检查员.
- 当我遇到一个我拥有三个以上 Draggable 的事件时,
放置的可拖动对象放错了位置,而 Tiles 和 DropTargets
其他小部件按预期显示。
期望:
当我重置我的消费者数据时,我希望使用新数据在它们的原始位置重新绘制 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:
- 让 Tiles 显示在目标上的放置位置
- 在不同的点击事件之后,将 Tiles 和 Targets 重新绘制到它们的原始位置。
注意 - 将以下代码应用于 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, ); }
每当您使用 Row
或 Column
或 List
相同类型的小部件并且您经常更新它们时,您必须使用 Key
s所以框架知道什么时候应该和什么时候不更新相同类型的小部件,
更多关于使用键的信息here
更新:
See latest updates at the bottom.
原创
我有 flutter Draggable 和 DragTarget 小部件可以正常工作,除了在我放置 DragTarget 之后我遇到了一种特殊情况,我想重建 DragTarget 并将 Draggable 移回其原始位置。
当我检查 Flutter 检查器时,它会在我期望的位置显示我的小部件,但是当我调用我的提供程序获取下一组信息时,可拖动对象仍处于其放下的位置,而不是返回到它们的起始位置。
如图像的最后一帧所示,三个目标帧仍然包含对可拖动小部件的引用,但我在每次单击红色 IconButton 时重建 Draggable 和 DragTarget 小部件。
观察
- 即使我正在包装可拖动对象仍处于下降位置 带有消费者的父小部件中的图块。
- 消费者正在传递更改后的数据值。
- 使用 Flutter 在我的小部件树中可以看到 DragTargets
检查员. - 当我遇到一个我拥有三个以上 Draggable 的事件时,
放置的可拖动对象放错了位置,而 Tiles 和 DropTargets 其他小部件按预期显示。
期望:
当我重置我的消费者数据时,我希望使用新数据在它们的原始位置重新绘制 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:
- 让 Tiles 显示在目标上的放置位置
- 在不同的点击事件之后,将 Tiles 和 Targets 重新绘制到它们的原始位置。
注意 - 将以下代码应用于 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, ); }
每当您使用 Row
或 Column
或 List
相同类型的小部件并且您经常更新它们时,您必须使用 Key
s所以框架知道什么时候应该和什么时候不更新相同类型的小部件,
更多关于使用键的信息here