Flutter - Dart特性語法

GitLqr發表於2021-03-21

溫馨提示:本文只羅列 Dart 中比較重要、奇特的語法,所以不合適沒有其他語言基礎的人學習!!

一、Dart 的基本語法

1、程式入口

  • Dart 的入口也是 main 函式,且沒有返回值。
  • 傳遞給 main 的命令列引數,會存放在 List<String> args 中。
  • 定義字串可以使用單引號或雙引號。
  • 每行語句必須使用分號結尾。
main(List<String> args) {
  print("Hello World");
}
複製程式碼

2、宣告變數

  • 明確宣告:變數型別 變數名稱 = 賦值;
  • 型別推導:var/dynamic/const/final 變數名稱 = 賦值;
// 1. 明確的宣告
String name = "lqr";

// 2. 型別推導(var/final/const)
// 型別推導的方式雖然沒有明確的指定變數的型別,但是變數是有自己明確的型別的
// 2.1 var宣告變數
var age = 18;
// age = "abc"; // IDE報錯:A value of type 'String' can't be assigned to a variable of type 'int'.

// 2.2 final宣告常量
final height = 1.88;
// height = 2.00; // IDE報錯:The final variable 'height' can only be set once.

// 2.3 const宣告常量
const address = "廣州市";
// address = "北京市"; // IDE報錯:Constant variables can't be assigned a value.

// 2.4 final和const的區別
// const必須賦值 常量值(編譯期間需要有一個確定的值)
// final可以通過計算/函式獲取一個值(執行期間來確定一個值)
// const date1 = DateTime.now(); // 寫法錯誤
final date2 = DateTime.now();

// 2.5 Object和dynamic的區別
// 分別使用Object 和 dynamic,讓父類引用指向子類物件
// Object呼叫方法時,編譯時會報錯
Object obj = "lqr";
print(obj.substring(1)); // IDE報錯:The method 'substring' isn't defined for the type 'Object'.
// dynamic呼叫方法時,編譯時不報錯,但是執行時會存在安全隱患
// dynamic是明確宣告(var是型別推導)
dynamic obj = "lqr";
print(obj.substring(1)); // qr
複製程式碼

3、[Dart 沒有]非零即真

js 中存在非零即真、非空即真的特性,但在 Dart 中沒有!!Dart 要求 if() 語句必須傳入一個 bool 型別:

// Dart中沒有非零即真,也沒有非空即真
var flag = "true";
if (flag) { // IDE報錯:Conditions must have a static type of 'bool'.
  print("執行程式碼");
}
複製程式碼

注意:Dart 不支援非空即真或者非 0 即真,必須有明確的 bool 型別

4、字串型別

Dart 有三種定義字串的方式:

  • 單引號(''):與雙引號相同。
  • 雙引號(""):與單引號相同。
  • 三引號(""""""):可以定義多行字串,不需要藉助 \n
// 1. 定義字串
var str1 = 'abc';
var str2 = "abc";
var str3 = """
abc
cba
nba
""";

// 小技巧:如果字串中存在雙引號,又不想轉義的話,可以使用單引號來定義該字串,反之使用雙引號。
print('a"b"c'); // a"b"c
print("a'b'c"); // a'b'c
複製程式碼

Dart 支援字串插值:

// 2. 字串和表示式拼接
var name = "lqr";
var age = 19;
var height = 1.88;
// 強調:${變數}可以省略{},${表示式}不能省略{}
// var message1 = "my name is ${name}, age is ${age}, height is ${height}";
var message1 = "my name is $name, age is $age, height is $height"; // my name is lqr, age is 19, height is 1.88
var message2 = "name is $name, type is ${name.runtimeType}"; // name is lqr, type is String
print(message1);
print(message2);
複製程式碼

5、集合型別

Dart 集合型別有:

  • 列表 List:[ele1, ele2, ele2, ele1]
  • 集合 Set:{ele1, ele2, ele3}
  • 對映 Map:{"key1" : value1, "key2" : value2}
// 1. 列表List:[];
var names = ["abc", "cba", "nba", "cba"];
names.add("mba");
// names.remove(value);
// names.removeAt(index);

// 2. 集合Set:{};
var movies = {"星際穿越", "大話西遊", "盜夢空間"};
names = Set<String>.from(names).toList(); // 可以用Set來去重
print(names);

