Flutter001-Why Flutter Why Dart

Flutter是開源并且免費(fèi)的,擁有現(xiàn)代的響應(yīng)式框架特性,高速的2D渲染引擎,方便快捷的開發(fā)工具以及各種開箱即用的UI組件庫。
Flutter使用Dart作為開發(fā)語言,這是一門簡潔、強(qiáng)類型的面向?qū)ο蟮木幊陶Z言。為什么Dart是一門適合移動(dòng)端開發(fā)的語言呢?首先是因?yàn)樗男阅?,包括開發(fā)和生產(chǎn)環(huán)境下的性能,它包含了兩種編譯模式Just-In-Time(即時(shí)編譯)和Ahead-Of-Time(運(yùn)行前編譯)。
JIT即時(shí)編譯可以使得Flutter在應(yīng)用運(yùn)行時(shí)同時(shí)執(zhí)行代碼編譯,這就讓Flutter具備了極速的開發(fā)體驗(yàn)。具備亞秒級(jí)的熱重載(Hot Reloading)特性。
AOT運(yùn)行前編譯意思就是將代碼庫直接編譯成原生的ARM指令集。這樣的方式對(duì)于應(yīng)用來說意味著快速的啟動(dòng)速度和可預(yù)見的卓越性能。
Dart同時(shí)具有強(qiáng)類型(static types)和類型推導(dǎo)(sound type)的特性。這樣可以讓Bug暴露于編譯期間,更放心的應(yīng)付代碼量的增長。
當(dāng)然,使用Flutter也有別的原因。

設(shè)置環(huán)境

我們可以通過https://flutter.io/setup](https://flutter.io/setup來設(shè)置自己的開發(fā)環(huán)境,有一點(diǎn)要注意的是如果大家用的是android studio 那么版本不能低于3.1.X。
如果大家使用的是模擬器進(jìn)行開發(fā)調(diào)試的話,理論上可以使用任何一種設(shè)備集成任何一種SDK。如果不確定自己該使用哪個(gè)系列或版本的話,建議使用Pixel 2 集成最常用的SDK就好了(設(shè)置項(xiàng)選擇默認(rèn)的就好了)。

Flutter

先通過些列子來過一下Flutter的框架(reactor framework)、渲染引擎(rendering engine)和開發(fā)工具(development tools)。讓我們對(duì)這個(gè)事情有一個(gè)更為宏觀的認(rèn)知。
項(xiàng)目創(chuàng)建成功之后你會(huì)發(fā)現(xiàn)存在一個(gè)iOS和Android平臺(tái)的文件夾。這是因?yàn)镕lutter的代碼會(huì)編譯成不同平臺(tái)的代碼。在新建的項(xiàng)目中,我們只需要把目光專注到lib下的main.dart文件就好了。這個(gè)文件就是Flutter啟動(dòng)的入口文件。之后我們?cè)俳榻B其他文件的作用。。。

Flutter的框架(reactor framework)

