1.為什么要學習Flutter?
對于移動端開發(fā)人員來說,跨平臺技術(shù)一直是關(guān)注的重點,從H5,React Native到Flutter,我們似乎一直在尋找一種能“一套代碼洽损,多端運行”,同時還能有不俗的用戶體驗的技術(shù)。對于當前的大前端來說潮太,React Native的綜合成熟度和生態(tài)都要比Flutter好一些,對于中短期項目我們可能會選用前者,但是對于更長期的項目和發(fā)展來說铡买,F(xiàn)lutter是一種更徹底的解決方案更鲁,渲染能力和平臺一致性以及性能,它都具有更大的優(yōu)勢奇钞。
我們從Web容器時代澡为,比如H5,Cordova,微信小程序,Ionic,這種基于web相關(guān)技術(shù)通過瀏覽器組件來實現(xiàn)界面及其功能景埃,在android上是WebView媒至,IOS上為UIWebView,這種采用原生內(nèi)嵌的瀏覽器來進行渲染谷徙,雖然可以做到多端一致拒啰,對于用戶體驗很是有限,尤其是一些復雜的UI邏輯完慧。
-
對于泛Web容器時代來說谋旦,代表的技術(shù)有React Native和Weex∑妫基本上是完全放棄了瀏覽器控件的渲染蛤织,而是采用原生自帶UI組件實現(xiàn)代替了核心的渲染引擎,僅僅保持必要的基本控件渲染能力鸿染,簡化渲染指蚜,保證了良好的渲染性能。這個時代總體來說是做一個平衡涨椒,依然采用了前端友好的Js進行開發(fā)摊鸡,原生來接管繪制,依托于Js虛擬機的Js代碼提供所需的UI控件的實體蚕冬。
但是對于這種映射來說免猾,在維護各個平臺的API升級方面往往需要巨大的成本,對于Android/Ios平臺上使用React Navie囤热,對開發(fā)人員的要求不僅僅是懂React那么簡單猎提,而是還需要懂兩個平臺的一些基礎(chǔ)開發(fā)知識。
-
接下來是自繪引擎時代旁蔼,即從頭到尾寫一套跨平臺的UI框架锨苏,包括渲染邏輯甚至開發(fā)語言。所以對于移動端的開發(fā)人員來說棺聊,其實還是有一定的學習成本的(但對于已掌握一門編程語言的人幾乎可以忽略不計)伞租,但從平臺一致性,維護成本限佩,性能以及開發(fā)效率上都是全方位碾壓之前兩個時代的技術(shù)葵诈。
1.Dart語言同時支持JIT和AOT裸弦。開發(fā)周期使用JIT,大大縮短開發(fā)周期作喘,調(diào)式模式支持有狀態(tài)的熱重載理疙;而發(fā)布期使用AOT,本地代碼的執(zhí)行更高效徊都,代碼性能和用戶體驗也更優(yōu)秀沪斟。Dart避免了搶占式調(diào)度和共享內(nèi)存,可以在沒有鎖的情況下進行對象分配和垃圾回收暇矫,在性能方面表現(xiàn)很是卓越主之。
2.渲染引擎依靠跨平臺的Skia圖像繪制引擎,Skia將使用Dart構(gòu)建的抽象的視圖結(jié)構(gòu)結(jié)構(gòu)數(shù)據(jù)加工成GPU數(shù)據(jù)李根,然后數(shù)據(jù)通過OpenGL最終提供給GPU渲染槽奕,因為安卓上已經(jīng)有Skia引擎了,這也是Flutter Android SDK 要比 Flutter iOS SDK 小一些的原因房轿,打包后的apk也會比ipa小粤攒。
明白了為什么要學習Flutter,我們就更能堅定我們的腳步囱持,要學好Flutter夯接,就要先學習它的開發(fā)語言Dart,下面我們來看看Dart基礎(chǔ)語法:
2.Dart基礎(chǔ)語法
數(shù)據(jù)類型及其相關(guān)
含義 | 使用 | |
---|---|---|
int |
整數(shù)纷妆,范圍為 -2^63 到 2^63 - 1. | int x = 1;//沒有小數(shù)點就是int |
double |
浮點數(shù)盔几,64位(雙精度)浮點數(shù) | double y = 1.1;//有小數(shù)點就是浮點數(shù) |
num |
num 是數(shù)字類型,既可以表示整數(shù)掩幢,也可以表示浮點數(shù)逊拍,具體看賦的值 | num x = 1;//num x是整數(shù) num y = 1.1;//num y是浮點數(shù) |
String |
字符串 Dart字符串采用UTF-16編碼 可以使用單引號或雙引號來創(chuàng)建字符串 | var s1 = 'string'; var s2 = "string";字符串拼接采用“+” |
bool |
布爾值 | var isTrue = true; |
List |
List<E> E 表示 List 里的數(shù)據(jù)類型 用中括號來賦值 | List<int> list = [1, 2, 3]; |
Set |
Set<E> E 表示 Set 里的數(shù)據(jù)類型 用大括號來賦值 | Set<String> halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'}; |
Map |
Map<K, V> K 是 Key 的數(shù)據(jù)類型,V是 Value 的數(shù)據(jù)類型 | Map<String,String> gifts = { // Key: Value 格式 'first': 'partridge', 'second': 'turtledoves', 'fifth': 'golden rings'}; |
Runes |
表示采用 UTF-32 的字符串,用于顯示 Unicode 因為Dart字符串是UTF-16际邻,因此在Dart中表示32位的Unicode值需要Runes這個特殊語法芯丧。 | Runes input = new Runes('\u{1f600}'); print(new String.fromCharCodes(input)); 打印出來是笑臉emoji:?? |
在Dart中,所有類型都是對象類型世曾,都繼承自頂層類型Object缨恒,因此一切變量都是對象,數(shù)字轮听,布爾值骗露,函數(shù)和null都不能例外。
未手動賦值的變量的值都是null蕊程,比如定義一個全局變量 int a,不進行手動賦值驼唱,打印出來a的值為null藻茂。
默認定義的變量都是public的,加個“_”則為private,限制范圍并不是類訪問級別的,而是庫訪問級別辨赐。
-
我們可以用var來聲明變量而不需要指定特別的數(shù)據(jù)類型优俘,但是一旦賦值后就不能改變數(shù)據(jù)類型的,而使用dynamic關(guān)鍵字就可以掀序。
1.為什么var能定義所有變量帆焕?因為var 并不是直接存儲值,而是存儲的值的對象的引用不恭,例如:var content = 'Dart 語法' 這句叶雹,是名字為 content 的 var 變量存儲了值為 'Dart 語法' 的 String 對象的引用,所以 var 才能定義任何變量换吧。
2.什么情況下用dynamic呢折晦?當這個變量沒法用 Dart 的類型來表示時,比如 Native 和 Flutter 交互沾瓦,從 Native 傳來的數(shù)據(jù)满着。所以你會看到 PlatformChannel 里有很多地方使用到了 dynamic。
final和Java中的final差不多贯莺,使用final和const的時候可以把var省略风喇,使用const的時候如果是類里的變量就必須加static,全局變量時不需要的缕探。在使用構(gòu)造器的時候也可以使用const變量魂莫,但是這個時候就不需要方法體了。const是編譯時常量撕蔼,在編譯時就進行初始化了豁鲤,但是final變量是當類創(chuàng)建的時候才初始化。
函數(shù)
-
Dart不支持方法重載鲸沮,而是提供了可選命名參數(shù)和可選參數(shù):
給參數(shù)增加{}琳骡,以 paramName: value 的方式指定調(diào)用參數(shù),也就是可選命名參數(shù)讼溺;
給參數(shù)增加[]楣号,則意味著這些參數(shù)是可以忽略的,也就是可選參數(shù)怒坯。
//要達到可選命名參數(shù)的用法炫狱,那就在定義函數(shù)的時候給參數(shù)加上 {}
void test1Flags({bool bold, bool hidden}) => printTest("$bold , $hidden");
//定義可選命名參數(shù)時增加默認值
void test2Flags({bool bold = true, bool hidden = false}) => printTest("$bold ,$hidden");
//可忽略的參數(shù)在函數(shù)定義時用[]符號指定
void test3Flags(bool bold, [bool hidden]) => printTest("$bold ,$hidden");
//定義可忽略參數(shù)時增加默認值
void test4Flags(bool bold, [bool hidden = false]) => printTest("$bold ,$hidden");
//可選命名參數(shù)函數(shù)調(diào)用
test1Flags(bold: true, hidden: false); //true, false
test1Flags(bold: true); //true, null
test2Flags(bold: false); //false, false
//可忽略參數(shù)函數(shù)調(diào)用
test3Flags(true, false); //true, false
test3Flags(true,); //true, null
test4Flags(true); //true, false
test4Flags(true,true); // true, true
但是不可以同時把這符號同時放到同一個函數(shù)聲明下面,比如:
void testFun({int a},[int b]){} //錯誤的寫法
- 對于只有一行的函數(shù)可以用 =>來進行簡寫
void main() => runApp(MyApp());
等價于
void main(){
return runApp(MyApp());//runApp() 返回的是 void
}
- Dart還提供了命名構(gòu)造函數(shù)的方式剔猿,使得類的實例化過程語義更加清晰视译。如下面實例,Point類中有兩個構(gòu)造函數(shù)归敬,TestA.bottom和TestA
class TestA {
num x, y, z;
TestA(this.x, this.y) : z = 0; // 初始化變量z
TestA.bottom(num x) : this(x, 0); // 重定向構(gòu)造函數(shù)
TestA.top():x=3; //重定向構(gòu)造器
void printInfo() => print('($x,$y,$z)');
}
var p = TestA.bottom(100);
p.printInfo(); // 輸出(100,0,0)
var p = TestA.top();
p.printInfo(); // 輸出(3,0,0)
對于這種寫構(gòu)造器的方式酷含,和我們用Java自定義控件構(gòu)造器嵌套有著異曲同工之妙鄙早。
復用(extends,implement,mixin,on)
- 在Dart中,你可以對同一個父類進行繼承或接口實現(xiàn)椅亚,但不可同時繼承和實現(xiàn)限番。
class TestA{
num x,y,z;
TestA(this.x,this.y):z =0;
TestA.bottom(num x):this(x,0);
TestA.top():x=3;
void testCall() => print("($x=======,$y======)");
}
class TestB extends TestA{
TestB(num x, num y) : super(x, y);
@override
void testCall() { //重寫testCall的實現(xiàn)
// TODO: implement testCall
super.testCall();
}
}
class TestC implements TestA{
@override
num x;
@override
num y;
@override
num z;
@override
void testCall() { //必須實現(xiàn)該方法
// TODO: implement testCall
}
}
可以看到,對于接口的實現(xiàn)方式呀舔,我們只是拿到了TestA的一個空殼子弥虐,不能復用原有的實現(xiàn),這個時候媚赖,我們可以用混入(Mixin)霜瘪,混入鼓勵代碼重用,可以視為具有實現(xiàn)方法的接口省古,不僅可以解決Dart缺少多重繼承的問題粥庄,同時也能避免多重繼承可能導致的歧義(菱形問題)。要使用混入豺妓,我們需要使用with關(guān)鍵字惜互,但是對于混入也有一定的要求,被混入的類不能有自己的構(gòu)造器琳拭,否則就會提示錯誤训堆。
class D{
num x,y;
D(this.x,this.y);
void testCall() => print("($x=======,$y======)");
}
class E with D{ //編譯失敗,提示D類不能被混入白嘁,因為已經(jīng)定義了構(gòu)造器坑鱼。
}
至于為什么不能能用構(gòu)造器,我覺得Mixin目前的實現(xiàn)是以完全忽略構(gòu)造函數(shù)絮缅,忽略構(gòu)造函數(shù)調(diào)用鏈的方式實現(xiàn)的鲁沥,因此只支持隱式的構(gòu)造函數(shù)。一旦放開mixin的構(gòu)造函數(shù)支持耕魄,Dart勢必需要支持一套新的關(guān)鍵字語法画恰,來支持Mixin類繼承鏈上的構(gòu)造轉(zhuǎn)發(fā),代價太大吸奴。
- mixin還可以和on進行配合使用**允扇,如果一個類mixin某個類,就必須先實現(xiàn)on的那個類则奥,詳情可以看看下面的代碼:
class F{
}
mixin G on F{
}
class H extends F with G{ //必須先繼承F類才可以考润,如果F是接口,那就必須先實現(xiàn)接口F
}
運算符
這里列出一些常用的表達读处,過于簡單的就不列出來了:
操作符 | 含義 | 例子 |
---|---|---|
as | 類型轉(zhuǎn)換 | (emp as Person).firstName = 'Bob';<br />如果emp為null糊治,拋出異常 |
is | 判斷是否是某個類型,如果是的話,就返回 true | if (emp is Person) { // 如果 emp 是 Person 類型 emp.firstName ='Bob'; }<br />如果emp 為null ,返回false |
is! | 判斷是否不是某個類型罚舱,如果不是的話井辜,就返回 true | if (emp is! Person) { // 如果 emp 不是 Person 類型 } <br /><br /如果emp 為null ,返回false |
?. | 類似于kotlin中的空安全揖赴,為空跳過下面的邏輯,避免拋出異常 | |
??= | 用默認值兜底的一種方式 | a??=value抑胎,如果a為null,則給a賦值value渐北,否則跳過 |
?? | 類似于三元表達式 | a ?? b 類似于(a != null)? a : b |
.. | 級聯(lián)操作符阿逃,允許你對同一對象進行一系列的操作。 | querySelector('#confirm') // Get an object. ..text = 'Confirm' // Use its members. ..classes.add('important') ..onClick.listen((e) => window.alert('Confirmed!')) |
- 運算符重載
我們可以通過運算符重載來實現(xiàn)更復雜的功能
// 自定義相加運算符赃蛛,實現(xiàn)向量相加
Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
// 覆寫相等運算符恃锉,判斷向量相等
bool operator == (dynamic v) => x == v.x && y == v.y;
3.寫在最后
最近在研究Flutter,寫這篇文章也是做一個簡單的終結(jié)呕臂,希望更多的人加入Flutter的開發(fā)當中破托,最近開了一個淘寶店鋪,店鋪名字叫“程哥的日用百貨鋪”歧蒋,這里附上鏈接 https://shop105359436.taobao.com/?spm=a1z10.1-c-s.0.0.c1b42dd2nh1YwP 土砂,希望大家多多支持!