Search code examples
flutterflutter-layoutdraggable

How to create a movable widget in flutter such that is stays at the position it is dragged to


How can i create a movable/draggable widget in flutter that stays at the position it is dragged to .I tried using draggable widget but the widget which gets wrapped in draggable returns back to the original position after releasing the the drag.

As you can see in this GIF that the draggable object returns back to its original position. How to make it stay there


Solution

  • On top of dragging the object around, you can also make it zoomable with the help of a GestureDetector. I applied the GestureDetector to the main Stack so that you can pinch to zoom in/out anywhere on the screen. It makes it somewhat easier to see what you are doing.

    enter image description here


    HookWidget version

    class DragArea extends HookWidget {
      final Widget child;
    
      const DragArea({Key key, this.child}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        final position = useState(Offset(100, 100));
        final prevScale = useState(1.0);
        final scale = useState(1.0);
        return GestureDetector(
          onScaleUpdate: (details) => scale.value = prevScale.value * details.scale,
          onScaleEnd: (_) => prevScale.value = scale.value,
          child: Stack(
            children: [
              Positioned.fill(
                  child: Container(color: Colors.amber.withOpacity(.4))),
              Positioned(
                left: position.value.dx,
                top: position.value.dy,
                child: Draggable(
                  maxSimultaneousDrags: 1,
                  feedback: Transform.scale(
                    scale: scale.value,
                    child: child,
                  ),
                  childWhenDragging: Opacity(
                    opacity: .3,
                    child: Transform.scale(
                      scale: scale.value,
                      child: child,
                    ),
                  ),
                  onDragEnd: (details) => position.value = details.offset,
                  child: Transform.scale(
                    scale: scale.value,
                    child: child,
                  ),
                ),
              )
            ],
          ),
        );
      }
    }
    

    StatefulWidget version

    class StatefulDragArea extends StatefulWidget {
      final Widget child;
    
      const StatefulDragArea({Key key, this.child}) : super(key: key);
    
      @override
      _DragAreaStateStateful createState() => _DragAreaStateStateful();
    }
    
    class _DragAreaStateStateful extends State<StatefulDragArea> {
      Offset position = Offset(100, 100);
      double prevScale = 1;
      double scale = 1;
    
      void updateScale(double zoom) => setState(() => scale = prevScale * zoom);
      void commitScale() => setState(() => prevScale = scale);
      void updatePosition(Offset newPosition) =>
          setState(() => position = newPosition);
    
      @override
      Widget build(BuildContext context) {
        return GestureDetector(
          onScaleUpdate: (details) => updateScale(details.scale),
          onScaleEnd: (_) => commitScale(),
          child: Stack(
            children: [
              Positioned.fill(
                  child: Container(color: Colors.amber.withOpacity(.4))),
              Positioned(
                left: position.dx,
                top: position.dy,
                child: Draggable(
                  maxSimultaneousDrags: 1,
                  feedback: widget.child,
                  childWhenDragging: Opacity(
                    opacity: .3,
                    child: widget.child,
                  ),
                  onDragEnd: (details) => updatePosition(details.offset),
                  child: Transform.scale(
                    scale: scale,
                    child: widget.child,
                  ),
                ),
              ),
            ],
          ),
        );
      }
    }