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"),
),
);
},
),
],
),
),
);
}
}
DragTarget widget must be used in conjunction with a Draggable widget to implement full drag and drop functionality.
Key Points
DragTargetdefines a drop zone for dragged content.- It provides callbacks to handle drag enter, update, exit, and accept events.
- The
builderproperty 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!'),
),
)
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
},
)
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 'package:flutter/material.dart';
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,
),
);
}
}
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]),
),
);
},
);
}
}
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
ListViewandColumn
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'),
),
)
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(),
)
ReorderableListView requires each child to have a unique key, typically using ValueKey.
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'),
),
);
}
}
data property is used to pass information during the drag operation.
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 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('DraggableScrollableSheet Example')),
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('Item $index'),
);
},
),
);
},
),
),
);
}
}
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.