Freelancing for Pale Blue

Looking for flexible work opportunities that fit your schedule?


Dragging (but not dropping) in Flutter

Flutter Aug 6, 2021

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?

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!

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.