页面看起来“滑不顺”,不应该立刻给所有 Widget 加 const。Flutter 的卡顿可能发生在 Dart 构建阶段,也可能出现在图片解码或光栅化阶段;两者的修复方向不同。
用可复现路径采样
在真机 profile 模式下,固定页面、数据集和手势路径;记录慢帧出现的时间点。先看帧时间线里 UI 与 raster 的耗时,再把慢帧与当时的页面操作对应起来。热重载、调试断点和首次着色器准备都可能改变测量结果,不能用一次偶发峰值下结论。
若 UI 线程耗时高,检查是否在 build 中解析 JSON、排序大数组,或让顶层状态变化重建整棵列表。把计算移到数据层,缩小监听范围,并使用 ListView.builder 按需创建行。若 raster 耗时高,检查大图尺寸、模糊阴影、频繁裁剪和重复离屏绘制;优先减少实际绘制工作。
优化要有前后对照
每次只改一个因素,记录同设备上的慢帧数量、峰值耗时和内存。某个修复使平均帧更快,但导致列表图片重新加载,也不算成功。最后在低端设备、深色模式和字体放大场景再跑同一路径。
先定位是哪一帧、哪一侧超时
商品详情页展开图片轮播时掉帧,不要凭感觉重写 Widget。先用 profile 模式在目标设备上录制同一操作,查看 Flutter DevTools 的 frame chart 和 timeline。每帧的预算取决于屏幕刷新率;60 Hz 约 16.7 ms,120 Hz 约 8.3 ms。UI 线程超时通常指向 build/layout 或 Dart 计算,raster 线程超时则更可能是复杂图层、裁剪、模糊或图片处理。分别记录高分位帧耗时,而非只报平均 FPS。
若 UI 侧耗时,在时间线里追查是哪一次状态更新让整个页面重建,把频繁变化的价格区域缩小为局部监听。若 raster 侧耗时,检查大图是否按目标尺寸解码,是否叠加了大量透明层与阴影;RepaintBoundary 只在隔离重复绘制确实有收益时使用,它也会占内存。图片处理或 JSON 解析很重时可以考虑 isolate,但先算数据传输成本,不能把每次几毫秒的小任务都搬过去。
复现 → profile 录制 → 找到超预算帧 → 分辨 UI/raster → 提出一个原因
→ 只改这一处 → 同设备同脚本复测 → 核对内存与视觉效果
给修复设置回归条件
把首屏、滚动中和动画结束后三个阶段分开记录。修复后除了高分位帧时间,还检查首屏可交互时间、图片清晰度、内存峰值与无障碍焦点。低端 Android 与高刷新率 iPhone 可能暴露不同瓶颈;至少在能复现问题的设备上做前后对照。若数据波动大,固定网络返回和测试数据,重复几轮再下结论。
参考:Flutter 性能分析。
