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

Flutter中如何在其他Widget的build方法访问State类的animation字段

Flutter ProgressBar动画状态监听与ListView条件渲染实现方案

核心实现逻辑:首先将ProgressBar私有动画的完成事件暴露给父组件,再结合列表项的状态管理实现组件的条件渲染。


步骤1:改造自定义ProgressBar,暴露动画完成回调

给ProgressBar新增回调参数,在State类中监听动画状态,完成时触发回调:

class ProgressBar extends StatefulWidget {
  // 新增动画完成回调参数
  final VoidCallback? onAnimationCompleted;
  // 保留你原有自定义参数,比如进度、宽高、样式等
  final double progress;

  const ProgressBar({
    super.key,
    this.onAnimationCompleted,
    required this.progress,
    // 其他原有参数
  });

  @override
  State<ProgressBar> createState() => _ProgressBarState();
}

class _ProgressBarState extends State<ProgressBar> with SingleTickerProviderStateMixin {
  late Animation<double> animation;
  late AnimationController controller;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      duration: const Duration(seconds: 2), // 替换为你原有动画时长
      vsync: this,
    );
    animation = Tween<double>(begin: 0, end: widget.progress).animate(controller)
      // 新增动画状态监听
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          widget.onAnimationCompleted?.call();
        }
      });
    controller.forward();
  }

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

  // 保留你原有build逻辑即可
  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: animation,
      builder: (context, child) {
        return Container(
          width: double.infinity,
          height: 6,
          child: LinearProgressIndicator(
            value: animation.value,
            // 原有样式配置
          ),
        );
      },
    );
  }
}

步骤2:改造Item数据类,新增状态标记

给每个文件对应的Item类新增动画完成状态字段,用于列表渲染判断:

class Item {
  final String filePath;
  final String fileName;
  // 新增动画完成标记,默认未完成
  bool isAnimationCompleted = false;
  // 保留你原有其他字段,比如文件大小、上传进度等

  Item({
    required this.filePath,
    required this.fileName,
    // 其他原有参数
  });
}

步骤3:ListView.separated中实现条件渲染

父组件中持有Item列表,通过setState更新状态触发UI重绘,同时可以加入你需要的超时兜底逻辑:

class FileListPage extends StatefulWidget {
  const FileListPage({super.key});

  @override
  State<FileListPage> createState() => _FileListPageState();
}

class _FileListPageState extends State<FileListPage> {
  // 存储选中的文件列表
  final List<Item> _fileList = [];

  // 你原有file_picker选择文件的逻辑
  Future<void> _pickFiles() async {
    final result = await FilePicker.platform.pickFiles(allowMultiple: true);
    if (result != null) {
      setState(() {
        _fileList.addAll(result.files.map((file) => Item(
          filePath: file.path!,
          fileName: file.name,
        )));
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: _pickFiles,
        child: const Icon(Icons.add),
      ),
      body: ListView.separated(
        padding: const EdgeInsets.all(16),
        itemCount: _fileList.length,
        separatorBuilder: (context, index) => const SizedBox(height: 12),
        itemBuilder: (context, index) {
          final item = _fileList[index];
          // 状态判断,渲染对应组件
          if (item.isAnimationCompleted) {
            // 替换为你需要的占位Padding
            return const Padding(
              padding: EdgeInsets.symmetric(vertical: 3),
              child: SizedBox.shrink(),
            );
          }
          return ProgressBar(
            progress: 1.0, // 替换为实际进度值(比如实时上传进度)
            onAnimationCompleted: () {
              // 动画完成后更新当前item状态
              if (mounted) {
                setState(() {
                  item.isAnimationCompleted = true;
                });
              }
              // === 你需要的超时兜底逻辑,放开即可使用 ===
              // Future.delayed(const Duration(seconds: 3), () {
              //   if (mounted && !item.isAnimationCompleted) {
              //     setState(() {
              //       item.isAnimationCompleted = true;
              //     });
              //   }
              // });
            },
          );
        },
      ),
    );
  }
}

注意事项

  • 如果ProgressBar的进度是动态更新的(比如实时上传进度),需要在ProgressBar的didUpdateWidget生命周期里更新动画的end值,重新调用controller.forward()即可
  • 超时兜底的时长建议设置为比动画时长多1~2秒,避免正常动画未结束就被强制标记为完成
  • 调用setState前判断mounted可以避免页面销毁后触发更新的异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 05:06:04