Search code examples
flutterconstraintsdraggable

Constrain stack area for Positioned widget


I have a Positioned widget that is draggable, by using Offset and wrapping it inside a Gesture Detector to update its position, and I want to constrain the area that this widget can move, so it cannot go beyond the boundaries. The structure is like this:

  • Scaffold -> Stack -> Positioned(Circle)

As it is shown below, I want the circle to move only in the are inside the gray lines. Is it possible?

enter image description here


Solution

  • Provide 2x value as limit, I did for touch position purpose. also, both dx and dy axis can work separately. If you don't want it, you can combine two condition on a single setState.

    Result

    enter image description here

    Widget

    class HomeWidget extends StatefulWidget {
      @override
      _HomeWidgetState createState() => _HomeWidgetState();
    }
    
    class _HomeWidgetState extends State<HomeWidget> {
      double dx = 0;
      double dy = 0;
    
      get limit => 50;
      get containerSize => 50;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: LayoutBuilder(
            builder: (context, constraints) => Stack(
              children: [
                Positioned(
                  left: limit * .5,
                  child: Container(
                    height: constraints.maxHeight,
                    width: 5,
                    color: Colors.grey,
                  ),
                ),
                Positioned(
                  bottom: limit * .5,
                  child: Container(
                    width: constraints.maxWidth,
                    height: 5,
                    color: Colors.grey,
                  ),
                ),
                Positioned(
                  top: dy - containerSize * .5,
                  left: dx - containerSize * .5,
                  child: Container(
                    height: containerSize,
                    width: containerSize,
                    decoration: BoxDecoration(
                      shape: BoxShape.circle,
                      color: Colors.deepPurple,
                    ),
                  ),
                ),
                GestureDetector(
                  onPanUpdate: (details) {
                    if (details.localPosition.dx > limit)
                      setState(() {
                        dx = details.localPosition.dx;
                      });
                    if (details.localPosition.dy < constraints.maxHeight - limit)
                      setState(() {
                        dy = details.localPosition.dy;
                      });
    
                    print(" $dx, $dy ");
                  },
                ),
              ],
            ),
          ),
        );
      }
    }