開發(fā)FlutterApp之前我們肯定要先了解Dart這門語言及語言的特性熊户、語法等吭服。最近看了大量的Dart語言相關(guān)內(nèi)容,本章會來簡述蝌戒。
目錄
- 概念及優(yōu)點
- 變量
- 函數(shù)
- 閉包
- 異步支持
概念及優(yōu)點:
- Dart:
Google及全球的其他開發(fā)者沼琉,使用 Dart 開發(fā)了一系列高質(zhì)量打瘪、 關(guān)鍵的 iOS、Android 和 web 應(yīng)用闺骚。 Dart 非常適合移動和 web 應(yīng)用的開發(fā)僻爽。
1.高效
Dart 語法清晰簡潔,工具簡單而強大敦捧。 輸入檢測可幫助您盡早識別細(xì)微錯誤。 Dart 擁有久經(jīng)考驗的 核心庫(core libraries) 和一個已經(jīng)擁有數(shù)以千計的 packages 生態(tài)系統(tǒng)
2.快速
Dart 提供提前編譯優(yōu)化习瑰,以在移動設(shè)備和 web 上實現(xiàn)可預(yù)測的高性能和快速啟動济蝉。
3.可移植
Dart 可以編譯成 ARM 和 x86 代碼,因此 Dart 移動應(yīng)用程序可以在 iOS贺嫂,Android 及 更高版本上實現(xiàn)本地運行雁乡。 對于 web 應(yīng)用程序,Dart 可以轉(zhuǎn)換為 JavaScript曲饱。
4.易學(xué)
Dart 是面向?qū)ο蟮木幊陶Z言珠月,語法風(fēng)格對于許多現(xiàn)有的開發(fā)人員來說都很熟悉啤挎。了解Java、JS語言 胜臊,使用 Dart 也就很簡單伙判,也有Swift的一些特性。
5.響應(yīng)式
Dart 可以便捷的進(jìn)行響應(yīng)式編程勒魔。由于快速對象分配和垃圾收集器的實現(xiàn)酱塔, 對于管理短期對象(比如 UI 小部件), Dart 更加高效唐全。 Dart 可以通過 Future 和 Stream 的特性和API實現(xiàn)異步編程。
變量
- var
自動推斷類型(這點與OC弥雹、Java不同)延届,接收任何類型的的變量方庭,但是一旦賦值,類型就不能改變械念,即本來是字符串龄减,之后就只能是字符串(這點與JS不同)。
var a = "字符串";
//主意:如果這樣就會報錯烁巫,類型在第一次指定后就不能改變
a = 1;
原因:Dart是強類型語言宠能,任何變量都有各自的類型,編譯時會根據(jù)首次賦值數(shù)據(jù)的類型來推斷其類型恃鞋,編譯結(jié)束后其類型不能更改亦歉。JS是純粹的弱類型腳本語言肴楷,var只是變量的聲明荠呐。
- dynamic
dynamic與var一樣都是關(guān)鍵詞,聲明的變量可以賦值任意類型對象。聲明的變量可以在后期改變賦值類型呵恢。即本來是字符串媚创,之后可以賦值為number等其他類型。
dynamic a = "字符串";
//不會報錯
a = 1;
- Object
Object與dynamic一樣也是聲明的變量可以賦值任意類型對象鳄橘,聲明的變量可以在后期改變賦值類型瘫怜。
Object b = "hello world";
//不會報錯
b = 10;
不同之處,dynamic聲明的對象編譯器會提供所有可能的組合赠涮,至少不會報錯(但有可能運行時會因為找不到之前預(yù)制的組合暗挑,造成崩潰), 而Object聲明的對象只能使用Object的屬性與方法, 否則編譯器會報錯。
dynamic a = "";
Object b = "";
//編譯器不報錯株憾,不警告晒衩。
print(a.length);
//編譯器會警告報錯(Object沒有l(wèi)ength的getter方法):The getter 'length' is not defined for the class 'Object'
print(b.length);
注意:dynamic可以理解為id類型听系,任何類型都可以轉(zhuǎn)換成id(dynamic)類型,可以用id(dynamic)去接掉瞳,編譯器不會報錯浪漠,但是在運行時可能會產(chǎn)生錯誤出現(xiàn)崩潰現(xiàn)象。
- final
final 為運行時常量该镣。
final修飾的常量必須在聲明的時候就進(jìn)行初始化响谓,而且在初始化之后值不可變娘纷,
final a = "名字";
//會報錯
a = "性別";
- const
const 為編譯時常量律适。
const不僅僅可以聲明常數(shù)變量,也可以聲明常量值以及聲明創(chuàng)建常量值的構(gòu)造函數(shù)棉圈,任何變量都可以有一個常量值眷蜓;
final aList = const[];
const bList = const[]德召;
var cList = const[]汽纤;
這里的aList和bList就是兩個空的蕴坪、不可變的列表集合,而cList則是空的呆瞻、可變的列表集合径玖;
需要注意的是:cList可以重新賦值,可以改變赞赖,而aList和bList不可以重新賦值冤灾;
- 函數(shù)
Dart是面向?qū)ο蟮恼Z言韵吨,所以即使是函數(shù)也是對象,并且有一個類型Function。這意味著函數(shù)可以賦值給變量或作為參數(shù)傳遞給其他函數(shù)吞杭,這是函數(shù)式編程的典型特征芽狗。
1.函數(shù)聲明
返回類型 方法體 (參數(shù)1, 參數(shù)2, ...){
方法體...
return 返回值
}
String getPerson(String name, int age){
return name + '${age}';
}
//如果返回類型不指定時,此時默認(rèn)為dynamic滴劲。
2.箭頭函數(shù)
對于只包含一個表達(dá)式的函數(shù),可以使用簡寫語法鲁捏。
getPerson(name, age) => name+ ', $age' ;
bool isNoble (int atomicNumber)=> _nobleGases [ atomicNumber ] 萧芙!= null ;
3.函數(shù)作為變量(方法對象)双揪、入?yún)?/p>
//函數(shù)作為變量
var method1 = (str){
print(str)
};
method1("kakalala");
//函數(shù)作為參數(shù)
void execute(var callbackMethod){
callbackMethod();
}
//兩種
execute(() => print("xxx"));
execute(method1("kakalala"));
4.可選參數(shù)(可選位置參數(shù)、可選命名參數(shù))
- 可選位置參數(shù):[param1, param2, ...],可以設(shè)置默認(rèn)參數(shù)
包裝一組函數(shù)參數(shù)运吓,用[]標(biāo)記為可選的位置參數(shù)疯趟,并放在參數(shù)列表的最后面:
getPerson(String name, [int age = 99, String gender = "御姐"]){
print ("name = $name, age = $age, gender = $gender");
}
//getPerson() ;這種不傳參是會報錯的迅办。
getPerson(null) ;
getPerson('不知火') ;
getPerson('不知火', 100);
getPerson('不知火', null, "蘿莉");
控制臺輸出
flutter: name = null, age = 99, gender = 御姐
flutter: name = 不知火, age = 99, gender = 御姐
flutter: name = 不知火, age = 100, gender = 御姐
flutter: name = 不知火, age = null, gender = 蘿莉
注意:name參數(shù)是必須傳入的,否則會報錯姨夹。后邊的可選位置參數(shù)如果不傳會是null矾策,傳null還是會返回null贾虽。可選位置參數(shù)可以設(shè)置默認(rèn)參數(shù)绰咽。
- 可選命名參數(shù):{param1, param2, ...}
在傳入的時候地粪,需要指定下對應(yīng)的參數(shù)名蟆技,放在參數(shù)列表的最后面斗忌,用于指定命名參數(shù)旺聚∨榇猓可以設(shè)置默認(rèn)參數(shù)。
getPerson(String name, {int age = 100, String gender = "狼狗"}){
print("name = $name, age = $age, gender = $gender");
}
//getPerson() ;這種不傳參是會報錯的惊窖。
getPerson(null) ;
getPerson('燼天玉藻前') ;
getPerson('燼天玉藻前', age: 99 );
getPerson('燼天玉藻前', gender: "御姐" );
getPerson('燼天玉藻前', age: 99, gender: "奶狗");
控制臺輸出:
flutter: name = null, age = 100, gender = 狼狗
flutter: name = 燼天玉藻前, age = 100, gender = 狼狗
flutter: name = 燼天玉藻前, age = 99, gender = 狼狗
flutter: name = 燼天玉藻前, age = 100, gender = 御姐
flutter: name = 燼天玉藻前, age = 99, gender = 奶狗
注意:固定參數(shù)必須傳入(那怕傳個null)界酒,可選命名參數(shù)可以設(shè)置默認(rèn)參數(shù)嘴秸。
- 默認(rèn)參數(shù)值
默認(rèn)參數(shù)值即我們在方法的參數(shù)列表上面使用 “=” 號給入一個常量值,如果沒有傳入該值的時候凭疮,就使用我們給入的常量值串述。
注意纲酗,不能同時使用可選的位置參數(shù)和可選的命名參數(shù)
//這種是不可以的,錯誤事例右蕊。
getPerson(String name, {int age = 100, String gender = "狼狗"}, [int age2 = 1002, String gender2 = "狼狗2"]){
}
閉包
閉包是一個方法(對象)吮螺,閉包定義在其它方法內(nèi)部,能夠訪問外部方法的局部變量萝风,并持有其狀態(tài)闹丐。
void main() {
// 創(chuàng)建一個函數(shù)add1被因,返回加2
Function add1 = addNum(2);
// 創(chuàng)建一個函數(shù)add2,返回加4
Function add2 = addNum(4);
// 2 + 3 = 5
print(add1(3));
// 4 + 3 = 7
print(add2(3));
}
// 返回一個函數(shù)對象堕花,功能是返回累加的數(shù)字
Function addNum(int addBy){
return (int i) => addBy + I;
}
控制臺輸出:
flutter: 5
flutter: 7
異步支持
Dart代碼運行在一個單線程,如果Dart代碼阻塞了---例如缘挽,程序計算很長時間呻粹,或者等待I/O等浊,整個程序就會凍結(jié)。
Dart異步函數(shù):Future轧飞、Stream撒踪,設(shè)置好耗時操作后返回,不會阻塞線程掸绞。
async和await關(guān)鍵詞支持了異步編程耕捞,允許寫出和同步代碼很像的異步代碼砸脊。
Future
Future與JS中的Promise和Swift的RXSwift非常相似,其語法也是鏈?zhǔn)胶瘮?shù)調(diào)用驱显,該函數(shù)異步操作執(zhí)行后瞳抓,最終返回成功(執(zhí)行成功的操作)孩哑、失敗(捕獲錯誤或者停止后續(xù)操作)胳蛮,失敗和成功是對立的只會出現(xiàn)一種。
注意:Future 的所有API的返回值都是一個Future對象斗幼,所以可以進(jìn)行鏈?zhǔn)秸{(diào)用蜕窿。
- Future構(gòu)造函數(shù)
Future(FutureOr<T> computation())
computation 的返回值可以是普通值或者是Future對象,但是都是Future對象接收呆馁。
Future<num> future1 = Future(() {
print('async call1');
return 123;
});
//直接調(diào)用
future1.then((data) {
//執(zhí)行成功會走到這里
print(data);
}, onError: (e) {
print("onError: \$e");
}).catchError((e) {
//執(zhí)行失敗會走到這里
print(e);
}).whenComplete(() {
//無論成功或失敗都會走到這里
});
Future<Future> future2 = Future((){
print('async call2');
return future1;
});
//嵌套調(diào)用
future2.then((value) => value).then((value) => {
print('222---'+value.toString())
});
控制臺打印
Reloaded 1 of 499 libraries in 154ms.
flutter: async call1
flutter: 123
flutter: async call2
flutter: 222---123
注意:computation函數(shù)體中的代碼是被異步執(zhí)行的浙滤,與JS中Promise構(gòu)造函數(shù)的回調(diào)執(zhí)行時機不一樣瓷叫,如需要被同步執(zhí)行,則使用如下這個命名構(gòu)造函數(shù):
Future.sync(FutureOr<T> computation())
//該段代碼放到上邊代碼之后執(zhí)行
Future<num> future3 = Future.sync((){
print('sync call');
return 333;
});
future3.then((value) => {
print('sync'+'$value')
});
控制臺輸出
flutter: sync call
flutter: sync333
flutter: async call1
flutter: 123
flutter: async call2
flutter: 222---123
由此可見盒卸,future3(sync)方法會先執(zhí)行蔽介,之后在執(zhí)行之前的future1煮寡、future2.可見正常的future中的computation函數(shù)體中的代碼是被異步執(zhí)行的。
- Future.then
then中接收異步結(jié)果
Future.delayed(new Duration(seconds: 2),(){
return "延遲2s執(zhí)行";
}).then((data){
print(data);
});
- Future.catchError
捕獲錯誤
Future.delayed(new Duration(seconds: 2),(){
//return "延遲2s執(zhí)行";
throw AssertionError("Error");
}).then((data){
//執(zhí)行成功會走到這里
print("success");
}).catchError((e){
//執(zhí)行失敗會走到這里
print(e);
});
在異步任務(wù)中拋出了一個異常薇组,then的回調(diào)函數(shù)將不會被執(zhí)行律胀, catchError回調(diào)函數(shù)將被調(diào)用貌矿;并不是只有 catchError回調(diào)才能捕獲錯誤逛漫,then方法還有一個可選參數(shù)onError(之前介紹結(jié)構(gòu)體時已經(jīng)提到),我們也可以它來捕獲異常:
Future.delayed(new Duration(seconds: 2), () {
//return "延遲2s執(zhí)行";
throw AssertionError("Error");
}).then((data) {
print("success");
}, onError: (e) {
print(e);
});
- Future.whenComplete
不管成功失敗都要處理事件的場景克握,會調(diào)用此方法菩暗,比如在網(wǎng)絡(luò)請求前彈出加載對話框,在請求結(jié)束后關(guān)閉對話框。這種場景下梢,有兩種方法孽江,第一種是分別在then或catch中關(guān)閉一下對話框,第二種就是使用Future的whenComplete回調(diào):
Future.delayed(new Duration(seconds: 2),(){
//return "延遲2s執(zhí)行";
throw AssertionError("Error");
}).then((data){
//執(zhí)行成功會走到這里
print(data);
}).catchError((e){
//執(zhí)行失敗會走到這里
print(e);
}).whenComplete((){
//無論成功或失敗都會走到這里
});
- Future.wait
需要等待多個異步任務(wù)都執(zhí)行結(jié)束后再統(tǒng)一進(jìn)行一些操作(比如我們有一個界面辆琅,需要先分別從兩個網(wǎng)絡(luò)接口獲取數(shù)據(jù)婉烟,獲取成功后暇屋,我們需要將兩個接口數(shù)據(jù)進(jìn)行特定的處理后再顯示到UI界面上)
Future.wait就是做這件事的(類似RXswift的zip函數(shù))咐刨,它接受一個Future數(shù)組參數(shù),只有數(shù)組中所有Future都執(zhí)行成功后而涉,才會觸發(fā)then的成功回調(diào)联予,只要有一個Future執(zhí)行失敗啼县,就會觸發(fā)錯誤回調(diào)。
Future<List<Future>> future4 = Future.wait([
// 2秒后返回結(jié)果
Future.delayed(new Duration(seconds: 2), () {
return "延遲2s執(zhí)行";
}),
// 4秒后返回結(jié)果
Future.delayed(new Duration(seconds: 4), () {
return " 延遲4s執(zhí)行";
})
])
future4.then((value) => {
print(value[0]+ value[1]);
}).catchError((e){
print(e);
});
控制臺輸出
//等待4s輸出
flutter: 延遲2s執(zhí)行 延遲4s執(zhí)行
- 回調(diào)地獄(Callback Hell)
代碼中有大量異步邏輯躯泰,并且出現(xiàn)大量異步任務(wù)依賴其它異步任務(wù)的結(jié)果時谭羔,必然會出現(xiàn)回調(diào)中套回調(diào)情況。我們需要使用async/await和Future.then來解決這種問題麦向。
使用場景:大量依賴的業(yè)務(wù)邏輯瘟裸,登錄流程邏輯等
先來看下回調(diào)地獄例子:
//先分別定義各個異步任務(wù)
Future<String> future1(String str1){
...
//第一個任務(wù)
};
Future<String> future2(String str2){
...
//第二個任務(wù)
};
Future future3(String info){
...
// 第三個任務(wù)
};
future1("str1").then((str2){
//1返回數(shù)據(jù),作為2的參數(shù)
future2(str2).then((info){
//2的返回數(shù)據(jù)诵竭,作為3的入?yún)? future3(info).then((){
//獲取3的返回數(shù)據(jù)
...
});
});
})
Future消除Callback Hell
使用Future的鏈?zhǔn)綑C制话告,依次向下就避免了嵌套。跟JS的Promise完全一樣沙郭。不足之處是還是有一層回調(diào)佛呻。
future1("str1").then((str2){
return future2(str2);
}).then((info){
return future3(info);
}).then((e){
//執(zhí)行3接下來的操作
}).catchError((e){
//錯誤處理
print(e);
});
async/await消除callback hell
上邊的方式雖然避免了嵌套,但是在每個方法還是有一層回調(diào)病线。我們可以使用async/await來實現(xiàn)像同步代碼那樣來執(zhí)行異步任務(wù)而不使用回調(diào)的方式吓著。
task() async {
try{
String str2 = await future1("str1");
String info = await future2(str2);
await future3(info);
//執(zhí)行接下來的操作
} catch(e){
//錯誤處理
print(e);
}
}
- async/await
async用來表示函數(shù)是異步的芹助,定義的函數(shù)會返回一個Future對象坷澡,可以使用then方法添加回調(diào)函數(shù)
await 后面是一個Future会通,表示等待該異步任務(wù)完成牙瓢,異步完成后才會往下走酣倾;await必須出現(xiàn)在 async 函數(shù)內(nèi)部簿盅。
async/await將一個異步流用同步的代碼表現(xiàn)出來祟身。
async/await只是一個語法糖菩貌,JS編譯器或Dart解釋器最終都會將其轉(zhuǎn)化為一個JS的Promise和Dart的Future的調(diào)用鏈司澎。
Stream
如果說Future是可以接收單個異步事件返回單個事件的成功失敗欺缘,那么Stream就可以接收多個異步事件,并返回多個事件的成功失敗挤安,供使用者使用谚殊。
使用場景:多次讀取數(shù)據(jù)的異步任務(wù)場景,如網(wǎng)絡(luò)內(nèi)容下載漱受、文件讀寫等
該例子借助了其他地方的例子络凿。
Stream.fromFutures([
// 1秒后返回結(jié)果
Future.delayed(new Duration(seconds: 1), () {
return "hello 1";
}),
// 拋出一個異常
Future.delayed(new Duration(seconds: 2),(){
throw AssertionError("Error");
}),
// 3秒后返回結(jié)果
Future.delayed(new Duration(seconds: 3), () {
return "hello 3";
})
]).listen((data){
print(data);
}, onError: (e){
print(e.message);
},onDone: (){
});
控制臺輸出
flutter: hello 1
flutter: Error
flutter: hello 3
Future.wait是函數(shù)中存在多個延時操作,則以延時最長操作完成后統(tǒng)一返回昂羡,其他的延時操作等待最長的延時操作完成絮记。
async/await:處理多個異步操作,前后有依賴邏輯的虐先,使用異步實現(xiàn)同步怨愤。
Stream:統(tǒng)一監(jiān)聽該Stream中的多個異步延時操作返回,相當(dāng)于之前的多個Future異步處理統(tǒng)一監(jiān)聽蛹批。
- 其中Future和Stream只做了常用的方法和函數(shù)的介紹撰洗,更詳細(xì)的會在之后依次給大家做下總結(jié)。
到這里大概把Dart中經(jīng)常使用的語法和屬性方法介紹了一遍腐芍,有錯誤或者理解不到位的地方差导,可以提出,共同進(jìn)步猪勇。
Dart相比Java和JavaScript還是有許多優(yōu)點有優(yōu)勢的设褐,Dart既能進(jìn)行服務(wù)端腳本、APP開發(fā)、web開發(fā)助析,但是生態(tài)目前不足犀被,不過Flutter目前火熱,相信生態(tài)之后會越來越好外冀。