// 3. 對映Map
var info = {
  "name": "lqr",
  "age": 18,
};
複製程式碼

二、Dart 的函式使用

1、函式的基本使用

Dart 的函式定義:

返回值 函式的名稱(引數列表) {
  函式體
  return 返回值
}
複製程式碼

例子:

main(List<String> args) {
  print(sum(20, 30));
}

int sum(int num1, int num2) {
  return num1 + num2;
}

// 返回值的型別可以省略(開發中不推薦)
// sum(int num1, int num2) {
//   return num1 + num2;
// }
複製程式碼

2、函式的可選引數

Dart 的函式引數有兩類:

  • 必選引數:必須傳
  • 可選引數:可不傳
main(List<String> args) {
  sayHello1("lqr");
}

// 必選引數:必須傳
void sayHello1(String name) {
  print(name);
}
複製程式碼
  • 可選引數有 2 種:
    • 位置可選引數:[int age, double height]
    • 命名可選引數:{int age, double height}
main(List<String> args) {
  sayHello2("lqr");
  sayHello2("lqr", 18, 1.88);

  sayHello3("lqr", age: 18, height: 1.88);
  sayHello3("lqr", height: 1.88, age: 18);
  sayHello3("lqr", height: 1.88);
}

// 位置可選引數:[int age, double height]
// 實參和形參在進行匹配時,是根據位置來匹配
void sayHello2(String name, [int age, double height]) {}

// 命名可選引數:{int age, double height}
// 實參和形參在進行匹配時,是根據變數名來匹配
void sayHello3(String name, {int age, double height}) {}
複製程式碼

3、函式的預設引數

Dart 中沒有函式過載!!!Dart 中沒有函式過載!!!Dart 中沒有函式過載!!!但是,Dart 函式支援為可選引數設定預設值:

void sayHello1(String name) {
  print(name);
}
// Dart中沒有函式的過載
// void sayHello1(){ // IDE報錯:The name 'sayHello1' is already defined.
//   ...
// }

// 注意:只有可選引數才可以有預設值,必傳引數沒有預設值
void sayHello4(String name, [int age = 20, double height = 1.7]) {}
void sayHello5(String name, {int age = 20, double height = 1.7}) {}
複製程式碼

注意:只有可選引數才能設定預設值。

4、函式是一等公民

函式是一等公民 意味著可以將函式賦值給一個變數,也可以將函式作為另外一個函式的引數或返回值來使用。

main(List<String> args) {
  // 1. 直接找到另外一個定義的函式傳進去
  test(bar); // bar函式被呼叫

  // 2. 匿名函式 (引數列表){函式體};
  test(() {
    print("匿名函式被呼叫"); // 匿名函式被呼叫
    return 10;
  });

  // 3. 箭頭函式:條件,函式體只有一行程式碼
  // 注意:Dart中的箭頭函式與js中的箭頭函式是兩回事
  test(() => print("箭頭函式被呼叫")); // 箭頭函式被呼叫
}

// 函式可以作為另外一個函式的引數
void test(Function foo) {
  var result = foo();
}

void bar() {
  print("bar函式被呼叫");
}
複製程式碼

上述程式碼中,當將函式作為另一個函式的引數時,使用 Function 來宣告這個引數型別,很明顯有個問題,那就是無法對傳入的函式做限制:

main(List<String> args) {
  test((name) {
    print(name); // lqr
  });
}

// 封裝test函式,要求:傳入一個函式
// 缺點:Function無法對傳入函式做限制(比如:函式的引數列表、返回值)
void test(Function foo) {
  foo("lqr");
}
複製程式碼

這時,可以使用 函式簽名 來代替 Function

main(List<String> args) {
  test((num1, num2) {
    return num1 + num2;
  });
}

// 可以限制 作為引數的函式的型別
// 缺點:相當於在形參位置上寫了一個函式宣告(函式簽名),整體看起來可讀性差
void test(int foo(int num1, int num2)) {
  foo(20, 30);
}
複製程式碼

為了有更好的可讀性,可以使用 typedef 來代替 函式簽名

main(List<String> args) {
  test((num1, num2) {
    return num1 + num2;
  });

  var demo1 = demo();
  print(demo1(20, 30)); // 600
}

