[此處應該有一張圖]
本文均依據于flutter官方文檔流程,只梳理其中的坑點。
使用方法:由于本文可能會較長,知識點零散,請讀者善用Ctrl+F
。
基于Flutter2.0
Windows+VS Code向
請注意:本文 不是教程內容,章節名與flutter官方文檔對應章節一致,只用于備查和幫助學習中遇到問題的童鞋,歡迎指正。
開始使用Flutter
安裝和環境配置
系統選擇:Windows
獲取 Flutter SDK
從教程中的鏈接下載最新的stable安裝包,建議解壓到其他盤或者用戶路徑下。然后win+s
輸入env添加flutter的bin路徑到環境變量中。
這里就有一個問題,修改環境變量后,需要使用環境變量的進程(如:ternimal,cmd,vscode等)都需要重啟,保證進程內環境變量被封信。
知識點:系統環境變量只會在啟動進程時注入進程,由一個進程向啟動的另外一個進程注入。
運行 flutter doctor
flutter doctor檢查flutter相關環境是否可用,按照你的需要來判斷,如果紅叉部分和你要用的無關,那么忽略它就是。如你想要用VS Code做Web開發,你只需要關注Flutter、瀏覽器和VS Code是不是勾,其他的叉叉對你沒有任何影響。
檢查結果里如果Android toolchain是叉叉,你又不想裝Android Studio,那么自己安裝Android SDK和Java就可以了。如果你安裝了VS,那么用VS Installer安裝xmarin相關的安卓SDK、虛擬機、JDK等組件就可以搞定。
請注意,Flutter教程中提供的Google USB Driver下載鏈接只是一個公版驅動,很多國產手機不一定能夠適配,請自行安裝手機廠商自己提供的驅動。如果廠商沒有提供驅動或者下載安裝之后仍然檢測不到,不用擔心,在安裝好Android SDK后,在Android SDK管理器里安裝附加程序中Google USB Driver己可。
提示:如果你安裝了Microfost Edge,檢查結果中Chrome是叉叉也沒關系,Flutter能夠識別Edge。
編譯工具設定
本教程選擇:Visual Studio Code
注意點,一定要按照教程安裝插件后重啟VS Code。然后,按快捷鍵 Ctrul + `
打開終端,鍵入Flutter命令是否能找到。
這里就有一個問題,VS Code終端可能會提示Flutter命名無效。原因有以下幾點:
- 很多童鞋在前面教程中下載Flutter SDK后解壓放到了高權限目錄,如C:盤除用戶目錄之外的其他目錄下。這里建議一般放在其他盤或者用戶目錄下。
- VS Code在設置Flutter的環境變量前就已經啟動,或者啟動VS Code的資源管理器/啟動器就已經打開。所以,喜歡使用啟動器的小伙伴注意了:
如果啟動器沒有重啟過,那么它的環境變量中必然是沒有FlutterSDK的Path的。請使用開始菜單或桌面圖表重啟VS Code,或者重啟啟動器再啟動VS Code,不必像網上那些教程那樣重啟電腦。
開發體驗初探
這個環節講如何創建應用和編譯運行應用。
注意,即使裝了插件,在VS Code的
Ctrl+Shift+P
中也沒有全部命令,只有一些常規的。
在官方后續的教程中,可能是因為教程不是由單一人員寫就,同樣的操作可能在不同的地方介紹的方式會不統一。所以,作為程序員,應該學會全部使用終端輸入命令的方式。本文都是采用終端命令來講述操作
前提一:要創建Windows桌面應用嗎?
官方教程鏈接:Flutter 桌面支持
在Flutter2.0版本中,桌面應用還是處于beta版本中,幸運的是stable版本包含了一份Beta版本的快照,如果你需要嘗試Windows桌面應用,那么你需要在終端中執行如下命令:
// 啟動windows桌面支持
PS >flutter config --enable-windows-desktop
// 檢查Windows的狀態
PS >flutter devices
// 檢查Windows開發相關有沒有安裝
PS >flutter doctor
前提二:在國內開發嗎?請設置鏡像站的環境變量
因為在國內訪問速度很慢或者有的地區的童鞋根本就不能訪問官方的包管理,而在創建應用的時候,會去包管理服務器下載一些依賴包,就會出現400系列錯誤或者500系列錯誤(因為網絡原因,官方的服務器會隨時掛掉)
注意:官方給出了好多個鏡像地址,經過使用后,推薦CNNIC,或Flutter社區,發現清華的不是太穩定,上海交大的十分不穩定。具體鏡像情況查看這個網址:Flutter 鏡像狀態檢測 (uptimerobot.com)
官方教程中沒有windows平臺設置方法,開發windows的童鞋下按照以下步驟設置:
1. win+s 搜索env打開環境變量
2. 在系統變量中`新建`輸入PUB的環境變量。如:變量名:```PUB_HOSTED_URL``` ,變量值:```http://mirrors.cnnic.cn/dart-pub```
3. 和FLUTTER的,如:變量名:```FLUTTER_STORAGE_BASE_URL```,變量值:```http://mirrors.cnnic.cn/flutter```
4. 保存后重啟VS Code,注意前文的環境變量注入原理
創建應用
// 簡單寫法
PS >flutter create yourappname
// 復雜寫法
PS >flutter create --template app --platforms linux,windows,android --project-name yourappname .\YourAppProjectFolderName\
// 為已有的應用添加桌面支持(多個平臺名稱用英文逗號隔開,如:windows,linux,maos)
// *** 最后的`點`很重要,是當前應用目錄的意思,別忘了 ***
PS >flutter create --platforms=windows .
注意:項目名稱一定!一定!一定!不能有大寫,單詞之間想隔開用
_
,dart不允許項目名稱有大寫。但是,文件夾名字可以隨便寫大小寫??
運行應用
// 運行當前app的命令,會自動按照當前選定的設備進行編譯。-v 參數是打印詳細日志
PS >flutter run -v
如果是要開發安卓APP的小童鞋,要注意了。
####### 1. Gradle生成工具
咱們安卓的Gradle生成工具要訪問國外的鏈接,不然編譯就會出現各種報錯問題,如卡在Gradle task assembleDebug
。遇到這個問題,可能需要替換成國內的包源。
這里只介紹項目內的更換Gradle包源的辦法,全局更換方法請google就是。
找到項目中的:\android\build.gradle 這個文件,對照如下代碼修改
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
//google()
//jcenter()
// 下面兩句是阿里包源
maven { url 'https://maven.aliyun.com/repository/google/' }
maven { url 'https://maven.aliyun.com/repository/jcenter/'}
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
//google()
//jcenter()
// 下面兩句是阿里包源
maven { url 'https://maven.aliyun.com/repository/google/' }
maven { url 'https://maven.aliyun.com/repository/jcenter/'}
}
}
####### 2. JDK版本之謎
咱們的Flutter兼容的JDK版本是個謎,編譯會報各種錯誤,網上有人說是只兼容JDK8,更為新的版本有問題。JDK有幾個下載渠道,在Oracle下載的應該沒有問題。
強烈建議不要!不要!不要在OpenJDK這個Oracle的換皮網站下載,我下的所有的二進制壓縮包發現都不全,最后上微軟官網下了一個,配置環境變量(記得要重啟vs code)后正常使用,就是版本號奇奇怪怪的。
20201/04/09熱點更新:伴隨著微軟在Google和甲骨文的Java專利官司中勝訴,微軟立馬發布自己的OpenJDK——Microsoft Build of OpenJDK,以后可以到微軟官網下載JDK。
####### 3. 編譯出錯怎么查看更多信息
安卓采用Gradle進行構建,所以當出錯后我們自然使用Gradle查看更多信息,那么Gradle在哪里呢?
// 在你的項目路徑下的這里
./android/gradlew
知道它在哪里,終端中錯誤的相關建議就知道怎么做了。下圖里的* Try:
后面的內容是指的Gradle而言,所以我們就能通過這樣的命令查看調用堆棧信息:
PS >./android/gradlew compileDebug --stacktrace -info
####### 4. 常見問題:
1. 'Finished with error: Gradle task assembleDebug failed with exit code 1'
出現這個報錯要檢查下JAVA的環境變量設置是否正確,VS Code有沒有重啟。
2. `javax.net.ssl.SSLException`
出現這個異常一般是JDK版本不兼容,更換為Flutter兼容SDK即可。
3. `Gradle "Could not open buildscript class cache"`
出現這個報錯一般關閉VS Code,刪除C盤用戶目錄下.gradle下caches文件夾。還不能解決有可能是JDK版本不兼容,更換為Flutter兼容SDK即可。
4. 出現運行錯誤,打印日志中提示需要 Android SDK Build tools 的某個版本
出現這個問題,打開Andorid SDK管理工具,勾選下載Android SDK Build tools下的對應版本即可
開發文檔
用戶界面
Widgets介紹
這個章節介紹,Flutter中一切UI元素都是Widget(組件),這是個基類。所以就肯定有繼承于基類的通用的基礎組件,和由基礎組件組成應用于不同場景的高級組件,也肯定可以自己編寫組件類。雖然邏輯抽象反而會增加代碼復雜度,面向對象(在面向UI對象上)真的是的好工具。
其中,我覺得傳達出一個很重要的概念:組件和狀態分離。
如果熟悉MVVM模式(vue.js、WPF)的童鞋一定很熟悉UI與數據分離的模式,通過更新數據就可以達到更新UI的效果。而Flutter采用的是另外一套MVU模式,我個人感覺是腳本+閉包
的嵌套寫法。
初次接觸這種寫法剛開始肯定會被搞蒙:代碼和數據混雜、作用域混亂、又臭又長,代碼層級巨深,閱讀體驗極差等。但是,它也有它的好處,就是一致性強。數據和UI在一起,清晰的代碼走向。所以,在一開始學習的時候,就要做好使用好面向對象這個工具,將可以抽離的模塊形成類、方法、對象等。而,代碼的簡潔和性能之道也是需要在后續的使用過程中持續提高的,可能在使用了flutter幾年之后,你仍然能提升寫法使你的代碼更加優雅。
布局構建
Fluter中的布局
筆者認為的布局組件結構表如下:
級別 | 標準 widgets | Material widgets |
---|---|---|
基礎 | Row、Column、Center、Container、Stack | |
高級 | ListView、GridView、Table | Card、ListTile、DataTable |
####### 1. 布局要點
第一,常規布局使用基礎基別的標準widgets即可,平面布局Row、Colume互相嵌套再加上Container,Container就是一個常見UI框架的容器大集合,和div很像。層次布局使用Stack嵌套。
第二,在一些局部的復雜數據呈現、批量同類數據和特定局部布局中使用高級的布局組件可以簡化布局工作。
最后,因為MVU的代碼編寫方式是高度嵌套的結構,為了最大限度地減少嵌套深入產生的代碼閱讀體驗差,可以在變量和函數中實現 UI 的各個部分。
布局約束[新增]
總結起來就是:
子組件的成長空間由父組件決定,而父組件和現實世界相同則有兩種:放任自流和進行約束,放任自流又分為自私和兒控,約束又分為寬松約束和嚴格約束。
注:這里是胡亂分類取名,搏君一笑爾,會意即可,切勿認真。
- 放任自流,子組件可以充分發揮自己的長度,不受屏幕限制。容易產生越界行為。
- 自私指的是,自己受父控件約束,子控件不管。
- 兒控指的是,既受父控件約束,在這個前提下能跟子控件一邊大就一邊大,沒兒子跟才父組件一樣大。
- 寬松約束是指下限可以跟上限不同,子組件在中間隨意,越界不行。
- 嚴格約束是指下限和上限一致,子組件必須和父組件的約束一樣大。
So,常見組件的約束分類如下:
父組件類型 | 約束類型 | 組件 |
---|---|---|
放任自流 | 自私 | Row、Column、UnconstrainedBox、OverflowBox |
兒控 | Container、FittedBox | |
約束 | 寬松約束 | Center、Align、ConstrainedBox、LimitedBox、Flexible、Scaffold |
嚴格約束 | App、Expanded、SizedBox.expand() |
####### 容易忽略的點
- Row不會對子組件有任何約束,所以在Row中直接放置Text,Text接收不到任何約束。在文本過長的情況下,會直接超出屏幕。為了能實現文本自動換行的效果,需要給Text加上Expanded,因為Expanded的寬度為自動填充計算出來的,由它來傳遞約束給Text,Text就能根據約束來進行換行。
- UnconstrainedBox和OverflowBox雖然對子組件無限制,但是前者在子組件越界了會管一下報錯,后者不會管。
- FittedBox它有的空間都會給兒子,但是兒子不能貪心要天上的月亮(無窮無盡),不然什么都沒有。
- LimitedBox能對子組件進行限制,但是卻需要它的父組件對它沒有限制。
- Flexible和Expanded都會忽略其子控件的寬度,而Flexible只是會讓瘦弱的孩子得到剛適合它的大小。
添加互動
互動中最核心的還是狀態的問題,Widget分為無狀態和有狀態的。無狀態的組件意味著數據無關,創建后就UI一直不變到生命消亡。有狀態組件有著狀態(數據),在生命周期內數據會發生變化而影響到UI變化。
看著這種區別涇渭分明,實則不用過多區別。其實它是一個層級關系,最基礎的組件都是無狀態的,有狀態的組件只是由無狀態組件組合成了一個能單獨支撐某一特定類型業務數據呈現的層面。實際上它的狀態更新導致的UI更新,也是對子無狀態組件的一次銷毀到重新構建的過程。
而互動中的狀態管理的意思是,對于有狀態的組件應該怎么樣管理狀態??偨Y一句話就是:
業務狀態業務控件管,界面狀態自己管。和人相關父親管,自身狀態自己管。
其實,熟悉MVVM的童鞋這個就很好理解,狀態對應ViewModel,我們會在Control和View層級對應相應的ViewModel。而控件自身的顯示狀態、動畫、效果等均由控件自身(也就是樣式)自己來維護。體現的就是一個作用域思想,也可以理解成層級關系。讓合適的層級干它能干的事情就行,不去可以擴大狀態管轄范圍也不縮小狀態范圍還得進行狀態同步。
這里介紹一個我認為的設計原則:
能做無狀態的Widget就做成無狀態,減少狀態管理的位置,進而減少狀態耦合和代碼復雜度,增加狀態的聚合度。通過父Widget狀態更新讓Flutter重新構建這個無狀態Widget達到更新界面的目的。
Windows窗口美化
一般來說,任何一個桌面商業軟件,都必然是要將默認系統窗口的窗體去掉,自己進行修飾的。修飾內容就主要包括:標題欄,三金剛鍵,標題拖動,窗體大小,窗體位置,邊框和陰影,窗體背景。
一般對于win32程序員來說,這根本不是一個問題,因為他們會自己對flutter項目中的windows文件夾中的win32代碼進行修改,實現他們想要的任何窗體效果。但是對于不熟悉win32的程序員來說,這就是一件特別難受的事情了。
很遺憾,直至2021/04/21,windows的CSD(Client-Side Decoration)支持還未得到官方支持,有興趣的可以去這個issue了解信息Desktop Client Side Decoration support · Issue #31373。
其中就包含一個解決方案,是一個開發者也是遇到需要美化窗體時,看到這條issue就自己動手實現了一個庫來解決這個問題。pub鏈接:bitsdojo_window,感謝他的付出。
同時,開發者還貼心的錄制了視頻放在某Y屏蔽網站上,B站的搬運在此:Flutter desktop - 自定義窗口標題欄包含Windows窗體的最大化最小化和關閉按鈕
這里需要注意:因為Flutter對于各個平臺的runner實現均放在對應平臺的文件夾下,windows文件下就有對應源代碼windows窗體相關的win32窗口和dart的封裝,但是經過一番研究和修改后發現它的主要結構還是為
主窗口+flutter子窗口
,他們都不是layer層。主窗口為進程的啟動窗口,子窗口用于放置flutter的app內容。可通過修改實現移除默認windows窗口,增加頂部拖動,增加按鈕,增加陰影等一系列功能,但是不能增加背景透明度達到類似win7的毛玻璃效果。所以要實現背景透明類似的效果需要等待官方的實現。
未完待續...