最近公司做技術分享寫的文章的demo

創(chuàng)新互聯(lián)公司堅持“要么做到,要么別承諾”的工作理念,服務領域包括:成都網(wǎng)站設計、做網(wǎng)站、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣等服務,滿足客戶于互聯(lián)網(wǎng)時代的準格爾網(wǎng)站設計、移動媒體設計的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡建設合作伙伴!
Flutter中的InheritedWidget狀態(tài)管理
1.InheritedWidget是什么?
InheritedWidget是Flutter中非常重要的一個功能型組件,它提供了一種數(shù)據(jù)在widget樹中從上到下傳遞、共享的方式,比如我們在應用的根widget中通過InheritedWidget共享了一個數(shù)據(jù),那么我們便可以在任意子widget中來獲取該共享的數(shù)據(jù)!這個特性在一些需要在widget樹中共享數(shù)據(jù)的場景中非常方便!如Flutter SDK中正是通過InheritedWidget來共享應用主題(Theme)和Locale (當前語言環(huán)境)信息的。
InheritedWidget和React中的context功能類似,和逐級傳遞數(shù)據(jù)相比,它們能實現(xiàn)組件跨級傳遞數(shù)據(jù)。InheritedWidget的在widget樹中數(shù)據(jù)傳遞方向是從上到下的,這和通知Notification的傳遞方向正好相反。
2.源碼分析
InheritedWidget
先來看下InheritedWidget的源碼:
abstract class?InheritedWidget?extends?ProxyWidget { ??const?InheritedWidget({ Key key,?Widget child }):?super(key: key,?child: child);??@override??InheritedElement?createElement() =InheritedElement(this);??@protected??bool?updateShouldNotify(covariant?InheritedWidget oldWidget);}
它繼承自ProxyWidget:
abstract class?ProxyWidget?extends?Widget { ??const?ProxyWidget({ Key key,?@required?this.child?}) :?super(key: key);??final?Widget?child;}
可以看出Widget內(nèi)除了實現(xiàn)了createElement方法外沒有其他操作了,它的實現(xiàn)關鍵一定就是InheritedElement了。
InheritedElement 來看下InheritedElement源碼
class?InheritedElement?extends?ProxyElement { ??InheritedElement(InheritedWidget widget) :?super(widget);??@override??InheritedWidget?get?widget?=?super.widget;??// 這個Set記錄了所有依賴的Elementfinal?MapElement,?Object?_dependents?=?HashMapElement,?Object();
//該方法會在Element mount和activate方法中調(diào)用,_inheritedWidgets為基類Element中的成員,用于提高Widget查找父節(jié)點中的InheritedWidget的效率,它使用HashMap緩存了該節(jié)點的父節(jié)點中所有相關的InheritedElement,因此查找的時間復雜度為o(1) ??@override??void?_updateInheritance() {final?MapType,?InheritedElement incomingWidgets =?_parent?._inheritedWidgets;if?(incomingWidgets !=?null)??????_inheritedWidgets?=?HashMapType,?InheritedElement.from(incomingWidgets);????else??????_inheritedWidgets?=?HashMapType,?InheritedElement();????_inheritedWidgets[widget.runtimeType] =?this;??}
//該方法在父類ProxyElement中調(diào)用,看名字就知道是通知依賴方該進行更新了,這里首先會調(diào)用重寫的updateShouldNotify方法是否需要進行更新,然后遍歷_dependents列表并調(diào)用didChangeDependencies方法,該方法內(nèi)會調(diào)用mardNeedsBuild,于是在下一幀繪制流程中,對應的Widget就會進行rebuild,界面也就進行了更新 ??@override??void?notifyClients(InheritedWidget oldWidget) {????assert(_debugCheckOwnerBuildTargetExists('notifyClients'));for?(Element dependent?in?_dependents.keys) {??????notifyDependent(oldWidget,?dependent);????}??}
其中_updateInheritance方法在基類Element中的實現(xiàn)如下:
void _updateInheritance() {
_inheritedWidgets = _parent?._inheritedWidgets;
}
總結(jié)來說就是Element在mount的過程中,如果不是InheritedElement,就簡單的將緩存指向父節(jié)點的緩存,如果是InheritedElement,就創(chuàng)建一個緩存的副本,然后將自身添加到該副本中,這樣做會有兩個值得注意的點:
InheritedElement的父節(jié)點們是無法查找到自己的,即InheritedWidget的數(shù)據(jù)只能由父節(jié)點向子節(jié)點傳遞,反之不能。
如果某節(jié)點的父節(jié)點有不止一個同一類型的InheritedWidget,調(diào)用inheritFromWidgetOfExactType獲取到的是離自身最近的該類型的InheritedWidget。
看到這里似乎還有一個問題沒有解決,依賴它的Widget是在何時被添加到_dependents這個列表中的呢?
回憶一下從InheritedWidget中取數(shù)據(jù)的過程,對于InheritedWidget有一個通用的約定就是添加static的of方法,該方法中通過inheritFromWidgetOfExactType找到parent中對應類型的的InheritedWidget的實例并返回,與此同時,也將自己注冊到了依賴列表中,該方法的實現(xiàn)位于Element類,實現(xiàn)如下:
@overrideT?dependOnInheritedWidgetOfExactType
// 這里通過上述mount過程中建立的HashMap緩存找到對應類型的InheritedElement final?InheritedElement ancestor =?_inheritedWidgets?==?null???null?:?_inheritedWidgets[T];if?(ancestor !=?null) {????assert(ancestor?is?InheritedElement);return?dependOnInheritedElement(ancestor,?aspect: aspect);??}??_hadUnsatisfiedDependencies?=?true;??return null;}
@overrideInheritedWidget?dependOnInheritedElement(InheritedElement ancestor,?{ Object aspect }) {??assert(ancestor !=?null);
// 這個列表記錄了當前Element依賴的所有InheritedElement,用于在當前Element deactivate時,將自己從InheritedElement的_dependents列表中移除,避免不必要的更新操作 ??_dependencies???=?HashSetInheritedElement();??_dependencies.add(ancestor);??ancestor.updateDependencies(this,?aspect);return?ancestor.widget;}
3.如何使用InheritedWidget
1)、創(chuàng)建一個類繼承自Inheritedwidget
class?InheritedContext?extends?InheritedWidget{??final?InheritedTestModel?inheritedTestModel;??InheritedContext({????Key key,????@required?this.inheritedTestModel,????@required?Widget child}):?super(key: key,?child: child);static?InheritedContext? of (BuildContext context) {????return?context.dependOnInheritedWidgetOfExactTypeInheritedContext();??}??@override??bool?updateShouldNotify(InheritedContext oldWidget) {????return?inheritedTestModel?!= oldWidget.inheritedTestModel;??}}
2)、InheritedTestModel類為數(shù)據(jù)容器(這里定義了一個Listint數(shù)據(jù)源)
class?InheritedTestModel{?final?List?_list;??InheritedTestModel(this._list);??List?getList(){????return?_list;??}}
class?ArrayListData{??static?List? _list ;static?List? getListData (){???? _list? =?new?List();???? _list .add(1);???? _list .add(2);???? _list .add(3);???? _list .add(4);return? _list ;??}}
3)、定義一個Widget?使用?InheritedContext類的數(shù)據(jù)?InheritedTestModel?
class?ListDemo?extends?StatefulWidget{??@override??State?createState() {????return new?ListDemoState();??}}class?ListDemoState?extends?StateListDemo{List?_list;??InheritedTestModel?_inheritedTestModel;??Timer?_timer;??Duration?oneSec?=?const?Duration(seconds:?1);??@override??void?initState() {????_list?= ArrayListData. getListData ();????_inheritedTestModel?=?new?InheritedTestModel(_list);????_timer?=?Timer.periodic(oneSec,?(timer) {??????_doTimer();????});??}??void?_doTimer() {????for(int i =?0;?i ?_list.length;?i++){??????_list[i] =?_list[i]+?1;????}??setState(() {????_inheritedTestModel?=?new?InheritedTestModel(_list);??});??}Widget?_buildBody() {????return?Container(child:?ListDemo2(),????);??}??@override??Widget?build(BuildContext context) {????return?InheritedContext(inheritedTestModel:?_inheritedTestModel,??????child:?Scaffold(appBar:?AppBar(title:?Text("ListDemo"),????????actions: Widget[????????????IconButton(icon:?Icon(Icons. add ),????????????)????????],),????????body: _buildBody(),??????),????);??}??@override??void?dispose() {????super.dispose();if?(_timer?!=?null) {??????_timer.cancel();????}??}}
4)、在ListDemo中通過Timer更新InheritedTestModel?中的數(shù)據(jù),然后再下一個Widget中獲取更新的數(shù)據(jù)作為展示
class?ListDemo2?extends?StatefulWidget{??@override??State?createState() {????return new?ListDemoState2();??}}class?ListDemoState2?extends?StateListDemo2{InheritedTestModel?_inheritedTestModel;??Widget?_buildListItem(BuildContext context,int index) {????return ?Container(height:?50,????????width:?100,????????alignment: Alignment. center ,????????child:?Text(_inheritedTestModel.getList()[index].toString()),????);??}Widget?_buildBody() {????_inheritedTestModel?= InheritedContext. of (context).inheritedTestModel;return?Container(child:?ListView.builder(itemBuilder:(context,?index)=_buildListItem(context,index),itemCount:?_inheritedTestModel.getList().length,),????);??}??@override??Widget?build(BuildContext context) {????return ?_buildBody();??}}
這樣就可以在父widget中更新數(shù)據(jù),子View不需任何操作直接從數(shù)據(jù)容器InheritedTestModel?中獲取到更新后的新數(shù)據(jù)
這是一個數(shù)據(jù)共享的簡單的例子,基本的流程,大致就是A去更新B的數(shù)據(jù),A和B有一個共同的父類,實現(xiàn)數(shù)據(jù)的共享
4.上面說了原理和基本的使用,但是在實際項目當中,我當然不建議這樣來使用,Google?已經(jīng)為我們封裝好了功能更加強大的插件Provider,其內(nèi)部原理就是基于InheritedWidget來實現(xiàn)的,我們理解了基本原理,可以更好的在項目中運用Provider
Image(圖片組件)是顯示圖像的組件,一個顯示圖片的widget,支持圖像格式:JPEG,PNG,GIF,動畫GIF,WebP,動畫WebP,BMP和WBM
構(gòu)造方法
Image: 從ImageProvider獲取數(shù)據(jù)
Image.network: 加載網(wǎng)絡圖片。
Image.asset: 加載本地圖片文件。
new Image.file: 加載本地圖片文件(File文件)圖片。
new Image.memory: 加載Uint8List資源圖片(byte數(shù)組)圖片。
常用屬性
方式一:CircleAvatar
CircleAvatar可以實現(xiàn)圓形頭像,也可以添加一個子Widget:
在圖片上加一個文本
方式二:ClipOval
ClipOval也可以實現(xiàn)圓角頭像,而且通常是在只有頭像時使用
方式三:Container+BoxDecoration
方法一:ClipRRect
方法二:Container + BoxDecoration
補充知識點
Icon字體圖標和圖片圖標的區(qū)別 ?
Colors.red 是一個MaterialColor對象,為什么可以使用[](Colors.red[10])來設置顏色 ?
MaterialColor 繼承于ColorSwatch,ColorSwatch中有[] 運算符重載;
在Tree中從上往下高效傳遞數(shù)據(jù)的基類widget , 定義為:abstract class InheritedWidget extends ProxyWidget
Flutter的響應式開發(fā)與React類似,數(shù)據(jù)都是自頂向下的。
假設有祖先組點A,中間經(jīng)過結(jié)點B, C,然后到結(jié)點D,D需要從A中獲取數(shù)據(jù)f,那按照自頂向下數(shù)據(jù)流轉(zhuǎn),f需要依次傳遞給B及C,最后才到C。這樣開發(fā)極為不靈活,成本也比較高。所有Flutter需要有跨結(jié)點(只能是祖先后代節(jié)點,不能跨兄弟節(jié)點)高效傳遞數(shù)據(jù)的方案。
大體意思如下:
InheritedWidget 是在樹中高效向下傳遞信息的基類部件;
調(diào)用[BuildContext.inheritFromWidgetOfExactType]方法可以從 BuildContext 中獲取到最近的 InheritedWidget 類型的實例;
在 InheritedWidget 類型的控件被引用,也就是調(diào)用過 inheritFromWidgetOfExactType 方法后,當 InheritedWidget 自身狀態(tài)改變時,會導致引用了 InheritedWidget 類型的子控件重構(gòu)(rebuild)。
這里隨便定義一個人 Person 類。
創(chuàng)建一個類繼承 InheritedWidget,并實現(xiàn) updateShouldNotify 方法。
之前說到調(diào)用[BuildContext.inheritFromWidgetOfExactType]方法可以從 BuildContext 中獲取到最近的 InheritedWidget 類型的實例,所以此處定義一個靜態(tài)的 of 方法,通過傳入的 context 獲取到最近的 InheriedDataWidget 實例。
1.定義數(shù)據(jù)模型
這里隨便定義一個 Person 類。
2.自定義 InheritedWidget 控件類
創(chuàng)建一個類繼承 InheritedWidget,并實現(xiàn) updateShouldNotify 方法。
之前說到調(diào)用[BuildContext.inheritFromWidgetOfExactType]方法可以從 BuildContext 中獲取到最近的 InheritedWidget 類型的實例,所以此處定義一個靜態(tài)的 of 方法,通過傳入的 context 獲取到最近的 InheriedDataWidget 實例。
3.InheriedDataWidget 的使用
InheriedDataWidget 使用起來也很簡單,它本身也是一個控件,只要在任意一個頁面的子控件調(diào)用其構(gòu)造方法就行,這里我們定義一個形如的 Widget 樹。
WidgetA 是一個 StatefulWidget 類型的控件,可以調(diào)用 setState 刷新,如果是繼承人 Stateless 類型的控件,那我們也可以通過 Stream 或者其他方式刷新數(shù)據(jù),感興趣的請看[什么是 Stream? Dart
WidgetA1_1 類
WidgetA1_2 類
WidgetA1_3 類
當我們點擊 floatingActionButton 的時候,WidgetA1, WidgetA1_1, WidgetA1_2 的控件都會更新 Person 的信息,而且每點 floatingActionButton 一次, 當我們點擊 floatingActionButton 的時候,WidgetA1, WidgetA1_1, WidgetA1_2 的控件都會更新 Person 的信息,而且每點 floatingActionButton 一次,都會輸出:
如果我們試圖在和 WidgetA 的同一層級的兄弟節(jié)點去訪問 InheriedDataWidget 的 Person 數(shù)據(jù),是不行的,因為父節(jié)點中并沒有插入 InheriedDataWidget。
把 WidgetB 和 WidgetA 保持同一節(jié)點
這也體現(xiàn)了 Inheried(遺傳) 這一單詞的特性,遺傳只存在于父子。兄弟不存在遺傳的關系。
這種數(shù)據(jù)共享的方式在某些場景還是很有用的,就比如說全局主題,字體大小,字體顏色的變更,只要在 App 根層級共享出這些配置數(shù)據(jù),然后在觸發(fā)數(shù)據(jù)改變之后,所有引用到這些共享數(shù)據(jù)的地方都會刷新,這換主題,字體是不是就很輕松,事實上 Theme.of(context).primaryColor 之流就是這么干的。
以上就是有關InheritedWidget的使用。
自己也是從事Android開發(fā)5年有余了;整理了一些Android開發(fā)技術核心筆記和面經(jīng)題綱,有關更多Android開發(fā)進階技術資料、面經(jīng)題綱、核心技術筆記; 想要進階自己、拿高薪的同學請私信我回復“核心筆記”或“面試”領取!
Dart的 IO 庫包含了文件讀寫的相關類,它屬于 Dart 語法標準的一部分,所以通過 Dart IO 庫,無論是 Dart VM 下的腳本還是 Flutter,都是通過 Dart IO 庫來操作文件的,不過和 Dart VM 相比,F(xiàn)lutter 有一個重要差異是文件系統(tǒng)路徑不同,這是因為Dart VM 是運行在 PC 或服務器操作系統(tǒng)下,而 Flutter 是運行在移動操作系統(tǒng)中,他們的文件系統(tǒng)會有一些差異。
Android 和 iOS 的應用存儲目錄不同, PathProvider 插件提供了一種平臺透明的方式來訪問設備文件系統(tǒng)上的常用位置。該類當前支持訪問兩個文件系統(tǒng)位置:
File代表一個整體的文件,他有三個構(gòu)造函數(shù),分別是:
文件讀取本身有兩種形式,一種是文本,一種是二進制。
2.2.1 讀取文本內(nèi)容
如果是文本文件,F(xiàn)ile提供了readAsString、readAsLines、readAsStringSync、readAsLinesSync方法,讀取文本內(nèi)容
readAsString 一次性讀取所有文本
readAsLines 一行行的讀取文本
結(jié)果返回的是一個List,list中表示文件每行的內(nèi)容
readAsStringSync、readAsLinesSync同步讀取文本
2.2.2 讀取二進制內(nèi)容
如果文件是二進制,那么可以使用readAsBytes或者同步的方法readAsBytesSync:
dart中表示二進制有一個專門的類型叫做Uint8List,他實際上表示的是一個int的List。
上面提到的讀取方式,都是一次性讀取整個文件,缺點就是如果文件太大的話,可能造成內(nèi)存空間的壓力。
所以File為我們提供了另外一種讀取文件的方法,流的形式來讀取文件.
示例
dart提供了open和openSync兩個方法來進行隨機文件讀寫:
寫入和文件讀取一樣,可以一次性寫入或者獲得一個寫入句柄,然后再寫入。
一次性寫入的方法有四種,分別對應字符串和二進制
句柄形式可以調(diào)用openWrite方法,返回一個IOSink對象,然后通過這個對象進行寫入:
默認情況下寫入是會覆蓋整個文件的,但是可以通過下面的方式來更改寫入模式:
雖然dart中所有的異常都是運行時異常,但是和java一樣,要想手動處理文件讀寫中的異常,則可以使用try,catch:
我們還是以計數(shù)器為例,實現(xiàn)在應用退出重啟后可以恢復點擊次數(shù)。 這里,我們使用文件來保存數(shù)據(jù):
1.引入PathProvider插件;在pubspec.yaml文件中添加如下聲明:
執(zhí)行 flutter pub get
2.實現(xiàn)如下
參考:
provider 是flutter 中的狀態(tài)管理 開源庫;
存儲的數(shù)據(jù)對象 必須extends ChangeNotifier;下層widget 通過 Provider.of(context) 函數(shù) 獲取model對象 ,并且可以建立依賴關系;當數(shù)據(jù)對象發(fā)生變化時,依賴的widget 會重新build,像不像InheritedWidget Provider 沒錯 下層widget就是 封裝了InheritedWidget
主要 通過 Provider.ofT(context) 函數(shù),來獲取;
推薦使用 Provider.of而不是 Consumer,因為 listen默認為true,也就是說 默認 依賴于 持有數(shù)據(jù)model的widget 對應的element;
數(shù)據(jù)類 可繼承的 ChangeNotifier,本身和privider框架 沒有關系;
ChangeNotifier 是 flutter框架 提供的工具類, 用來實現(xiàn)一對多的訂閱通知功能。
當前題目:flutter獲取數(shù)據(jù),flutter 傳值
新聞來源:http://chinadenli.net/article39/dsgsosh.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供微信公眾號、網(wǎng)站制作、網(wǎng)站內(nèi)鏈、營銷型網(wǎng)站建設、面包屑導航、商城網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)