// 可以使用typedef定義一個函式型別
typedef Calculate = int Function(int num1, int num2);

void test(Calculate calc) {
  calc(20, 30);
}

// 函式作為返回值
Calculate demo() {
  return (num1, num2) {
    return num1 * num2;
  };
}
複製程式碼

三、Dart 的特殊運算子

1、運算子

  • ??=:賦值運算子(變數會 null 時才執行賦值操作)
  • ??:條件運算子(有點像簡化版的三元運算子)
// 1. ??=
// 當原來的變數有值時,那麼 ??= 不執行
// 原來的變數為null時,那麼將值賦值給這個變數
var name = null;
name ??= "lilei";
print(name); // lilei

// 2. ??
// ??前面的資料有值,那麼就使用??前面的資料
// ??前面的資料為null,那麼就使用後面的值
var name = null;
var temp = name ?? "lilei";
print(temp); // lilei
複製程式碼

2、級聯語法(..)

Dart 可以使用級聯語法 (..) 將任意物件的 變數賦值方法呼叫 操作都變成 鏈式呼叫

main(List<String> args) {
  // var p = Person();
  // p.name = "lqr";
  // p.run();
  // p.eat();

  // 級聯運算子
  var p = Person()
    ..name = "lqr"
    ..eat()
    ..run();
}
複製程式碼

3、for 迴圈的使用

Dart 支援 普通 for 迴圈,以及 for-in 迴圈

// 1. 基礎for迴圈
for (var i = 0; i < 10; i++) {
  print(i);
}

// 2. 遍歷陣列
var names = ["why", "cba", "cba"];
for (var i = 0; i < names.length; i++) {
  print(names[i]);
}

for (var name in names) {
  print(name);
}
複製程式碼

while 和 do-while 和其他語言一致,break 和 continue 用法也是一致

四、Dart 的物件導向

1、類的定義

Dart 中使用 class 關鍵字來定義類:

main(List<String> args) {
  var p1 = new Person("lqr", 18);
  // 從Dart2開始,new關鍵字可以省略
  var p2 = Person("lqr", 18);
}

class Person {
  String name;
  int age;

  // Person(String name, int age) {
  //   this.name = name;
  //   this.age = age;
  // }

  // 等同於上面的程式碼
  Person(this.name, this.age);
}
複製程式碼

2、類的建構函式

Dart 不支援函式過載,所以,建構函式也一樣不能過載。不過,可以使用 命名建構函式 來解決:

main(List<String> args) {
  var p = Person("lqr", 18);
  var p1 = new Person.withNameAgeHeight("lqr", 18, 1.88);
  var p2 = Person.fromMap({
    "name": "lqr",
    "age": 18,
    "height": 1.88,
  });
}

class Person {
  String name;
  int age;
  double height;

  Person(this.name, this.age);

  // Dart中沒有函式過載
  // Person(this.name, this.age, this.height); // IDE報錯:The default constructor is already defined.

  // 命名建構函式
  Person.withNameAgeHeight(this.name, this.age, this.height);

  Person.fromMap(Map<String, dynamic> map) {
    this.name = map['name'];
    this.age = map['age'];
    this.height = map['height'];
  }

  @override
  String toString() {
    return "$name $age $height";
  }
}
複製程式碼

3、類的初始化列表

如果類中的屬性使用 final 修飾,那麼在必須在建構函式中對 final 屬性進行賦值,但要注意的是,不能在建構函式的函式體內對 final 屬性賦值:

class Person {
  final String name;
  final int age;

  // Person(this.name, this.age) {} // 必傳引數
  Person(this.name, {this.age = 10}) {} // (帶預設值的)可選引數

  // Person(this.name) { // IDE報錯:All final variables must be initialized, but 'age' isn't.
  //   this.age = 10; // IDE報錯:'age' can't be used as a setter because it's final.
  // }
}
複製程式碼

雖然 Dart 不支援在建構函式函式體中對 final 屬性賦值,但是可以用 初始化列表 對其賦值:

初始化列表一般與命名可選引數配合使用,當外界呼叫建構函式時,可以對形參對應的屬性進行校驗,以及設定初始值。

class Person {
  final String name;
  final int age;

