Skip to main content

Implementing Drag and Drop

1. Using Draggable Widget

Property Values Description
child Widget The widget to be dragged
feedback Widget Widget shown during drag operation
data Object Data to be passed to the drop target
axis Axis.horizontal, Axis.vertical Restricts drag direction

Example: Basic Draggable Widget

<Draggable
  child: Container(
    width: 100,
    height: 100,
    color: Colors.blue,
    child: Text('Drag me'),
  ),
  feedback: Container(
    width: 100,
    height: 100,
    color: Colors.blue.shade300,
  ),
  data: 'Dragged item',
/>

2. Using DragTarget Widget

The DragTarget widget in Flutter is used to define a drop zone where dragged content can be dropped. It listens for drag operations and handles the dropping of data.

Property Description Values
child The widget to display inside the drag target Widget
onAccept Callback when a draggable item is dropped onto this target void Function(T data)
onDragEntered Callback when a drag enters the target area void Function(DragEnterDetails details)
onDragUpdated Callback when a drag is updated inside the target area void Function(DragUpdateDetails details)
onDragExited Callback when a drag exits the target area void Function(DragExitDetails details)
dragAnchorStrategy Defines how the drag anchor is positioned DragAnchorStrategy

Example: Basic DragTarget Usage

class SimpleDragTarget extends StatefulWidget {
  @override
  _SimpleDragTargetState createState() => _SimpleDragTargetState();
}

class _SimpleDragTargetState extends State<SimpleDragTarget> {
  String droppedData = "";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Drag Target Example")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text("Dropped data: $droppedData"),
            DragTarget<String>(
              onAccept: (data) {
                setState(() {
                  droppedData = data;
                });
              },
              builder: (context, candidateData, rejectedData) {
                return Container(
                  width: 200,
                  height: 100,
                  decoration: BoxDecoration(
                    border: Border.all(color: Colors.blue, width: 2),
                    color: Colors.lightBlue[100],
                  ),
                  child: Center(
                    child: Text("Drop here"),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}
Note: The DragTarget widget must be used in conjunction with a Draggable widget to implement full drag and drop functionality.

Key Points

  • DragTarget defines a drop zone for dragged content.
  • It provides callbacks to handle drag enter, update, exit, and accept events.
  • The builder property allows customization of the target's appearance based on drag state.
  • It works with generic types to support any kind of data being dragged and dropped.

3. Using LongPressDraggable Widget

Property Values Description
child Widget The widget to be dragged.
dragAnchorStrategy DragAnchorStrategy Defines where the drag anchor is positioned.
feedback Widget Widget shown during drag operation.
data Object Data to be transferred during the drag.
onDragStarted VoidCallback Callback when drag starts.
onDragCompleted VoidCallback Callback when drag completes.

Example: Basic LongPressDraggable Usage

LongPressDraggable<String>(
  data: 'Dragged Item',
  feedback: Container(
    child: Text('Dragging...'),
  ),
  child: Card(
    child: Text('Drag me!'),
  ),
)
Note: The LongPressDraggable widget requires a feedback widget to show during the drag operation.

4. Implementing Drag Feedback

Drag feedback in Flutter allows you to customize how a widget appears while being dragged. This is particularly useful for improving the user experience during drag-and-drop operations.

Property Description Values
feedback Widget displayed during the drag operation Widget
child The widget that is being dragged Widget

Example: Custom Drag Feedback

class CustomDragFeedback extends StatelessWidget {
  final Widget child;

  const CustomDragFeedback({Key? key, required this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100,
      height: 100,
      decoration: BoxDecoration(
        color: Colors.blue.withOpacity(0.7),
        borderRadius: BorderRadius.circular(8),
      ),
      child: Center(
        child: Text(
          'Dragging...',
          style: TextStyle(color: Colors.white),
        ),
      ),
    );
  }
}

// Usage in a DragTarget
DragTarget<String>(
  builder: (context, candidateData, rejectedData) {
    return Container(
      width: 200,
      height: 100,
      color: Colors.grey[300],
      child: Center(child: Text('Drop here')),
    );
  },
  feedback: CustomDragFeedback(
    child: Container(
      width: 50,
      height: 50,
      color: Colors.red,
    ),
  ),
  onAccept: (data) {
    // Handle drop
  },
)
Note: The feedback property is used to define how the dragged widget looks during the drag operation. It's important to keep the feedback lightweight to ensure smooth performance.

5. Handling Drag Events

Event Description Usage
onDragStart Triggered when a drag operation begins. onDragStart: (details) => print('Drag started')
onDragUpdate Called repeatedly as the drag moves. onDragUpdate: (details) => print('Dragging at ${details.globalPosition}')
onDragEnd Executed when the drag ends. onDragEnd: (details) => print('Drag ended')

Example: Basic Drag Handling

import &#x27;package:flutter/material.dart&#x27;;

class DraggableWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onDragStart: (details) => print("Drag started"),
      onDragUpdate: (details) => print("Dragging at ${details.globalPosition}"),
      onDragEnd: (details) => print("Drag ended"),
      child: Container(
        width: 100,
        height: 100,
        color: Colors.blue,
      ),
    );
  }
}
Note: onDragStart, onDragUpdate, and onDragEnd are part of the Gestures API and require a GestureDetector to be effective.

6. Using Dismissible Widget

The Dismissible widget in Flutter allows users to swipe items away from a list or other UI elements, commonly used for deleting or archiving items. It provides a clean and intuitive way to remove list items.

Property Description Values
key Unique identifier for the widget Key?
direction Swipe direction for dismissal DismissDirection
child The widget to be dismissed Widget
onDismissed Callback when item is dismissed (DismissDirection) -> void
background Background shown during swipe Widget?
secondaryBackground Secondary background for swipe in opposite direction Widget?
confirmDismiss Async confirmation before dismissal ((DismissDirection) -> Future<bool>)?

Example: Basic Dismissible Usage

class DismissibleExample extends StatefulWidget {
  @override
  _DismissibleExampleState createState() => _DismissibleExampleState();
}

class _DismissibleExampleState extends State<DismissibleExample> {
  List<String> items = List.generate(20, (i) => "Item $i");

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: items.length,
      itemBuilder: (context, index) {
        return Dismissible(
          key: Key(items[index]),
          direction: DismissDirection.endToStart,
          background: Container(color: Colors.red),
          onDismissed: (direction) {
            setState(() {
              items.removeAt(index);
            });
          },
          child: ListTile(
            title: Text(items[index]),
          ),
        );
      },
    );
  }
}
Note: The background and secondaryBackground properties allow customization of the swipe effect, providing visual feedback to users.

