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, ); }, ); }, ), ), ); }
I've found a workaround for this issue. It's not exactly the output I was looking for but I thought this could be useful to somebody else.
Instead of trying to control the drag object during dragging, I just let it go outside of my screen and I placed it back to its original position in case it goes outside of the screen.
Just a quick note if someone tries my code, I forgot to mention that I'm trying to develop a game for the web. The output on a mobile device might be a little bit odd!
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; } }); }, ), ), ); }}
I'm still interested if someone can find a proper solution to the initial issue :-)