setState會(huì)刷新整個(gè)頁(yè)面,在只需要刷新部分組件的時(shí)候可以用StatefulBuilder
成都創(chuàng)新互聯(lián)公司專注骨干網(wǎng)絡(luò)服務(wù)器租用十年,服務(wù)更有保障!服務(wù)器租用,遂寧托管服務(wù)器 成都服務(wù)器租用,成都服務(wù)器托管,骨干網(wǎng)絡(luò)帶寬,享受低延遲,高速訪問(wèn)。靈活、實(shí)現(xiàn)低成本的共享或公網(wǎng)數(shù)據(jù)中心高速帶寬的專屬高性能服務(wù)器。
先給需要局部刷新的組件創(chuàng)建一個(gè)StateSetter
需要刷新的組件用StatefulBuilder包裹,綁定_stateSetter
然后在需要刷新的時(shí)候調(diào)用
實(shí)例中有3個(gè)Tab標(biāo)簽Home, School,Self;在School頁(yè)面添加功能跳轉(zhuǎn)到Home.
需要注意的地方:
沒(méi)有的話再次設(shè)置selectedIndex不會(huì)刷新UI,是一個(gè)比較特殊的地方.
Flutter中有兩個(gè)常用的狀態(tài)Widget分為StatefulWidget和StatelessWidget,分別為動(dòng)態(tài)視圖和靜態(tài)視圖,視圖的更新需要調(diào)用StatefulWidget的setState方法,這會(huì)遍歷調(diào)用子Widget的build方法。如果一個(gè)頁(yè)面內(nèi)容比較復(fù)雜時(shí),會(huì)包含多個(gè)widget,如果直接調(diào)用setState,會(huì)遍歷所有子Widget的build,這樣會(huì)造成很多不必要的開(kāi)銷(xiāo),所以非常有必要了解Flutter中局部刷新的方式:
globalkey唯一定義了某個(gè)element,它使你能夠訪問(wèn)與element相關(guān)聯(lián)的其他對(duì)象,例如buildContext、state等。應(yīng)用場(chǎng)景:跨widget訪問(wèn)狀態(tài)。
例如:可以通過(guò)key.currentState拿到它的狀態(tài)對(duì)象,然后就可以調(diào)用其中的onPressed方法。
Flutter框架內(nèi)部提供了一個(gè)非常小巧精致的組件,專門(mén)用于局部組件的刷新。適用于值改動(dòng)的刷新。
實(shí)現(xiàn)原理:在 initState 中對(duì)傳入的可監(jiān)聽(tīng)對(duì)象進(jìn)行監(jiān)聽(tīng),執(zhí)行 _valueChanged 方法,_valueChanged 中進(jìn)行了 setState 來(lái)觸發(fā)當(dāng)前狀態(tài)的刷新。觸發(fā) build 方法,從而觸發(fā) widget.builder 回調(diào),這樣就實(shí)現(xiàn)了局部刷新。可以看到這里回調(diào)的 child 是組件傳入的 child,所以直接使用,這就是對(duì) child 的優(yōu)化的根源。
可以看到 ValueListenableBuilder 實(shí)現(xiàn)局部刷新的本質(zhì),也是進(jìn)行組件的抽離,讓組件狀態(tài)的改變框定在狀態(tài)內(nèi)部,并通過(guò) builder 回調(diào)控制局部刷新,暴露給用戶使用。
通過(guò)這個(gè)可以創(chuàng)建一個(gè)支持局部刷新的widget樹(shù),比如你可以在StatelessWidget里面刷新某個(gè)布局,但是不需要改變成StatefulWidget;也可以在StatefulWidget中使用做部分刷新而不需要刷新整個(gè)頁(yè)面,這個(gè)刷新是不會(huì)調(diào)用Widget build(BuildContext context)刷新整個(gè)布局樹(shù)的。
異步UI更新:
很多時(shí)候我們會(huì)依賴一些異步數(shù)據(jù)來(lái)動(dòng)態(tài)更新UI,比如在打開(kāi)一個(gè)頁(yè)面時(shí)我們需要先從互聯(lián)網(wǎng)上獲取數(shù)據(jù),在獲取數(shù)據(jù)的過(guò)程中顯示一個(gè)加載框,等獲取到數(shù)據(jù)時(shí)我們?cè)黉秩卷?yè)面;又比如我們想展示Stream(比如文件流、互聯(lián)網(wǎng)數(shù)據(jù)接收流)的進(jìn)度。當(dāng)然StatefulWidget我們完全可以實(shí)現(xiàn)以上功能。但由于在實(shí)際開(kāi)發(fā)中依賴異步數(shù)據(jù)更新UI的這種場(chǎng)景非常常見(jiàn),并且當(dāng)StatefulWidget中控件樹(shù)較大時(shí),更新一個(gè)屬性導(dǎo)致整個(gè)樹(shù)重建,消耗性能,因此Flutter專門(mén)提供了FutureBuilder和SteamBuilder兩個(gè)組件來(lái)快速實(shí)現(xiàn)這種功能。
通常情況下,子Widget無(wú)法單獨(dú)感知父Widget的變化,當(dāng)父state變化時(shí),通過(guò)其build重建所有子widget;
InheriteddWidget可以避免這種全局創(chuàng)建,實(shí)現(xiàn)局部子Widget更新。InheritedWidget提供了一種在Widget樹(shù)中從上到下傳遞、共享數(shù)據(jù)的方式。Flutter SDK正是通過(guò)InheritedWidget來(lái)共享應(yīng)用主題和Locale等信息。
InheritedWidgetData
TestData
InheritedTest1Page
provider是Google I/O 2019大會(huì)上宣布的現(xiàn)在官方推薦的管理方式,而ChangeNotifierProvider可以說(shuō)是Provider的一種:
yaml文件需要引入provider: ^3.1.0
頂層嵌套ChangeNotifierProvider
創(chuàng)建共享數(shù)據(jù)類DataInfo:
數(shù)據(jù)類需要with ChangeNotifier 以使用 notifyListeners()函數(shù)通知監(jiān)聽(tīng)者更新界面。
使用Provider.of(context)獲取DataInfo
nextPage:
使用Consumer包住需要使用共享數(shù)據(jù)的Widget
RepaintBoundary就是重繪邊界,用于重繪時(shí)獨(dú)立于父視圖。頁(yè)面需要更新的頁(yè)面結(jié)構(gòu)可以用 RepaintBoundary組件嵌套,flutter 會(huì)將包含的組件獨(dú)立出一層"畫(huà)布",去繪制。官方很多組件 外層也包了層 RepaintBoundary 標(biāo)簽。如果你的自定義view比較復(fù)雜,應(yīng)該盡可能的避免重繪。
以上總結(jié)了幾種Flutter的局部刷新的方式,可根據(jù)實(shí)際需要使用不同的方式,最適合的才是最好的。
Flutter中Widget分為StatefulWidget和StatelessWidget,分別為動(dòng)態(tài)視圖和靜態(tài)視圖,視圖的更新需要調(diào)用StatefulWidget的setState方法,這會(huì)遍歷調(diào)用子Widget的build方法。當(dāng)一個(gè)主頁(yè)面比較復(fù)雜時(shí),會(huì)包含多個(gè)widget,如果直接調(diào)用setState,會(huì)遍歷所有子Widget的build,這是非常不必要的性能開(kāi)銷(xiāo),有沒(méi)有單獨(dú)刷新指定Widget的方式呢?這個(gè)時(shí)候就要用到GlobalKey了。
一個(gè)StatefulWidget包含一個(gè)Button,一個(gè)Text,通過(guò)點(diǎn)擊Button調(diào)用主Widget的setState方法,刷新Text,示例如下:
同樣一個(gè)StatefulWidget包含一個(gè)多個(gè)Text和Button,點(diǎn)擊Button我們只需要刷新指定的Text,通過(guò)GlobalKey的方式,實(shí)現(xiàn)如下:
主Widget,包含一個(gè)需要更新的TextWidget和一個(gè)不需要更新的Text
需要單獨(dú)更新的Widget
傳遞事件的Button
這樣點(diǎn)擊Button就只會(huì)更新指定的TextWidget了,效果如下:
這只是一個(gè)簡(jiǎn)單的例子,在實(shí)際開(kāi)發(fā)中為了頁(yè)面刷新的高效率,模塊化封裝非常重要。很多情況下都只需要局部刷新,而不是重構(gòu)整個(gè)視圖。所以Globalkey的運(yùn)用在項(xiàng)目中需要熟練掌握
這種方法最常見(jiàn),但是有些地方引用的話,刷新的成本比較大,刷新的是整個(gè)頁(yè)面,數(shù)據(jù)太多加載太慢的話,會(huì)有閃爍的現(xiàn)象
這種方法類似于iOS中的set方法,通過(guò)設(shè)置某個(gè)屬性的時(shí)候,去刷新某個(gè)控件。在flutter中這種刷新方式,是對(duì)上面setState(){}方法的改進(jìn),根本的方法還是setState(){},只不過(guò)是通過(guò)方法去刷新某個(gè)控件。如下:
首先在pubspec.yaml中添加provider依賴
下面通過(guò)provider來(lái)實(shí)現(xiàn)一個(gè)發(fā)送驗(yàn)證碼的案例。
創(chuàng)建一個(gè)TimerModel文件
頁(yè)面布局如下: