Dart語言精簡入門介紹
1、介紹
-
Dart 在設計時應該是同時借鑒了 Java 和 JavaScript和kotlin
-
面向對象
-
JIT&AOT:JIT(Just in Time)優點:即時編譯,開發期間更快編譯,更快的重載;缺點:在運行時將代碼編譯成機器碼,給用戶最直接的感受就是慢;AOT(Ahead Of Time)事前編譯,release期間更快更流暢,典型的例子就是C和C++,它能夠直接編譯機器碼即二進制代碼,所以它的加載和執行速度是非常快的。你會發現在開發期間安裝Flutter的時候會有一些卡頓,但是在release就是發布之后會變好,這種模式使得APP在發布之后能夠以更快的速度運行。
2、基本數據類型
-
數字類型(num、int和double)
1)、num:
Dart數字類型的父類,它既接受浮點類型,也接受整數類型
num num1 = -1.0; //num是數字類型的父類 num num2 = 2; //num是數字類型的父類
2)、int、double
num有兩個子類,一個是int,一個是double,int類型只能接收整數,double類型是雙精度
int int1 = 3; //子類一 int類型 只能接收整數 double d1 = 2.22; //子類二 雙精度
-
字符串(String)
在dart中定義字符串可以用單引號也可以用雙引號,一行可以定義一個字符串,也可以定義多個字符串,中間用逗號隔開:
String str1 = '字符串', str2 = "雙引號字符串"; //字符串的定義
在dart中如何進行字符串拼接直接使用加號“+”拼接,當然還有另外一種方法,在上面數字類型的講解中也有使用到,我們可以使用"$"加上變量名去引用一個變量,這個變量可以是數字類型當然也可以是字符串類型,這個和kotlin語言有點像
String str3 = 'str1:$str1 str2:$str2'; //字符串拼接 String str4 = 'str1:' + str1 + "str2:" + str2; //字符串拼接 String s = "this is dart2"; String s2 = 'this is dart2'; // 使用==判斷字符串是否相等 print(s == s2); // true
字符串的方法和java里面的字符串方法類似
-
布爾
Dart是強bool類型檢查,只有bool類型的值是true,才被認為是true
-
集合之List
在Dart中定義集合使用List關鍵字,List表明集合是個泛型,這里可以傳入任何數據類型。List集合可以使用“[]”進行初始化,例如:List list = [1,2,‘測試’]; 這種形式,這里沒使用泛型所以中括號里面添加元素時既可以傳入數字也可以傳入字符串,如果需要指定泛型類型,例如:List list1 = []; 這種形式下,添加元素的時候只能添加int類型的數據。
List list = [1, 2, 3, '集合']; //初始化添加元素 List list3 = []; list3.add('list3'); //通過add方法添加元素 list3.addAll(list); print(list3);
List集合的遍歷
第一種:沒有語言之分的for循環方式:
for (int i = 0; i < list.length; i++) {print(list[i]); }
第二種:for…in…,同樣是for,但是表達式內部我們做一點修改,使用var關鍵字定義一個變量,然后使用in關鍵字在集合中進行遍歷:
for (var o in list) {print(o); }
第三種:forEach()循環,將函數應用于集合的每個元素,括號內傳入的為集合的元素:
list.forEach((val) {print(val); });
-
集合之Map
Map集合的初始化
Map是將key和value相關聯的對象,key和value都可以是任意類型的對象,并且key是唯一的,如果key重復,后面添加的key會替換前面的內容。
///Map初始化 Map names = {'xiaoming': '小明', "xiaohong": "小紅"}; print(names); Map ages = {}; ages['xiaoming'] = 12; ages['xiaohong'] = 18; print(ages);
Map的遍歷
它的里面需要一個回調函數,回調函數里面會輸出我們的key和value
///Map遍歷方式 ages.forEach((k, v) {print('$k,$v'); });
map()方法,我們可以調用Map集合的map()方法遍歷生成一個新的map集合,這個方法接收一個回調函數,入參為key、value,函數內部必須接受一個return,也就是說它會返回一個新的map,這里通過MapEntry來返回之前Map里面每一項的元素,我這里將原map的key和value進行顛倒,那么就可以得到一個相反的集合
Map ages2 = ages.map((k, v) {return MapEntry(v, k); }); print(ages2);
第三種:for循環
首先循環遍歷map集合中所有的key,可以通過map.keys獲取,它返回的是一個所有key值的數組(同樣的也有map.values),然后我們就可以通過map[key]獲取集合中的每一個元素了,代碼如下:
for (var key in ages.keys) {print('$key , ${ages[key]}'); }
3、變量聲明
-
var
類似于 JavaScript 中的
var
,它可以接收任何類型的變量,但最大的不同是 Dart 中 var 變量一旦賦值,類型便會確定,則不能再改變其類型,如:var t = "hi world"; // 下面代碼在dart中會報錯,因為變量t的類型已經確定為String, // 類型一旦確定后則不能再更改其類型。 t = 1000;
-
dynamic 和 Object
Object
是 Dart 所有對象的根基類,也就是說在 Dart 中所有類型都是Object
的子類(包括Function和Null),所以任何類型的數據都可以賦值給Object
聲明的對象。dynamic
與Object
聲明的變量都可以賦值任意對象,且后期可以改變賦值的類型,這和var
是不同的,如:dynamic t; Object x; t = "hi world"; x = 'Hello Object'; //下面代碼沒有問題 t = 1000; x = 100
dynamic
與Object
不同的是dynamic
聲明的對象編譯器會提供所有可能的組合,而Object
聲明的對象只能使用Object
的屬性與方法, 否則編譯器會報錯dynamic a;Object b = "";main() {a = "";printLengths();} printLengths() {// 正常print(a.length);// 報錯 The getter 'length' is not defined for the class 'Object'print(b.length);}
4、常量和空安全
-
final和const
如果您從未打算更改一個變量,那么使用
final
或const
,不是var
,也不是一個類型。 一個final
變量只能被設置一次,兩者區別在于:const
變量是一個編譯時常量(編譯時直接替換為常量值),final
變量在第一次使用時被初始化。被final
或者const
修飾的變量,變量類型可以省略//可以省略String這個類型聲明 final str = "hi world"; //final String str = "hi world"; const str1 = "hi world"; //const String str1 = "hi world";
-
空安全(null-safety)
Dart 中一切都是對象,這意味著如果我們定義一個數字,在初始化它之前如果我們使用了它,假如沒有某種檢查機制,則不會報錯,比如:
test() {int i; print(i*8); }
在 Dart 引入空安全之前,上面代碼在執行前不會報錯,但會觸發一個運行時錯誤,原因是 i 的值為 null 。但現在有了空安全,則定義變量時我們可以指定變量是可空還是不可空。
int i = 8; //默認為不可空,必須在定義時初始化。 int? j; // 定義為可空類型,對于可空變量,我們在使用前必須判空。// 如果我們預期變量不能為空,但在定義時不能確定其初始值,則可以加上late關鍵字, // 表示會稍后初始化,但是在正式使用它之前必須得保證初始化過了,否則會報錯 late int k; k=9;
如果一個變量我們定義為可空類型,在某些情況下即使我們給它賦值過了,但是預處理器仍然有可能識別不出,這時我們就要顯式(通過在變量后面加一個”!“符號)告訴預處理器它已經不是null了,比如:
class Test{int? i;Function? fun;say(){if(i!=null) {print(i! * 8); //因為已經判過空,所以能走到這 i 必不為null,如果沒有顯式申明,則 IDE 會報錯}if(fun!=null){fun!(); // 同上}} }
上面中如果函數變量可空時,調用的時候可以用語法糖:
fun?.call() // fun 不為空時則會被調用
這里和kotlin有點類似
5、函數
Dart是一種真正的面向對象的語言,所以即使是函數也是對象,并且有一個類型Function。這意味著函數可以賦值給變量或作為參數傳遞給其他函數,這是函數式編程的典型特征。
-
函數聲明
bool isNoble(int atomicNumber) {return _nobleGases[atomicNumber] != null; }
這里函數聲明跟java或javascript有點類似
對于只包含一個表達式的函數,可以使用簡寫語法:
bool isNoble (int atomicNumber)=> true ;
-
函數作為變量
var say = (str){print(str); }; say("hi world");
-
函數作為參數傳遞
void execute(var callback) {callback(); } execute(() => print("xxx"))
-
可選的位置參數
String say(String from, String msg, [String device]) {var result = '$from says $msg';if (device != null) {result = '$result with a $device';}return result; } say('Bob', 'Howdy'); //結果是: Bob says Howdy say('Bob', 'Howdy', 'smoke signal'); //結果是:Bob says Howdy with a smoke signal
-
可選的命名參數
定義函數時,使用{param1, param2, …},放在參數列表的最后面,用于指定命名參數。例如:
//設置[bold]和[hidden]標志 void enableFlags({bool bold, bool hidden}) {// ... }
調用函數時,可以使用指定命名參數。例如:
paramName: value
enableFlags(bold: true, hidden: false);
可選命名參數在Flutter中使用非常多。注意,不能同時使用可選的位置參數和可選的命名參數。
6、類和繼承
Dart中的繼承:
1.子類使用extends關鍵詞來繼承父類
2.子類會繼承父類里面可見的屬性和方法,但是不會繼承構造函數
3.子類能復寫父類的方法 getter和setter
4.Dart 是不支持多繼承的,類和接口是統一的,類就是接口。
一般情況使用抽象類作為接口
void main() {new Student().run();
}abstract class Person {void run();
}class Student implements Person {@overridevoid run() {print("....");}
}
7、異步的支持
Dart類庫有非常多的返回Future
或者Stream
對象的函數。 這些函數被稱為異步函數:它們只會在設置好一些耗時操作之后返回,比如像 IO操作。而不是等到這個操作完成。
-
Future
Future
與JavaScript中的Promise
非常相似,表示一個異步操作的最終完成(或失敗)及其結果值的表示。簡單來說,它就是用于處理異步操作的,異步處理成功了就執行成功的操作,異步處理失敗了就捕獲錯誤或者停止后續操作。一個Future只會對應一個結果,要么成功,要么失敗。由于本身功能較多,這里我們只介紹其常用的API及特性。還有,請記住,
Future
的所有API的返回值仍然是一個Future
對象,所以可以很方便的進行鏈式調用1)、Future.then
為了方便示例,在本例中我們使用
Future.delayed
創建了一個延時任務(實際場景會是一個真正的耗時任務,比如一次網絡請求),即2秒后返回結果字符串"hi world!",然后我們在then
中接收異步結果并打印結果,代碼如下:Future.delayed(Duration(seconds: 2),(){return "hi world!"; }).then((data){print(data); });
2)、Future.catchError
Future.delayed(Duration(seconds: 2),(){//return "hi world!";throw AssertionError("Error"); }).then((data){//執行成功會走到這里 print("success"); }).catchError((e){//執行失敗會走到這里 print(e); });
3)、Future.whenComplete
有些時候,我們會遇到無論異步任務執行成功或失敗都需要做一些事的場景,比如在網絡請求前彈出加載對話框,在請求結束后關閉對話框。這種場景,有兩種方法,第一種是分別在
then
或catch
中關閉一下對話框,第二種就是使用Future
的whenComplete
回調,我們將上面示例改一下:Future.delayed(Duration(seconds: 2),(){//return "hi world!";throw AssertionError("Error"); }).then((data){//執行成功會走到這里 print(data); }).catchError((e){//執行失敗會走到這里 print(e); }).whenComplete((){//無論成功或失敗都會走到這里 });
4)、Future.wait
有些時候,我們需要等待多個異步任務都執行結束后才進行一些操作,比如我們有一個界面,需要先分別從兩個網絡接口獲取數據,獲取成功后,我們需要將兩個接口數據進行特定的處理后再顯示到UI界面上,應該怎么做?答案是
Future.wait
,它接受一個Future
數組參數,只有數組中所有Future
都執行成功后,才會觸發then
的成功回調,只要有一個Future
執行失敗,就會觸發錯誤回調。下面,我們通過模擬Future.delayed
來模擬兩個數據獲取的異步任務,等兩個異步任務都執行成功時,將兩個異步任務的結果拼接打印出來,代碼如下:Future.wait([// 2秒后返回結果 Future.delayed(Duration(seconds: 2), () {return "hello";}),// 4秒后返回結果 Future.delayed(Duration(seconds: 4), () {return " world";}) ]).then((results){print(results[0]+results[1]); }).catchError((e){print(e); });
執行上面代碼,4秒后你會在控制臺中看到“hello world”。
5)、Async/await
如果代碼中有大量異步邏輯,并且出現大量異步任務依賴其它異步任務的結果時,必然會出現
Future.then
回調中套回調情況。舉個例子,比如現在有個需求場景是用戶先登錄,登錄成功后會獲得用戶ID,然后通過用戶ID,再去請求用戶個人信息,獲取到用戶個人信息后,為了使用方便,我們需要將其緩存在本地文件系統,代碼如下://先分別定義各個異步任務 Future<String> login(String userName, String pwd){...//用戶登錄 }; Future<String> getUserInfo(String id){...//獲取用戶信息 }; Future saveUserInfo(String userInfo){...// 保存用戶信息 };
接下來,執行整個任務流:
login("alice","******").then((id){//登錄成功后通過,id獲取用戶信息 getUserInfo(id).then((userInfo){//獲取用戶信息后保存 saveUserInfo(userInfo).then((){//保存用戶信息,接下來執行其它操作...});}); })
消除嵌套
login("alice","******").then((id){return getUserInfo(id); }).then((userInfo){return saveUserInfo(userInfo); }).then((e){//執行接下來的操作 }).catchError((e){//錯誤處理 print(e); });
使用 async/await 消除嵌套
task() async {try{String id = await login("alice","******");String userInfo = await getUserInfo(id);await saveUserInfo(userInfo);//執行接下來的操作 } catch(e){//錯誤處理 print(e); } }
-
async
用來表示函數是異步的,定義的函數會返回一個Future
對象,可以使用 then 方法添加回調函數。 -
await
后面是一個Future
,表示等待該異步任務完成,異步完成后才會往下走;await
必須出現在async
函數內部。
-
-
Stream
Stream
也是用于接收異步事件數據,和Future
不同的是,它可以接收多個異步操作的結果(成功或失敗)。 也就是說,在執行異步任務時,可以通過多次觸發成功或失敗事件來傳遞結果數據或錯誤異常。Stream
常用于會多次讀取數據的異步任務場景,如網絡內容下載、文件讀寫等。舉個例子:
Stream.fromFutures([// 1秒后返回結果Future.delayed(Duration(seconds: 1), () {return "hello 1";}),// 拋出一個異常Future.delayed(Duration(seconds: 2),(){throw AssertionError("Error");}),// 3秒后返回結果Future.delayed(Duration(seconds: 3), () {return "hello 3";})
]).listen((data){print(data);
}, onError: (e){print(e.message);
},onDone: (){});
上面的代碼依次會輸出:
I/flutter (17666): hello 1
I/flutter (17666): Error
I/flutter (17666): hello 3
參考鏈接