  // function ():xxxxxx {} ,其中xxxxxx部分就是初始化列表
  Person(this.name) : this.age = 10 {} // 初始化列表:可能使用 表示式 來為屬性賦值
  // Person(this.name, {this.age = 10}) {} // (帶預設值的)可選引數:只能使用 賦值語句 來為屬性賦值
  // Person(this.name, {int age}) : this.age = age ?? 10 {} // 初始化列表:可以對形參進行校驗,以及設定初始化值

  // 初始化列表有多個時,用逗號隔開
  // Person(this.name, {int age, double height}) : this.age = age ?? 10, this.height = height ?? 1.88 {}
}
複製程式碼

4、重定向建構函式

重定向建構函式,其實就是在一個建構函式中,去呼叫另一個建構函式:

class Person {
  String name;
  int age;

  // 建構函式的重定向
  Person(String name) : this._internal(name, 0);

  Person._internal(this.name, this.age);
}
複製程式碼

5、常量建構函式

在某些情況下,我們希望 傳入相同的值返回同一個物件,這時,可以使用常量建構函式:

  • 常量建構函式:
    • 在建構函式前加 const 進行修改。
    • 擁有常量建構函式的類中,所有的成員變數必須使用 final 修飾。
    • 為了可以通過常量建構函式建立出相同的物件,不再使用 new 關鍵字,而是使用 const 關鍵字。
      • 如果是將結果賦值給 const 修飾的識別符號時,const 可以省略。
main(List<String> args) {
  // 用 const常量 去接收一個常量建構函式的結果,可以省略 const
  // const p1 = const Person("lqr", 18);
  const p1 = Person("lqr", 18);
  const p2 = Person("lqr", 18);
  print(identical(p1, p2)); // true

  // 如果用 var變數 去接收一個常量建構函式的結果,則這時省略的不是 const,而是 new!!
  var p11 = Person("lqr", 18); // ==> var p11 = new Person("lqr");
  var p22 = Person("lqr", 18);
  print(identical(p11, p22)); // false

  // 必須所有的屬性值相同,物件才是同一個
  const p111 = Person("lqr", 18);
  const p222 = Person("lqr", 20);
  print(identical(p111, p222)); // false
}

class Person {
  // 擁有常量構造方法的類中,所有的成員變數必須是final修飾
  final String name;
  final int age;

  // 一個類中只能有一個常量構造方法,命名構造方法也不行
  // const Person(this.name);
  const Person(this.name, this.age);
}
複製程式碼

6、工廠建構函式

為了滿足 傳入相同的值,得到相同的物件 這個需求,除了可以使用常量建構函式外,還可以使用工廠建構函式,而且工廠建構函式更加靈活。

  • 工廠建構函式:在建構函式前加 factory 關鍵字進行修改。
  • 工廠建構函式與普通的建構函式的區別:
    • 普通的建構函式:會自動返回建立出來的物件,不能手動返回
    • 工廠建構函式最大的特點:可以手動的返回一個物件
main(List<String> args) {
  final p1 = Person.withName("lqr");
  final p2 = Person.withName("lqr");
  print(identical(p1, p2));
}

class Person {
  String name;
  String color;

  // static 修飾的屬性是類屬性,可以通過類名直接呼叫
  static final Map<String, Person> _nameCache = {};

  Person(this.name, this.color);

  // 普通的建構函式:會自動返回建立出來的物件,不能手動返回
  // 工廠建構函式最大的特點:可以手動的返回一個物件
  factory Person.withName(String name) {
    if (_nameCache.containsKey(name)) {
      return _nameCache[name];
    } else {
      final p = Person(name, "default");
      _nameCache[name] = p;
      return p;
    }
  }
}
複製程式碼

7、類的 setter 和 getter

很多時候,我們並不希望外部可以直接訪問物件欄位,而是經過 settergetter 中轉,這樣的好處是,可以在 settergetter 中做一些額外的工作,Dart 支援為欄位增加 settergetter

  • setter :前面使用 set 宣告,並且需要用 () 接收引數。
  • getter :前面使用 get 宣告,還需要宣告返回值型別,但是不能有 () 接收引數。
  • settergetter 像欄位一樣使用。
main(List<String> args) {
  final p1 = Person();

  // p1.setName("lqr"); // IDE報錯:The method 'setName' isn't defined for the type 'Person'.

  // setter 和 getter 像欄位一樣使用
  p1.setName = "lqr";
  print(p1.getName); // lqr
}

