28

So I'm starting to learn Flutter and would like to use a material design drag and drop list just like the one seen on the material guidelines website.

https://storage.googleapis.com/spec-host-backup/mio-design%2Fassets%2F1dtprsH4jZ2nOnjBCJeJXd7n4U-jmWyas%2F03-list-reorder.mp4

All of the libraries I have tried out so far look like garbage compared to that. Is there a good library for this that I am missing or a native Flutter widget?

CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
Jared
  • 2,029
  • 5
  • 20
  • 39

5 Answers5

39

enter image description here

You can use native flutter widget, ReorderableListView to achieve it, here is the example of doing it.

List<String> _list = ["Apple", "Ball", "Cat", "Dog", "Elephant"];

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(),
    body: ReorderableListView(
      children: _list.map((item) => ListTile(key: Key("${item}"), title: Text("${item}"), trailing: Icon(Icons.menu),)).toList(),
      onReorder: (int start, int current) {
        // dragging from top to bottom
        if (start < current) {
          int end = current - 1;
          String startItem = _list[start];
          int i = 0;
          int local = start;
          do {
            _list[local] = _list[++local];
            i++;
          } while (i < end - start);
          _list[end] = startItem;
        }
        // dragging from bottom to top
        else if (start > current) {
          String startItem = _list[start];
          for (int i = start; i > current; i--) {
            _list[i] = _list[i - 1];
          }
          _list[current] = startItem;
        }
        setState(() {});
      },
    ),
  );
}
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
  • 11
    Long press to reorder – Sondre Sørbye Jul 19 '19 at 18:47
  • Just a detail, as I understand it you should avoid empty setState calls and it seems to me as all the code in onReorder could just as well be inside the setState call? – UglyBob Aug 22 '19 at 13:21
  • 2
    Do note, the long press to reorder is absolutely breaking the material design spec and should not be used on Android (no idea about iOS). As far as I have been able to figure out so far there is no way to provide handles which can be simply grabbed with `ReorderableListView` – David Mulder Sep 12 '19 at 17:33
  • @DavidMulder That's how Flutter implemented it, you can file a bug on Github mentioning the issue. – CopsOnRoad Sep 12 '19 at 18:01
  • @CopsOnRoad Was going to do that, but then noticed there are 5000 open issues on the Flutter Github and realized that 1) it would be pointless to post there and 2) that I will definitely not be doing any production work with flutter . So yeah, just wanted to leave that as an aside here as it's a pretty important thing to realize and it's not that obvious from the image you included. – David Mulder Sep 12 '19 at 18:10
  • @DavidMulder 5000+ issues doesn't really mean they are going to be solved in "oldest to newest" pattern, you are most welcome to post any bug you see, and you should get the reply in around 24-48 hours. – CopsOnRoad Sep 12 '19 at 18:17
  • @DavidMulder and for future readers : Do use long press to reorder, it is absolutely part of material design. See https://material.io/design/interaction/gestures.html#types-of-gestures. – A. Bourgoin Jun 05 '20 at 10:25
  • 2
    @A.Bourgoin That's incorrect, please check https://material.io/components/lists#behavior "To reorder a list item, drag it.". The "long press" is described for content blocks, not lists (e.g. an editor where a normal drag means selection) or elements with a clear drag handle. – David Mulder Jun 05 '20 at 10:42
13

I've tried flutter_reorderable_list and dragable_flutter_list but none of them worked properly - there was some unwanted artifacts during dragging. So I've tried to make own solution:

ListView.builder(
  itemBuilder: (context, index) => buildRow(index),
  itemCount: trackList.length,
),

