ByteNoteByteNote

字节笔记本

2026年2月22日

Flutter 拖拽列表实现:ReorderableListView 到自定义拖拽

API中转
¥120

Flutter 中实现拖拽列表的几种方式,从官方组件到自定义实现。

一、官方方案:ReorderableListView

最简单的方式,官方组件直接支持拖拽排序:

dart
class DragListPage extends StatefulWidget {
  @override
  State<DragListPage> createState() => _DragListPageState();
}

class _DragListPageState extends State<DragListPage> {
  final List<String> _items = ['Item 1', 'Item 2', 'Item 3', 'Item 4'];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ReorderableListView(
        onReorder: (oldIndex, newIndex) {
          setState(() {
            if (newIndex > oldIndex) newIndex--;
            final item = _items.removeAt(oldIndex);
            _items.insert(newIndex, item);
          });
        },
        children: _items.map((item) => ListTile(
          key: ValueKey(item),  // key 必须要有
          title: Text(item),
          trailing: const Icon(Icons.drag_handle),
        )).toList(),
      ),
    );
  }
}

注意:onReordernewIndex > oldIndex 时要先 -1,这是官方的一个小坑。

二、自定义拖拽样式

ReorderableListView.builder + proxyDecorator 控制拖拽时的外观:

dart
ReorderableListView.builder(
  proxyDecorator: (child, index, animation) {
    return Material(
      elevation: 8,
      borderRadius: BorderRadius.circular(8),
      child: child,
    );
  },
  itemCount: _items.length,
  itemBuilder: (context, index) {
    return ListTile(
      key: ValueKey(_items[index]),
      title: Text(_items[index]),
    );
  },
  onReorder: (oldIndex, newIndex) {
    setState(() {
      if (newIndex > oldIndex) newIndex--;
      final item = _items.removeAt(oldIndex);
      _items.insert(newIndex, item);
    });
  },
)

三、第三方库

flutter_reorderable_list

需要更复杂的交互(如跨列表拖拽):

yaml
dependencies:
  flutter_reorderable_list: ^1.0.0

reorderable_grid_view

支持 Grid 拖拽:

yaml
dependencies:
  reorderable_grid_view: ^2.0.0

四、跨列表拖拽(高级)

用 Flutter 原生的 Draggable + DragTarget 组合实现:

dart
// 拖拽源
Draggable<String>(
  data: item,
  feedback: Material(
    child: Text(item),  // 拖拽时跟随手指的 widget
  ),
  childWhenDragging: Opacity(
    opacity: 0.3,
    child: Text(item),  // 原位置变透明
  ),
  child: Text(item),
)

// 放置目标
DragTarget<String>(
  onAccept: (data) {
    // data 就是拖过来的数据
    setState(() { /* 更新列表 */ });
  },
  builder: (context, candidateData, rejectedData) {
    return Container(
      color: candidateData.isNotEmpty ? Colors.blue[100] : Colors.white,
      child: Text('放这里'),
    );
  },
)

选择建议

场景方案
单列表排序ReorderableListView,够用且简单
自定义拖拽 UIReorderableListView + proxyDecorator
Grid 拖拽reorderable_grid_view
跨列表 / 跨区域拖拽Draggable + DragTarget 手动实现

总结

Flutter 拖拽列表的实现方式:

  1. 简单场景:直接用 ReorderableListView
  2. 自定义样式proxyDecorator 控制拖拽外观
  3. 复杂交互:第三方库或原生 Draggable + DragTarget

根据需求选择合适方案,大部分场景官方组件已足够。

分享: