Flutter : Add Drag and Drop Anywhere Widget in Stack Widget Dynamically

How can we dynamically add drag and drop anywhere widget in flutter? Here is what I tried to implement. Suggest changes to solve the problem or suggest the proper method to implement it.

The problem I need to solve is that the canvasItemObj.x and canvasItemObj.y variables are updating according to t the dragUpdate. But the position of the widget is not updating. (top and left parameters of the Positioned() widget)

Here is the CanvasItemProps class for holding a widget and its properties.

class CanvasItemProps {
  Widget canvasItem;
  double x = 0, y = 0, dx, dy;
  double scale = 1;
  Color color;
  CanvasItemProps();
  CanvasItemProps.fromItem(this.canvasItem);
}

canvasItemProps holds the list of CanvasItemProps

List<CanvasItemProps> canvasItemProps = [];

I have a flatbutton which onPressed execute the following:

var canvasItemObj = CanvasItemProps();
canvasItemProps.add(canvasItemObj);

canvasItemObj.canvasItem = Positioned(
                  left: canvasItemObj.x,
                  top: canvasItemObj.y,
                  child: GestureDetector(
                    child: Text(
                      addTextController.text,
                      textScaleFactor: 5,
                    ),
                    onVerticalDragUpdate: (dragDetails) {
                      setState(() {
                        canvasItemObj.y = dragDetails.localPosition.dy;
                        canvasItemObj.x = dragDetails.localPosition.dx;
                      });
                    },

                  ));
              addTextController.text = "";
              setState(() {});
            },

In build function there is a Stack:

Stack(children:canvasItemProps.isEmpty()?
               Text("No Item"):
                getCanvasItem)

The following getCanvasItem function returns the list of widget to pass to the Stack widget

List<Widget> getCanvasItem() {
    List<Widget> list = [];

    for (int i = 0; i < canvasItemProps.length; i++) {
      list.add(canvasItemProps[i].canvasItem);
    }
    return list;
  }

On the other hand, this works fine when I am dealing with only one widget that is not dynamically added(x and y are two variables declared in the scope and are initialized to 0.)

double x=0,y=0;

widget is as follows:

Container(
            margin: EdgeInsets.fromLTRB(50, 100, 90, 100),
            color: Colors.yellow,
            child: Stack(
            children: <Widget>[
              Positioned(
            top: y,
            left: x,
            child: GestureDetector(
              onVerticalDragUpdate: (v){
                setState(() {
                  x = v.localPosition.dx;
                  y = v.localPosition.dy;
                });
              },
              child:Text("Text"),
            )
          ),
            ],
          ),
          )

This Post Has One Comment

  1. No Fault

    First, wrap your Container inside the Stack with Positioned.

    Then, use Pan Gesture to implement a Pan in your Container and use onPan… methods to handle Pan Gesture

    Here is code:

    Offset position;

    @override
    void initState() {
    super.initState();
    position = Offset(10, 10);
    }

    @override
    Widget build(BuildContext context) {

    double _width = MediaQuery.of(context).size.width;
    double _height = _width * 9 / 16;

    return GestureDetector(
    onPanStart: (details) => _onPanStart(context, details),
    onPanUpdate: (details) => _onPanUpdate(context, details, position),
    onPanEnd: (details) => _onPanEnd(context, details),
    onPanCancel: () => _onPanCancel(context),

    child: SafeArea(
    child: Stack(
    children: [
    Positioned(
    top: position.dy,
    child: Container(
    color: Colors.red,
    width: _width,
    height: _height,
    ),
    ),
    ],
    ),
    ),
    );
    }

    void _onPanStart(BuildContext context, DragStartDetails details) {
    print(details.globalPosition.dy);

    }

    void _onPanUpdate(BuildContext context, DragUpdateDetails details, Offset offset) {
    setState(() {
    position = details.globalPosition;
    });
    }

    void _onPanEnd(BuildContext context, DragEndDetails details) {
    print(details.velocity);
    }

    void _onPanCancel(BuildContext context) {
    print(“Pan canceled !!”);
    }
    Hope this helps!

Leave a Reply