Skip to content

Flutter 掉影格定位:從時間線找到真正昂貴的 Widget

頁面看起來「滑不順」,不應該立刻給所有 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,但先算數據傳輸成本,不能把每次幾毫秒的小任務都搬過去。

text
復現 → profile 錄制 → 找到超預算幀 → 分辨 UI/raster → 提出一個原因
     → 只改這一處 → 同設備同腳本復測 → 核對內存與視覺效果

給修復設置回歸條件 ​

把首屏、滾動中和動畫結束後三個階段分開記錄。修復後除了高分位幀時間,還檢查首屏可交互時間、圖片清晰度、內存峰值與無障礙焦點。低端 Android 與高刷新率 iPhone 可能暴露不同瓶頸;至少在能復現問題的設備上做前後對照。若數據波動大,固定網絡返回和測試數據,重復幾輪再下結論。

參考:Flutter 性能分析。

MIT Licensed