Flutter 介紹
有人邀請我去開個沙龍,我決定將這個,這個就是當(dāng)時我的演講稿。
什么是 Flutter & Flutter 的好處
Flutter 是一個跨平臺的客戶端(以及網(wǎng)絡(luò)前端)開發(fā)工具,官方定義為:
Flutter 是 Google 開源的應(yīng)用開發(fā)框架,僅通過一套代碼庫,就能構(gòu)建精美的、原生平臺編譯的多平臺應(yīng)用。
鑒于入門介紹,我就說的明白些。
這玩意是用來寫客戶端程序的,也就是面向用戶的程序。
這個東西能夠?yàn)楹芏嗥脚_生成應(yīng)用,盡量做到了“平臺無關(guān)”。
這個東西上手比較簡單,性能比較高,開發(fā)效率很高。
目前這個和 React Native 并列兩大最流行的跨平臺開發(fā)平臺。而 React Native 還是占用了 React 前端開發(fā)框架(Flutter 受 React 影響很大)的優(yōu)勢,F(xiàn)lutter 相比之下就比較小眾了,找工作不太好找:-P
對我而言,有了 Flutter 的基礎(chǔ),后面要適應(yīng)其他的類似框架就方便多了。最近我被(zi)人(ji)拉(zhao)過(shi)去(qing)寫 vue 去了,我之前沒有接觸過。但是我稍微看了一下 vue 組合式的教程,就能給人打下手了。CSS 我現(xiàn)在還不會,感覺要會了,我就又會了一個框架(逃)。
Dart 語言介紹
Flutter 使用的是 Dart 語言,目前是 Google 專門為 Flutter 設(shè)計(jì)的語言,因?yàn)槲腋緵]找到任何在其他方面用 Dart 編程的例子。而且這玩意曾經(jīng)還想嵌入到 Chrome......
Dart = Javascript + Java
語法像 Javascript,運(yùn)行時環(huán)境像 Java。
像 Javascript 在于存在箭頭函數(shù),函數(shù)變量之類。Dart 對異步的實(shí)現(xiàn) Future 也借鑒了 JS 的 Promise。因?yàn)?Dart 設(shè)計(jì)的時候,對標(biāo)的就是 JavaScript。
而運(yùn)行環(huán)境像 Java,因?yàn)樗念愒O(shè)計(jì),編譯和運(yùn)行也很像 Java。類的方面下面會說明。
Dart 代碼的運(yùn)行有三種方式:一種是直接解釋,一種是轉(zhuǎn)碼成 Javascript ,一種是編譯成 DartVM 虛擬機(jī)機(jī)器碼,然后在 DartVM 里面運(yùn)行。最后一種有一種 Java VM 的既視感講道理:-P
上面三種方式對應(yīng)了 Flutter 的開發(fā):調(diào)試開發(fā),網(wǎng)頁開發(fā),客戶端程序。
給點(diǎn)例子吧
基本
函數(shù)
類
這玩意東西太多了,我就光碼字吧:
類的成員默認(rèn)都是公共成員,私有成員是在變量名前加
_
號,有@protected
宏。Dart 的類是單向繼承,支持接口類
支持 abstract 抽象類,也就是需要繼承來實(shí)現(xiàn)的類
異步方法
先來個定義
異步是在很多領(lǐng)域都有的概念,在編程中,是相對于同步的。同步就是一條指令一條指令,按順序執(zhí)行。異步則可以同時運(yùn)行多個任務(wù),執(zhí)行任務(wù)的時候,可以先返回一個“包含進(jìn)度的實(shí)例”。然后有“回調(diào)函數(shù)”來把該實(shí)例中執(zhí)行的狀態(tài)返回。
Dart 的異步叫 Future,其中 T 是泛型啦。當(dāng)你運(yùn)行異步方法的時候,他會先返回一個 Future 類,然后按需返回結(jié)果,或者處理結(jié)果。我們有兩個方式處理異步編程:
相當(dāng)于這段代碼
空安全
在你們使用 C 語言變量的時候,經(jīng)常出現(xiàn)變量尚未定義就被使用了。Dart 引入了空安全機(jī)制,來幫助避免這個現(xiàn)象,讓代碼更穩(wěn)定。
// 默認(rèn)所有類型均不可空,類型加問號,表示該變量可空
int? a = null;
// 如此寫會報編譯錯誤,語言會進(jìn)行空檢查的
int a = null;
// 可以使用 late 表示稍后賦值,但你不能忘了
late int a;
當(dāng)然還有很多,想知道的話請去看官方介紹。我當(dāng)時看了倆下就上手了......
Flutter 的基本部件介紹
Flutter 的 Widget 是一個一個的類,描述了在當(dāng)前的配置和狀態(tài)下視圖所應(yīng)該呈現(xiàn)的樣子。在 Flutter 里面,萬物都是圍繞部件旋轉(zhuǎn)的。
接下來我要展示一個信息卡,用這個方式給大家展示 Flutter 的基本組件。順便我搞點(diǎn) HTML 之類的東西,來給大家做點(diǎn)對比。接下來的部件,都是按照 Material 部件來說明的,iOS 的不在此說明。
Text 部件
Text 是用來渲染一段文字的。
Text("Maggie Rules!");
效果大致說,跟HTML的這個一樣。
<p>Maggie Rules!</p>
Text 的屬性有很多,比如說大小,斜體之類。有一個類叫 TextStyle,來給Text加屬性,比如字體,陰影,顏色之類。那么,我可以這么寫一個綠色的字。
Text("50 sucks",size:14,style:TextStyle(color: Colors.green));
效果大致說,跟HTML的這個一樣。
<p style="color: green"> 50 sucks </p>
我感覺通過這個,你們知道這玩意和 HTML+CSS 的對應(yīng)了吧,也許。
Row,Column,Warp
你們可以看到,我在這些卡片上畫了幾條線。這是為了說明我們設(shè)計(jì)該卡片的基本架構(gòu),行和列。Flutter 的部件構(gòu)造,就是在 Row 和 Column 之上的。
Row 和 Column 的寫法差不多,都是這樣的,更多屬性一會再說:
Row(children:<Widget>[]);
Column(children:<Widget>[]);
Row 代表行,Column 代表列。我們這個卡片是有三行的,每行是有對應(yīng)元素的。通過這個,我們可以寫出這個東西的框架了。
我們先實(shí)現(xiàn)每一行,第一行是在兩側(cè)的兩個元素,注意到中間很大間隔了嗎?這個是 Row 的一個屬性,AxisAlignment。
AxisAlignment 是指這個部件兩個軸上部件的排列方式,分為主軸 MainAxisAlignment 和交叉軸 CrossAxisAlignment。這張圖片顯示出這兩個部件的主軸和交叉軸。我們通過修改這個,來規(guī)劃好在該列/行上元素的排列方式。對于第一行,我們是這樣寫的:
剩下兩行我這里就不贅述了,他們的排列方式都是靠左,也就是默認(rèn)值。大致的代碼如下:
以上部分是最基本設(shè)計(jì)Flutter布局的樣例了。實(shí)際使用中,這樣寫的方式很死板,遇到一些動態(tài)變化的組件,比如說很多行的文字,Column高度偵測問題等等,會花費(fèi)大量的時間設(shè)置這些東西的樣式。所以,在實(shí)際PDA的編寫中,我是使用了Warp來讓其自動排列這些組件,你只是需要輸入這些部件就好了。
其中 InformationWithIcon 是這樣的:
TagsBoxes 需要在 Container 講明白了之后才能說明。
Container
Container是一個擁有繪制、定位、調(diào)整大小的 widget,是開發(fā)中最常用、最基礎(chǔ)的組件。顧名思義,他能包裝很多的組件。地位類似于 HTML 的 div。
上面的組件,如果我要成為一個個卡片,我得用這個包裝:
對于 Container,我們需要引入一些對于有些人很熟悉的東西,也就是說,Margin 和 Padding,外邊距和內(nèi)邊距。對于 Container 而言,內(nèi)邊距用到的最多。我們還可以設(shè)置這玩意的邊框,圓角,背景顏色之類。擴(kuò)展完相當(dāng)于這樣:
類似于這個:
實(shí)際上 Container 是很多部件的最終實(shí)現(xiàn)方式,比如 Card,他就說按照設(shè)計(jì)規(guī)范,設(shè)計(jì)好背景顏色,邊框圓角,背景顏色之類。除此之外,還有強(qiáng)制設(shè)定長寬的 SizedBox,強(qiáng)制設(shè)定裝飾的 DecortatedBox 等,都可以算 Container 的擴(kuò)展。實(shí)際代碼中,我直接把上面提到的 Warp 套進(jìn) Card 了。
最終,我說明一下上面說到的 TagBoxes。代碼是這樣的:
ListView
卡片介紹就這樣了,在實(shí)際情況下,我們會有超級多的記錄。根據(jù)思維慣性,我們會想讓其做成一個可以滾動的菜單。不過不能用 Column,因?yàn)閱渭兊?Column 缺少滾動偵測器,也就是說,我們?nèi)鄙僖粋€偵測目前該滾動菜單滾動位置的偵測器。所以,我們需要使用 ListView 部件,他默認(rèn)有一個滾動偵測器。
滾動偵測器涉及到接下來要說的狀態(tài)管理。
Scafford
Material 設(shè)計(jì)的頁面部件框架,包括但不限于:
appBar:上面的導(dǎo)航欄(可以設(shè)置標(biāo)題和右面的小按鈕,稱為 action)
tabBar:一個框架的分頁,分頁內(nèi)容另有設(shè)置
body:頁面的主要部分,對于截圖是打卡記錄
bottomNavigationBar:底部的導(dǎo)航欄,對于截圖是展示次數(shù)以及轉(zhuǎn)換
Flutter 內(nèi)部的狀態(tài)管理
聲明式編程
我先念一段上網(wǎng)找到的定義:
命令式編程就像它的名字一樣,它由開發(fā)者我們一步一步的告述計(jì)算機(jī),執(zhí)行一系列的操作,然后得到想要的結(jié)果,起主要作用的是開發(fā)者,計(jì)算機(jī)只是幫助開發(fā)者執(zhí)行計(jì)算而已。我們?nèi)粘J褂玫拇蠖鄶?shù)語言都屬于命令式。
而聲明式編程卻與此相反,它不是告述計(jì)算機(jī)做什么做,而是直接告述計(jì)算它想要的結(jié)果,至于怎么做,由預(yù)先寫好的程序依據(jù)一定的算法由計(jì)算機(jī)自動推算出來。這類定義比如 SQL,Vue 的響應(yīng)式組件。
官方給了個這個公式:
UI = f(state)
Flutter 部件的構(gòu)造過程,如這個公式所見,是這樣的:
我們有一個UI,或者說部件,的構(gòu)造函數(shù),里面寫好了這個部件需要接收,或者監(jiān)聽的狀態(tài)。我們通過創(chuàng)建,修改這個狀態(tài),讓程序組建/更新我們的部件。這個狀態(tài)就是我們希望的結(jié)果。這說起來十分拗口,我們上兩個例子。
StatefulWidget 內(nèi)部管理和 setstate
之前我們提到的部件,都是 Stateless 部件,也就是說,這個部件的狀態(tài)不會變,在我們一開始渲染的時候,就寫死了。
但是,狀態(tài)有時候是需要更新的。比如說,最開始那個計(jì)數(shù)器應(yīng)用,我們需要記下來目前數(shù)字是多少,并且我們需要能響應(yīng)添加和減少。鑒于這個,我們需要引入 StatefulWidget 來實(shí)現(xiàn)這個。
StatefulWidget 依靠 setState 來刷新部件,我們看一下計(jì)數(shù)器代碼。
StatefulWidget 適合于一個小部件內(nèi)部短時狀態(tài)的維護(hù)。如果我們要搞牽扯到許多部件,乃至于各個頁面的共同狀態(tài),就很難辦了。這里我要給大家介紹一個我日常在使用的狀態(tài)管理器:GetX。
GetX
GetX 是三個庫的集合:狀態(tài)管理,路由管理,和依賴管理。這里只關(guān)注狀態(tài)管理。
GetX 觀察者模式狀態(tài)管理
第一個狀態(tài)管理使用的是obs->觀察者模式,我們記住這么幾點(diǎn):
在變量初始化的時候,初始化值的后面添加
.obs
來使其可觀察化使用
Obx
部件來渲染需要用到可觀察化變量的部件使用平常的方法修改可觀察化變量的值
比如這個計(jì)數(shù)器應(yīng)用:
GetX 控制器類狀態(tài)管理
再給大家介紹一下GetxController
,我 PDA 用的后者更多。
我先給大家介紹 MVC 架構(gòu),Model 模型,是說程序的功能??刂破魇呛鸵晥DView進(jìn)行交流,View視圖就是顯示了。View 通過 Controller 獲取 Model 中的東西。
每個 GetX Controller 都是繼承 GetController 虛擬類的一個類。這個類里面,除了你要使用到的值和方法,還有兩個方法:
onInit():在這個控制器初始化的時候使用。
onReady():在這個控制器剛初始化(時間大約一幀后)運(yùn)行,處理異步請求。
在使用控制器的時候,我們可以直接用:
建議閱讀 Traintime PDA 代碼中的controller/sport_controller.dart
,repository/xidian_sport/xidian_sport_session.dart
,以及 page 下面關(guān)于體育部件的代碼。以下這段展示的是我程序?qū)?yīng)的模型各組成的部分。
Controller(GetX Controller) -- Model(Dio網(wǎng)絡(luò)庫) -- View(Flutter)
雜項(xiàng)
路由棧
棧是先進(jìn)后出的結(jié)構(gòu),而路由棧里面,存的是每個頁面的信息了。在 Flutter 中,我們這么處理路由棧:
這里面的 context 是指,這個應(yīng)用,或者這個部件的狀態(tài)。
Dio 網(wǎng)絡(luò)插件
Flutter 提供了很多的插件,來方便我們的開發(fā)體驗(yàn)。其中最著名的就是 Dio 網(wǎng)絡(luò)庫。他是一個異步網(wǎng)絡(luò)訪問庫,使用方式和 axios 比較像。
先說明一下攔截器,它可以在獲取回復(fù)/發(fā)送請求時,先攔截之,然后對該包進(jìn)行修改。
Dio 類的定義,其中我用到了攔截器和對基地址的設(shè)置,設(shè)置了這個,后面的訪問就可以輸入那個網(wǎng)站的子路由了。
Dio 的使用示例,它可以支持 POST,GET 等常見的 HTTP 請求方式??梢栽O(shè)定傳輸參數(shù),請求頭等很多東西。它的返回和 axios 大致相同,有響應(yīng)數(shù)據(jù),響應(yīng)代碼等。
存儲
Dart 操作文件的函數(shù)
path_provider
作為一個跨平臺的開發(fā)框架,F(xiàn)lutter 要能適應(yīng)很多方面,其中最主要的就是存儲位置。我們要存儲一個文件的時候,需要在不同設(shè)備上,找到對應(yīng)的位置。而在很多設(shè)備上,相同類型文件的存儲地方是不一致的。path_provider
能夠讓我們找到相應(yīng)的位置。具體使用方式請參閱它的文檔。
以下是我程序的一份示例:
shared_preferences
我們程序更多的是要在本地存儲一些簡單的設(shè)置信息,具體來說,是很簡單的 key-value 東西了。比如說,你的學(xué)號和密碼是什么,你的宿舍號之類。我們使用 shared_preferences 來解決這個問題。
推薦閱讀
https://dart.dev/language? ? ? ? ? ?Dart 語言官方簡介
https://flutter.cn/docs/get-started/codelab? ? ? ? ? ? ? ? ? Flutter 上手教程
https://flutter.cn/docs/development/ui/layout/tutorial? ? ? ?布局構(gòu)建教程
https://github.com/BenderBlog/watermeter? ? ? ? ? ??Traintime PDA (Watermeter) 代碼