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

Flutter ReorderableListView悬停异常:所有Tile同步触发交互效果

问题根源

你用了一个全局的isHover布尔变量,所有列表项都共享这个状态。只要任何一个项触发hover事件,调用setState后整个组件树重建,所有列表项都会根据这个全局变量更新UI,导致全部同时变化。

解决方法

有两种常用方案,任选其一即可:

方案1:用索引跟踪当前悬停的项

在父组件中维护一个hoveredIndex变量,记录当前被悬停的项的索引,每个列表项根据自身索引是否等于hoveredIndex来切换状态:

// 父组件中添加状态变量
int? hoveredIndex;

// 修改ReorderableListView的children部分
children: <Widget>[
  for (int index = 0; index < widget.fieldsList.length; index += 1)
    ListTile(
      key: Key('$index'),
      leading: SizedBox(
        width: 50,
        child: ReorderableDragStartListener(
          index: index,
          child: const Icon(Icons.drag_handle)
        ),
      ),
      title: MouseRegion(
        onEnter: (details) => setState(() => hoveredIndex = index),
        onExit: (details) => setState(() => hoveredIndex = null),
        onHover: (details) => setState(() => hoveredIndex = index),
        child: Stack(
          children: [
            Container(
                color: hoveredIndex == index
                    ? Colors.grey.shade100
                    : Colors.transparent,
                child: showComponent(componentList[index].type)),
            Positioned(
                top: 0,
                right: 0,
                child: hoveredIndex == index
                    ? Row(
                        children: [
                          Container(
                            child: IconButton(
                                icon: const Icon(Icons.edit), onPressed: () {}),
                          ),
                          Container(
                            child: IconButton(
                                icon: const Icon(Icons.delete),
                                onPressed: () {}),
                          ),
                        ],
                      )
                    : Container()),
          ],
        ),
      ),
    ),
],

方案2:将列表项抽为独立的StatefulWidget

把每个列表项封装成单独的有状态组件,让每个项自己维护自身的hover状态,这样各个项的状态完全独立,不会互相影响:

// 单独的列表项组件
class ReorderableListItem extends StatefulWidget {
  final int index;
  final Widget child;
  final VoidCallback onEdit;
  final VoidCallback onDelete;

  const ReorderableListItem({
    super.key,
    required this.index,
    required this.child,
    required this.onEdit,
    required this.onDelete,
  });

  @override
  State<ReorderableListItem> createState() => _ReorderableListItemState();
}

class _ReorderableListItemState extends State<ReorderableListItem> {
  bool _isHover = false;

  @override
  Widget build(BuildContext context) {
    return ListTile(
      key: Key('${widget.index}'),
      leading: SizedBox(
        width: 50,
        child: ReorderableDragStartListener(
          index: widget.index,
          child: const Icon(Icons.drag_handle),
        ),
      ),
      title: MouseRegion(
        onEnter: (details) => setState(() => _isHover = true),
        onExit: (details) => setState(() => _isHover = false),
        child: Stack(
          children: [
            Container(
              color: _isHover ? Colors.grey.shade100 : Colors.transparent,
              child: widget.child,
            ),
            Positioned(
              top: 0,
              right: 0,
              child: _isHover
                  ? Row(
                      children: [
                        IconButton(
                          icon: const Icon(Icons.edit),
                          onPressed: widget.onEdit,
                        ),
                        IconButton(
                          icon: const Icon(Icons.delete),
                          onPressed: widget.onDelete,
                        ),
                      ],
                    )
                  : Container(),
            ),
          ],
        ),
      ),
    );
  }
}

// 父组件中使用这个组件
ReorderableListView(
  padding: const EdgeInsets.symmetric(horizontal: 40),
  buildDefaultDragHandles: false,
  children: <Widget>[
    for (int index = 0; index < widget.fieldsList.length; index += 1)
      ReorderableListItem(
        index: index,
        child: showComponent(componentList[index].type),
        onEdit: () {
          // 处理编辑逻辑
        },
        onDelete: () {
          // 处理删除逻辑
        },
      ),
  ],
  onReorder: (int oldIndex, int newIndex) {
    setState(() {
      final item = componentList[oldIndex];
      componentList[oldIndex] = componentList[newIndex];
      componentList[newIndex] = item;
    });
  },
);
说明
  • 方案1适合简单场景,不需要额外创建组件,直接在父组件中管理状态;
  • 方案2更符合组件化思想,当列表项逻辑复杂时,代码更清晰易维护。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 16:07:44