Key Features of Dismissible

  • Supports swipe in multiple directions
  • Customizable background for swipe actions
  • Async confirmation before dismissal
  • Integrates well with ListView and Column

7. Customizing Dismissible Directions

Property Values Description
dismissibleDirection DismissibleDirection.startToEnd, DismissibleDirection.endToStart, DismissibleDirection.up, DismissibleDirection.down, DismissibleDirection.horizontal, DismissibleDirection.vertical Defines the direction in which the widget can be dismissed. Default is DismissibleDirection.startToEnd.

Example: Customizing Dismissible Direction

Dismissible(
  key: Key('dismissible_item'),
  direction: DismissibleDirection.up,
  onDismissed: (direction) {
    print('Dismissed in direction: $direction');
  },
  child: ListTile(
    title: Text('Swipe me up'),
  ),
)
Note: The direction property controls the swipe direction and affects how the widget animates when dismissed.

8. Implementing Reorderable Lists

Reorderable lists in Flutter allow users to drag and drop list items to change their order. This is commonly used in task lists, shopping carts, or any UI where items can be reordered.

Property Description Values
child The widget to display in the list item. Widget
onReorder Callback when an item is reordered. Function(int oldIndex, int newIndex)
index Index of the item in the list. int

Example: ReorderableListView

ReorderableListView(
  onReorder: (oldIndex, newIndex) {
    setState(() {
      if (newIndex > oldIndex) {
        newIndex -= 1;
      }
      final item = items.removeAt(oldIndex);
      items.insert(newIndex, item);
    });
  },
  children: items.map((item) => ListTile(
    key: ValueKey(item),
    title: Text(item),
  )).toList(),
)
Note: The ReorderableListView requires each child to have a unique key, typically using ValueKey.
Warning: Reordering items in a list can cause performance issues if the list is very long. Consider using ReorderableListView.builder for better performance.
Feature Status Notes
Drag and Drop Supported Works with touch and mouse input.
Reordering Supported Items can be reordered by dragging.
Performance Good Use ReorderableListView.builder for large lists.

9. Creating Custom Drag Behaviors

Property Description Values
child The widget to be dragged Widget
feedback Widget shown during drag operation Widget
dragAnchor Point to anchor the drag from DragAnchor

Example: Custom Drag Behavior

class CustomDraggable extends StatefulWidget {
  @override
  _CustomDraggableState createState() => _CustomDraggableState();
}

class _CustomDraggableState extends State<CustomDraggable> {
  @override
  Widget build(BuildContext context) {
    return Draggable<String>(
      data: 'Dragged Item',
      feedback: Container(
        width: 100,
        height: 100,
        child: Text('Dragging...'),
      ),
      child: Container(
        width: 100,
        height: 100,
        child: Text('Drag Me'),
      ),
    );
  }
}
Note: The data property is used to pass information during the drag operation.
Warning: Ensure that feedback is visible and distinct from the original widget to provide clear user feedback.

10. Using DraggableScrollableSheet Widget

The DraggableScrollableSheet widget in Flutter allows you to create a sheet that can be dragged to expand or collapse, while still maintaining scrollable content inside it. It's particularly useful for creating bottom sheets with dynamic height that can be adjusted by the user.

Property Description Values
child The widget to be displayed inside the sheet. Widget
initialChildSize The initial size of the sheet as a fraction of the total available height. double (0.0 to 1.0)
minChildSize The minimum size the sheet can shrink to as a fraction of the total available height. double (0.0 to 1.0)
maxChildSize The maximum size the sheet can expand to as a fraction of the total available height. double (0.0 to 1.0)
builder A function that builds the content of the sheet, which is typically a scrollable widget. WidgetBuilder

Example: Basic DraggableScrollableSheet

import &#x27;package:flutter/material.dart&#x27;;

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text(&#x27;DraggableScrollableSheet Example&#x27;)),
        body: DraggableScrollableSheet(
          initialChildSize: 0.5,
          minChildSize: 0.25,
          maxChildSize: 0.75,
          builder: (BuildContext context, ScrollController scrollController) {
            return Container(
              color: Colors.blue[100],
              child: ListView.builder(
                controller: scrollController,
                itemCount: 50,
                itemBuilder: (BuildContext context, int index) {
                  return ListTile(
                    title: Text(&#x27;Item $index&#x27;),
                  );
                },
              ),
            );
          },
        ),
      ),
    );
  }
}
Note: The DraggableScrollableSheet is designed to work with a ScrollController to manage scrolling inside the sheet. Make sure to pass the controller to your scrollable widget (e.g., ListView, SingleChildScrollView) to ensure proper scrolling behavior.

Was this article helpful?