Flutter: How to set boundaries for a Draggable widget?
Try this. I tweaked this from: 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,
);
},
);
},
),
),
);
}
cmaxetom
I created some years back a website to help kids learn to read: www.maxetom.com It's packed with games in Flash/ActionScript 3. Hence, soon my website will be out of use as even Chrome will soon ban Flash :-( I decided to migrate all the games to Flutter! This way I hope I'll be able to run the games on the web, but also create an App with the same code! I'm sure I'll need some help along the way!
Updated on December 20, 2022Comments
-
cmaxetom over 1 year
I'm trying to create a drag and drop game. I would like to make sure that the
Draggable
widgets don't get out of the screen when they are dragged around.I couldn't find an answer to this specific question. Someone asked something similar about constraining draggable area Constraining Draggable area but the answer doesn't actually make use of
Draggable
.To start with I tried to implement a limit on the left-hand side.
I tried to use a Listener with
onPointerMove
. I've associated this event with a limitBoundaries method to detect when theDraggable
exits from the left side of the screen. This part is working as it does print in the console theOffset
value when theDraggable
is going out(position.dx < 0)
. I also associated a setState to this method to set the position of the draggable toOffset(0.0, position.dy)
but this doesn't work.Could anybody help me with this?
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; }); }, ), ), ); } }