Flutter Drag and Drop ListView Examples
Drag and drop is a common user interface pattern that allows users to move objects from one place to another. In Flutter, this functionality can be implemented using the DragTarget and Draggable widgets. In this article, we will explore how to implement drag and drop in a ListView in Flutter.
Steps:
Follow these steps:
Step 1: Create a ListView widget
The first step is to create a ListView widget that will display the items that can be dragged and dropped. Here is an example of a simple ListView widget that displays a list of strings:
ListView(
children: [
ListTile(title: Text('Item 1')),
ListTile(title: Text('Item 2')),
ListTile(title: Text('Item 3')),
],
)
Step 2: Wrap each item in a Draggable widget
To make each item in the ListView draggable, we need to wrap it in a Draggable widget. The Draggable widget takes two required parameters: child and feedback. The child parameter is the widget that will be dragged, and the feedback parameter is the widget that will be displayed when the item is being dragged.
Here is an example of a Draggable widget wrapped around a ListTile widget:
Draggable(
child: ListTile(title: Text('Item 1')),
feedback: ListTile(title: Text('Item 1')),
)
Step 3: Create a DragTarget widget
To define where the draggable items can be dropped, we need to create a DragTarget widget. The DragTarget widget takes a single required parameter: builder. The builder parameter is a function that returns the widget that will be displayed when a draggable item is being hovered over the DragTarget.
Here is an example of a DragTarget widget:
DragTarget(
builder: (BuildContext context, List<String> candidateData, List<dynamic> rejectedData) {
return Container(
height: 100,
color: Colors.blue,
);
},
)
Step 4: Handle the onAccept callback
When a draggable item is dropped onto a DragTarget, the onAccept callback is called. This is where we can handle the logic for what should happen when an item is dropped.
Here is an example of an onAccept callback:
void _handleAccept(String item) {
setState(() {
_items.remove(item);
});
}
Step 5: Combine all the widgets
To combine all the widgets together, we can use the ListView.builder widget. The ListView.builder widget takes a single required parameter: itemBuilder. The itemBuilder parameter is a function that returns a widget for each item in the ListView.
Here is an example of a ListView.builder widget that combines all the widgets together:
ListView.builder(
itemCount: _items.length,
itemBuilder: (BuildContext context, int index) {
return Draggable(
child: ListTile(title: Text(_items[index])),
feedback: ListTile(title: Text(_items[index])),
childWhenDragging: Container(),
onDragCompleted: () {
setState(() {
_items.removeAt(index);
});
},
data: _items[index],
onDraggableCanceled: (Velocity velocity, Offset offset) {},
);
},
),
More Examples
In this section we look at full examples how to drag drop items in a listview, thus reordering them.
Example 1: Flutter Drag Drop ListView
This example teaches us the implementation of drag drop in a listview. You can not only drag the individual listview items but also the whole listview.
Here is the demo screenshot:

