網(wǎng)站首頁 編程語言 正文
本文為大家分析了Flutter之可滾動(dòng)組件子項(xiàng)緩存 KeepAlive,供大家參考,具體內(nèi)容如下
首先回想一下,在介紹 ListView 時(shí),有一個(gè)addAutomaticKeepAlives 屬性我們并沒有介紹,如果addAutomaticKeepAlives 為 true,則 ListView 會(huì)為每一個(gè)列表項(xiàng)添加一個(gè) AutomaticKeepAlive 父組件。雖然 PageView 的默認(rèn)構(gòu)造函數(shù)和 PageView.builder 構(gòu)造函數(shù)中沒有該參數(shù),但它們最終都會(huì)生成一個(gè) SliverChildDelegate 來負(fù)責(zé)列表項(xiàng)的按需加載,而在 SliverChildDelegate 中每當(dāng)列表項(xiàng)構(gòu)建完成后,SliverChildDelegate 都會(huì)為其添加一個(gè) AutomaticKeepAlive 父組件。下面我們就先介紹一下 AutomaticKeepAlive 組件。
AutomaticKeepAlive
AutomaticKeepAlive 的組件的主要作用是將列表項(xiàng)的根 RenderObject 的 keepAlive 按需自動(dòng)標(biāo)記 為 true 或 false。為了方便敘述,我們可以認(rèn)為根 RenderObject 對(duì)應(yīng)的組件就是列表項(xiàng)的根 Widget,代表整個(gè)列表項(xiàng)組件,同時(shí)我們將列表組件的 Viewport區(qū)域 + cacheExtent(預(yù)渲染區(qū)域)稱為加載區(qū)域 :
1.當(dāng) keepAlive 標(biāo)記為 false 時(shí),如果列表項(xiàng)滑出加載區(qū)域時(shí),列表組件將會(huì)被銷毀。
2.當(dāng) keepAlive 標(biāo)記為 true 時(shí),當(dāng)列表項(xiàng)滑出加載區(qū)域后,Viewport 會(huì)將列表組件緩存起來;當(dāng)列表項(xiàng)進(jìn)入加載區(qū)域時(shí),Viewport 從先從緩存中查找是否已經(jīng)緩存,如果有則直接復(fù)用,如果沒有則重新創(chuàng)建列表項(xiàng)。
那么 AutomaticKeepAlive 什么時(shí)候會(huì)將列表項(xiàng)的 keepAlive 標(biāo)記為 true 或 false 呢?答案是開發(fā)者說了算!Flutter 中實(shí)現(xiàn)了一套類似 C/S 的機(jī)制,AutomaticKeepAlive 就類似一個(gè) Server,它的子組件可以是 Client,這樣子組件想改變是否需要緩存的狀態(tài)時(shí)就向 AutomaticKeepAlive 發(fā)一個(gè)通知消息(KeepAliveNotification),AutomaticKeepAlive 收到消息后會(huì)去更改 keepAlive 的狀態(tài),如果有必要同時(shí)做一些資源清理的工作(比如 keepAlive 從 true 變?yōu)?false 時(shí),要釋放緩存)。
我們基于上一節(jié) PageView 示例,實(shí)現(xiàn)頁面緩存,根據(jù)上面的描述實(shí)現(xiàn)思路就很簡單了:讓Page 頁變成一個(gè) AutomaticKeepAlive Client 即可。為了便于開發(fā)者實(shí)現(xiàn),F(xiàn)lutter 提供了一個(gè) AutomaticKeepAliveClientMixin ,我們只需要讓 PageState 混入這個(gè) mixin,且同時(shí)添加一些必要操作即可:
class _PageState extends State<Page> with AutomaticKeepAliveClientMixin {
?
? @override
? Widget build(BuildContext context) {
? ? super.build(context); // 必須調(diào)用
? ? return Center(child: Text("${widget.text}", textScaleFactor: 5));
? }
?
? @override
? bool get wantKeepAlive => true; // 是否需要緩存
}
代碼很簡單,我們只需要提供一個(gè) wantKeepAlive,它會(huì)表示 AutomaticKeepAlive 是否需要緩存當(dāng)前列表項(xiàng);另外我們必須在 build 方法中調(diào)用一下 super.build(context),該方法實(shí)現(xiàn)在 AutomaticKeepAliveClientMixin 中,功能就是根據(jù)當(dāng)前 wantKeepAlive 的值給 AutomaticKeepAlive 發(fā)送消息,AutomaticKeepAlive 收到消息后就會(huì)開始工作。
現(xiàn)在我們重新運(yùn)行一下示例,發(fā)現(xiàn)每個(gè) Page 頁只會(huì) build 一次,緩存成功了。需要注意,如果我們采用 PageView.custom 構(gòu)建頁面時(shí)沒有給列表項(xiàng)包裝 AutomaticKeepAlive 父組件,則上述方案不能正常工作,因?yàn)榇藭r(shí)Client 發(fā)出消息后,找不到 Server,404 了。
KeepAliveWrapper
雖然我們可以通過 AutomaticKeepAliveClientMixin 快速的實(shí)現(xiàn)頁面緩存功能,但是通過混入的方式實(shí)現(xiàn)不是很優(yōu)雅,因?yàn)楸仨毟?Page 的代碼,有侵入性,這就導(dǎo)致不是很靈活,比如一個(gè)組件能同時(shí)在列表中和列表外使用,為了在列表中緩存它,則我們必須實(shí)現(xiàn)兩份。為了解決這個(gè)問題,筆者封裝了一個(gè) KeepAliveWrapper 組件,如果哪個(gè)列表項(xiàng)需要緩存,只需要使用 KeepAliveWrapper 包裹一下它即可。
@override
Widget build(BuildContext context) {
? var children = <Widget>[];
? for (int i = 0; i < 6; ++i) {
? ? //只需要用 KeepAliveWrapper 包裝一下即可
? ? children.add(KeepAliveWrapper(child:Page( text: '$i'));
? }
? return PageView(children: children);
}
下面是 KeepAliveWrapper 的實(shí)現(xiàn)源碼:
class KeepAliveWrapper extends StatefulWidget {
? const KeepAliveWrapper({
? ? Key? key,
? ? this.keepAlive = true,
? ? required this.child,
? }) : super(key: key);
? final bool keepAlive;
? final Widget child;
?
? @override
? _KeepAliveWrapperState createState() => _KeepAliveWrapperState();
}
?
class _KeepAliveWrapperState extends State<KeepAliveWrapper>
? ? with AutomaticKeepAliveClientMixin {
? @override
? Widget build(BuildContext context) {
? ? super.build(context);
? ? return widget.child;
? }
?
? @override
? void didUpdateWidget(covariant KeepAliveWrapper oldWidget) {
? ? if(oldWidget.keepAlive != widget.keepAlive) {
? ? ? // keepAlive 狀態(tài)需要更新,實(shí)現(xiàn)在 AutomaticKeepAliveClientMixin 中
? ? ? updateKeepAlive();
? ? }
? ? super.didUpdateWidget(oldWidget);
? }
?
? @override
? bool get wantKeepAlive => widget.keepAlive;
}
原文鏈接:https://blog.csdn.net/wywinstonwy/article/details/122550018
相關(guān)推薦
- 2022-06-16 GO語言入門學(xué)習(xí)之基本數(shù)據(jù)類型字符串_Golang
- 2021-12-02 C語言GetStdHandle函數(shù)使用方法_C 語言
- 2022-09-17 docker資源控制管理Cgroup的實(shí)現(xiàn)_docker
- 2023-10-14 uni-app adb安卓wifi無線調(diào)試
- 2022-12-23 利用Python實(shí)現(xiàn)簡易計(jì)算器的示例代碼_python
- 2022-10-02 react中(含hooks)同步獲取state值的方式_React
- 2022-04-18 前端,服務(wù)端渲染引入第三方依賴window全局對(duì)象的插件,window is not defined
- 2022-06-20 Flutter?Navigator路由傳參的實(shí)現(xiàn)_Android
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支