前面已經(jīng)說了,F(xiàn)lutter包含了一個(gè)先進(jìn)的響應(yīng)式框架和2D的渲染引擎,這里說的響應(yīng)式框架到底指的是什么呢?一個(gè)View被構(gòu)建為一個(gè)由許多個(gè)widget組成的不可變(immutable)的樹狀結(jié)構(gòu)。widget是Flutter的基礎(chǔ)單元,widget是用dart編寫的來描述用戶界面的代碼塊。在Flutter的世界里幾乎所有的都是用Dart寫好的widget小部件。
沒有獨(dú)立的布局文件來自定義排列和文本對(duì)齊等,所有的代碼都是在一個(gè)Flutter widget中定義。當(dāng)一個(gè)widget的state發(fā)生改變時(shí),比如發(fā)生了用戶輸入或者觸發(fā)了動(dòng)畫,widget會(huì)根據(jù)它當(dāng)前的最新的state來進(jìn)行重新繪制。這種方式節(jié)省了開發(fā)者的時(shí)間,因?yàn)檫@種UI可以直接按照某一種state來進(jìn)行描述。這樣我們就不需要再為widget狀態(tài)的改變而去寫多余的代碼了。
Flutter提供了框架來區(qū)別出widget所發(fā)生的改變,并將這個(gè)改變更新到渲染樹上。渲染引擎是我們應(yīng)用中的一部分,所以我們不需要將UI渲染的代碼和原生平臺(tái)做橋接。因?yàn)橐苿?dòng)應(yīng)用開發(fā)中,布局和渲染的觸發(fā)要比調(diào)用平臺(tái)的攝像頭這種操作更為頻繁。通過在應(yīng)用層把所有渲染工作完成,F(xiàn)lutter就可以快速渲染并對(duì)widget樹重新渲染,讓應(yīng)用具備更豐富和流暢的動(dòng)畫效果。這種渲染引擎建立于Skia之上,那么什么又是Skia呢?skia是使用Dart編寫的2D圖形渲染框架,它提供了在iOS和android平臺(tái)繪制widget的能力。所以平臺(tái)上的系統(tǒng)僅需要提供一個(gè)畫布(canvas)就好了,widget的渲染就在Flutter內(nèi)部的引擎中執(zhí)行了。AOT模式會(huì)編譯成機(jī)器指令進(jìn)而執(zhí)行。如果任何人希望編寫擴(kuò)展到其他平臺(tái)的應(yīng)用(非iOS和Android),比如相機(jī)和Wi-Fi插件,只要數(shù)據(jù)可以從操作系統(tǒng)傳給畫布(canvas),那么Flutter應(yīng)用就有可能編譯到你的目標(biāo)平臺(tái)運(yùn)行,理論上(我猜測)。

開發(fā)工具

這里有幾個(gè)名詞,熱重載(Hot reloading)、widget檢查工具(widget inspector)和代碼格式化工具(code auto-formatter)。這幾個(gè)工具在開發(fā)中真的提高了我們的效率。
Flutter的熱重載工具,能夠讓UI發(fā)生變化后就即刻進(jìn)行重新渲染,這個(gè)功能基于即時(shí)編譯JIT。而且熱重載工具可以在模擬器和真機(jī)上運(yùn)行。如果我們?cè)陂_發(fā)過程中想讓APP重置,那么我們可以通過點(diǎn)擊綠色的按鈕讓應(yīng)用直接重啟。那么這個(gè)重啟的操作會(huì)將應(yīng)用的state重置。
widget檢查器,有點(diǎn)像Chrome瀏覽器的web檢察器。檢查器可以讓我們?cè)谠O(shè)備上的各個(gè)像素之間來回切換,也可以在widget tree中上下切換還可以一行一行的調(diào)試在應(yīng)用里創(chuàng)建的widget。
打開widget檢查器,可以很方便的了解內(nèi)置widgets的結(jié)構(gòu)。
X的圓形按鈕,這個(gè)圖標(biāo)是inspect的圖標(biāo);
有兩個(gè)方形的圖標(biāo)(toggle platform mode),用來在不同設(shè)備平臺(tái)之間進(jìn)行切換;
在我們的widget tree中,加粗的widget表示是我們自己創(chuàng)建的,而非內(nèi)置的widget。如果此時(shí)你雙擊選擇某個(gè)加粗的widget時(shí),對(duì)應(yīng)的widget代碼就會(huì)高亮顯示。在應(yīng)用的某一處雙擊也可以達(dá)到同樣的效果。

在常量前加一個(gè)下劃線來表示局部常量(僅在這個(gè)文件中生效)
const _padding = EdgeInsets.all(20.0);

最后,說一說代碼格式化,在每一個(gè)屬性的后面加上一個(gè)逗號(hào),可以被代碼格式化工具(code auto-formatter)檢測到并自動(dòng)換行。

小部件 Widget

Widgets are the foundation of Flutter apps. A widget is a description of part of a user interface.

