溫馨提示:本文只羅列 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
很多時候,我們並不希望外部可以直接訪問物件欄位,而是經過 setter
和 getter
中轉,這樣的好處是,可以在 setter
和 getter
中做一些額外的工作,Dart 支援為欄位增加 setter
和 getter
:
setter
:前面使用set
宣告,並且需要用()
接收引數。getter
:前面使用get
宣告,還需要宣告返回值型別,但是不能有()
接收引數。setter
和getter
像欄位一樣使用。
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 中的
setter
和getter
已經不能算是方法了吧。
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:io
、dart:async
、dart: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 get
或 flutter 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 進來的庫中所有內容都是可見的,但是很多時候我們希望只匯入庫中的某些內容,或者希望隱藏庫中的某些內容,這時可以使用 show
和 hide
關鍵字:
- 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 檔案)呼叫,可以在名字前加個下劃線
_
。