Dragging (but not dropping) in Flutter
Tags: flutter
Published: 2021-08-06 19:01:44

---
title: Dragging (but not dropping) in Flutter
published: true
date: 2021-08-06 18:46:35 UTC
tags: Flutter
canonical_url: https://www.rockandnull.com/flutter-drag/
---

 ![Dragging (but not dropping) in Flutter](https://images.unsplash.com/photo-1617041519719-a5f54d97dbb1?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8c2VhcmNofDQyfHxtb3Zpbmd8ZW58MHx8fHwxNjI4Mjc1NDgz&ixlib=rb-1.2.1&q=80&w=2000)

Flutter's way of drawing things on the screen is awesome. But it comes a time (i.e. a project) where you need not to position things on the screen yourself but allow the user to add and move things around.

The framework is mature enough to offer a way to drag-and-drop things around using the `Draggable` and `DragTarget` classes. But what if you don't want to "drop" things? Just to allow users to "drag" elements on the screen?

![Dragging (but not dropping) in Flutter](https://www.rockandnull.com/content/images/2021/08/drag.gif)

A combination of `Draggable` and `Stack` will do the trick. Let me show you an (almost) complete example and will explain it line by line.

```
// 1.
var _x = 0.0;
var _y = 0.0;
final GlobalKey stackKey = GlobalKey();

[...]

Widget dragArea() => Stack( // 2.
      key: stackKey, // 3.
      fit: StackFit.expand,
      children: [
        Container(color: Colors.blue), // 4.
        Positioned( // 5.
          left: _x,
          top: _y,
          child: Draggable( // 6.
            child: Text('Move me'), // 7.
            feedback: text('Move me'), // 8.
            childWhenDragging: Container(), // 9.
            onDragEnd: (dragDetails) { // 10.
              setState(() {
                final parentPos = stackKey.globalPaintBounds;
                if (parentPos == null) return;
                _x = dragDetails.offset.dx - parentPos.left; // 11.
                _y = dragDetails.offset.dy - parentPos.top;
              });
            },
          ),
        ),
      ],
    );
    
[...]

// A useful extention for getting absolute coordinates of a widget 
// (found somewhere in SO)
extension GlobalKeyExtension on GlobalKey {
  Rect? get globalPaintBounds {
    final renderObject = currentContext?.findRenderObject();
    var translation = renderObject?.getTransformTo(null).getTranslation();
    if (translation != null && renderObject?.paintBounds != null) {
      return renderObject!.paintBounds
          .shift(Offset(translation.x, translation.y));
    } else {
      return null;
    }
  }
}

```

1. These `_x` and _`_y`_ coordinates are for the location of the movable item when it's still.
2. The `Stack` is the top-level widget used as the area in which the movable item can be moved. This widget allows other `children` widgets be drawn on top of each other if desired.
3. We set a global key to this `Stack` because we want to refer to it later (in [11]).
4. The `children` of a `Stack` are drawn in the order they are defined. This is the first `children` and acts as the background of the movable area (just a plain blue container).
5. We wrap the movable item in a `Positioned` widget. This is to allow absolute positioning when the item is still.
6. The `Draggable` widget is what does the job of moving around the item. Note that the widget allows dragging/moving the item in the entire screen, not just the `Stack` widget we have. Additional logic is needed if you want to restrict moving within this area, not covered in this article.
7. The `child` of the `Draggable` is what renders on screen when the item is not being dragged/moved. In our case it's a simple `Text` widget for demo purposes.
8. The `feedback` is what renders while the dragging is in progress. In our simple demo case it's the same as the `child` but it can be customized to indicate that the item is being dragged (e.g. some gray overlay).
9. The `childWhenDragging` (as the very descriptive name implies) is what is renderes in the place of `child` (i.e. at the starting position). In our demo case we chose to show nothing to make it clearer that the item is moved.
10. This is what called when the dragging ends (i.e. the movable item is dropped). Normally, the `Draggable` will return the `child` to the original starting place. But we want to move the item to the place that it was dropped.
11. We need to do some maths to calculate the new **relative** position of the item. The `dragDetails` contain the universal coordinates that the item was dropped. But `Positioned` wants coordinates relative to the `Stack` parent. To do this we subtract the parent container position from the absolute item position. 

Hopefully in this super quick turorial-snippet you got an idea on how to drag items around in a movable area using Flutter.

Now go, create :) Happy coding!