引言
在掘金上瀏覽到Nayuta
開源的貝殼flutter流暢優化組件 Keframe。在Demo上試用了一番,確有奇效,下面記錄一下筆記心得。
Keframe 處理思路
- 卡頓的本質
在一幀內,模塊運行的時間太長,計算量太大
Keframe 方案:分幀入屏 ,既然單幀的繪制時間太長,那我們就將一幀分成多幀。
將屏幕上的 widget 扔進一個執行隊列,將模塊內的時間做拆分,多個 widget 不同時進行繪制,一個繪制完成后再進行下一個 widget 的繪制。
Keframe 使用方法
- 添加依賴(非空安全使用: 1.0.2; 空安全版本使用: 2.0.2)
dependencies:
keframe: version
- 優化前的代碼
// CellWidget 是一個復雜的Item widget
ListView.builder(
itemCount: 100t,
itemBuilder: (c, i) => CellWidget(),
)
- 使用
Keframe
優化
ListView.builder(
itemCount: 100t,
itemBuilder: (c, i) => FrameSeparateWidget(
index: i,
placeHolder: placeHolderWidget(),
widget: CellWidget(),
),
)
FrameSeparateWidget
: 分屏組件
index
: id標識,非必傳,使用SizeCacheWidget
的場景必傳
placeHolder
: 占位widget
widget
: 真實需要渲染的widget
借用一下作者的圖解:
假如現在頁面由 A、B、C、D 四部分組成,每部分耗時 10ms,在頁面時構建為 40ms。使用分幀組件 FrameSeparateWidget 嵌套每一個部分。頁面構建時會在第一幀渲染簡單的占位,在后續四幀內分別渲染 A、B、C、D。
另外 Keframe
還提供了一個工具類SizeCacheWidget
用于緩存子節點中,分幀組件嵌套的實際 widget 的尺寸信息。對于列表,在每一個 item 中嵌套 FrameSeparateWidget,并將 ListView 嵌套在 SizeCacheWidget 內即可。
SizeCacheWidget(
child: ListView.builder(
...省略
itemBuilder: (c, i) => FrameSeparateWidget(
...省略
),
),
)
SizeCacheWidget 的作用:
當不確定實際 item 高度的時候,給 placeholder 設置一個近似的高度。并且在將 ListView 嵌套在 SizeCacheWidget 中。記錄已渲染 widget 的大小尺寸,對于已渲染過的 widget 設置占位的尺寸。在滾動過程中,已經渲染過的 item 將不會出現跳動情況。
原理分析
-
分幀入屏
FrameSeparateWidget
initState
初始化時 resultWidget 賦值為占位WidgettransformWidget
initState和didUpdate都會觸發,監聽占位繪制完成,并將替換任務扔進分幀隊列中
await SchedulerBinding.instance!.endOfFrame
: 如果當前正在繪制,等待當前幀結束。如果當前空閑,強制進行一幀的繪制,并等待結束。await taskItemQueue.first.run()
: 該方法為callback回調,真實內容就是替換占位widget
- SizeCacheWidget
//自定義冒泡通知
class LayoutInfoNotification extends Notification {
final Size size;
final int? index;
LayoutInfoNotification(this.index, this.size);
}
子組件重寫
performLayout
方法,將尺寸大小通過冒泡通知
給父組件,父組件根據id進行存儲。