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了。
成都創(chuàng)新互聯(lián)是一家專(zhuān)注于網(wǎng)站設(shè)計(jì)制作、成都網(wǎng)站建設(shè)與策劃設(shè)計(jì),凌源網(wǎng)站建設(shè)哪家好?成都創(chuàng)新互聯(lián)做網(wǎng)站,專(zhuān)注于網(wǎng)站建設(shè)10余年,網(wǎng)設(shè)計(jì)領(lǐng)域的專(zhuān)業(yè)建站公司;建站業(yè)務(wù)涵蓋:凌源等地區(qū)。凌源做網(wǎng)站價(jià)格咨詢(xún):18980820575
一個(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)目中需要熟練掌握
Flutter有兩個(gè)常用的狀態(tài)類(lèi):
標(biāo)記為dirty,執(zhí)行的markNeedsBuild,定義在Element類(lèi)中:
當(dāng)前Element節(jié)點(diǎn)被標(biāo)記為dirty,同時(shí)調(diào)用owner的scheduleBuildFor方法:
將element元素添加到全局的“臟”鏈表里。
BuildOwner用來(lái)管理哪些需要更新的Widget。這個(gè)owner最開(kāi)始被初始化的地方在WidgetsBinding的initInstances方法中,隨后初始化了onBuildScheduled方法,對(duì)應(yīng)執(zhí)行的是_handleBuildScheduled,定義在WidgetsBinding類(lèi)中:
ensureVisualUpdate 方法定義在SchedulerBinding類(lèi)中:
在提交下一幀繪制的時(shí)候會(huì)調(diào)用到scheduleFrame方法,提交給引擎繪制,看看scheduleFrame方法,也定義在SchedulerBinding類(lèi)中:
提交給引擎繪制之后,會(huì)收到onDrawFrame的回調(diào),最終執(zhí)行到_handleDrawFrame方法中,對(duì)應(yīng)的是handleDrawFrame方法,定義在SchedulerBinding類(lèi)中:
在RendererBinding的initInstances方法中添加了一個(gè)回調(diào)到這個(gè)List中,對(duì)應(yīng)的是RenderBinding的drawFrame方法,對(duì)應(yīng)的節(jié)點(diǎn)進(jìn)行繪制渲染操作。
WidgetsBinding中的drawFrame方法:
看看這里的buildScope方法,定義在BuildOwner方法中。在上面 scheduleBuildFor 方法介紹中有提到:"scheduleBuildFor 是把一個(gè) element 添加到 _dirtyElements 鏈表,以便當(dāng)[WidgetsBinding.drawFrame]中調(diào)用 buildScope 的時(shí)候能夠重構(gòu) element。onBuildScheduled()是一個(gè) BuildOwner 的回調(diào)"。在 drawFrame 中調(diào)用 buildOwner.buildScope(renderViewElement)更新 elements。
_dirtyElements列表在遍歷的過(guò)程中執(zhí)行rebuild方法,此時(shí)將所有標(biāo)記為dirty的Element節(jié)點(diǎn)依次執(zhí)行rebuild,preformRebuild,build,updateChild,update方法,執(zhí)行界面更新。完成build,update操作完成之后,后續(xù)會(huì)將需要繪制的RenderObject添加到需要layout的列表中,等待繪制渲染。所有繪制完成之后將_dirtyElments列表清空,_inDirtyList標(biāo)記位置為false。
提交給引擎繪制渲染
看看super.drawFrame(),這里就執(zhí)行到了RendererBinding類(lèi)中,定義如下:
這里就是將最終需要繪制渲染的畫(huà)面提交給引擎的地方了,繪制完成之后就在界面顯示更新后的視圖了。
狀態(tài)可變的 widget 。
通過(guò)其類(lèi)的定義能夠看到 StatefulWidget 配置 StatefulElement 。
State 是 StatefulWidget 的內(nèi)部邏輯與狀態(tài),由 StatefulWidget 的 createState 創(chuàng)建。
StatefulWidget 實(shí)例本身是不可變的, 但是 StatefulWidget 將其可變的狀態(tài),存儲(chǔ)在與之關(guān)聯(lián)的 State 對(duì)象中。
不管什么時(shí)候,只要在樹(shù)中 mount 一個(gè)新的 StatefulElement ,必然需要注入一個(gè) StatefulWidget ,注入一個(gè) StatefulWidget 時(shí), framework 都會(huì)調(diào)用一次 createState 方法。
其實(shí),在 StatefulElement 構(gòu)造的時(shí)候,就會(huì)調(diào)用 createState ,創(chuàng)建 _state 對(duì)象,( _state 是 StatefulElement 的變量)并且在 StatefulElement 的初始化方法中為 _state 關(guān)聯(lián)當(dāng)前的 StatefulElement 和用以配置 StatefulElement 的 StatefulWidget 。
StatefulElement 初始化方法如下:
這意味著如果 StatefulWidget 被插入到樹(shù)中的多個(gè)位置,則會(huì)有多個(gè) State 對(duì)象分別與它們關(guān)聯(lián)。
關(guān)于此類(lèi)的定義如下:
描述: 重寫(xiě)此方法以執(zhí)行初始化。
場(chǎng)景: 如果 State 的 build 方法依賴(lài)于本身可以改變狀態(tài)的對(duì)象時(shí)。(例如 ChangeNotifier 或 Stream ,或者可以訂閱并接收通知的其他對(duì)象)正確的方式是:
注意點(diǎn): 此方法中不能使用 BuildContext.dependOnInheritedWidgetOfExactType 。但是此方法被調(diào)用后會(huì)立即調(diào)用 didChangeDependencies ,在 didChangeDependencies 可以使用 BuildContext.dependOnInheritedWidgetOfExactType 。
調(diào)用時(shí)機(jī): StatefulElement ,首次插入樹(shù)中時(shí)會(huì)調(diào)用此方法,在 build 方法調(diào)用之前調(diào)用。
描述: StatefulElement 通過(guò)此方法返回的 widget 并通過(guò)調(diào)用 updateChild 來(lái)更新自己。
調(diào)用時(shí)機(jī): framework 調(diào)用此方法的幾個(gè)不同的場(chǎng)景如下:
描述: StatefulElement 存在,并且符合 Widget.canUpdate 的情況下對(duì) StatefulWidget 進(jìn)行更新。
調(diào)用時(shí)機(jī): 不論何時(shí)只要 StatefulElement 的配置 widget 改變的時(shí)候就會(huì)調(diào)用。
注意: didUpdateWidget 方法最終會(huì)調(diào)用 build 方法,因此在此方法中調(diào)用 setState 是多余的。如果重寫(xiě)此方法,請(qǐng)確保調(diào)用 super.didUpdateWidget(oldWidget) 。
調(diào)用時(shí)機(jī): 當(dāng)此 State 對(duì)象的依賴(lài)項(xiàng)( InheritedWidget )更改時(shí)調(diào)用。
描述: 用于開(kāi)發(fā)階段 hot reload 。
調(diào)用時(shí)機(jī): hot reload 時(shí)調(diào)用,調(diào)用后 build 方法也將被調(diào)用。無(wú)需在此方法中做任何操作。
調(diào)用時(shí)機(jī): 當(dāng) StatefulElement 從樹(shù)中移除的時(shí)候會(huì)調(diào)用。
調(diào)用時(shí)機(jī): 當(dāng) StatefulElement 從樹(shù)中 unmount 的時(shí)候會(huì)調(diào)用。
StatefulWidget 用以配置 StatefulElement ,但在這兩者之間的 State 承接了 StatefulElement 的生命周期,而 StatefulWidget 僅僅只是連接了 State 與 StatefulElement 的不可變的實(shí)例,因此 StatefulWidget 的生命周期,依賴(lài)于 StatefulElement ,而 State 卻是其最簡(jiǎn)單直接的體現(xiàn)形式。
為了能更好的理解 StatefulWidget 的生命周期,我畫(huà)了一張關(guān)于 State 、 StatefulElement 、 Component 、 Element 的關(guān)系圖。
我想你想要的是:target.difference(DateTime.now()).toString().split('.')[0])
使用.split('.')[0]持續(xù)時(shí)間來(lái)去掉秒的分?jǐn)?shù)。
其中target是DateTime對(duì)象。flutter計(jì)算給定小時(shí)的剩余時(shí)間,以秒為單位更新flutter,因此,時(shí)間以h:m:s為單位,例如,如果給定的時(shí)間是(6:27pm),我希望得到此結(jié)果(剩余時(shí)間02:21:02)。
打印結(jié)果:Text('Timeuntil${DateFormat.Hms().format(target)}');Text(target.difference(DateTime.now()).toString().split('.')[0])
在此之前先推薦看大佬的: 填坑指導(dǎo)
iOS需要注意:
1、flutter2.0要求cocoapods 升級(jí)到1.9.0
詳情看這篇博客
2、原來(lái)flutter項(xiàng)目中的podfile文件是舊版本的ccocoapods了,刪除podfile和對(duì)應(yīng)的.lock,然后flutter項(xiàng)目重新運(yùn)行使用它自動(dòng)生成的podfile文件
3、安裝CocoaPods
卸載cocoapods:sudo gem uninstall cocoapods
查看cocoapods版本:pod --version
指定版本安裝:
sudo gem install -n /usr/local/bin cocoapods -v 1.9.3(新MacOS系統(tǒng)升級(jí))
不指定版本安裝
sudo gem install -n /usr/local/bin cocoapods
說(shuō)明 :老項(xiàng)目sdk1.17.0===升級(jí)到2.0.1,當(dāng)前所有操作基于win平臺(tái)
到此為止環(huán)境已經(jīng)準(zhǔn)備妥當(dāng),正式進(jìn)入項(xiàng)目修改。
所有的插件都要適配到空安全,插件是否支持均會(huì)有對(duì)應(yīng)說(shuō)明Null safety,適配過(guò)程不確定版本的話(huà),可以使用dio: any,適配完事后再在pubspec.lock文件中查看具體的版本修改過(guò)來(lái),實(shí)在有部分插件沒(méi)有支持的,參考下面
部分插件在適配空安全的版本放棄維護(hù)了,得自行更新或?qū)ふ姨娲?,如?flutter_swiper 變?yōu)?flutter_swiper_null_safety ,插件更新后要注意項(xiàng)目中的用法是否需要更新
2.1.1: 以前采用的是 provide 插件共享全局?jǐn)?shù)據(jù),現(xiàn)在變化為 provider ,用法改變, 點(diǎn)擊參考 ,以防文章丟失,我重復(fù)一遍:
比如:
2.1.2: dio版本升級(jí)到4.0.0最新版后,部分用法改變
2.2.1
2.2.2
解決方案:
2.2.3
解決方案:
2.2.4
解決方案:
2.2.5
解決方案:
2.2.6
解決方案:
2.2.7
解決方案:
2.2.8
解決方案: child 換為sliver
2.2.8.1
解決方案: 項(xiàng)目目錄下: android--app-build.gradle --minSdkVersion改為:18 或者19
2.2.8.2
解決方案: 在pubspec.yarm管理里面添加:publish_to
2.2.8.3
解決方案: video_player升級(jí)后字段發(fā)生了變化,initialized字段更換為:isInitialized(_controller.value.isInitialized)
2.2.8.4
解決方案:
2.2.8.5
解決方案:
2.2.8.6
解決方案: 方案一:刪除ios目錄下的Podfile.lock 文件然后重新運(yùn)行 pod install命令
方案二:刪除ios目錄下的Podfile.lock與Podfile文件 重新運(yùn)行flutter run或flutter build ios
方案三:刪除ios目錄,重新運(yùn)行 flutter create . 命令,注意有"."這個(gè)符號(hào)不要忘記
2.2.8.7
這個(gè)報(bào)錯(cuò)一般對(duì)應(yīng)的就是下面的報(bào)錯(cuò),注意看后面的報(bào)錯(cuò)信息,看是哪個(gè)插件報(bào)錯(cuò)。
解決方案: 把Podfile的版本注釋打開(kāi),改為platform :ios, '9.0' 或者是更高的版本
全局替換
1.將new List() 替換為[];
2.TextField的inputFormatters:[WhitelistingTextInputFormatter.digitsOnly] 替換為[FilteringTextInputFormatter.digitsOnly]
3.TextField的inputFormatters:[WhitelistingTextInputFormatter(RegExp("[a-z|A-Z|0-9]"))]替換為FilteringTextInputFormatter.allow(RegExp("[a-z|A-Z|0-9]"))
4.Stack組件中overflow: Overflow.visible改為 clipBehavior: Clip.none;overflow: Overflow.clip改為clipBehavior:Clip.hardEdge
5.ListWheelScrollView組件中clipToSize = false改為clipBehavior: Clip.none,clipToSize = true改為 Clip.hardEdge
6.TextField中maxLengthEnforced: true改為maxLengthEnforcement:MaxLengthEnforcement.enforced
7.FlatButton、RaisedButton、OutlineButton的變化: 官方參考
顏色的屬性發(fā)生了變化,由原來(lái)的Color 變?yōu)榱薓aterialStatePropertyColor, 這是未了解決不同狀態(tài)(pressed、hovered、focused、disabled)下按鈕顏色的變化
例如
8.出現(xiàn)如下警告
9.showSnackBar報(bào)錯(cuò)誤
解決方案: Scaffold換為ScaffoldMessenger
10.textSelectionColor棄用
解決方案:
11.charts_flutter升級(jí)后屬性報(bào)錯(cuò)
解決方案:
12.flutter 真機(jī)調(diào)試無(wú)法訪(fǎng)問(wèn)網(wǎng)絡(luò),dio報(bào)錯(cuò)
解決方案:
android:
ios:
問(wèn)題12完整參考