嗯嗯,F(xiàn)lutter的世界里一切皆組件,所見之處都是widget。那么它們又被分為Stateless無狀態(tài)的和stateful有狀態(tài)的兩種。stateless組件是不可變的,這意味著該組件一旦創(chuàng)建那么它的所有屬性都是最終狀態(tài)。在widget創(chuàng)建的時(shí)候,我們可以傳遞參數(shù),比如背景色。但一旦創(chuàng)建好,這些屬性就是不可改變的了。同樣這類組件也是不可交互的。
另一類呢,stateful widgets 就是可以創(chuàng)建start對(duì)象。
那么無狀態(tài)的組件是怎么布局的呢?
我們?cè)趯?shí)例化widget的時(shí)候,會(huì)傳遞給它一些參數(shù),有些是必須的,有些是可選的。我們可以通過輸入一些屬性值來定制化一個(gè)組件的對(duì)象。大多數(shù)的組件都會(huì)有 height、width和child屬性,用來嵌套其他的子組件。child屬性只允許嵌套一個(gè)子widget,而children屬性則可以嵌套多個(gè)。當(dāng)然child是可選參數(shù),F(xiàn)lutter允許我們創(chuàng)建不包含子組件的組件。

runApp函數(shù)可以接受任意的組件作為參數(shù)

MateralApp組件中 debugShowCheckedModeBanner: false -- 快速移除角標(biāo)

為了讓我們的函數(shù)看起來簡潔,我們可以把某些組件在函數(shù)外部創(chuàng)建。我們不應(yīng)該在工程和應(yīng)用里硬編碼所有的widget,這樣它們就完全不能重新被創(chuàng)建。更建議是把創(chuàng)建在函數(shù)外部的抽離成可以傳參的組件。

center組件只對(duì)它第一層child有效

自定義Widget

自定義widget可以為應(yīng)用添加個(gè)性化風(fēng)格,可以設(shè)計(jì)符合品牌或主題的內(nèi)容。widget完全可以自定義,我們也可以擴(kuò)展和自定義現(xiàn)有widget,或?qū)⑺鼈冏鳛檎w框架來使用。

route 是頁面的別名

Clarification - a route takes you to a page , or screen.

our code and animation mentions 'categoryRoute' and 'converterRoute' , but these are more aptly named 'categoryScreen' and 'converterScreen'. These widgets are responsible for the UI at the route's destination.

有狀態(tài)的組件 stateful widget

之前我們說widget是不可修改的,也就是說它沒有狀態(tài)可言,也不能在創(chuàng)建之后再修改。我們?cè)趹?yīng)用中加入一些狀態(tài),這樣widget就可能因?yàn)橛脩舨僮鞫l(fā)生一些變化。
當(dāng)用戶進(jìn)行操作widget發(fā)生變化時(shí),我們希望保持狀態(tài)并響應(yīng)用戶交互和事件,這時(shí)候就需要用到stateful widget。

stateful widget本身也是不可改變的

因?yàn)樗旧淼牟豢筛淖?,所以它用createState方法創(chuàng)建一個(gè)state對(duì)象。state對(duì)象存儲(chǔ)了widgetState信息并且可以在對(duì)象的生命周期中發(fā)生改變。實(shí)例化過程中傳入的文本是不可改變的,但是存在于狀態(tài)對(duì)象中的顏色卻是可以改變的。仔細(xì)觀察我們的stateful組件的方法,下劃線開頭的類說明它是一個(gè)私有類,因而唯一能夠訪問其資源的就是類本身了。在Flutter中為了保持這種一致性,所以我們會(huì)看到狀態(tài)對(duì)象也命名為對(duì)應(yīng)的statefulWidget。
下面來聊一聊狀態(tài)對(duì)象本身。它提供了一套可調(diào)用的方法用于改變各種狀態(tài),比如說,如果我們要在每次點(diǎn)擊矩形的時(shí)候改變其顏色,為了方便我們選取FlatButton組件作為容器,這樣就可以使用onPressed事件屬性了。這個(gè)widget在狀態(tài)的數(shù)據(jù)發(fā)生改變時(shí)不會(huì)自動(dòng)觸發(fā)重新渲染的過程。改變顏色這個(gè)事情就交給了widget的實(shí)現(xiàn)者來調(diào)用setState方法,在下一幀中觸發(fā)重建。

僅僅只有發(fā)生改變的widget會(huì)被重建