Step 1: Create Project
Start by creating an empty flutter project.
Step 2: Install drag_and_drop_lists
Add the dependency drag_and_drop_lists in your pubspec.yaml and flutter pub get or sync to fetch it:
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
# drag & drop listview
drag_and_drop_lists: ^0.3.1
Step 3: Create Model and Data
Start by creating the model class, which is a class to represent a single List alongside it's ListView items:
/model/draggable_list.dart
class DraggableList {
final String header;
final List<DraggableListItem> items;
const DraggableList({
required this.header,
required this.items,
});
}
class DraggableListItem {
final String title;
final String urlImage;
const DraggableListItem({
required this.title,
required this.urlImage,
});
}
Then draggable lists, which will prepare a list of lists:
/data/draggable_lists.dart
import 'package:drag_drop_listview_example/model/draggable_list.dart';
List<DraggableList> allLists = [
DraggableList(
header: 'Best Fruits',
items: [
DraggableListItem(
title: 'Orange',
urlImage:
'https://images.unsplash.com/photo-1582979512210-99b6a53386f9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=934&q=80',
),
DraggableListItem(
title: 'Apple',
urlImage:
'https://images.unsplash.com/photo-1560806887-1e4cd0b6cbd6?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=3367&q=80',
),
DraggableListItem(
title: 'Blueberries',
urlImage:
'https://images.unsplash.com/photo-1595231776515-ddffb1f4eb73?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1950&q=80',
),
],
),
DraggableList(
header: 'Good Fruits',
items: [
DraggableListItem(
title: 'Lemon',
urlImage:
'https://images.unsplash.com/photo-1590502593747-42a996133562?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=975&q=80',
),
DraggableListItem(
title: 'Melon',
urlImage:
'https://images.unsplash.com/photo-1571575173700-afb9492e6a50?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=976&q=80',
),
DraggableListItem(
title: 'Papaya',
urlImage:
'https://images.unsplash.com/photo-1617112848923-cc2234396a8d?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1567&q=80',
),
],
),
DraggableList(
header: 'Disliked Fruits',
items: [
DraggableListItem(
title: 'Banana',
urlImage:
'https://images.unsplash.com/photo-1543218024-57a70143c369?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=975&q=80',
),
DraggableListItem(
title: 'Strawberries',
urlImage:
'https://images.unsplash.com/photo-1464965911861-746a04b4bca6?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1950&q=80',
),
DraggableListItem(
title: 'Grapefruit',
urlImage:
'https://images.unsplash.com/photo-1577234286642-fc512a5f8f11?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=975&q=80',
),
],
),
];
Step 4: Create main class
main.dart
import 'package:drag_and_drop_lists/drag_and_drop_item.dart';
import 'package:drag_and_drop_lists/drag_and_drop_list.dart';
import 'package:drag_and_drop_lists/drag_and_drop_lists.dart';
import 'package:drag_drop_listview_example/data/draggable_lists.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/services.dart';
import 'model/draggable_list.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
static final String title = 'Drag & Drop ListView';
Widget build(BuildContext context) => MaterialApp(
debugShowCheckedModeBanner: false,
title: title,
theme: ThemeData(primarySwatch: Colors.red),
home: MainPage(),
);
}
class MainPage extends StatefulWidget {
_MainPage createState() => _MainPage();
}
class _MainPage extends State<MainPage> {
late List<DragAndDropList> lists;
void initState() {
super.initState();
lists = allLists.map(buildList).toList();
}
Widget build(BuildContext context) {
final backgroundColor = Color.fromARGB(255, 243, 242, 248);
return Scaffold(
backgroundColor: backgroundColor,
appBar: AppBar(
title: Text(MyApp.title),
centerTitle: true,
),
body: DragAndDropLists(
// lastItemTargetHeight: 50,
// addLastItemTargetHeightToTop: true,
// lastListTargetSize: 30,
listPadding: EdgeInsets.all(16),
listInnerDecoration: BoxDecoration(
color: Theme.of(context).canvasColor,
borderRadius: BorderRadius.circular(10),
),
children: lists,
itemDivider: Divider(thickness: 2, height: 2, color: backgroundColor),
itemDecorationWhileDragging: BoxDecoration(
color: Colors.white,
boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 4)],
),
listDragHandle: buildDragHandle(isList: true),
itemDragHandle: buildDragHandle(),
onItemReorder: onReorderListItem,
onListReorder: onReorderList,
),
);
}
DragHandle buildDragHandle({bool isList = false}) {
final verticalAlignment = isList
? DragHandleVerticalAlignment.top
: DragHandleVerticalAlignment.center;
final color = isList ? Colors.blueGrey : Colors.black26;
return DragHandle(
verticalAlignment: verticalAlignment,
child: Container(
padding: EdgeInsets.only(right: 10),
child: Icon(Icons.menu, color: color),
),
);
}
DragAndDropList buildList(DraggableList list) => DragAndDropList(
header: Container(
padding: EdgeInsets.all(8),
child: Text(
list.header,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
),
children: list.items
.map((item) => DragAndDropItem(
child: ListTile(
leading: Image.network(
item.urlImage,
width: 40,
height: 40,
fit: BoxFit.cover,
),
title: Text(item.title),
),
))
.toList(),
);
void onReorderListItem(
int oldItemIndex,
int oldListIndex,
int newItemIndex,
int newListIndex,
) {
setState(() {
final oldListItems = lists[oldListIndex].children;
final newListItems = lists[newListIndex].children;
final movedItem = oldListItems.removeAt(oldItemIndex);
newListItems.insert(newItemIndex, movedItem);
});
}
void onReorderList(
int oldListIndex,
int newListIndex,
) {
setState(() {
final movedList = lists.removeAt(oldListIndex);
lists.insert(newListIndex, movedList);
});
}
}
Run
Copy the code or download it in the link below, build and run.
Reference
Here are the reference links:
| Number | Link |
|---|---|
| 1. | Download Example |
| 2. | Follow code author |
Example 2: Flutter Reorderable list
This is another drag to reorder list in flutter. This time round we use the flutter_reorderable_list package. This library allows us reorder lists during which we animate them.
Here are its features:
- Smooth reordering animations
- Supports different item heights
- iOS like reordering with drag handle
- Android like (long touch) reordering
- Works with slivers so it can be placed in
CustomScrollViewand used withSliverAppBar - Supports large lists (thousands of items) without any issues
Here is the demo:
Here's a screenshot of the demo project:
Step 1: Create Project
Start by creating an empty Android Studio project.
Step 2: Install it
To install it, simply download this file and add to your project.
Step 3: Write Code
Here is the full code:
main.dart
import 'package:flutter/cupertino.dart' hide ReorderableList;
import 'package:flutter/material.dart' hide ReorderableList;
import 'package:flutter_reorderable_list/flutter_reorderable_list.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Rerderable List',
theme: ThemeData(
dividerColor: Color(0x50000000),
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Reorderable List'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
_MyHomePageState createState() => _MyHomePageState();
}
class ItemData {
ItemData(this.title, this.key);
final String title;
// Each item in reorderable list needs stable and unique key
final Key key;
}
enum DraggingMode {
iOS,
Android,
}
class _MyHomePageState extends State<MyHomePage> {
late List<ItemData> _items;
_MyHomePageState() {
_items = [];
for (int i = 0; i < 500; ++i) {
String label = "List item $i";
if (i == 5) {
label += ". This item has a long label and will be wrapped.";
}
_items.add(ItemData(label, ValueKey(i)));
}
}
// Returns index of item with given key
int _indexOfKey(Key key) {
return _items.indexWhere((ItemData d) => d.key == key);
}
bool _reorderCallback(Key item, Key newPosition) {
int draggingIndex = _indexOfKey(item);
int newPositionIndex = _indexOfKey(newPosition);
// Uncomment to allow only even target reorder possition
// if (newPositionIndex % 2 == 1)
// return false;
final draggedItem = _items[draggingIndex];
setState(() {
debugPrint("Reordering $item -> $newPosition");
_items.removeAt(draggingIndex);
_items.insert(newPositionIndex, draggedItem);
});
return true;
}
void _reorderDone(Key item) {
final draggedItem = _items[_indexOfKey(item)];
debugPrint("Reordering finished for ${draggedItem.title}}");
}
//
// Reordering works by having ReorderableList widget in hierarchy
// containing ReorderableItems widgets
//
DraggingMode _draggingMode = DraggingMode.iOS;
Widget build(BuildContext context) {
return Scaffold(
body: ReorderableList(
onReorder: this._reorderCallback,
onReorderDone: this._reorderDone,
child: CustomScrollView(
// cacheExtent: 3000,
slivers: <Widget>[
SliverAppBar(
actions: <Widget>[
PopupMenuButton<DraggingMode>(
child: Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 30.0),
child: Text("Options"),
),
initialValue: _draggingMode,
onSelected: (DraggingMode mode) {
setState(() {
_draggingMode = mode;
});
},
itemBuilder: (BuildContext context) =>
<PopupMenuItem<DraggingMode>>[
const PopupMenuItem<DraggingMode>(
value: DraggingMode.iOS,
child: Text('iOS-like dragging')),
const PopupMenuItem<DraggingMode>(
value: DraggingMode.Android,
child: Text('Android-like dragging')),
],
),
],
pinned: true,
expandedHeight: 150.0,
flexibleSpace: const FlexibleSpaceBar(
title: const Text('Demo'),
),
),
SliverPadding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Item(
data: _items[index],
// first and last attributes affect border drawn during dragging
isFirst: index == 0,
isLast: index == _items.length - 1,
draggingMode: _draggingMode,
);
},
childCount: _items.length,
),
)),
],
),
),
);
}
}
class Item extends StatelessWidget {
Item({
required this.data,
required this.isFirst,
required this.isLast,
required this.draggingMode,
});
final ItemData data;
final bool isFirst;
final bool isLast;
final DraggingMode draggingMode;
Widget _buildChild(BuildContext context, ReorderableItemState state) {
BoxDecoration decoration;
if (state == ReorderableItemState.dragProxy ||
state == ReorderableItemState.dragProxyFinished) {
// slightly transparent background white dragging (just like on iOS)
decoration = BoxDecoration(color: Color(0xD0FFFFFF));
} else {
bool placeholder = state == ReorderableItemState.placeholder;
decoration = BoxDecoration(
border: Border(
top: isFirst && !placeholder
? Divider.createBorderSide(context) //
: BorderSide.none,
bottom: isLast && placeholder
? BorderSide.none //
: Divider.createBorderSide(context)),
color: placeholder ? null : Colors.white);
}
// For iOS dragging mode, there will be drag handle on the right that triggers
// reordering; For android mode it will be just an empty container
Widget dragHandle = draggingMode == DraggingMode.iOS
? ReorderableListener(
child: Container(
padding: EdgeInsets.only(right: 18.0, left: 18.0),
color: Color(0x08000000),
child: Center(
child: Icon(Icons.reorder, color: Color(0xFF888888)),
),
),
)
: Container();
Widget content = Container(
decoration: decoration,
child: SafeArea(
top: false,
bottom: false,
child: Opacity(
// hide content for placeholder
opacity: state == ReorderableItemState.placeholder ? 0.0 : 1.0,
child: IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Padding(
padding:
EdgeInsets.symmetric(vertical: 14.0, horizontal: 14.0),
child: Text(data.title,
style: Theme.of(context).textTheme.subtitle1),
)),
// Triggers the reordering
dragHandle,
],
),
),
)),
);
// For android dragging mode, wrap the entire content in DelayedReorderableListener
if (draggingMode == DraggingMode.Android) {
content = DelayedReorderableListener(
child: content,
);
}
return content;
}
Widget build(BuildContext context) {
return ReorderableItem(
key: data.key, //
childBuilder: _buildChild);
}
}
Run
Copy the code or download it in the link below, build and run.
Reference
Here are the reference links:
| Number | Link |
|---|---|
| 1. | Download Example |
| 2. | Follow code author |
Conclusion:
In conclusion, drag and drop functionality can be implemented in a ListView widget in Flutter using the DragTarget and Draggable widgets. By following the steps outlined in this article, you can create a ListView widget that allows users to drag and drop items from one place to another.