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
相关产品推荐
相关产品推荐