class Person {
  String name;

  // setter
  // set setName(String name) {
  //   this.name = name;
  // }
  set setName(String name) => this.name = name;

  // getter:注意getter沒有()
  // String get getName {
  //   return this.name;
  // }
  String get getName => this.name;
}
複製程式碼

嚴格意義上來說,Dart 中的 settergetter 已經不能算是方法了吧。

8、類的繼承

Dart 中的繼承使用 extends 關鍵字,子類中使用 super 來訪問父類。

注意:Dart 只支援單繼承。

main(List<String> args) {}

class Animal {
  int age;

  Animal(this.age);

  void run() {
    print("向前跑~");
  }
}

class Person extends Animal {
  String name;

  // 必須在初始化列表中,呼叫父類的建構函式
  Person(this.name, int age) : super(age);

  @override
  void run() {
    // super.run();
    print("向前走~");
  }
}

// Dart只支援單繼承
// class SuperMan extends Runner, Flyer{ // IDE報錯:Each class definition can hava at most one extends clause.
// }
複製程式碼

9、抽象類的使用

Dart 可以使用 abstract 關鍵字來定義抽象類:

  • 抽象類中的 抽象方法不需要使用 abstract 關鍵字修飾
  • 抽象類不能例項化
main(List<String> args) {
  final s = Shape(); // IDE報錯:Abstract classes can't be instantiated.
}

// 注意2:抽象類不能例項化
abstract class Shape {
  // 抽象方法,沒有方法體,也不用 abstract 關鍵字修飾
  int getArea();

  String getInfo() {
    return "形狀";
  }
}

// 注意1:繼承自抽象類後,必須實現抽象類的抽象方法
class Rectangle extends Shape {
  @override
  int getArea() {
    return 100;
  }
}
複製程式碼

注意:Dart 中抽象類不能例項化,但也有特殊情況,有工廠建構函式(factory)的抽象類可以例項化,比如:Map。

10、隱式介面

Dart 使用 implements 關鍵字來實現多個介面,但奇葩的是,Dart 中沒有用來定義介面的關鍵字,預設情況下所有的類都是隱式介面,即可以 implements 任意類:

注意:要重寫被 implements 的類中的所有方法!

main(List<String> args) {}

// Dart中沒有哪一個關鍵字是來定義介面的
// 沒有這些關鍵字interface/protocol
// 預設情況下所有的類都是隱式介面
class Runner {
  void running() {
    print("跑吧");
  }
}

class Flyer {
  void flying() {
    print("飛吧");
  }
}

// 當將一個類當做介面使用時,那麼實現這個介面的類,必須實現這個介面中所有方法(不可以在這些方法裡使用super)
class SuperMan implements Runner, Flyer {
  @override
  void flying() {
    // super.flying(); // IDE報錯:The method 'flying' is always abstract in the supertype.
  }

  @override
  void running() {}
}
複製程式碼

建議:用 abstract class 來宣告介面,反正被 implements 的類的所有方法都要重寫。

11、mixin 混入的使用

當遇到要複用 2 個類中方法的時候,要怎麼辦?

  • 如果使用介面方式則需要重新實現 2 個類中各自的方法,這樣根本就不能算是複用;
  • 而 Dart 又只能單繼承,最多隻能複用 1 個類中方法;

Dart 提供了另一個方案:Mixin 混入。可以定義 2 個 mixin 類,通過 with 關鍵字把這 2 個 mixin 類混入到一個類 A 中,這時類 A 就擁有了它們的方法,從而達到複用 2 個類中方法的目的:

main(List<String> args) {
  final superMan = SuperMan();
  superMan.eating(); // 動作吃東西
  superMan.running(); // running(呼叫的是混入類Runner中的running())
  superMan.flying(); // flying
}

class Animal {
  void eating() {
    print("動作吃東西");
  }

  void running() {
    print("動物running");
  }
}

mixin Runner {
  void running() {
    print("running");
  }
}

mixin Flyer {
  void flying() {
    print("flying");
  }
}

// Runner中的running()與Animal中的running()衝突,這時會發生覆蓋,即SuperMan中的running()呼叫的是Runner的running()
class SuperMan extends Animal with Runner, Flyer {}
複製程式碼

