Flutter基礎(三)Dart快速入門

劉望舒發表於2019-05-22

本文首發於微信公眾號「劉望舒」

前言

Dart是Flutter SDK指定的語言,因此要學習Flutter,Dart是必須掌握的。關於Dart可以寫一本書了,這裡用一篇文章來介紹下Dart的精髓,帶你快速入門。和Java語言類似的部分,這篇文章就儘量不再講了。

1. Dart開發環境搭建

學習Dart語法最好需要用一個編輯器來實踐,這裡推薦使用IntelliJ IDEA。先下載Dart SDK,地址為:www.gekorm.com/dart-window… 開啟IntelliJ IDEA,選單中點選File-->Settings-->plugins,在plugins的搜尋框中搜尋Dart並安裝,然後重啟IntelliJ IDEA。 點選File-->New Project-->Dart,按照下圖配置Dart SDK。

Flutter基礎(三)Dart快速入門
注意要選擇第三個選項Constole Application,否則會預設建立一個Web專案。點選Next然後配置專案的名稱就可以建立專案了。 在專案中的bin/main.dart中加入如下測試程式碼:

void main() {
  print("Hello World");
}
複製程式碼

點選選單的Run-->Run'main.dart'或者點選工具條的執行圖示,就能在控制檯看到輸出的結果:

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的特性主要有以下幾點:

  1. 執行速度快,Dart是AOT(Ahead Of Time)編譯的,可以編譯成快速的、可預測的原生程式碼,這使得Flutter幾乎都可以使用Dart來編寫。也可以採用JIT(Just In Time)編譯。
  2. 易於移植,Dart可編譯成ARM和X86程式碼,這樣Dart可以在Android、iOS和其他地方執行。
  3. 容易上手,Dart充分吸收了高階語言特性,如果你已經熟悉C++、C、Java,可以在短短几天內用Dart來開發。
  4. 易於閱讀,Dart使Flutter不需要單獨的宣告式佈局語言(XML或JSX),或者單獨的視覺化介面構建器,這是因為Dart的宣告式程式設計佈局易於閱讀。
  5. 避免搶佔式排程,Dart可以在沒有鎖的情況下進行物件分配和垃圾回收,和JavaScript一樣,Dart避免了搶佔式排程和共享記憶體,因此不需要鎖。

Dart的重要概念有以下幾點:

  1. 在Dart中,一切都是物件,每個物件都是一個類的例項,所有物件都繼承自Object。
  2. Dart在執行前解析所有的程式碼,指定資料型別和編譯時常量,可以使程式碼執行的更快。
  3. 與Java不同,Dart不具備關鍵字public、protected、private。如果一個識別符號以下劃線_開始,那麼它和它的庫都是私有的。
  4. Dart支援頂級的函式如main(),也支援類或物件的靜態和例項方法,還可以在函式內部建立函式。
  5. Dart支援頂級的變數,也支援類或物件的靜態變數和例項變數,例項變數有時稱為欄位或屬性。
  6. Dart支援泛型型別,如List<int>(整數列表)或List<dynamic>(任何型別的物件列表)。
  7. 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();
複製程式碼

對於Android開發來說用new可能更習慣一些,可讀性也稍微好點,不用new的話顯得更簡潔,至於用不用new就看團隊的要求和個人的習慣吧,沒有絕對的好壞之分。

總結

Dart的知識點有很多,這裡只介紹了一部分我認為需要重點掌握的部分,如果想了解更多,可以檢視官方文件,關於Dart的學習可以結合Flutter邊寫邊學,不要只摳Dart的細節。


分享大前端、Android、Java等技術,助力5萬程式設計師成長進階。

Flutter基礎(三)Dart快速入門

相關文章