Flutter AnimatedList:为SlideTransition添加CurvedAnimation
解决AnimatedList滑入动画曲线及插入无效果问题
嘿,我看了你的代码,发现问题出在你没有正确利用AnimatedList提供的项专属动画实例,反而自己维护了一个全局的动画控制器,这就是插入新项时没动画的核心原因。下面一步步帮你修复:
问题分析
你在_RecentListsState里创建的_controller和_position是全局的,只会在组件初始化时执行一次动画,而AnimatedList的itemBuilder参数里的animation才是每个列表项插入/删除时的专属动画对象,由AnimatedList内部自动管理生命周期。
修复步骤
1. 移除全局动画控制器
删掉State里的_controller、_position,以及initState和dispose里对应的代码——这些完全不需要,AnimatedList会帮我们处理每个项的动画。
2. 基于AnimatedList的animation构建带曲线的滑入动画
在itemBuilder中,用传入的animation来构建带缓动曲线的Offset动画,有两种写法都可以:
写法一:用chain结合CurveTween
itemBuilder: (ctx, i, animation) { return SlideTransition( position: animation.drive( Tween<Offset>( begin: Offset(0.5, 0.0), // 从右侧50%位置滑入 end: Offset(0.0, 0.0), ).chain(CurveTween(curve: Curves.decelerate)), // 添加减速曲线 ), child: _buildListItem(checklists.lists[i], i), ); },
写法二:用CurvedAnimation包裹
itemBuilder: (ctx, i, animation) { return SlideTransition( position: CurvedAnimation( parent: animation, curve: Curves.decelerate, ).drive(Tween<Offset>( begin: Offset(0.5, 0.0), end: Offset(0.0, 0.0), )), child: _buildListItem(checklists.lists[i], i), ); },
3. 修复删除动画的removeItem回调
原来的removeItem返回Container()会导致删除时没有动画,应该返回要删除的列表项,并给它添加滑出动画:
void _onDeleteList(Checklist list, int listIndex) { Provider.of<Checklists>(context).deleteList(list.id); recentListsAnimationKey.currentState.removeItem( listIndex, (context, animation) => SlideTransition( position: animation.drive( Tween<Offset>( begin: Offset(0.0, 0.0), end: Offset(1.0, 0.0), // 向右滑出 ).chain(CurveTween(curve: Curves.easeIn)), ), child: _buildListItem(list, listIndex), ), ); // ... 其余SnackBar代码不变 }
4. 统一动画时长
把插入新项的duration改成和动画一致的200ms,效果更协调:
void createNewList(BuildContext context) { if (nameController.text.isNotEmpty) { Provider.of<Checklists>(context).addList(nameController.text); recentListsAnimationKey.currentState.insertItem(0, duration: Duration(milliseconds: 200)); nameController.clear(); } Navigator.of(context).pop(); }
修改后的完整代码
GlobalKey<AnimatedListState> recentListsAnimationKey = GlobalKey(); class RecentLists extends StatefulWidget { @override _RecentListsState createState() => _RecentListsState(); } class _RecentListsState extends State<RecentLists> { Widget _buildListItem(Checklist list, int listIndex) { return Dismissible( key: ObjectKey(list.id), direction: DismissDirection.endToStart, background: Container( alignment: AlignmentDirectional.centerEnd, color: Theme.of(context).accentColor, child: Padding( padding: EdgeInsets.fromLTRB(0.0, 0.0, 10.0, 0.0), child: Icon( Icons.delete, color: Colors.white, ), ), ), child: ListTile( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (ctx) => ListItemsScreen(list.id), ), ); }, title: Text(list.name), leading: Checkbox( value: list.completed, onChanged: (value) { setState(() { list.completed = value; }); }, ), ), onDismissed: (direction) { _onDeleteList(list, listIndex); }, ); } void _onDeleteList(Checklist list, int listIndex) { Provider.of<Checklists>(context).deleteList(list.id); recentListsAnimationKey.currentState.removeItem( listIndex, (context, animation) => SlideTransition( position: animation.drive( Tween<Offset>( begin: Offset(0.0, 0.0), end: Offset(1.0, 0.0), ).chain(CurveTween(curve: Curves.easeIn)), ), child: _buildListItem(list, listIndex), ), ); Scaffold.of(context).showSnackBar( SnackBar( action: SnackBarAction( label: 'UNDO', onPressed: () { Provider.of<Checklists>(context).undoDeleteList(list, listIndex); recentListsAnimationKey.currentState.insertItem(listIndex, duration: Duration(milliseconds: 200)); }, ), content: Text( 'List deleted', style: TextStyle(color: Theme.of(context).accentColor), ), ), ); } @override Widget build(BuildContext context) { final checklists = Provider.of<Checklists>(context); return AnimatedList( key: recentListsAnimationKey, initialItemCount: checklists.lists.length, itemBuilder: (ctx, i, animation) { return SlideTransition( position: animation.drive( Tween<Offset>( begin: Offset(0.5, 0.0), end: Offset(0.0, 0.0), ).chain(CurveTween(curve: Curves.decelerate)), ), child: _buildListItem(checklists.lists[i], i), ); }, ); } } // 插入新项的代码 void createNewList(BuildContext context) { if (nameController.text.isNotEmpty) { Provider.of<Checklists>(context).addList(nameController.text); recentListsAnimationKey.currentState.insertItem(0, duration: Duration(milliseconds: 200)); nameController.clear(); } Navigator.of(context).pop(); }
这样修改后,新项插入时会带着Curves.decelerate的缓动曲线从右侧滑入,删除时会带着Curves.easeIn的曲线向右滑出,完全符合你的需求啦~
内容的提问来源于stack exchange,提问作者rejy11
相关产品推荐
相关产品推荐

