Working with ReorderableListView in Flutter

Updated: February 6, 2023 By: Augustus Post a comment

ReorderableListView is a built-in widget in Flutter that can help us create a list view whose items can be repositioned and reordered by dragging and dropping.

This article will cover the most important things about the ReorderableListView widget and walks you through 2 complete examples of using it in Flutter applications.

What are the points?

Constructors

There are 2 constructors that can be used to implement reorderable lists.

ReorderableListView.builder

Using the ReorderableListView.builder constructor is suitable when your list has a large number of items (usually loaded from APIs or from a database like SQLite). This has three important parameters:

  • itemCount: The number of items in the list
  • itemBuilder: A function to build list item widgets
  • reOrder: A callback function used by the list to report that a list item has been dragged to a new location in the list, and the application should update the order of the items.

Full implementation:

ReorderableListView({
  Key? key, 
  required List<Widget> children, 
  required ReorderCallback onReorder, 
  void onReorderStart(int index)?, 
  void onReorderEnd(int index)?, 
  double? itemExtent, 
  Widget? prototypeItem, 
  ReorderItemProxyDecorator? proxyDecorator, 
  bool buildDefaultDragHandles = true, 
  EdgeInsets? padding, 
  Widget? header, 
  Widget? footer, 
  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
})

ReorderableListView

Using the ReorderableListView constructor is convenient when your list has a small number of items. The main parameters are:

  • children: A list of widgets.
  • reOrder: A callback function used by the list to report that a list item has been dragged to a new location in the list, and the application should update the order of the items.

Full implementation:

ReorderableListView.builder({
  Key? key, 
  required IndexedWidgetBuilder itemBuilder, 
  required int itemCount, 
  required ReorderCallback onReorder, 
  void onReorderStart(int index)?, 
  void onReorderEnd(int index)?, 
  double? itemExtent, 
  Widget? prototypeItem, 
  ReorderItemProxyDecorator? proxyDecorator, 
  bool buildDefaultDragHandles = true, 
  EdgeInsets? padding, 
  Widget? header, 
  Widget? footer, 
  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
})

The onReorder function

In general, ReorderableListView goes with a stateful widget. The onReorder functions are almost the same in many cases:

onReorder: (oldIndex, newIndex) {
            setState(() {
              if (newIndex > oldIndex) {
                newIndex = newIndex - 1;
              }
              final item = _myListData.removeAt(oldIndex);
              _myListData.insert(newIndex, element);
            });
}

// Don't forget to change _myListData with your own list 

Keys

Each item of a ReorderableListView must have a key in order to distinguish the list items after their position is changed. Using ValueKey() is fine. If the keys are missing, a major error will occur.

Examples

ReorderableListView.builder

Preview

This sample app contains a ReorderableListView that holds one hundred fiction products. Each product in this list can be moved to a new location, starting with a long press gesture and then dragging and dropping.

The code

// main.dart
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      // Remove the debug banner
      debugShowCheckedModeBanner: false,
      title: 'Kindacode.com',
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // Generate dummy data for the list view
  final List<String> _products =
      List.generate(100, (index) => "Product ${index.toString()}");

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Kindacode.com'),
      ),
      body: ReorderableListView.builder(
          itemCount: _products.length,
          itemBuilder: (context, index) {
            final String productName = _products[index];
            return Card(
              key: ValueKey(productName),
              color: Colors.amberAccent,
              elevation: 1,
              margin: const EdgeInsets.all(10),
              child: ListTile(
                contentPadding: const EdgeInsets.all(25),
                title: Text(
                  productName,
                  style: const TextStyle(fontSize: 18),
                ),
                trailing: const Icon(Icons.drag_handle),
                onTap: () {/* Do something else */},
              ),
            );
          },
          // The reorder function
          onReorder: (oldIndex, newIndex) {
            setState(() {
              if (newIndex > oldIndex) {
                newIndex = newIndex - 1;
              }
              final element = _products.removeAt(oldIndex);
              _products.insert(newIndex, element);
            });
          }),
    );
  }
}

ReorderableListView

Preview

This example demonstrates a very common case in real life: a task app allows the user to change the priority of to-do tasks.

The code

// main.dart
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      // Remove the debug banner
      debugShowCheckedModeBanner: false,
      title: 'Kindacode.com',
      // home: HomePage(),
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // This variable holds the list's items
  final List<String> _todos = ['Task A', 'Task B', 'Task C', 'Task D'];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Kindacode.com'),
      ),
      body: ReorderableListView(
          header: Container(
              padding: const EdgeInsets.all(25),
              color: Colors.amber,
              child: const Text('My Todo List')),
          children: _todos
              .map((task) => Container(
                    key: ValueKey(task),
                    decoration: BoxDecoration(
                        color: Colors.greenAccent,
                        border: Border.all(width: 1, color: Colors.green)),
                    child: ListTile(
                      contentPadding: const EdgeInsets.all(25),
                      leading: const Icon(Icons.lock_clock),
                      title: Text(
                        task,
                        style: const TextStyle(fontSize: 24),
                      ),
                      trailing: const Icon(Icons.drag_handle_outlined),
                    ),
                  ))
              .toList(),
          // The reorder function
          onReorder: (oldIndex, newIndex) {
            setState(() {
              if (newIndex > oldIndex) {
                newIndex -= 1;
              }
              final element = _todos.removeAt(oldIndex);
              _todos.insert(newIndex, element);
            });
          }),
    );
  }
}

Conclusion

We have gone over the fundamentals of the ReorderableListView widget and seen 2 examples of implementing it in practice. If you would like to learn more interesting and new things about Flutter, take a look at the following articles:

You can also check out our Flutter category page or Dart category page for the latest tutorials and examples.

Related Articles