Flutter基础(前端教程①②-序列帧动画)
🧠 核心思路总结
彻底绕过 Image
组件重建带来的性能瓶颈:不再让 setState
重建包含 Image
的 Widget 树(这是开销大、可能导致闪烁的根源),改为使用底层画布 (Canvas
) 直接绘制预先处理好的图像帧数据。
好的,我来帮你清晰、简洁地总结一下你提出的 Flutter GIF 序列帧动画闪烁解决方案(使用 CustomPaint
) 的核心要点:
🧠 核心思路总结
彻底绕过 Image
组件重建带来的性能瓶颈:不再让 setState
重建包含 Image
的 Widget 树(这是开销大、可能导致闪烁的根源),改为使用底层画布 (Canvas
) 直接绘制预先处理好的图像帧数据。
💡 核心改进点总结(方案精髓)
👉 高效预加载:
做什么? 在
initState
中,预先将 GIF 的所有帧 (AssetImage
或其他来源) 解码成ui.Image
对象。为什么?
ui.Image
是 Flutter 渲染引擎直接使用的底层图片格式,内存驻留,避免运行时重复解码带来的卡顿和延迟。将它们缓存在内存中(如List<ui.Image>
)。
👉 专用绘制器:
做什么? 创建一个继承自
CustomPainter
的类(例如_EarthPainter
)。做什么? 在这个类的
paint
方法中,接收当前应该显示的帧索引(或ui.Image
对象),并调用canvas.drawImage
(或drawImageRect
) 方法直接将对应的ui.Image
绘制到画布上。为什么?
CustomPainter
非常轻量,它的paint
调用是 Flutter 渲染管线中优化非常好的部分。仅绘制一个位图 (ui.Image
) 到画布上性能开销极低。
👉 轻量级状态更新:
做什么? 使用
CustomPaint
Widget 作为地球动画的容器,将上面创建的_EarthPainter
实例传入。做什么? 当需要切换到下一帧动画时,只更新
_EarthPainter
内部存储的当前帧索引
(或当前帧的ui.Image
) 状态。做什么? 通知
CustomPaint
需要重绘(通过ChangeNotifier
或简单地标记_EarthPainter
实例为shouldRepaint
)。为什么? 这个更新只触发
paint
方法的再次调用(在CustomPaint
范围内),完全不涉及重建上层 Widget 树 (如Image
,Container
,Stack
等组件)。大大减少了渲染开销。
📝 简明提示词(Action Plan)
预加载和解码:
initState
中加载GIF,将所有帧解码并缓存为List<ui.Image>
。创建绘制器: 写一个
CustomPainter
子类(_EarthPainter
)。它持有:缓存的帧列表 (
List<ui.Image>
)。当前帧索引
(int) 或当前帧Image
(ui.Image?)。paint
方法中:canvas.drawImage(_cachedFrames[currentIndex], ...)
。
使用
CustomPaint
: 在connectWidget
/disconnectWidget
等位置,用CustomPaint(painter: _EarthPainter(...))
替代原来的Image
组件。驱动动画: 使用
AnimationController
+Ticker
/Timer
按帧率 (如 24 fps) 只更新_EarthPainter
的当前帧索引
并调用painter.markNeedsPaint()
或更新CustomPaint
的painter
引用(触发shouldRepaint
逻辑)。