問題描述:

發展壯大離不開廣大客戶長期以來的信賴與支持,我們將始終秉承“誠信為本、服務至上”的服務理念,堅持“二合一”的優良服務模式,真誠服務每家企業,認真做好每個細節,不斷完善自我,成就企業,實現共贏。行業涉及房屋鑒定等,在網站建設公司、網絡營銷推廣、WAP手機網站、VI設計、軟件開發等項目上具有豐富的設計經驗。
在Flutter開發的過程中,當我們獲取到新的數據或者數據發生變化,需要去執行setState進行頁面刷新的時候,經常會出現不必要的子節點Widget也進行了build,但實際上我們是不想讓它再次build,出現這些問題的典型情況是在使用FutureBuilder的時候,例如:
在上面這個示例中,如果再次調用Build方法,則會觸發httpCall()的方法。
那么怎樣才能避免不必要的部件構建呢?
分析:
在Flutter中,Build方法的設計方式是pure/without side effects,書面意思是無副作用的/純粹的,簡單點理解我們可以將其含義看作不會對外部的方法或者變量產生影響的。這是因為許多外部因素能夠觸發新的小部件的構建,例如這些情況:
但是,這也意味著Build方法可以不去觸發httpCall()的方法或者不修改任何狀態。
解決
回歸問題,當前我們面臨的問題是Build方法造成了副作用,也就是造成了無關的Build調用麻煩。
所以,只要我們使Build方法保持純粹/無副作用,這樣就算多少次調用它,也不會對其他Widget的Build方法產生影響。
在上面的示例中,我們將Widget轉換為StatefulWidget,然后提取httpCall()到initState中,這樣問題就解決了
另外,還可以使一個Widget能夠在不強迫其子部件也構建的情況下進行重新構建。
在Widget的實例保持不變時;Flutter會有意識的不去重建子部件。這意味著我們可以緩存Widget樹的某些部分,以防止不必要的重新構建。
最簡單的方法是使用const修飾構造函數:
由于const的修飾,即使調用了數百次build,DecoratedBox的實例也將保持不變。
或者你可以這樣使用以達到相同的結果:
在這個例子中,當StreamBuilder收到新值的通知時,即使StreamBuilder的Column進行了重構,subtree也不會進行重構。這是因為由于閉包,MyWidget的實例沒有改變。
這種模式在動畫中經常使用。典型的是使用AnimatedBuilder和所有的*Transition時,例如AlignTransition。
我們還可以將subtree存儲到類的一個字段中,但是并不推薦你這樣做,因為它會破壞Flutter的熱重載。
狀態可變的 widget 。
通過其類的定義能夠看到 StatefulWidget 配置 StatefulElement 。
State 是 StatefulWidget 的內部邏輯與狀態,由 StatefulWidget 的 createState 創建。
StatefulWidget 實例本身是不可變的, 但是 StatefulWidget 將其可變的狀態,存儲在與之關聯的 State 對象中。
不管什么時候,只要在樹中 mount 一個新的 StatefulElement ,必然需要注入一個 StatefulWidget ,注入一個 StatefulWidget 時, framework 都會調用一次 createState 方法。
其實,在 StatefulElement 構造的時候,就會調用 createState ,創建 _state 對象,( _state 是 StatefulElement 的變量)并且在 StatefulElement 的初始化方法中為 _state 關聯當前的 StatefulElement 和用以配置 StatefulElement 的 StatefulWidget 。
StatefulElement 初始化方法如下:
這意味著如果 StatefulWidget 被插入到樹中的多個位置,則會有多個 State 對象分別與它們關聯。
關于此類的定義如下:
描述: 重寫此方法以執行初始化。
場景: 如果 State 的 build 方法依賴于本身可以改變狀態的對象時。(例如 ChangeNotifier 或 Stream ,或者可以訂閱并接收通知的其他對象)正確的方式是:
注意點: 此方法中不能使用 BuildContext.dependOnInheritedWidgetOfExactType 。但是此方法被調用后會立即調用 didChangeDependencies ,在 didChangeDependencies 可以使用 BuildContext.dependOnInheritedWidgetOfExactType 。
調用時機: StatefulElement ,首次插入樹中時會調用此方法,在 build 方法調用之前調用。
描述: StatefulElement 通過此方法返回的 widget 并通過調用 updateChild 來更新自己。
調用時機: framework 調用此方法的幾個不同的場景如下:
描述: StatefulElement 存在,并且符合 Widget.canUpdate 的情況下對 StatefulWidget 進行更新。
調用時機: 不論何時只要 StatefulElement 的配置 widget 改變的時候就會調用。
注意: didUpdateWidget 方法最終會調用 build 方法,因此在此方法中調用 setState 是多余的。如果重寫此方法,請確保調用 super.didUpdateWidget(oldWidget) 。
調用時機: 當此 State 對象的依賴項( InheritedWidget )更改時調用。
描述: 用于開發階段 hot reload 。
調用時機: hot reload 時調用,調用后 build 方法也將被調用。無需在此方法中做任何操作。
調用時機: 當 StatefulElement 從樹中移除的時候會調用。
調用時機: 當 StatefulElement 從樹中 unmount 的時候會調用。
StatefulWidget 用以配置 StatefulElement ,但在這兩者之間的 State 承接了 StatefulElement 的生命周期,而 StatefulWidget 僅僅只是連接了 State 與 StatefulElement 的不可變的實例,因此 StatefulWidget 的生命周期,依賴于 StatefulElement ,而 State 卻是其最簡單直接的體現形式。
為了能更好的理解 StatefulWidget 的生命周期,我畫了一張關于 State 、 StatefulElement 、 Component 、 Element 的關系圖。
Flutter的數據存儲分為三類
Preference相當于iOS的NSUserDefaults,其實也是按plist的方式存儲的
step1:添加依賴
step2:pub get
step3:導入頭文件
在path_provider中有三個獲取文件路徑的方法:
- getTemporaryDirectory()
://獲取應用緩存目錄,等同iOS的NSTemporaryDirectory()和Android的getCacheDir() 方法。
- getApplicationDocumentsDirectory():
//獲取應用文件目錄類似于iOS的NSDocumentDirectory和Android上的 AppData目錄。
step1:添加依賴
step2:pub get
step3:導入頭文件
Dart的 IO 庫包含了文件讀寫的相關類,它屬于 Dart 語法標準的一部分,所以通過 Dart IO 庫,無論是 Dart VM 下的腳本還是 Flutter,都是通過 Dart IO 庫來操作文件的,不過和 Dart VM 相比,Flutter 有一個重要差異是文件系統路徑不同,這是因為Dart VM 是運行在 PC 或服務器操作系統下,而 Flutter 是運行在移動操作系統中,他們的文件系統會有一些差異。
Android 和 iOS 的應用存儲目錄不同, PathProvider 插件提供了一種平臺透明的方式來訪問設備文件系統上的常用位置。該類當前支持訪問兩個文件系統位置:
File代表一個整體的文件,他有三個構造函數,分別是:
文件讀取本身有兩種形式,一種是文本,一種是二進制。
2.2.1 讀取文本內容
如果是文本文件,File提供了readAsString、readAsLines、readAsStringSync、readAsLinesSync方法,讀取文本內容
readAsString 一次性讀取所有文本
readAsLines 一行行的讀取文本
結果返回的是一個List,list中表示文件每行的內容
readAsStringSync、readAsLinesSync同步讀取文本
2.2.2 讀取二進制內容
如果文件是二進制,那么可以使用readAsBytes或者同步的方法readAsBytesSync:
dart中表示二進制有一個專門的類型叫做Uint8List,他實際上表示的是一個int的List。
上面提到的讀取方式,都是一次性讀取整個文件,缺點就是如果文件太大的話,可能造成內存空間的壓力。
所以File為我們提供了另外一種讀取文件的方法,流的形式來讀取文件.
示例
dart提供了open和openSync兩個方法來進行隨機文件讀寫:
寫入和文件讀取一樣,可以一次性寫入或者獲得一個寫入句柄,然后再寫入。
一次性寫入的方法有四種,分別對應字符串和二進制
句柄形式可以調用openWrite方法,返回一個IOSink對象,然后通過這個對象進行寫入:
默認情況下寫入是會覆蓋整個文件的,但是可以通過下面的方式來更改寫入模式:
雖然dart中所有的異常都是運行時異常,但是和java一樣,要想手動處理文件讀寫中的異常,則可以使用try,catch:
我們還是以計數器為例,實現在應用退出重啟后可以恢復點擊次數。 這里,我們使用文件來保存數據:
1.引入PathProvider插件;在pubspec.yaml文件中添加如下聲明:
執行 flutter pub get
2.實現如下
參考:
有時候我們不希望某個頁面每次打開時都重新加載,比如就我們之前的Tabbar結構的頁面,每當我們在切換Tab的時候都會執行 void initState() ,這就意味著頁面每次都會重新渲染,之所以這樣就是因為我們的 State 狀態沒有保存,如下圖所示:
[沒有狀態保存效果圖]
給當前 State 類添加一個擴展(這里就用擴展這個詞吧,其實類似于iOS下的 Category ),一個系統的擴展類 AutomaticKeepAliveClientMixin ,并重寫 wantKeepAlive 方法,讓一個普通的 State 類,具有保存狀態的能力。
在Dart語法中通過使用 with 關鍵字來添加擴展:
bool get wantKeepAlive = true; 之后,當前 State 就具備保存能力了,也就意味著重復切換Tab后, void initState() 就不會重復執行了(由原來的 viewWillAppear() 變成了 viewDidLoad() )。
按照上面方式修改后,發現切換Tab后 void initState() 依然重復執行了,這是為什么吶?這里我們看下我們之前 root_page.dart 里面是如何配置我們的tabbar結構的:
這里我們是通過一個 _viewControllers 的List,把4個子頁面放在了里面,全局有一個 _currentIndex ,當 onTap 回調后后,更新 _currentIndex 的值,執行 setState () 后, body 對應的 widget 頁面發生改變。而問題也就出在這里,當 body 部分發生改變時,根據Flutter的底層渲染邏輯,這里會移除掉之前的 Widget ,并重新創建新的 Widget ,我們之前在 _viewControllers 放的子頁面,并不像iOS下是一個實例對象,存在就直接拿來使用。在Flutter 中 setState () 后界面會被重新繪制,而 body 部分只知道我要渲染一個什么樣的 widget ,而該類型的 widget 每次都是會重新創建,這也就意味著我們在Tab切換時,每次都是重新創建,所以每次都執行了 initState() 。
顯然我們現在的方式是不合理的,那在Flutter中如何管理這樣的子頁面,而避免重復渲染吶?
這就要用到一個新的部件了: PageView() ,內部的2個關鍵屬性:
子頁面切換通過 _controller.jumpToPage(index); 來實現。
這樣子頁面也就不會重新創建渲染了,我們的狀態保存也就能正常實現了。
學習是一個循序漸進的過程,我們總是在踩坑中不斷的前行,把坑填平了也就意味著我們在這個新的東西面前立了足,就可能進行更多為什么的探索了。
provider 是flutter 中的狀態管理 開源庫;
存儲的數據對象 必須extends ChangeNotifier;下層widget 通過 Provider.of(context) 函數 獲取model對象 ,并且可以建立依賴關系;當數據對象發生變化時,依賴的widget 會重新build,像不像InheritedWidget Provider 沒錯 下層widget就是 封裝了InheritedWidget
主要 通過 Provider.ofT(context) 函數,來獲取;
推薦使用 Provider.of而不是 Consumer,因為 listen默認為true,也就是說 默認 依賴于 持有數據model的widget 對應的element;
數據類 可繼承的 ChangeNotifier,本身和privider框架 沒有關系;
ChangeNotifier 是 flutter框架 提供的工具類, 用來實現一對多的訂閱通知功能。
文章名稱:javascript入門教程,JavaScript快速入門
網頁路徑:http://www.yijiale78.com/article16/dsiojdg.html
成都網站建設公司_創新互聯,為您提供商城網站、、動態網站、小程序開發、Google、云服務器
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