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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:44:34