Widget buildRow(int index) {
  final track = trackList[index];
  ListTile tile = ListTile(
    title: Text('${track.getName()}'),
  );
  Draggable draggable = LongPressDraggable<Track>(
    data: track,
    axis: Axis.vertical,
    maxSimultaneousDrags: 1,
    child: tile,
    childWhenDragging: Opacity(
      opacity: 0.5,
      child: tile,
    ),
    feedback: Material(
      child: ConstrainedBox(
        constraints:
            BoxConstraints(maxWidth: MediaQuery.of(context).size.width),
        child: tile,
      ),
      elevation: 4.0,
    ),
  );

  return DragTarget<Track>(
    onWillAccept: (track) {
      return trackList.indexOf(track) != index;
    },
    onAccept: (track) {
      setState(() {
        int currentIndex = trackList.indexOf(track);
        trackList.remove(track);
        trackList.insert(currentIndex > index ? index : index - 1, track);
      });
    },
    builder: (BuildContext context, List<Track> candidateData,
        List<dynamic> rejectedData) {
      return Column(
        children: <Widget>[
          AnimatedSize(
            duration: Duration(milliseconds: 100),
            vsync: this,
            child: candidateData.isEmpty
                ? Container()
                : Opacity(
                    opacity: 0.0,
                    child: tile,
                  ),
          ),
          Card(
            child: candidateData.isEmpty ? draggable : tile,
          )
        ],
      );
    },
  );
}

I guess, this is not the best solution, and I'm maybe will change it further, but for now it works quite well

Andrii Turkovskyi
  • 27,554
  • 16
  • 95
  • 105
13

Flutter itself provides a (Material) ReorderableListView class.

jamesdlin
  • 81,374
  • 13
  • 159
  • 204
  • It seems that I've done a little useless work ))) Anyway, it was interesting experience. It's a pity that `ReorderableListView' doesn't use builder, but I think I'll use it for my needs. Thanks – Andrii Turkovskyi May 17 '19 at 07:51
  • @AndreyTurkovsky ReorderableListView also having builder https://api.flutter.dev/flutter/material/ReorderableListView/ReorderableListView.builder.html – MSARKrish Jun 15 '21 at 13:23
11

Check knopp/flutter_reorderable_list. It accomplishes just that. It's really smooth and it's got no performance issues, being able to handle thousands of items.

However, it's implementation is not easy as usual flutter widgets.

If you struggle with that, I'd recommend you to use a widget I created to port flutter/ReorderableListViews to the knopp/ReorderableList.

This widget makes it really easy to use, however it doesn't provide the same flexibility, and as it works with a children List, it's not as scalable as the original.

Here's the code for ReorderableListSimple and this is the demo.

Feu
  • 5,372
  • 1
  • 31
  • 57
  • 1
    Your version worked great, thanks. I would like it to be a little less laggy though when reordering every tile. Don't know if it will be smoother in the release version. Also, I can't figure out how to customize / remove the vibrations when reordering. – Jared Dec 24 '18 at 23:29
  • 1
    I also just ran into the issue where when I make the list tile dismissable (https://flutter.io/docs/cookbook/gestures/dismissible) the handle doesn't move while the rest of it slides away as it should. – Jared Dec 25 '18 at 00:39
  • 1
    I know it's not a solution, but in my app, after some fighting, I decided to use a reordering mode. :( So in reordering mode I use the ReorderingList and there's a handle (and you can't do anything else), and when it's not reordering then the user can do all the rest. – Feu Dec 25 '18 at 12:34
  • 1
    If you are using the `ReorderingListSimple`, maybe you could use `childrenAlreadyHaveListener = true`, then place the `ReorderableListener` manually in to the `ListTile` and make it disappear on `Dismissible``s `onRezize`! Let me know if it works!! I know that's not great, but maybe it's valid temporary solution. – Feu Dec 25 '18 at 12:39
4

Flutter team introduced ReorderableListView widget.

ReorderableListView(
      children: <Widget>[
        for (var item in appState.menuButtons) 
        Text('data')              

      ],
    )
Benjith Kizhisseri
  • 2,311
  • 2
  • 13
  • 24
  • 1
    Be aware this is currently lacking some features that are implemented in ListView. The Flutter team is aware, but they are still open to recommendations. You can follow this here: https://github.com/flutter/flutter/issues/66080 – Manu Nov 06 '20 at 10:31