Flutter - Using ReorderableListView Widget Examples

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:

Flutter - ReorderableListView

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:

Flutter - ReorderableListView - Proxy Decorator

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:

Flutter - ReorderableListView - Reverse List

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:

Flutter - ReorderableListView - Padding

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 to true.
  • 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 to Axis.vertical.
  • bool reverse: Whether the scroll view scrolls in the reading direction. Defaults to false.
  • 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 parent PrimaryScrollController..
  • ScrollPhysics? physics: How the scroll view should respond when the user interacts.
  • bool shrinkWrap: Whether the extent of the scroll view in the scrollDirection should be determined by the contents being viewed. Defaults to false.
  • double anchor: The relative position of the zero scroll offset. Defaults to 0.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 to DragStartBehavior.start.
  • ScrollViewKeyboardDismissBehavior keyboardDismissBehavior: How the ScrollView dimisses the keyboard automatically. Defaults to ScrollViewKeyboardDismissBehavior.manual.
  • String? restorationId: Used to save and restore the scroll offset.
  • Clip clipBehavior: How to clip the content. Defaults to Clip.hardEdge.

?: value can be null.
required: value must be passed.