12、類屬性和類方法

類中使用 static 關鍵字修飾的屬性(或方法)就是類屬性(或類方法),類屬性和類方法可以通過類名直接呼叫:

main(List<String> args) {
  Person.courseTime = "8:00";
  print(Person.courseTime);
  Person.gotoCourse();
}

class Person {
  // 成員變數
  String name;

  // 靜態屬性(類屬性)
  static String courseTime;

  // 物件方法
  void eating() {
    print("eating");
  }

  // 靜態方法(類方法)
  static void gotoCourse() {
    print("去上課");
  }
}
複製程式碼

13、列舉的使用

列舉使用 enum 關鍵字來定義,列舉有 2 個常見的屬性:

  • index:用於表示每個列舉的索引,從 0 開始
  • values:包含每個列舉值的集合
// 列舉:enum
main(List<String> args) {
  final color = Colors.red;
  switch (color) {
    case Colors.red:
      print("紅色");
      break;
    case Colors.blue:
      print("藍色");
      break;
    case Colors.green:
      print("綠色");
      break;
  }

  print(Colors.values); // [Colors.red, Colors.blue, Colors.green]
  print(Colors.red.index); // 0
}

enum Colors {
  red,
  blue,
  green,
}
複製程式碼

14、泛型的使用

集合型別使用泛型:

// List使用時的泛型寫法:
var names1 = ["abc", "cba", "nba", 111]; // List<Object>
var names2 = ["abc", "cba", "nba"]; // List<String>
// var names3 = <String>["abc", "cba", "nba", 111]; // IDE報錯:The element type 'int' can't be assigned to the list type 'String'.
var names3 = <String>["abc", "cba", "nba"]; // List<String>
List<String> names4 = ["abc", "cba", "nba"]; // List<String>

// Map使用時的泛型寫法:
var info1 = {"name": "lqr", "age": 18}; // _InternalLinkedHashMap<String, Object>
var info2 = <String, String>{'name': 'lqr', 'age': '18'}; // _InternalLinkedHashMap<String, String>
Map<String, String> info3 = {'name': 'lqr', 'age': '18'}; // _InternalLinkedHashMap<String, String>
複製程式碼

類定義使用泛型:

main(List<String> args) {
  var point = Point<double>(1.23333, 1.9527); // Point<double>
  final pointX = point.setAndGetX(5.55);
  print(pointX); // 5.55
}

// class Point<T> // 泛型T是Object的子類
class Point<T extends num> { // 泛型T是數字型別
  T x;
  T y;

  Point(this.x, this.y);

  T setAndGetX(T x) {
    this.x = x;
    return this.x;
  }
}
複製程式碼

T extends num 用於限制泛型的型別必須是數字,不限制的話則預設是 Object。

五、Dart 中庫的使用

  • Dart 中你可以匯入一個庫來使用它所提供的功能。
  • Dart 中任何一個 dart 檔案都是一個庫,即使你沒有用關鍵字 library 宣告。

庫匯入的語法:

import '庫所在的 uri';
複製程式碼

1、使用系統的庫

系統庫的 uri 一般以 dart: 開頭,常見的系統庫有:dart:iodart:asyncdart:math 等:

// import 'dart:io';
// import 'dart:isolate';
// import 'dart:async';
// import 'dart:math';

// 系統的庫:import 'dart:庫的名字';

import 'dart:math';

main(List<String> args) {
  final num1 = 20;
  final num2 = 30;
  print(min(num1, num2));
}
複製程式碼

注意:dart:core 也是很常用的系統庫,不過可以省略不寫。

2、使用自定義庫

自定義庫就是自己專案中定義的其他 dart 檔案,比如在 utils 目錄下建立一個 math_utils.dart 檔案,內容如下:

// utils 目錄下的 math_utils.dart 檔案內容:
int sum(int num1, int num2) {
  return num1 + num2;
}

int mul(int num1, int num2) {
  return num1 * num2;
}
複製程式碼

這裡的 math_utils.dart 檔案就是一個自定義庫,你可以在其他 dart 檔案中匯入:

// 匯入自定義庫可以是相對路徑或絕對路徑。
import 'utils/math_utils.dart';

