APP 啟動頁在國內(nèi)是最常見也是必備的場景,其中啟動頁在 iOS 上算是強制性的要求,其實配置啟動頁挺簡單,因為在 Flutter 里現(xiàn)在只需要:
創(chuàng)新互聯(lián)建站主要從事網(wǎng)站設(shè)計制作、成都網(wǎng)站設(shè)計、網(wǎng)頁設(shè)計、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)陸川,十余年網(wǎng)站建設(shè)經(jīng)驗,價格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):13518219792
一般只要配置無誤并且圖片尺寸匹配,基本上就不會有什么問題, 那既然這樣,還有什么需要適配的呢?
事實上大部分時候 iOS 是不會有什么問題, 因為 LaunchScreen.storyboard 的流程本就是 iOS 官方用來做應(yīng)用啟動的過渡;而對于 Andorid 而言,直到 12 之前 windowBackground 這種其實只能算“民間”野路子 ,所以對于 Andorid 來說,這其中就涉及到一個點:
所以下面主要介紹 Flutter 在 Android 上為了這個啟動圖做了哪些騷操作~
在已經(jīng)忘記版本的“遠古時期” , FlutterActivity 還在 io.flutter.app.FlutterActivity 路徑下的時候,那時啟動頁的邏輯相對簡單,主要是通過 App 的 AndroidManifest 文件里是否配置了 SplashScreenUntilFirstFrame 來進行判斷。
在 FlutterActivity 內(nèi)部 FlutterView 被創(chuàng)建的時候,會通過讀取 meta-data 來判斷是否需要使用 createLaunchView 邏輯 :
是不是很簡單,那就會有人疑問為什么要這樣做?我直接配置 Activity 的 android:windowBackground 不就完成了嗎?
這就是上面提到的時間差問題, 因為啟動頁到 Flutter 渲染完第一幀畫面中間,會出現(xiàn)概率出現(xiàn)黑屏的情況,所以才需要這個行為來實現(xiàn)過渡 。
經(jīng)歷了“遠古時代”之后, FlutterActivity 來到了 io.flutter.embedding.android.FlutterActivity , 在到 2.5 版本發(fā)布之前,F(xiàn)lutter 又針對這個啟動過程做了不少調(diào)整和優(yōu)化,其中主要就是 SplashScreen 。
自從開始進入 embedding 階段后, FlutterActivity 主要用于實現(xiàn)了一個叫 Host 的 interface ,其中和我們有關(guān)系的就是 provideSplashScreen 。
默認情況下它會從 AndroidManifest 文件里是否配置了 SplashScreenDrawable 來進行判斷 。
默認情況下當 AndroidManifest 文件里配置了 SplashScreenDrawable ,那么這個 Drawable 就會在 FlutterActivity 創(chuàng)建 FlutterView 時被構(gòu)建成 DrawableSplashScreen 。
DrawableSplashScreen 其實就是一個實現(xiàn)了 io.flutter.embedding.android.SplashScreen 接口的類,它的作用就是:
之后 FlutterActivity 內(nèi)會創(chuàng)建出 FlutterSplashView ,它是個 FrameLayout。
FlutterSplashView 將 FlutterView 和 ImageView 添加到一起, 然后通過 transitionToFlutter 的方法來執(zhí)行動畫,最后動畫結(jié)束時通過 onTransitionComplete 移除 splashScreenView 。
所以整體邏輯就是:
當然這里也是分狀態(tài):
當然這個階段的 FlutterActivity 也可以通過 override provideSplashScreen 方法來自定義 SplashScreen 。
看到?jīng)]有,做了這么多其實也就是為了彌補啟動頁和 Flutter 渲染之間, 另外還有一個優(yōu)化,叫 NormalTheme 。
通過該配置 NormalTheme ,在 Activity 啟動時,就會首先執(zhí)行 switchLaunchThemeForNormalTheme(); 方法將主題從 LaunchTheme 切換到 NormalTheme 。
大概配置完就是如下樣子, 前面分析那么多其實就是為了告訴你,如果出現(xiàn)問題了,你可以從哪個地方去找到對應(yīng)的點 。
講了那么多, Flutter 2.5 之后 provideSplashScreen 和 io.flutter.embedding.android.SplashScreenDrawable 就被棄用了,驚不喜驚喜,意不意外,開不開心 ?
通過源碼你會發(fā)現(xiàn),當你設(shè)置了 splashScreen 的時候,會看到一個 log 警告:
為什么會棄用?
其實這個提議是在 這個 issue 上,然后通過 這個 pr 完成調(diào)整。
大概意思就是: 原本的設(shè)計搞復(fù)雜了,用 OnPreDrawListener 更精準,而且不需要為了后面 Andorid12 的啟動支持做其他兼容,只需要給 FlutterActivity 等類增加接口開關(guān)即可 。
也就是2.5之后 Flutter 使用 ViewTreeObserver.OnPreDrawListener 來實現(xiàn)延遲直到加載出 Flutter 的第一幀。
為什么說默認情況? 因為這個行為在 FlutterActivity 里,是在 getRenderMode() == RenderMode.surface 才會被調(diào)用,而 RenderMode 又和 BackgroundMode 有關(guān)心 。
所以在 2.5 版本后, FlutterActivity 內(nèi)部創(chuàng)建完 FlutterView 后就會執(zhí)行一個 delayFirstAndroidViewDraw 的操作。
這里主要注意一個參數(shù): isFlutterUiDisplayed 。
當 Flutter 被完成展示的時候, isFlutterUiDisplayed 就會被設(shè)置為 true。
所以當 Flutter 沒有執(zhí)行完成之前, FlutterView 的 onPreDraw 就會一直返回 false ,這也是 Flutter 2.5 開始之后適配啟動頁的新調(diào)整。
看了這么多,大概可以看到其實開源項目的推進并不是一帆風順的,沒有什么是一開始就是最優(yōu)解,而是經(jīng)過多方嘗試和交流,才有了現(xiàn)在的版本,事實上開源項目里,類似這樣的經(jīng)歷數(shù)不勝數(shù):
這篇將會解決手動切換主題以及跟隨手機切換主題來更新UI(包括自己創(chuàng)建的Widget)
主題切換有個問題,就是如果是我們自定義或者在 build() 自己創(chuàng)建的部件是不會隨著系統(tǒng)的主題切換而發(fā)生主題色變化的(實際測試中,如果頁面在頂層(沒有被push)切換主題并不會觸發(fā) build() 方法,(push之后的頁面切換系統(tǒng)主題是可以觸發(fā) build() 的,而且會頻繁觸發(fā)好多次...),既然無法通過重新 build 更新組件的主題色,那么我們在切換主題后,強制觸發(fā)整個app的 build() 就可以了)
Flutter中有兩個常用的狀態(tài)Widget分為StatefulWidget和StatelessWidget,分別為動態(tài)視圖和靜態(tài)視圖,視圖的更新需要調(diào)用StatefulWidget的setState方法,這會遍歷調(diào)用子Widget的build方法。如果一個頁面內(nèi)容比較復(fù)雜時,會包含多個widget,如果直接調(diào)用setState,會遍歷所有子Widget的build,這樣會造成很多不必要的開銷,所以非常有必要了解Flutter中局部刷新的方式:
globalkey唯一定義了某個element,它使你能夠訪問與element相關(guān)聯(lián)的其他對象,例如buildContext、state等。應(yīng)用場景:跨widget訪問狀態(tài)。
例如:可以通過key.currentState拿到它的狀態(tài)對象,然后就可以調(diào)用其中的onPressed方法。
Flutter框架內(nèi)部提供了一個非常小巧精致的組件,專門用于局部組件的刷新。適用于值改動的刷新。
實現(xiàn)原理:在 initState 中對傳入的可監(jiān)聽對象進行監(jiān)聽,執(zhí)行 _valueChanged 方法,_valueChanged 中進行了 setState 來觸發(fā)當前狀態(tài)的刷新。觸發(fā) build 方法,從而觸發(fā) widget.builder 回調(diào),這樣就實現(xiàn)了局部刷新??梢钥吹竭@里回調(diào)的 child 是組件傳入的 child,所以直接使用,這就是對 child 的優(yōu)化的根源。
可以看到 ValueListenableBuilder 實現(xiàn)局部刷新的本質(zhì),也是進行組件的抽離,讓組件狀態(tài)的改變框定在狀態(tài)內(nèi)部,并通過 builder 回調(diào)控制局部刷新,暴露給用戶使用。
通過這個可以創(chuàng)建一個支持局部刷新的widget樹,比如你可以在StatelessWidget里面刷新某個布局,但是不需要改變成StatefulWidget;也可以在StatefulWidget中使用做部分刷新而不需要刷新整個頁面,這個刷新是不會調(diào)用Widget build(BuildContext context)刷新整個布局樹的。
異步UI更新:
很多時候我們會依賴一些異步數(shù)據(jù)來動態(tài)更新UI,比如在打開一個頁面時我們需要先從互聯(lián)網(wǎng)上獲取數(shù)據(jù),在獲取數(shù)據(jù)的過程中顯示一個加載框,等獲取到數(shù)據(jù)時我們再渲染頁面;又比如我們想展示Stream(比如文件流、互聯(lián)網(wǎng)數(shù)據(jù)接收流)的進度。當然StatefulWidget我們完全可以實現(xiàn)以上功能。但由于在實際開發(fā)中依賴異步數(shù)據(jù)更新UI的這種場景非常常見,并且當StatefulWidget中控件樹較大時,更新一個屬性導(dǎo)致整個樹重建,消耗性能,因此Flutter專門提供了FutureBuilder和SteamBuilder兩個組件來快速實現(xiàn)這種功能。
通常情況下,子Widget無法單獨感知父Widget的變化,當父state變化時,通過其build重建所有子widget;
InheriteddWidget可以避免這種全局創(chuàng)建,實現(xiàn)局部子Widget更新。InheritedWidget提供了一種在Widget樹中從上到下傳遞、共享數(shù)據(jù)的方式。Flutter SDK正是通過InheritedWidget來共享應(yīng)用主題和Locale等信息。
InheritedWidgetData
TestData
InheritedTest1Page
provider是Google I/O 2019大會上宣布的現(xiàn)在官方推薦的管理方式,而ChangeNotifierProvider可以說是Provider的一種:
yaml文件需要引入provider: ^3.1.0
頂層嵌套ChangeNotifierProvider
創(chuàng)建共享數(shù)據(jù)類DataInfo:
數(shù)據(jù)類需要with ChangeNotifier 以使用 notifyListeners()函數(shù)通知監(jiān)聽者更新界面。
使用Provider.of(context)獲取DataInfo
nextPage:
使用Consumer包住需要使用共享數(shù)據(jù)的Widget
RepaintBoundary就是重繪邊界,用于重繪時獨立于父視圖。頁面需要更新的頁面結(jié)構(gòu)可以用 RepaintBoundary組件嵌套,flutter 會將包含的組件獨立出一層"畫布",去繪制。官方很多組件 外層也包了層 RepaintBoundary 標簽。如果你的自定義view比較復(fù)雜,應(yīng)該盡可能的避免重繪。
以上總結(jié)了幾種Flutter的局部刷新的方式,可根據(jù)實際需要使用不同的方式,最適合的才是最好的。
一年半前玩過flutter,忘光光...現(xiàn)在是時候重新拾取了。~
啟動頁一般只放圖片或者加幾行文字。
1、創(chuàng)建好flutter項目之后,在lib文件下面新建launch.dart或xx.dart.
2、在根目錄下新建images文件夾,如已有直接放入圖片
3、flutter_yijiake.iml中加入注入該圖片,并注意空格
4、在根目錄下的test/widget_test.dart中更改默認的啟動頁為當前的啟動頁路徑
5、最后重新設(shè)置啟動時的頁面
6、非常簡單的啟動頁面放logo圖片
值得一說的是,flutter框架的UI組件需要已new 組件的形式展開。
在耗時操作的時候,一般都要彈出一個加載框,然后在完成的時候再把加載框關(guān)掉,在Flutter中可以直接用showDialog()來彈出一個對話框。
這是一個簡單的提示對話框,包含了關(guān)閉按鈕,點擊就能關(guān)閉。但一般的耗時操作完成,就需要我們自己把dialog關(guān)閉掉。
首先,開啟dialog的時機。由于我們需要獲取到BuildContext,所以就得等build()方法走完,這里可以用Future.delayed()來等創(chuàng)建好BuildContext再進行創(chuàng)建,或者用Timer來延遲操作,我選擇了前者。
其中delayed()在initState()結(jié)尾來做就行,這里參考網(wǎng)友封裝了一個LoadingDialog。
那么接下來要在什么時機關(guān)閉呢?
一開始,我理所當然的以為,是在異步方法結(jié)束后,去更新界面的時候關(guān)閉,也就是setState(() {})的時候,可是不管怎么嘗試,用Navigator.pop()不行,用Navigator.of(context, rootNavigator: true).pop(result)也不行,用FlutterBoost.singleton.close(id)也不行,用FlutterBoost.singleton.closeCurrent()也不行,都會直接把非Dialog的頁面也關(guān)閉掉,這讓我百思不得其解,因為showDialog()的本質(zhì)也是新建了一個Route出來,也就是最頂層的頁面是彈出的Dialog,可是為什么關(guān)不掉呢。
一番思前想后,把showDialog的邏輯移到和異步邏輯同級,也就是setState(() {})外面,然后把showDialog()自身創(chuàng)建的BuildContext傳進去就能正常關(guān)閉了。也就是,在setState(() {})的時候,其實用的context還是非Dialog頁面的,所以關(guān)閉的當然就不是Dialog了。
持有Dialog自己的BuildContext,然后在異步以后調(diào)用就行了。
相對于iOS開發(fā),F(xiàn)lutter的布局更具有靈活性,每個頁面設(shè)計都不一樣,相同頁面可選擇的布局方式也不一樣,如果單純的說應(yīng)該如何去布局,我覺得不現(xiàn)實,大家可以參考下 Flutter官方的布局教程 。接下來,筆者,通過項目中的一個頁面,來一步一步的拆解布局的流程。整個過程,基本上按照拆解、組件封裝、具體布局這三步來的。
根據(jù)設(shè)計圖,可以看出整體可以分成兩部分,上面一部分是系統(tǒng)介紹模塊,下面一部分是真正的登錄內(nèi)容,因為涉及到疊加,因此考慮用Stack;
系統(tǒng)介紹模塊部分:整體也是涉及到疊加,考慮用Stack,分為四部分。最底部漸變色背景用一個contanier,無須指定位置,全視圖擴展;載放logo圖標在上一層,用Image。最后兩個Text同級放在最上層。Image,Text各用Positioned包裹去指定位置。
登錄內(nèi)容模塊是最外層是一個Contanier容器,去控制背景色和圓角。然后是一個Column元素,逐行排列。
第一行為Image,
第二行為Text,
第三行可以看成一個小Column,分兩塊進行布局
第四行可以看成一個小Column,分兩塊進行布局
第五行可以看作一個TextButton,
第六行可以看作一個Row,分三塊進行布局
通過上面這樣一步一步的分析后,基本上對大致的布局有了一個了解,最外層的控件大致選對(只要能實現(xiàn)的話,就是復(fù)雜度以及效率的問題),然后一步一步的拆解每一行的元素,如果有重復(fù)的或者覺得可以封裝出來的部分,則進行下一步。
每一行的拆解,大致也是按照這個思路來進行,因此筆者在這里就不做講解了。
在做到第三第四行的時候,發(fā)現(xiàn)這兩個很相似,而且設(shè)計到一些交互邏輯,筆者就想對第三第四行的這種展示進行封裝,覺得今后的布局可能會用到,因此在這一步,可以先把這一塊兒抽離出一個控件。利用TextField來實現(xiàn)這種輸入操作,具體的實現(xiàn)筆者不再詳細的描述了。
經(jīng)過這一步,整體的規(guī)劃設(shè)計圖已經(jīng)有了,各個組件也都有了,接下來的工作就是組裝了。
具體布局設(shè)計到一些細節(jié)的地方,例如整體Column的居中對齊(crossAxisAlignment)、間隔(Padding或Container包裹,筆者更喜歡用SizedBox占位)、居左居右居中(Align)、點擊事件(GestureDetector)以及圓角(BorderRadius)等一些特殊情況。
像第六行row是放在底部的,就可以在第六行前面增加一個Spacer()去填充空白區(qū)域。
對文字顏色大小等,可以用TextStyle直接設(shè)置。
對于輸入框的刪除按鈕,可以用Offstage這種Flutter特有的控制顯示隱藏的控件。
網(wǎng)站題目:flutter頂層頁,flutter 蒙層
網(wǎng)站路徑:http://aaarwkj.com/article28/dsiipjp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站導(dǎo)航、網(wǎng)站排名、網(wǎng)站改版、微信公眾號、品牌網(wǎng)站建設(shè)、云服務(wù)器
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)