This tutorial shows you how to use ReorderableListView
in Flutter.
If you want to create a list view that can be ordered by the users, it can be done easily in Flutter. You can use ReorderableListView
widget for that purpose. Below are the examples
Using ReorderableListView
The constructor of ReorderableListView
can be seen below.
ReorderableListView({
Key? key,
required List<Widget> children,
required ReorderCallback onReorder,
ReorderItemProxyDecorator? proxyDecorator,
bool buildDefaultDragHandles = true,
EdgeInsets? padding,
Widget? header,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController? scrollController,
bool? primary,
ScrollPhysics? physics,
bool shrinkWrap = false,
double anchor = 0.0,
double? cacheExtent,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
ScrollViewKeyboardDismissBehavior keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
String? restorationId,
Clip clipBehavior = Clip.hardEdge,
})
To use the ReorderableListView
widget, you have to pass the children
and onReorder
arguments. The chlldren
argument is used to define the list items, while the onReorder
argument is used to handle events when an item has been dragged to a new position.
Add List Items
For the list of items, you need to pass a list of widget as the children
argument. It can be a any widget, usually a ListTile
. Each widget passed as list item must have a unique key as well since Flutter needs the keys to handle how to rebuild the widgets after an item has been dragged to a new position. Below is an example of ListTile
created using ListTile
widgets.
List<Widget> _widgets = Iterable<int>.generate(50)
.map((i) => ListTile(
key: ValueKey(i),
title : Text('Item $i', style: const TextStyle(color: Colors.white)),
tileColor: i % 2 == 0 ? Colors.pinkAccent : Colors.purple),
)
.toList();
Then pass it as the children
argument of ReorderableListView
.
ReorderableListView(
onReorder: (int oldIndex, int newIndex) {
// TODO implement later
},
children: _widgets,
)
Handle Drag
When an item is successfully dragged to a new position, the list item should be adjusted to show the items based on the new order. However, the order of the passed widgets is not updated automatically. To update the order, you need to pass a function as onReorder
argument which will be called every time an item moved to a new position. The passed function must have two parameters oldIndex
and newIndex
. As the names suggeset, those parameters are the position indexes of the dragged item before and after the drag. Inside the function, you can write your own logic to change the order of the widgets passed as the children
argument.
ReorderableListView(
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (oldIndex < newIndex) {
newIndex -= 1;
}
final Widget widget = _widgets.removeAt(oldIndex);
_widgets.insert(newIndex, widget);
});
},
children: _widgets,
)
Output:
Control Default Drag Handler
Flutter adds a default drag handler which depends on the platform. On mobile platforms, it allows the user to perform a drag by long pressing on an item. On desktop or web platforms, there is a drag handle on the trailing edge of each item. It's enanled by default. You can control to enable or disbale it by passing the buildDefaultDragHandles
argument.
ReorderableListView(
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (oldIndex < newIndex) {
newIndex -= 1;
}
final Widget widget = _widgets.removeAt(oldIndex);
_widgets.insert(newIndex, widget);
});
},
buildDefaultDragHandles: false,
children: _widgets,
)
Add Proxy Decorator
You can add a decoration on the item that's being dragged by passing a function as proxyDecorator
. The function accepts three parameters: Widget child
, int index
, and Animation<double> animation
. Based on the passed parameter, you need to return a widget
.
ReorderableListView(
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (oldIndex < newIndex) {
newIndex -= 1;
}
final Widget widget = _widgets.removeAt(oldIndex);
_widgets.insert(newIndex, widget);
});
},
proxyDecorator: (Widget child, int index, Animation<double> animation) {
return Material(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black, width: 5)
),
child: child,
),
);
},
children: _widgets,
)
Output:
Reverse List
By default, the scroll view scrolls from reading direction. That means the item is rendered in the same direcftion as the reading direction. If you want to reverse it, you can pass the reverse
argument and set the value to true
.
ReorderableListView(
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (oldIndex < newIndex) {
newIndex -= 1;
}
final Widget widget = _widgets.removeAt(oldIndex);
_widgets.insert(newIndex, widget);
});
},
reverse: true,
children: _widgets,
)
Output:
Set Shrink Wrap
By default, the ReorderableListView
will be extended to occupy the available space in the scroll direction, even if the content doesn't fill the entire space. If you want to shrink the size according to the content, you can set the shrinkWrap
argument to true
.
ReorderableListView(
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (oldIndex < newIndex) {
newIndex -= 1;
}
final Widget widget = _widgets.removeAt(oldIndex);
_widgets.insert(newIndex, widget);
});
},
shrinkWrap: true,
children: _widgets,
)
Set Cache Extent
Flutter has the capability to only load items currently displayed on the screen That has big performance impact especially if the list is very long. Flutter also makes it possible to control whether you want to cache the list items that are about to visible by passing a double
value as the cacheExtent
argument. The passed value indicates the area before and after the currently visible area that should be cached.
ReorderableListView(
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (oldIndex < newIndex) {
newIndex -= 1;
}
final Widget widget = _widgets.removeAt(oldIndex);
_widgets.insert(newIndex, widget);
});
},
cacheExtent: true,
children: _widgets,
)
Add Padding
Flutter makes it easy to add a padding around the list contents by provding an argument named padding
. You can pass an EdgeInsetsGeometry
value (the same type when you define a padding using Padding
widget).
ReorderableListView(
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (oldIndex < newIndex) {
newIndex -= 1;
}
final Widget widget = _widgets.removeAt(oldIndex);
_widgets.insert(newIndex, widget);
});
},
padding: const EdgeInsets.symmetric(horizontal: 40),
children: _widgets,
)
Output:
ReorderableListView
- Parameters
Key? key
: The widget's key, used to control how a widget is replaced with another.required List<Widget> children
: The list items.required ReorderCallback onReorder
: A callback to be called when a list item has been dragged to a new position.ReorderItemProxyDecorator? proxyDecorator
: A callback for adding decoration around the item that's being dragged.bool buildDefaultDragHandles
: Whether to add a drag handle on the trailing edge of each item (on desktop platforms) or allow long press anywhere to start a drag (on mobile platforms). Defaults totrue
.EdgeInsets? padding
: The amount of space by which to inset the list contents.Widget? header
: A non-reorderable header item to show before the item list.Axis scrollDirection
: The axis along which the scroll view scrolls. Defaults toAxis.vertical
.bool reverse
: Whether the scroll view scrolls in the reading direction. Defaults tofalse
.ScrollController? scrollController
: Used to control the position to which this scroll view is scrolled..bool? primary
: Whether this is the primary scroll view associated with the parentPrimaryScrollController
..ScrollPhysics? physics
: How the scroll view should respond when the user interacts.bool shrinkWrap
: Whether the extent of the scroll view in thescrollDirection
should be determined by the contents being viewed. Defaults tofalse
.double anchor
: The relative position of the zero scroll offset. Defaults to0.0
.double? cacheExtent
: Whether to cache items that are about to become visible when the user scrolls.DragStartBehavior dragStartBehavior
: How the drag start behavior is handled. Defaults toDragStartBehavior.start
.ScrollViewKeyboardDismissBehavior keyboardDismissBehavior
: How theScrollView
dimisses the keyboard automatically. Defaults toScrollViewKeyboardDismissBehavior.manual
.String? restorationId
: Used to save and restore the scroll offset.Clip clipBehavior
: How to clip the content. Defaults toClip.hardEdge
.
?: value can be null.
required: value must be passed.