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

Flutter:如何为ListView.builder列表项添加逐项动画延迟?

实现ListView.builder列表项逐项延迟过渡动画

问题核心:当前所有列表项共用同一个动画控制器,导致动画同时触发。要实现逐项依次显示,需给每个列表项分配独立的动画时间区间。

解决方案思路

利用Interval曲线,在全局动画控制器的时间轴上,为每个列表项划分专属的动画时间段,通过index计算每个项的延迟偏移,从而实现逐项启动动画的效果。

修改后的完整代码

import 'package:calendar_app/constants/customColor.dart';
import 'package:calendar_app/models/announcement.dart';
import 'package:calendar_app/services/getAnnouncement.dart';
import 'package:calendar_app/widgets/announcementItem.dart';
import 'package:flutter/material.dart';

class Announcement extends StatefulWidget {
  const Announcement({Key? key}) : super(key: key);

  @override
  State<Announcement> createState() => _AnnouncementState();
}

class _AnnouncementState extends State<Announcement>
    with SingleTickerProviderStateMixin {
  Future<List<AnnouncementItem>> announcements = getAnnouncement();
  
  // 全局动画控制器,控制整体动画时长
  late final AnimationController _controller = AnimationController(
    duration: const Duration(milliseconds: 1500), // 延长总时长,给所有项留出动画时间
    vsync: this,
  )..forward();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: CustomColor.pastelBlue,
      body: Container(
        padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 20),
        child: FutureBuilder<List<AnnouncementItem>>(
          future: announcements,
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return const Center(
                child: CircularProgressIndicator(),
              );
            } else if (snapshot.hasError) {
              return Center(
                child: Text('Error: ${snapshot.error}'),
              );
            } else if (snapshot.hasData) {
              final announcements = snapshot.data!;
              return Expanded(
                child: ListView.builder(
                  itemCount: announcements.length,
                  itemBuilder: (context, index) {
                    // 计算每个项的动画区间:每个项延迟15%总时长启动
                    final animationInterval = Interval(
                      (index * 0.15).clamp(0.0, 1.0), // clamp避免区间超过1.0
                      1.0,
                      curve: Curves.easeInOut,
                    );

                    // 渐入动画:基于区间计算当前进度
                    final fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
                      CurvedAnimation(
                        parent: _controller,
                        curve: animationInterval,
                      ),
                    );

                    // 滑动动画:基于区间计算当前偏移
                    final slideAnimation = Tween<Offset>(
                      begin: const Offset(0.0, 0.4),
                      end: Offset.zero,
                    ).animate(
                      CurvedAnimation(
                        parent: _controller,
                        curve: animationInterval,
                      ),
                    );

                    return FadeTransition(
                      opacity: fadeAnimation,
                      child: SlideTransition(
                        position: slideAnimation,
                        child: AnnouncementListItem(
                          announcement: announcements[index],
                        ),
                      ),
                    );
                  },
                ),
              );
            } else {
              return const Center(
                child: Text('No announcements found.'),
              );
            }
          },
        ),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

关键修改点说明

  • 全局控制器总时长调整:把控制器时长从1000ms改为1500ms,确保所有列表项都有足够时间完成动画。
  • Interval区间计算:通过index * 0.15给每个项设置15%总时长的延迟,clamp(0.0,1.0)防止当列表项过多时区间超过1.0。
  • 独立动画实例:每个列表项根据自己的index生成专属的fadeAnimation和slideAnimation,确保动画启动时间错开。
  • 移除全局动画变量:不再使用全局的_fadeAnimation和_animation,改为在itemBuilder中按需创建。

自定义调整建议

  • 修改index * 0.15中的0.15,可以调整每个项的延迟间隔:数值越小,间隔越短;数值越大,间隔越长。
  • 调整控制器的duration,可以控制整体动画的快慢。
  • 更换Curves参数,能改变动画的缓动效果,比如保留Curves.elasticOut可实现弹性动画。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 18:47:15