前言
Dart是Flutter SDK指定的語言,因此要學習Flutter,Dart是必須掌握的。
2. Dart概述
Dart是谷歌開發的計算機編程語言,亮相於2011年10月,最新的版本是Dart2。Dart誕生的原因是谷歌的工程師出於對JavaScript的不滿,誕生的初期也贏得了部分前端開發者的青睞。但是這時JavaScript藉着NodeJS火了起來,在前端、後端、移動端無孔不入,Dart就漸漸被人遺忘了,可見Dart本身是具有很強的實力的,只是不大走運。谷歌並沒有放棄Dart,不遺餘力的推廣Dart:谷歌的Angular提供了Dart版本,指定Dart爲新系統Fuchsia的官方開發語言,Dart爲移動UI框架Flutter的開發語言,因此Dart又重新回到了人們的視野中。 Dart通常情況下運行在DartVM上,但是在特定情況下它也可以編譯成本地代碼運行在硬件上,比如Flutter會將代碼編譯成指定平臺的本地代碼來提高性能。
3. Dart特性和重要概念
Dart的特性主要有以下幾點:
- 執行速度快,Dart是AOT(Ahead Of Time)編譯的,可以編譯成快速的、可預測的本地代碼,這使得Flutter幾乎都可以使用Dart來編寫。也可以採用JIT(Just In Time)編譯。
- 易於移植,Dart可編譯成ARM和X86代碼,這樣Dart可以在Android、iOS和其他地方運行。
- 容易上手,Dart充分吸收了高級語言特性,如果你已經熟悉C++、C、Java,可以在短短几天內用Dart來開發。
- 易於閱讀,Dart使Flutter不需要單獨的聲明式佈局語言(XML或JSX),或者單獨的可視化界面構建器,這是因爲Dart的聲明式編程佈局易於閱讀。
- 避免搶佔式調度,Dart可以在沒有鎖的情況下進行對象分配和垃圾回收,和JavaScript一樣,Dart避免了搶佔式調度和共享內存,因此不需要鎖。
Dart的重要概念有以下幾點:
- 在Dart中,一切都是對象,每個對象都是一個類的實例,所有對象都繼承自Object。
- Dart在運行前解析所有的代碼,指定數據類型和編譯時常量,可以使代碼運行的更快。
- 與Java不同,Dart不具備關鍵字public、protected、private。如果一個標識符以下劃線
_
開始,那麼它和它的庫都是私有的。 - Dart支持頂級的函數如main(),也支持類或對象的靜態和實例方法,還可以在函數內部創建函數。
- Dart支持頂級的變量,也支持類或對象的靜態變量和實例變量,實例變量有時稱爲字段或屬性。
- Dart支持泛型類型,如
List<int>
(整數列表)或List<dynamic>
(任何類型的對象列表)。 - Dart工具可以報告兩種問題:警告和錯誤。警告只是說明代碼可能無法正常工作,但不會阻止程序執行。錯誤可以是編譯時或運行時的。編譯時錯誤會阻止代碼執行; 運行時錯誤會導致代碼執行時報出異常。
4. Dart關鍵字
關鍵字 | |||
---|---|---|---|
abstract | dynamic | implements | show |
as | else | import | static |
assert | enum | in | super |
async | export | interface | switch |
await | extends | is | sync |
break | external | library | this |
case | factory | factory | factory |
catch | false | new | true |
class | class | null | try |
const | finally | on | typedef |
continue | for | operator | var |
covariant | Function | part | part |
default | get | rethrow | while |
deferred | hide | return | with |
do | if | set | set |
5. 變量
變量聲明使用var關鍵字,未初始化的變量的初始值爲null,即便是數字類型的變量也是null。
var name = 'liuwangshu';
name變量的類型被推斷爲String,也可以顯示聲明:
String name = 'liuwangshu' ;
如果對象不限於單一類型,可以指定Object或dynamic類型。
Object name = 'liuwangshu' ;
如果定義的變量不會變化,可以使用final或const來代替var,final變量只能設置一次。
final name = 'liuwangshu'
//name = 'zhangwuji' ; //會報錯
const變量爲編譯時常量,如果const變量在類級別,可以使用static const。
const pi = 3.1415926;
const area = pi * 60 * 60;
const不僅僅用來定義常量,也可以使用const來創建常量的值。
var foo = const []; final bar = const []; const baz = [];//相當於`const []`
6. 基本數據類型
Dart的基本數據類型包括Number、String、Boolean、List、Set、Map、 Symbol、Runes。
6.1 Number
number類型爲兩類:
- int:整數值不大於64位,具體取決於平臺。在Dart VM上,值可以是-2 ^63到2 ^63 - 1,如果編譯爲JavaScript,允許值爲-2^53 to 2^53 - 1。
- double:64-bit (雙精度) 浮點數,符合 IEEE 754 標準。
6.2 String
Dart 字符串是 UTF-16 編碼的字符序列。 可以使用單引號或者雙引號來創建字符串:
var s1 = '單引號適用於字符串文字';
var s2 = "雙引號同樣有效";
複製代碼
可以在字符串中使用表達式,用法是: ${expression}
。如果表達式是一個標識符,可以省略 {}。
var s = '乾坤大挪移';
assert('張無忌的$s' ==
'張無忌的乾坤大挪移');
使用三個單引號或者三個雙引號可以創建多行字符串對象:
var s1 = '''
第一行
第二行
''';
var s2 = """第一行
第二行""";
6.3 Boolean
Dart是強bool類型檢查,只有true對象才被認爲是true。
var name = '張無忌';
if (name) {
print('明教教主');
}
上面的代碼編譯不能通過,因爲name是一個字符串,而不是bool類型。
6.4 List
下面是一個List 的示例:
var list = [1, 2, 3];
List的第一個元素的索引是0,最後一個元素的索引是 list.length - 1 。
var list = [1, 2, 3, 4, 5, 6];
print(list.length);
print(list[list.length-1]);
6.5 Set
Dart中的Set是一組無序的集合。
var hero = ['張無忌', '風清揚', '張三丰', '獨孤求敗', '蕭峯'];
要創建一個空集,可以在{}前面帶有類型參數:
var heros= <String> {};
使用add()或addAll()方法將條目添加到現有集中:
var heros = <String>{};
heros.add('石破天');
heros.addAll(hero);
複製代碼
6.6 Map
Map是一個鍵值對相關的對象,鍵和值可以是任何類型的對象,每個鍵都是唯一的,而一個值則可以出現多次。
var player= {
// Keys Values
'20' : '斯諾',
'3': '艾弗森',
'40' : '希爾',
'8' : '麥基',
'55' : '穆託姆博'
};
複製代碼
使用Map構造函數也可以實現同樣的功能:
var player = new Map();
player['20'] = '斯諾';
player['3'] = '艾弗森';
player['40'] = '希爾';
複製代碼
7. 函數
Dart是一個真正面向對象的語言,函數屬於Function對象。這意味着,函數可以賦值給變量,也可以當做其他函數的參數。
void printName(String name) {
print('name is $name');
}
複製代碼
7.1 可選參數
可選參數可以是可選位置參數,也可以是可選命名參數,但不能同時使用。
可選命名參數 調用方法的時候,可以使用 paramName: value
的形式來指定參數的名稱,這樣就可以根據paramName得知參數的含義,提高代碼的可讀性。
coffeeFlavor (sugar :true ,sugar :false );
複製代碼
定義函數時,使用{param1, param2, …}
的形式來指定命名參數:
coffeeFlavor ({bool sugar , bool sugar}) {
}
複製代碼
可選位置參數 把函數的參數放到 [] 中就變成可選位置參數了:
String go(String to, [String who]) {
var result = 'go to the $to';
if (who != null) {
result = '$result with $who';
}
return result;
}
複製代碼
7. 2 默認參數值
可以使用 =
來定義可選參數的默認值, 默認值必須是編譯時常量。 如果沒有提供默認值,則默認值爲 null。
String go(String to, [String who= 'liuwangshu']) {
var result = 'go to the $to';
if (who != null) {
result = '$result with $who';
}
return result;
}
String result= go ('beijing');
複製代碼
7.3 main函數
每個應用都需要有個頂級的main() 函數來作爲入口才能執行。 main()函數的返回值爲 void 並且有個可選的 List<String>
參數。此前我們舉的例子都是在main函數中運行才能得已驗證:
void main(){
void printName(String name) {
print('name is $name');
}
printName('liuwangshu');
}
複製代碼
7.4 匿名函數
大部分函數都有名字,例如 main() 或者 printElement()。 可以創建沒有名字的匿名方法,格式如下所示。
([[Type] param1[, …]]) {
codeBlock;
};
複製代碼
下面的代碼定義了一個參數爲i(該參數沒有指定類型)的匿名函數。 list中的每個元素都會調用這個函數打印出來.
var list = ['張無忌', '風清揚', '張三丰', '獨孤求敗', '蕭峯'];
list.forEach((i) {
print(list.indexOf(i).toString() + ': ' + i);
});
複製代碼
8. 流程控制語句
Dart的流程控制語句如下:
- if 和 else
- for循環
- while和do- while循環
- break和continue
- switch和case
- assert
這些語句的大部分都和Java差不多,這裏主要講解for循環和switch語句。
8.1 for循環
標準的 for 循環:
var message = new StringBuffer("張無忌");
for (var i = 0; i < 3; i++) {
message.write('!');
}
複製代碼
List和Set等實現了Iterable接口的類還支持for-in
形式的遍歷:
var hero = ['張無忌', '風清揚', '張三丰', '獨孤求敗', '蕭峯'];
for (var h in hero) {
print(h);
}
複製代碼
8.2 switch和case
Dart中Switch語句通過使用 == 來比較整型、字符串或者編譯時常量。被比較的對象必須都是同一個類的實例(不能是其子類),並且這個類不允許覆寫 ==。另外,枚舉類型很適用於在Switch語句使用。
String today='Friday';
switch(today){
case 'Monday':
print('星期一');
break;
case 'Friday':
print('星期五');
break;
}
複製代碼
9.捕獲異常
捕獲異常可以避免異常繼續傳遞。
try {
//...
} on OutOfLlamasException {
//...
} on Exception catch (e) {
print('Unknown exception: $e');
} catch (e) {
print('Something really unknown: $e');
}
複製代碼
使用on或者catch來聲明捕獲語句,也可以同時使用。其中on來指定異常類型,catch來捕獲異常對象。 確保某些代碼不管有沒有出現異常都會執行,可以使用finally語句來實現。
try {
//...
} catch(e) {
print('Error: $e');
} finally {
//...
}
複製代碼
10.爲類添加新的功能
Dart是一個面向對象編程語言,支持基於Mixin的繼承機制。Mixin可以理解爲多繼承,在with關鍵字的後面爲一個或者多個類。
class Person{
run(){
print('跑');
}
}
class Wushu{
use(){
print('乾坤大挪移');
}
}
class Zhangwuji extends Person with Wushu{
int age;
Zhangwuji(int age){
this.age=age;
}
}
void main() {
var zhangwuji=new Zhangwuji(30);
zhangwuji.run();
zhangwuji.use();
}
複製代碼
通過如上代碼的驗證,Zhangwuji類擁有了Person和Wushu這兩個類的方法。
11.庫的使用
使用import來引入一個庫,對於Dart語言內置的庫,使用dart: scheme。 對於第三方的庫,可以使用文件系統路徑或者 package: scheme。
import 'dart:io';
import 'package:mylib/mylib.dart';
import 'package:utils/utils.dart';
複製代碼
指定庫前綴 如果導入的兩個庫具有衝突的名字, 可以使用庫的前綴來進行區分。 例如,如果library1和library2 都有一個名字爲Element的類,可以這樣使用:
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// ...
Element element1 = new Element(); //使用lib1中的Element
lib2.Element element2 = new lib2.Element(); //使用lib2中的Element
複製代碼
導入庫的一部分 如果只使用庫的一部分功能,則可以選擇需要導入的部分內容。其中show代表只導入指定的部分,hide代表除了指定的部分都導入。
// 只導入foo
import 'package:lib1/lib1.dart' show foo;
// 除了foo,其他部分都導入
import 'package:lib2/lib2.dart' hide foo;
複製代碼
延遲加載庫 延遲加載意味着應用程序可以在需要的時候再加載庫,使用延遲加載庫的場景主要有以下幾點:
- 減少APP的初始啓動時間。
- 執行A/B測試,例如嘗試各種算法的不同實現。
- 加載很少使用的功能。
要延遲加載一個庫,需要先使用 eferred as來導入:
import 'package:deferred/hello.dart' deferred as hello;
複製代碼
當需要使用的時候,調用loadLibrary() 函數來加載庫:
greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
複製代碼
12.異步支持
Dart庫中包含許多返回Future或Stream對象的函數。這些函數是異步的,它們在基本操作後會返回,而不等待該操作完成,例如讀取一個文件,在打開文件後就返回了。 雖然看起來有點像同步代碼,但是async和await的代碼是的確異步的。
await readFile()
複製代碼
要使用await,其方法必須帶有async關鍵字:
FileOperate() async {
var file= await readFile()
//其他處理
}
複製代碼
13.讓類可調用
如果Dart中的類實現了call()函數,那麼這個類可以當做方法來調用。
class JointFunction {
call(String a, String b, String c, String d) => '$a $b $c $d';
}
main() {
var jf = new JointFunction();
var out = jf("放","手","去","做");//1
print('$out');
}
在下面的示例中,JointFunction類定義了一個call()函數,它接收三個字符串並拼接它們。這樣在註釋1處就可以調用JointFunction類了。
14.創建實例
在Java中創建實例可以用new,在Dart中你可以選擇用new,也可以選擇不用:
Element element = Element();