要注意的是文本是不會(huì)被修改的,因?yàn)樗鼪]有存儲(chǔ)在狀態(tài)對(duì)象中。當(dāng)Stateful組件被初始化的時(shí)候,我們可以通過重新initState方法來加入一些自定義的代碼。
Stateful 組件也可以用于動(dòng)畫,這一過程發(fā)生在UI從一個(gè)狀態(tài)變?yōu)榱硪粋€(gè)狀態(tài)的過程中?;龅牟藛尉褪且粋€(gè)簡單的例子,它內(nèi)部存儲(chǔ)了關(guān)閉、隱藏和開啟三個(gè)狀態(tài)屬性,

顏色

顏色的透明,范圍從00開始,即完全透明,可增加到FF,即完全不透明。

Text Input

能夠吸引用戶的應(yīng)用有什么秘訣?當(dāng)然是人機(jī)交互了。人機(jī)交互包括文本輸入,下拉列表選項(xiàng),手勢和數(shù)據(jù)API交互等等。Flutter的Widget通常以內(nèi)置方式捕捉用戶的操作,我們先從文本的輸入開始聊。
在Flutter中通過TextField組件來捕獲文本輸入,如果要?jiǎng)?chuàng)建表格的話就需要用TextFormField小部件。
通過keyboardType 來設(shè)置虛擬鍵盤類型

字符

Flutter支持Unicode和emojis。
那么如何獲取用戶輸入的字符串呢?類似前面所說,在Flutter中都是通過用戶的操作來進(jìn)行相應(yīng)的反饋進(jìn)而修改Widget小部件的內(nèi)置屬性。檢索用戶輸入的文本有三種方法,onChanged方法、onSubmitted方法和controller控制器。

  1. onChanged屬性接受一個(gè)函數(shù),每次文本更改時(shí)都會(huì)調(diào)用此函數(shù)。在函數(shù)內(nèi)部可以設(shè)置狀態(tài),檢查文本的輸入是否存在驗(yàn)證錯(cuò)誤。
  2. 當(dāng)用戶點(diǎn)擊return鍵時(shí),將調(diào)用onSubmitted屬性,同樣也傳遞一個(gè)函數(shù)來執(zhí)行此操作。只有用戶完成輸入后才做響應(yīng)。這種對(duì)于需要驗(yàn)證數(shù)據(jù)有效性的場景十分有用。
  3. 控制器屬性允許對(duì)用戶輸入進(jìn)行更多自定義和控制。我們可以創(chuàng)建并傳入文本編輯控制器,添加監(jiān)聽此控制器的listener,這不僅允許我們?cè)谟脩糨斎朊總€(gè)字符時(shí)進(jìn)行響應(yīng),還允許我們修改和覆蓋文本字段的內(nèi)容。比如自動(dòng)補(bǔ)全功能。
    在用戶操作文本輸入這一塊,我們可以定義鍵盤為數(shù)字鍵盤、郵件鍵盤,但是我們沒有辦法規(guī)避用戶輸入的空格,所以在每一個(gè)input框中都最好加入驗(yàn)證。

手勢

Flutter的手勢系統(tǒng)中有兩個(gè)獨(dú)立的層,第一層時(shí)原始指針事件,它描述了指針在屏幕上的位置和移動(dòng),這些包括指針向下、指針移動(dòng)、指針向上和指針取消事件。第二層是手勢,手勢表示從多個(gè)單獨(dú)指針事件中識(shí)別的語義動(dòng)作,比如點(diǎn)擊、拖動(dòng)和縮放。
手勢在生命周期中傳遞多重事件,如果我們想要將自己的交互性添加任意一個(gè)小部件上,我們可以將這個(gè)widget小部件包裹在GestureDetector小部件中。

響應(yīng)式

