Flutter:如何为 Draggable 小部件设置边界?
Posted
技术标签:
【中文标题】Flutter:如何为 Draggable 小部件设置边界?【英文标题】:Flutter: How to set boundaries for a Draggable widget? 【发布时间】:2020-09-10 03:47:54 【问题描述】:我正在尝试创建一个拖放游戏。我想确保Draggable
小部件在被拖动时不会离开屏幕。
我找不到这个特定问题的答案。有人问过类似的关于约束可拖动区域Constraining Draggable area 的问题,但答案实际上并没有使用Draggable
。
首先,我尝试在左侧实施限制。
我尝试使用带有onPointerMove
的侦听器。我已将此事件与 limitBoundaries 方法相关联,以检测 Draggable
何时从屏幕左侧退出。当Draggable
输出(position.dx < 0)
时,这部分正在控制台中打印Offset
值。我还将 setState 与此方法相关联,以将可拖动对象的位置设置为 Offset(0.0, position.dy)
,但这不起作用。
有人可以帮我解决这个问题吗?
import 'package:flutter/material.dart';
void main()
runApp(MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'Draggable Test',
home: GamePlay(),
);
class GamePlay extends StatelessWidget
@override
Widget build(BuildContext context)
return Scaffold(
body: Stack(
children: <Widget>[
Row(
children: [
Container(
width: 360,
height: 400,
decoration: BoxDecoration(
color: Colors.lightGreen,
border: Border.all(
color: Colors.green,
width: 2.0,
),
),
),
Container(
width: 190,
height: 400,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.purple,
width: 2.0,
),
),
),
],
),
DragObject(
key: GlobalKey(),
initPos: Offset(365, 0.0),
id: 'Item 1',
itmColor: Colors.orange),
DragObject(
key: GlobalKey(),
initPos: Offset(450, 0.0),
id: 'Item 2',
itmColor: Colors.pink,
),
],
),
);
class DragObject extends StatefulWidget
final String id;
final Offset initPos;
final Color itmColor;
DragObject(Key key, this.id, this.initPos, this.itmColor) : super(key: key);
@override
_DragObjectState createState() => _DragObjectState();
class _DragObjectState extends State<DragObject>
GlobalKey _key;
Offset position;
Offset posOffset = Offset(0.0, 0.0);
@override
void initState()
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
_key = widget.key;
position = widget.initPos;
super.initState();
void _getRenderOffsets()
final RenderBox renderBoxWidget = _key.currentContext.findRenderObject();
final offset = renderBoxWidget.localToGlobal(Offset.zero);
posOffset = offset - position;
void _afterLayout(_)
_getRenderOffsets();
void limitBoundaries(PointerEvent details)
if (details.position.dx < 0)
print(details.position);
setState(()
position = Offset(0.0, position.dy);
);
@override
Widget build(BuildContext context)
return Positioned(
left: position.dx,
top: position.dy,
child: Listener(
onPointerMove: limitBoundaries,
child: Draggable(
child: Container(
width: 80,
height: 80,
color: widget.itmColor,
),
feedback: Container(
width: 82,
height: 82,
color: widget.itmColor,
),
childWhenDragging: Container(),
onDragEnd: (drag)
setState(()
position = drag.offset - posOffset;
);
,
),
),
);
【问题讨论】:
【参考方案1】:试试这个。我对此进行了调整:Constraining Draggable area。
ValueNotifier<List<double>> posValueListener = ValueNotifier([0.0, 0.0]);
ValueChanged<List<double>> posValueChanged;
double _horizontalPos = 0.0;
double _verticalPos = 0.0;
@override
void initState()
super.initState();
posValueListener.addListener(()
if (posValueChanged != null)
posValueChanged(posValueListener.value);
);
@override
Widget build(BuildContext context)
return Scaffold(
body: Stack(
children: <Widget>[
_buildDraggable(),
]));
_buildDraggable()
return SafeArea(
child: Container(
margin: EdgeInsets.only(bottom: 100),
color: Colors.green,
child: Builder(
builder: (context)
final handle = GestureDetector(
onPanUpdate: (details)
_verticalPos =
(_verticalPos + details.delta.dy / (context.size.height))
.clamp(.0, 1.0);
_horizontalPos =
(_horizontalPos + details.delta.dx / (context.size.width))
.clamp(.0, 1.0);
posValueListener.value = [_horizontalPos, _verticalPos];
,
child: Container(
child: Container(
margin: EdgeInsets.all(12),
width: 110.0,
height: 170.0,
child: Container(
color: Colors.black87,
),
decoration: BoxDecoration(color: Colors.black54),
),
));
return ValueListenableBuilder<List<double>>(
valueListenable: posValueListener,
builder:
(BuildContext context, List<double> value, Widget child)
return Align(
alignment: Alignment(value[0] * 2 - 1, value[1] * 2 - 1),
child: handle,
);
,
);
,
),
),
);
【讨论】:
【参考方案2】:我找到了解决此问题的方法。这不是我正在寻找的输出,但我认为这可能对其他人有用。
我没有尝试在拖动过程中控制拖动对象,而是让它离开我的屏幕,然后我将它放回原来的位置,以防它离开屏幕。
如果有人尝试我的代码,请快速说明一下,我忘了提到我正在尝试为网络开发游戏。移动设备上的输出可能有点奇怪!
import 'package:flutter/material.dart';
void main()
runApp(MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'Draggable Test',
home: GamePlay(),
);
class GamePlay extends StatelessWidget
@override
Widget build(BuildContext context)
return Scaffold(
body: Stack(
children: <Widget>[
Row(
children: [
Container(
width: 360,
height: 400,
decoration: BoxDecoration(
color: Colors.lightGreen,
border: Border.all(
color: Colors.green,
width: 2.0,
),
),
),
Container(
width: 190,
height: 400,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.purple,
width: 2.0,
),
),
),
],
),
DragObject(
key: GlobalKey(),
initPos: Offset(365, 0.0),
id: 'Item 1',
itmColor: Colors.orange),
DragObject(
key: GlobalKey(),
initPos: Offset(450, 0.0),
id: 'Item 2',
itmColor: Colors.pink,
),
],
),
);
class DragObject extends StatefulWidget
final String id;
final Offset initPos;
final Color itmColor;
DragObject(Key key, this.id, this.initPos, this.itmColor) : super(key: key);
@override
_DragObjectState createState() => _DragObjectState();
class _DragObjectState extends State<DragObject>
GlobalKey _key;
Offset position;
Offset posOffset = Offset(0.0, 0.0);
@override
void initState()
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
_key = widget.key;
position = widget.initPos;
super.initState();
void _getRenderOffsets()
final RenderBox renderBoxWidget = _key.currentContext.findRenderObject();
final offset = renderBoxWidget.localToGlobal(Offset.zero);
posOffset = offset - position;
void _afterLayout(_)
_getRenderOffsets();
@override
Widget build(BuildContext context)
return Positioned(
left: position.dx,
top: position.dy,
child: Listener(
child: Draggable(
child: Container(
width: 80,
height: 80,
color: widget.itmColor,
),
feedback: Container(
width: 82,
height: 82,
color: widget.itmColor,
),
childWhenDragging: Container(),
onDragEnd: (drag)
setState(()
if (drag.offset.dx > 0)
position = drag.offset - posOffset;
else
position = widget.initPos;
);
,
),
),
);
如果有人能找到最初问题的适当解决方案,我仍然很感兴趣 :-)
【讨论】:
以上是关于Flutter:如何为 Draggable 小部件设置边界?的主要内容,如果未能解决你的问题,请参考以下文章
如何为可扩展的 Flutter 小部件设置动画以将其滑出屏幕
如何为我的小部件添加边距?了解 EdgeInsets 的效果
Flutter:如何为 Selectable Text 构建自定义工具栏~