Flutter中如何为AnimatedList的项重新排序添加动画?
Flutter AnimatedList 项移动至首位动画修复
问题分析
当前实现存在几个核心问题:
- 选中项的移除动画是向左滑出视野,而非连贯移动到首位,视觉上呈现“消失再出现”的割裂效果
- 插入首位的动画与移除动画无衔接,无法形成移动的连贯感
- 未处理其他项向右偏移的动画逻辑,不符合需求中“其余项向右偏移”的要求
- 移除项时
hasDropdown的判断逻辑错误,误用了原位置index
解决方案
以下是修正后的代码及关键调整说明:
1. 新增状态变量记录选中项原位置
在State类中添加变量,用于计算动画偏移:
int? _lastSelectedIndex; final double _itemWidth = 100.w; // 根据你的实际项宽调整
2. 修正AnimatedList实现
AnimatedList( shrinkWrap: true, key: listKey, padding: const EdgeInsets.only(left: 14).w, initialItemCount: _categories.length, scrollDirection: Axis.horizontal, itemBuilder: (_, index, animation) { final item = _categories[index]; final isSelectedItem = selectedCategory.value == item; // 计算项的动画偏移 Offset beginOffset = Offset.zero; Offset endOffset = Offset.zero; if (isSelectedItem && _lastSelectedIndex != null && _lastSelectedIndex! > 0) { // 选中项从原位置滑向首位 beginOffset = Offset(_lastSelectedIndex! * _itemWidth, 0); endOffset = Offset.zero; } else if (_lastSelectedIndex != null && index < _lastSelectedIndex!) { // 原位置在选中项左侧的项,向右偏移腾位置 beginOffset = Offset.zero; endOffset = Offset(_itemWidth, 0); } return GestureDetector( onTap: () { if (index == 0) return; // 已在首位,无需操作 _lastSelectedIndex = index; selectedCategory.value = item; final movedItem = _categories.removeAt(index); // 移除项动画:滑向首位+抬起效果 listKey.currentState!.removeItem( index, (context, anim) => SlideTransition( position: Tween<Offset>( begin: Offset.zero, end: Offset(-index * _itemWidth, 0), ).animate(CurvedAnimation(parent: anim, curve: Curves.easeInOut)), child: ScaleTransition( scale: Tween<double>(begin: 1, end: 1.1).animate(anim), child: _CourseCategoryItem( title: movedItem, hasDropdown: false, ), ), ), duration: const Duration(seconds: 1), ); // 插入项动画:衔接移除动画,平滑显示在首位 listKey.currentState!.insertItem(0, duration: const Duration(seconds: 1)); _categories.insert(0, movedItem); setState(() {}); }, child: SlideTransition( position: Tween<Offset>(begin: beginOffset, end: endOffset).animate(animation), child: ScaleTransition( scale: isSelectedItem ? Tween<double>(begin: 1.1, end: 1).animate(animation) : Tween<double>(begin: 1, end: 1).animate(animation), child: _CourseCategoryItem( title: item, hasDropdown: index == 0, isSelected: selectedCategory.value == item, ), ), ), ); }, )
关键调整点
- 用
_lastSelectedIndex追踪选中项的原位置,精准计算所有项的动画偏移量 - 移除选中项时,通过
SlideTransition让项滑向首位位置,同时用ScaleTransition实现“抬起”的缩放效果 - 插入首位的动画与移除动画保持相同时长,保证视觉连贯性
- 原位置在选中项左侧的项会向右偏移一个项宽,实现“腾位置”的效果
- 修正
hasDropdown逻辑,仅首位项显示下拉,移除过程中不显示
内容的提问来源于stack exchange,提问作者Dhruv
相关产品推荐
相关产品推荐