響應(yīng)式,意味著我們需要一種基于屏幕方向或其高度和寬度重新調(diào)整子widget的方法。在Flutter中,可以使用媒體查詢(media query)、方向構(gòu)建器(orientation builder)和布局構(gòu)建器(layout builder)。
MediaQuery.of 可以提供有關(guān)當(dāng)前設(shè)備上運(yùn)行的應(yīng)用程序的大小,方向和其他數(shù)據(jù)。
orientation builder widget方向構(gòu)建器的builder屬性,是一個(gè)將父類widget方向作為參數(shù)的函數(shù),我們可以使用此方向來布局子widget。
布局構(gòu)建器的builder屬性是一個(gè)帶有盒子約束參數(shù)的函數(shù),盒子約束可以提供有關(guān)當(dāng)前尺寸約束的信息,比如父類widget的高度或縱橫比。
這兩個(gè)構(gòu)建器和媒體查詢之間的區(qū)別在于它們可以特定于一個(gè)小部件而不是這個(gè)應(yīng)用程序。同樣如果我們使用的是構(gòu)建器,那么在用戶更改APP的尺寸大小時(shí),構(gòu)建函數(shù)會(huì)自動(dòng)重新運(yùn)行。
還有一些其他的方法,比如在widget周圍包含寬高比(aspect ratio 小部件)來強(qiáng)制使用某個(gè)寬高比。比如使用Fitted box 小部件來根據(jù)子widget來進(jìn)行縮放。

package plugin和pubspec.yaml

就像widget小部件一樣,我們?cè)陂_發(fā)新的應(yīng)用或功能時(shí)都不希望重復(fù)造輪子。
package可以創(chuàng)建易于共享的模塊化代碼,我們可以使用Flutter和Dart包,有些與設(shè)備API集成比如Battery包。有些被稱為插件,因?yàn)樗鼈兣ciOS或Android平臺(tái)交互,比如Firebase包。
在pubspec.yaml中包含的信息有名稱、版本、描述、作者和依賴項(xiàng)。Flutter應(yīng)用程序會(huì)依賴Flutter SDK。我們還可以在Pubspec中指定資源和字體。當(dāng)我們對(duì)Pubspec進(jìn)行更改時(shí),如果編輯器沒有自動(dòng)執(zhí)行此操作,我們還可以通過命令 flutter packages get來執(zhí)行。這個(gè)命令會(huì)獲取或更新應(yīng)用包所依賴的必須軟件包。Pub創(chuàng)建一個(gè)a.packages文件,該文件從包名稱映射到那些實(shí)際的URI中。即便是導(dǎo)入了大型的軟件包,也只有所調(diào)用的函數(shù)最終會(huì)在發(fā)布模式下編譯為代碼,這是因?yàn)镕lutter使用了tree shaking來刪除生產(chǎn)環(huán)境中的二進(jìn)制文件在編譯過程中產(chǎn)生的冗余和未使用的代碼。

tree shaking:process where redundant and unused code is removed during code compilation.

通過這樣,我們無需指定要導(dǎo)入的特定類,而只需要導(dǎo)入整個(gè)包且不用擔(dān)心有未使用的依賴項(xiàng)。

File assets

所謂 assets,指的是和APP捆綁和部署的文件。在運(yùn)行時(shí)可以訪問并且是只讀的。我們可以通過AssetBundle來訪問這個(gè)文件夾。Flutter中還附帶了rootBundle來訪問主資源包。這里我建議使用DefaultAssetBundle來獲得當(dāng)前的資源。在定位和運(yùn)行測試的時(shí)候,可以使父類widget在運(yùn)行時(shí)替換資源。
使用DefaultAssetBundle.of 來間接加載資源,然后導(dǎo)入dart convert 包來將每一個(gè)單元映射到對(duì)應(yīng)的類里。

異步

API的調(diào)用可能需要一些時(shí)間,在flutter中我們將它包裹在異步操作中。這么操作可以讓應(yīng)用程序繼續(xù)運(yùn)行而不會(huì)被阻止。
當(dāng)調(diào)用返回future的函數(shù)時(shí),會(huì)發(fā)生兩件事。首先,該功能提示要完成的工作并返回一個(gè)不完整的future對(duì)象;然后,當(dāng)值可用時(shí),future對(duì)象將使用該值或返回一個(gè)錯(cuò)誤。
當(dāng)值可用時(shí),將future返回的值保存到變量中,并在函數(shù)上調(diào)用await,我們需要使用async關(guān)鍵字包裝調(diào)用的函數(shù)。
關(guān)于錯(cuò)誤,我們可以使用條件檢查和try-catch語句來捕捉錯(cuò)誤,并且根據(jù)所捕捉的錯(cuò)誤在頁面中顯示與錯(cuò)誤消息關(guān)聯(lián)的UI小部件。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容