main(List<String> args) {
  print(sum(20, 30));
複製程式碼

3、使用第三方庫

那些存放在遠端倉庫上的庫檔案就是第三方庫,如果專案中需要依賴第三方庫,就需要在專案目錄下建立一個 pubspec.yaml 檔案,其中 dependencies 部分填寫要依賴的第三方庫的描述(庫名: 版本):

name: myProjectName
description: my project name
dependencies:
  http: ^0.12.0+4
environment:
  sdk: ">=2.10.0 <3.0.0"
複製程式碼

可以在 pub.dev 上查詢第三方庫的使用說明。

填寫好依賴後,需要執行 dart pub getflutter pub get 命令,讓終端從伺服器上拉取第三方對應的版本檔案,之後就可以在程式碼中使用這個第三方庫了:

import 'package:http/http.dart' as http;

main(List<String> args) async {
  var url = 'https://example.com/whatsit/create';
  var response = await http.post(url, body: {'name': 'doodle', 'color': 'blue'});
  print('Response status: ${response.statusCode}');
  print('Response body: ${response.body}');
  print(await http.read('https://example.com/foobar.txt'));
}
複製程式碼

4、庫方法名衝突

比如:math_utils.dart 庫中有 int sum(int num1, int num2) 方法,當前 dart 檔案中又定義了 void sum(int num1, int num2) 方法,這 2 個方法名相同,但返回值不同:

import 'utils/math_utils.dart';

main(List<String> args) {
  print(sum(20, 30)); // IDE報錯:This experssion has a type of 'void' so its value can't be used.
}

// void sum(num1, num2) // 引數型別可以不寫,預設是 dynamic
void sum(int num1, int num2) {
  print(num1 + num2);
}
複製程式碼

這時,當前 dart 檔案識別到的是自己的 sum()方法,而實際上我們想要使用的是 math_utils.dart 庫中 sum()方法,這就發生了庫方法名衝突,可以使用 as 關鍵字給庫起別名來解決:

import 'utils/math_utils.dart' as mUtils;

main(List<String> args) {
  print(mUtils.sum(20, 30));
}

void sum(sum1, sum2) {
  print(sum1 + sum2);
}
複製程式碼

5、庫內容的顯示和隱藏

預設 import 進來的庫中所有內容都是可見的,但是很多時候我們希望只匯入庫中的某些內容,或者希望隱藏庫中的某些內容,這時可以使用 showhide 關鍵字:

  • show:顯示某個成員( 遮蔽其他)
// import "utils/math_utils.dart" show sum, mul;
import "utils/math_utils.dart" show sum;

main(List<String> args) {
  print(sum(20, 30));
  print(mul(20, 30)); // IDE報錯:The function 'mul' isn't defined.
}
複製程式碼
  • hide:隱藏某個成員(顯示其他)
// import "utils/math_utils.dart" hide sum, mul;
import "utils/math_utils.dart" hide sum;

main(List<String> args) {
  print(sum(20, 30)); // IDE報錯:The function 'sum' isn't defined.
  print(mul(20, 30));
}
複製程式碼

6、批量匯入庫檔案

有些時候,可能需要在一個 dart 檔案中,匯入 n 個庫,這還是能接受的:

import 'utils/math_utils.dart';
import 'utils/date_utils.dart';
import ...

main(List<String> args) {
  print(sum(20, 30));
  print(dateFormat());
}
複製程式碼

但如果是 n 個 dart 檔案都要匯入相同的 m 個庫呢?很明顯,需要有個統一批量匯入庫檔案的方案:可以建立一個新的 dart 檔案,使用 export 關鍵字統一導庫。

// utils 目錄下的 utils.dart 檔案內容:
export 'math_utils.dart';
export 'date_utils.dart';
export ...
複製程式碼

這時,其他 dart 檔案中,只需要匯入這個 utils.dart 就好了:

import 'utils/utils.dart';

main(List<String> args) {
  print(sum(20, 30));
  print(dateFormat());

  // math_utils.dart中的_min()無法被外界呼叫
  // print(_min(20, 30)); // IDE報錯:The function '_min' isn't defined.
}
複製程式碼

注意:Dart 語言中沒有可見性修飾符,即沒有 public、protected、private。如果某個成員屬性或方法不想被外界(其他 dart 檔案)呼叫,可以在名字前加個下劃線 _

相關文章