How do I make a ReorderableListView ListTile draggable on button/icon press for Flutter?
Asked Answered
I

3

23

So I saw the ReorderableListView demo and saw that they had the

"secondary: const Icon(Icons.drag_handle)"

but looking at the reorderable_list.dart file, I noticed that the entire list was draggable on LongPressDraggable anyway [line 424]. So how can I explicitly make changes to either the source code or my own, in order to properly make the icon an actual drag handle?

CheckboxListTile(
      key: Key(item.value),
      isThreeLine: true,
      value: item.checkState ?? false,
      onChanged: (bool newValue) {
        setState(() {
          item.checkState = newValue;
        });
      },
      title: Text('This item represents ${item.value}.'),
      subtitle: secondary,
      secondary: const Icon(Icons.drag_handle),  // Make this Icon drag source
    );

Thanks

Insociable answered 8/11, 2018 at 4:16 Comment(0)
F
11

I think the Icon(Icons.drag_handle) it's just for the looks there, to drag the item in a ReorderableListView you have to long press it.

You can use flutter_reorderable_list and achieve that. As you can see in its demo, this plugin works just the way you want to.

However, it works quite differently of ReorderableListView, the code change can be a bit overwhelming. I created a Widget to simplify that switch, the widget is here and its demo is here.

Take a look and use it if it fits your use case.

Floyfloyd answered 13/12, 2018 at 13:42 Comment(1)
Beautiful. Thank you!Insociable
B
16

2021/05/29

As an update of the answer, there's already a customizable handler for the ReorderableListView:

With the recent refactoring of the ReorderableListView (PRs: #74299 and #74697), we have added automatic drag handles when running on the desktop (with a buildDefaultDragHandles property to turn it off). If this isn't what you want, you can add your own drag handle as a widget to each of your items with something like:

ReorderableDragStartListener(
  index: index,
  child: const Icon(Icons.drag_handle),
),

You can check the details here: https://github.com/flutter/flutter/issues/46805

Beowulf answered 29/5, 2021 at 13:35 Comment(0)
F
11

I think the Icon(Icons.drag_handle) it's just for the looks there, to drag the item in a ReorderableListView you have to long press it.

You can use flutter_reorderable_list and achieve that. As you can see in its demo, this plugin works just the way you want to.

However, it works quite differently of ReorderableListView, the code change can be a bit overwhelming. I created a Widget to simplify that switch, the widget is here and its demo is here.

Take a look and use it if it fits your use case.

Floyfloyd answered 13/12, 2018 at 13:42 Comment(1)
Beautiful. Thank you!Insociable
F
4

DragHandles are hidden on mobile platforms by default. However, you can add a drag Handle in a ReorderableListView using ReorderableDragStartListener in your List item.

 ListTile(
   key: Key('$index'),
   tileColor: _items[index].isOdd ? oddItemColor : evenItemColor,
   title: Text('Item ${_items[index]}'),
   trailing: ReorderableDragStartListener(
      key: ValueKey<int>(_items[index]),
      index: index,
      child: const Icon(Icons.drag_handle),
   ),
),

Output

enter image description here

complete code sample

import 'dart:ui';

import 'package:flutter/material.dart';

void main() => runApp(const ReorderableApp());

class ReorderableApp extends StatelessWidget {
  const ReorderableApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('ReorderableListView Sample')),
        body: const ReorderableExample(),
      ),
    );
  }
}

class ReorderableExample extends StatefulWidget {
  const ReorderableExample({super.key});

  @override
  State<ReorderableExample> createState() => _ReorderableExampleState();
}

class _ReorderableExampleState extends State<ReorderableExample> {
  final List<int> _items = List<int>.generate(50, (int index) => index);

  @override
  Widget build(BuildContext context) {
    final ColorScheme colorScheme = Theme.of(context).colorScheme;
    final Color oddItemColor = colorScheme.secondary.withOpacity(0.05);
    final Color evenItemColor = colorScheme.secondary.withOpacity(0.15);
    final Color draggableItemColor = colorScheme.secondary;

    Widget proxyDecorator(
        Widget child, int index, Animation<double> animation) {
      return AnimatedBuilder(
        animation: animation,
        builder: (BuildContext context, Widget? child) {
          final double animValue = Curves.easeInOut.transform(animation.value);
          final double elevation = lerpDouble(0, 6, animValue)!;
          return Material(
            elevation: elevation,
            color: draggableItemColor,
            shadowColor: draggableItemColor,
            child: child,
          );
        },
        child: child,
      );
    }

    return ReorderableListView(
      padding: const EdgeInsets.symmetric(horizontal: 40),
      children: <Widget>[
        for (int index = 0; index < _items.length; index += 1)
          ListTile(
            key: Key('$index'),
            tileColor: _items[index].isOdd ? oddItemColor : evenItemColor,
            title: Text('Item ${_items[index]}'),
            trailing: ReorderableDragStartListener(
              key: ValueKey<int>(_items[index]),
              index: index,
              child: const Icon(Icons.drag_handle),
            ),
          ),
      ],
      onReorder: (int oldIndex, int newIndex) {
        setState(() {
          if (oldIndex < newIndex) {
            newIndex -= 1;
          }
          final int item = _items.removeAt(oldIndex);
          _items.insert(newIndex, item);
        });
      },
    );
  }
}

Originally posted here: https://github.com/flutter/flutter/issues/25065#issuecomment-1381324936

Foetation answered 13/1, 2023 at 5:20 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.