Flutter Drag and Drop Basics

The following lesson will teach you how to build a simple drag-and-drop UI with the Draggable and DragTarget widgets. The demo app is a kid’s game (ages 2 to 4) that requires the user to drag a fruit emoji 🍋 from the left column to the matching color on the right. If successfully dropped it will mark that item complete ✅ and the score will increase by one. The user can also reset the game at any time by pressing on the floating action button.

Initial Setup

In most cases, you will need your widgets organized within a StatefulWidget to render changes to the UI when certain drag/drop events take place.

file_type_dartlang main.dart
class DragScreen extends StatefulWidget {
  DragScreen({Key key}) : super(key: key);
  createState() => ColorGameState();
}

class DragScreenState extends State<DragScreen> {

  @override
  Widget build(BuildContext context) {
    return Scaffold( 
        body: Row(
        children: [
          Column(
              // draggable widgets here
          ),
          Column(
            // droppable widgets here
          )
        ],
      ),
    );
  }
}

How to Drag Widgets

Draggble

There are three different visual states to consider when building a draggable widget.

  1. child - This is the child that is initially present.
  2. childWhenDragging - This is the widget that gets left behind after user starts dragging.
  3. feedback - This is the widget that moves or sticks to the user’s finger. It may be identical to the child, or you may want to add some extra shadow to increase the realism of movement.
The different parts of a Draggable widget after the user starts dragging

The different parts of a Draggable widget after the user starts dragging

file_type_dartlang main.dart
Draggable(
    data: // optional data to send to the drag target in next step,
    child: // widget
    feedback: // widget
    childWhenDragging: // widget
);

How to Drop Widgets

DragTarget

Now that we have a Draggable widget, we need to give it a “drop zone” using the DragTarget widget. It provides a builder function that gives you access to both the incoming and rejected data.

There are several functions that can be used to listen to the various drag/drop events:

  • builder constructs the UI for the DragTarget.
  • onWillAccept fires when the user starts hovering, it must return a boolean to decide whether or not the user can drop the widget here.
  • onAccept fires when the user drops and onWillAccept returns true.
  • onLeave fires if the user leaves without dropping or onWillAcccept returns false;
file_type_dartlang main.dart
// Prop on StatefulWidget
bool successfulDrop;

// Used inside build method
DragTarget<String>(
      builder: (BuildContext context, List<String> incoming, List rejected) {
        if (successfulDrop == true) {
            return Text('Dropped!')
        } else {
          return Text('Empty dropzone');
        }
      },
 
      onWillAccept: (data) => data == 'GOOD',

      onAccept: (data) {
        setState(() {
          successfulDrop = true;
        });
      },

      onLeave: (data) {

      },
    );

Questions? Let's chat

Open Discord