Flutter中Draggable与ReorderableListView实现拖拽排序及拖入垃圾桶删除
Flutter实现拖拽排序与拖拽删除功能
结合ReorderableListView实现列表内部拖拽排序,同时通过Draggable+DragTarget实现拖拽到垃圾桶删除元素的功能,以下是针对A、B、C三个元素的完整实现方案:
核心思路
- 用
ReorderableListView处理列表内部的拖拽排序逻辑 - 给每个列表项套上
Draggable组件,支持将元素拖出列表 - 实现一个
DragTarget作为垃圾桶区域,接收拖拽过来的元素并触发删除
完整代码实现
主页面组件
import 'package:flutter/material.dart'; class DragSortDeleteDemo extends StatefulWidget { const DragSortDeleteDemo({super.key}); @override State<DragSortDeleteDemo> createState() => _DragSortDeleteDemoState(); } class _DragSortDeleteDemoState extends State<DragSortDeleteDemo> { // 初始列表数据:A、B、C final List<String> _listItems = ['A', 'B', 'C']; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('拖拽排序&删除')), body: Column( children: [ // 可拖拽排序的列表区域 Expanded( child: ReorderableListView.builder( padding: const EdgeInsets.symmetric(vertical: 16), itemCount: _listItems.length, // 列表内部拖拽排序的回调 onReorder: (oldIndex, newIndex) { setState(() { // 处理拖拽位置逻辑:如果新位置在旧位置之后,需要减1 if (newIndex > oldIndex) { newIndex -= 1; } final movedItem = _listItems.removeAt(oldIndex); _listItems.insert(newIndex, movedItem); }); }, itemBuilder: (context, index) { final item = _listItems[index]; // 每个列表项同时支持内部排序和向外拖拽删除 return DraggableListItem( key: ValueKey(item), // Reorderable要求子项有唯一key itemContent: item, onItemDraggedToTrash: () { setState(() { _listItems.remove(item); }); }, ); }, ), ), // 垃圾桶区域 const SizedBox(height: 30), const TrashBinArea(), const SizedBox(height: 30), ], ), ); } }
可拖拽列表项组件
class DraggableListItem extends StatelessWidget { final String itemContent; final VoidCallback onItemDraggedToTrash; const DraggableListItem({ super.key, required this.itemContent, required this.onItemDraggedToTrash, }); @override Widget build(BuildContext context) { return Draggable<String>( // 传递拖拽的数据(当前元素内容) data: itemContent, // 拖拽时显示的反馈组件 feedback: Material( child: Container( width: MediaQuery.of(context).size.width - 32, height: 60, margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.circular(8), ), child: Center( child: Text( itemContent, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w500), ), ), ), ), // 拖拽时原位置显示的占位组件 childWhenDragging: Container( height: 60, margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Colors.grey[100], borderRadius: BorderRadius.circular(8), ), ), // 拖拽到垃圾桶完成后的回调 onDragCompleted: onItemDraggedToTrash, // 实际显示的列表项内容(给ReorderableListView用) child: ListTile( key: key, title: Text(itemContent, style: const TextStyle(fontSize: 18)), tileColor: Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), trailing: const Icon(Icons.drag_handle, color: Colors.grey), ), ); } }
垃圾桶区域组件
class TrashBinArea extends StatelessWidget { const TrashBinArea({super.key}); @override Widget build(BuildContext context) { return DragTarget<String>( // 构建垃圾桶UI,根据是否有拖拽元素悬停改变样式 builder: (context, candidateData, rejectedData) { return Container( width: 120, height: 120, decoration: BoxDecoration( color: candidateData.isNotEmpty ? Colors.red[200] : Colors.grey[300], shape: BoxShape.circle, ), child: const Icon(Icons.delete, size: 48, color: Colors.white), ); }, // 接受拖拽元素时的回调(这里只需触发Draggable的完成回调即可) onAccept: (data) {}, ); } }
关键细节说明
- ReorderableListView排序:通过
onReorder回调处理列表元素的位置调整,注意新位置的边界处理(当新位置在旧位置之后时需要减1) - Draggable拖拽逻辑:
feedback定义拖拽时跟随鼠标/手指的组件,childWhenDragging定义原位置的占位,onDragCompleted在拖拽到垃圾桶并释放后触发删除 - DragTarget垃圾桶:通过
builder动态改变样式,提示用户可以释放元素;onAccept接收拖拽数据,这里无需额外处理,删除逻辑由Draggable的回调触发
内容的提问来源于stack exchange,提问作者mukai masakazu
相关产品推荐
相关产品推荐

