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

如何通过拖拽ExpansionTile调整ListView.builder中列表项顺序?

Flutter实现ExpansionTile列表拖拽排序功能

要实现ExpansionTile列表的拖拽排序并同步更新数据,最简便的方式是使用Flutter原生的ReorderableListView组件,它内置了拖拽排序逻辑,只需处理数据位置更新即可。以下是具体实现步骤:

1. 替换ListView为ReorderableListView

将原有的ListView.builder替换为ReorderableListView.builder,并实现onReorder回调函数处理数据位置交换。

2. 为每个列表项设置唯一Key

ReorderableListView要求每个子组件必须拥有唯一的Key,确保拖拽时能正确识别和定位列表项。

3. 实现拖拽排序逻辑

在onReorder回调中,根据拖拽的起始位置(oldIndex)和目标位置(newIndex)调整数据列表顺序,最后调用setState更新UI。


修改后的完整代码示例

List<Map<String, dynamic>> articulosSeleccionados = [];

// 拖拽排序回调
void _onReorder(int oldIndex, int newIndex) {
  setState(() {
    // 处理拖拽到当前位置之后的情况,调整插入索引
    if (newIndex > oldIndex) {
      newIndex -= 1;
    }
    // 移除原位置元素并插入到新位置
    final item = articulosSeleccionados.removeAt(oldIndex);
    articulosSeleccionados.insert(newIndex, item);
  });
}

Padding(
  padding: const EdgeInsets.all(10.0),
  child: Container(
    constraints: BoxConstraints.tightFor(height: max(350, 550)),
    decoration: BoxDecoration(
      border: Border.all(color: Colors.black45, width: 2),
      borderRadius: BorderRadius.circular(10)
    ),
    padding: const EdgeInsets.symmetric(vertical: 10),
    child: Padding(
      padding: const EdgeInsets.symmetric(horizontal: 10),
      child: ReorderableListView.builder(
        itemCount: articulosSeleccionados.length,
        onReorder: _onReorder,
        itemBuilder: (context, index) {
          // 用数据中的nombre作为唯一Key(需确保nombre值唯一)
          return _articuloSeleccionado(index, key: ValueKey(articulosSeleccionados[index]['nombre']));
        },
      ),
    ),
  ),
);

Widget _articuloSeleccionado(int index, {required Key key}) {
  return Row(
    key: key, // 必须将Key传递给父组件
    children: [
      Expanded(
        child: IconButton(
          onPressed: () {
            setState(() {
              articulosSeleccionados.removeAt(index);
            });
          },
          icon: const Icon(Icons.arrow_back))),
      const SizedBox(width: 10),
      Expanded(
        flex: 10,
        child: Container(
          margin: const EdgeInsets.symmetric(vertical: 5),
          child: ClipRRect(
            borderRadius: BorderRadius.circular(10),
            child: ExpansionTile(
              backgroundColor: Colors.lightBlue[50],
              collapsedBackgroundColor: Colors.lightBlue[50],
              textColor: Colors.black,
              title: Text(articulosSeleccionados[index]['nombre']),
              children: [
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 10),
                  child: Text(articulosSeleccionados[index]['contenido']),
                ),
              ],
            ),
          ),
        ),
      ),
    ],
  );
}

关键说明

  • ReorderableListView会自动处理拖拽时的视觉反馈(如拖动阴影、位置交换动画),无需手动实现Draggable和DragTarget组件。
  • 建议使用数据中的唯一标识(如nombre)作为Key,避免用index(拖拽后index会变化,可能导致组件状态异常)。
  • onReorder中的索引调整逻辑是必须的:当拖拽到原位置之后的位置时,newIndex会比实际插入位置大1,因此需要减1修正。

内容的提问来源于stack exchange,提问作者Christian Godoy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 13:15:38