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
相关产品推荐
相关产品推荐

