引言
在掘金上瀏覽到Nayuta
開(kāi)源的貝殼flutter流暢優(yōu)化組件 Keframe拨扶。在Demo上試用了一番,確有奇效脓匿,下面記錄一下筆記心得蒸矛。
Keframe 處理思路
- 卡頓的本質(zhì)
在一幀內(nèi)浅悉,模塊運(yùn)行的時(shí)間太長(zhǎng),計(jì)算量太大
Keframe 方案:分幀入屏 ,既然單幀的繪制時(shí)間太長(zhǎng)冗酿,那我們就將一幀分成多幀辑奈。
將屏幕上的 widget 扔進(jìn)一個(gè)執(zhí)行隊(duì)列,將模塊內(nèi)的時(shí)間做拆分已烤,多個(gè) widget 不同時(shí)進(jìn)行繪制鸠窗,一個(gè)繪制完成后再進(jìn)行下一個(gè) widget 的繪制。
Keframe 使用方法
- 添加依賴(非空安全使用: 1.0.2胯究; 空安全版本使用: 2.0.2)
dependencies:
keframe: version
- 優(yōu)化前的代碼
// CellWidget 是一個(gè)復(fù)雜的Item widget
ListView.builder(
itemCount: 100t,
itemBuilder: (c, i) => CellWidget(),
)
- 使用
Keframe
優(yōu)化
ListView.builder(
itemCount: 100t,
itemBuilder: (c, i) => FrameSeparateWidget(
index: i,
placeHolder: placeHolderWidget(),
widget: CellWidget(),
),
)
FrameSeparateWidget
: 分屏組件
index
: id標(biāo)識(shí)稍计,非必傳,使用SizeCacheWidget
的場(chǎng)景必傳
placeHolder
: 占位widget
widget
: 真實(shí)需要渲染的widget
借用一下作者的圖解:
假如現(xiàn)在頁(yè)面由 A裕循、B臣嚣、C、D 四部分組成剥哑,每部分耗時(shí) 10ms硅则,在頁(yè)面時(shí)構(gòu)建為 40ms。使用分幀組件 FrameSeparateWidget 嵌套每一個(gè)部分株婴。頁(yè)面構(gòu)建時(shí)會(huì)在第一幀渲染簡(jiǎn)單的占位怎虫,在后續(xù)四幀內(nèi)分別渲染 A、B困介、C大审、D。
另外 Keframe
還提供了一個(gè)工具類SizeCacheWidget
用于緩存子節(jié)點(diǎn)中座哩,分幀組件嵌套的實(shí)際 widget 的尺寸信息徒扶。對(duì)于列表,在每一個(gè) item 中嵌套 FrameSeparateWidget根穷,并將 ListView 嵌套在 SizeCacheWidget 內(nèi)即可姜骡。
SizeCacheWidget(
child: ListView.builder(
...省略
itemBuilder: (c, i) => FrameSeparateWidget(
...省略
),
),
)
SizeCacheWidget 的作用:
當(dāng)不確定實(shí)際 item 高度的時(shí)候导坟,給 placeholder 設(shè)置一個(gè)近似的高度。并且在將 ListView 嵌套在 SizeCacheWidget 中圈澈。記錄已渲染 widget 的大小尺寸乍迄,對(duì)于已渲染過(guò)的 widget 設(shè)置占位的尺寸。在滾動(dòng)過(guò)程中士败,已經(jīng)渲染過(guò)的 item 將不會(huì)出現(xiàn)跳動(dòng)情況。
原理分析
-
分幀入屏
initState
初始化時(shí) resultWidget 賦值為占位WidgettransformWidget
initState和didUpdate都會(huì)觸發(fā)褥伴,監(jiān)聽(tīng)占位繪制完成谅将,并將替換任務(wù)扔進(jìn)分幀隊(duì)列中
await SchedulerBinding.instance!.endOfFrame
: 如果當(dāng)前正在繪制,等待當(dāng)前幀結(jié)束重慢。如果當(dāng)前空閑饥臂,強(qiáng)制進(jìn)行一幀的繪制,并等待結(jié)束似踱。await taskItemQueue.first.run()
: 該方法為callback回調(diào)隅熙,真實(shí)內(nèi)容就是替換占位widget
- SizeCacheWidget
//自定義冒泡通知
class LayoutInfoNotification extends Notification {
final Size size;
final int? index;
LayoutInfoNotification(this.index, this.size);
}
子組件重寫(xiě)
performLayout
方法,將尺寸大小通過(guò)冒泡通知
給父組件核芽,父組件根據(jù)id進(jìn)行存儲(chǔ)囚戚。