You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中Draggable与ReorderableListView实现拖拽排序及拖入垃圾桶删除

Flutter实现拖拽排序与拖拽删除功能

结合ReorderableListView实现列表内部拖拽排序,同时通过Draggable+DragTarget实现拖拽到垃圾桶删除元素的功能,以下是针对A、B、C三个元素的完整实现方案:

核心思路

  1. 用ReorderableListView处理列表内部的拖拽排序逻辑
  2. 给每个列表项套上Draggable组件,支持将元素拖出列表
  3. 实现一个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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 15:25:20