//判断是否包含某个key或者value
debugPrint(‘${tempMap.containsKey(“age”)} ${tempMap.containsValue(18)}’); //true true
//获取key的List
debugPrint(“${tempMap.keys.toList()}”);//[name, age]
//获取value的List
debugPrint(“${tempMap.values.toList()}”);//[zhang san, 18]
//根据key删除元素
tempMap.remove(“name”);
debugPrint(“$tempMap”); //{age: 18}
//根据key修改value
tempMap[“age”] = 28;
debugPrint(“$tempMap”); //{age: 28}
<a name = "XbVg6" ></a>
## 五、运算符
<a name = "I7UyJ" ></a>
### 5.1 算术运算符
| **名称** | **运算符** | **举例** |
| --- | --- | --- |
| 加 | + | var result1 = 5 + 3; //8 |
| 减 | - | var result2 = 5 - 3; //2 |
| 乘 | * | var result3 = 5 * 3; //15 |
| 除 | / | var result4 = 5 / 3; //1.6666666666666667 |
| 整除 | ~/ | var result5 = 5 ~/ 3; //1 |
| 取模 | % | var result6 = 5 % 3; //2 |
<a name = "u3qmN" ></a>
### 5.2 条件运算符
| **名称** | **运算符** | **举例** |
| --- | --- | --- |
| 大于 | > | debugPrint("${5 > 3}"); //true |
| 小于 | < | debugPrint("${5 < 3}"); //false |
| 等于 | == | debugPrint("${5 == 3}"); //false |
| 不等于 | != | debugPrint("${5 != 3}"); //true |
| 大于等于 | >= | debugPrint("${5 >= 3}"); //true |
| 小于等于 | <= | debugPrint("${5 <= 3}"); //false |
<a name = "FLqdD" ></a>
### 5.3 逻辑运算符
| **名称** | **运算符** | **举例** |
| --- | --- | --- |
| 与 | && | debugPrint("${5 > 3 && 1 < 2}"); //true |
| 或 | || | debugPrint("${5 < 3 || 1 > 2}"); //false |
| 非 | ! | debugPrint("${!(5 == 3)}"); //true |
<a name = "cQPpp" ></a>
### 5.4 三目运算符
condition ? expr1 : expr2
bool isShow = true;
var result = isShow ? “Dart” : “Flutter”;
debugPrint(result); //Dart
<a name = "qVdcr" ></a>
### 5.5 空安全运算符
- result = expr1 ?? expr2
如果发现 expr1 为 null,就返回 expr2 的值,否则就返回 expr1 的值。
var temp = “zhang san”;
// var temp = null;
var name = temp ?? “li si”;
debugPrint(name); //zhang san
- expr1 ??= expr2
等价于 expr1 = expr1 ?? expr2
var name = “zhang san”;
// var name = null;
debugPrint(name); //zhang san
name ??= “li si”;
debugPrint(name); //zhang san
- result = expr1 ?. value
如果 expr1 不为 null 就返回 expr1 . value ,否则就会返回 null 。
< a name = "zR11K" ></ a >
### 5.6 级联操作符
级联操作符是 ..,可以让你对一个对象中字段进行链式调用操作
var p1 = Person();
p1.name = “zhang san”;
p1.run(); //zhang san 跑步
var p2 = Person()
..name = “li si”
..run(); //li si 跑步
class Person {
String? name;
int? age;
void run () {
debugPrint(“$name 跑步”);
}
}
<a name = "S7QUJ" ></a>
## 六、流程控制
<a name = "v4dgl" ></a>
### 6.1 if-else
if后面跟一个可选的else块。如果if块测试的布尔表达式求值为false,则执行else块。 <br /> 条件控制语句,不支持非空即真或者非0即真,必须有明确的bool类型
bool isShow = true;
if (isShow) {
debugPrint(“条件成立”);
} else {
debugPrint(“条件不成立”);
}
<a name = "jlRJ0" ></a>
### 6.2 for 循环
for循环是一个确定循环的实现,用于执行代码块指定的次数。 <br /> for...in循环用于循环对象的属性,在每次迭代中,对象的一个属性分配给变量名称,并且此循环继续,直到对象的所有属性都用完为止。
var names = [“zhang san”, “li si”, “wang wu”];
for (var i = 0; i < names.length; i++) {
debugPrint(names[i]); //zhang san li si wang wu
}
for (var name in names) {
debugPrint(name); //zhang san li si wang wu
}
<a name = "VYRNJ" ></a>
### 6.3 while 循环
while(<条件>) <语句>; <br /> 每次指定的条件求值为true时,while循环都会执行指令。在执行代码块之前评估条件。
var names = [“zhang san”, “li si”, “wang wu”];
var index = 0;
while (index < names.length) {
debugPrint(names[index++]);//zhang san li si wang wu
}
<a name = "n8ULn" ></a>
### 6.4 do-while 循环
do <语句> while(<条件>); <br /> do...while循环类似于while循环,只是do...while循环不会在第一次循环执行时评估条件。
var names = [“zhang san”, “li si”, “wang wu”];
var index = 0;
do {
debugPrint(names[index++]); //zhang san li si wang wu
} while (index < names.length);
<a name = "noqrS" ></a>
### 6.5 break 和 continue
break语句用于将控件从构造中取出。在循环中使用break会导致程序退出循环。 <br /> continue语句跳过当前迭代中的后续语句,并将控制权带回循环的开头。
var names = [“zhang san”, “li si”, “wang wu”];
for (var i = 0; i < names.length; i++) {
if (names[i] == “li si”) {
continue;
}
if (names[i] == “wang wu”) {
break;
}
debugPrint(names[i]); //zhang san
}
<a name = "QKafr" ></a>
### 6.6 switch-case
switch语句计算表达式,将表达式的值与case子句指定的值匹配,然后执行与该case相关的语句。 <br /> 注意:每一个case语句,默认情况下必须以一个break结尾
var name = “zhang san”;
switch (name) {
case “zhang san”:
debugPrint(“名字叫$name”);
break;
case “li si”:
debugPrint(“名字叫$name”);
break;
case “wang wu”:
debugPrint(“名字叫$name”);
break;
default:
debugPrint(“其他名字”);
break;
}
<a name = "It3HG" ></a>
## 七、异常
Dart 中的异常捕获方使用的是 try-catch-finally
int a = 18;
int result = 0;
try {
result = a ~/ 0;
} catch (error) {
debugPrint(“$error”); // 抛出异常 IntegerDivisionByZeroException
} finally {
debugPrint(“执行完成”); // 抛出异常后也会执行
}
<a name = "y7aT2" ></a>
## 八、函数
<a name = "NcoTo" ></a>
### 8.1 函数的基本用法
函数的定义方式:
返回值 函数的名称(参数列表) {
函数体
return 返回值
}
void 关键字表示该函数不向调用者返回任何值。< br />示例代码:
void test() {
debugPrint(“test”);
}
<a name = "mz0u1" ></a>
### 8.2 函数的参数
参数是一种将值传递给函数的机制。 <br /> 函数的参数可以分成两类: 必须参数和可选参数。
<a name = "DCNZt" ></a>
#### 8.2.1 必须参数
int sum(int a, int b) {
return a + b;
}
<a name = "ltPe8" ></a>
#### 8.2.2 可选参数
可选参数可以分为 命名可选参数 和 位置可选参数 <br /> 定义方式:
命名可选参数: {param1, param2, …}
位置可选参数: [param1, param2, …]
命名可选参数示例:< br />命名可选参数,可以指定某个参数是必传的,在参数前面添加 required 即可< br />命名可选参数,可以设置参数默认值
printUserInfo1(“zhang san”, age: 18, height: 1.8); //name=zhang san age=18 height=1.8
void printUserInfo1(String name, {int? age, double? height}) {
debugPrint(“name=$name age=$age height=$height”);
}
printUserInfo2(“li si”, age: 28); //name=li si age=28 height=null
void printUserInfo2(String name, {required int age, double? height}) {
debugPrint(“name=$name age=$age height=$height”);
}
printUserInfo3(“wang wu”); //name=wang wu age=null height=2.26
void printUserInfo3(String name, {int? age, double height = 2.26}) {
debugPrint(“name=$name age=$age height=$height”);
}
位置可选参数示例:< br />位置可选参数,可以设置参数默认值
printUserInfo4(“zhao liu”); //name=zhao liu age=null height=2.26
printUserInfo4(“zhao liu”, 38, 1.98); //name=zhao liu age=38 height=1.98
void printUserInfo4(String name, [int? age, double height = 2.26]) {
debugPrint(“name=$name age=$age height=$height”);
}
<a name = "oMWY8" ></a>
### 8.3 高阶函数
Dart中函数类型为Function,函数可以作为变量定义或者其他函数的参数或者返回值。
//函数传给变量
var fn1 = run;
fn1(“zhang san”);
//函数作为另一个函数的参数
test2(run);
//函数作为另一个函数的返回值
var fn2 = getFunc();
fn2(“wang wu”); //wang wu 跑步
void run(String name) {
debugPrint(“$name 跑步”);
}
void test2(Function fn) {
fn(“li si”);
}
Function getFunc() {
return run;
}
<a name = "qZ3pg" ></a>
### 8.4 箭头函数
如果函数中只有一个表达式,可以使用箭头函数
返回值 函数的名称(参数列表)=>函数体
箭头函数示例:
printHelloFlutter(); //Hello Flutter
void printHelloFlutter() => debugPrint(“Hello Flutter”);
<a name = "ODiPs" ></a>
### 8.5 匿名函数
Dart 中可以创建一个没有函数名称的函数,称为匿名函数,或者lambda 函数或者闭包函数。
var names = [“zhang san”, “li si”, “wang wu”];
//有名字的函数
void printElement(element) {
debugPrint(element);
}
names.forEach(printElement); //zhang san li si wang wu
//匿名函数
names.forEach((element) {
debugPrint(element); //zhang san li si wang wu
});
//箭头
names.forEach((element) => debugPrint(element)); //zhang san li si wang wu
<a name = "shjKt" ></a>
### 8.6 词法作用域
Dart中的词法有自己明确的作用域范围,根据代码的结构({})来决定作用域范围的 <br /> 变量查找的规则是从当前作用域查找,如果没找到再到父级作用域中查找,一层层往外查找,如果在全局作用域还没找到的话,就会报错。
String name = “li si”;
void testDomain(){
// String name = “zhang san”;
debugPrint(name);
}
testDomain(); //li si
<a name = "gDEXk" ></a>
### 8.7 返回值问题
- 所有函数都返回一个值。如果没有指定返回值,则语句返回null。
- return类型可以是任何有效的数据类型。
- 返回值的数据类型必须与函数的返回类型匹配。
- 函数最多可以返回一个值,每个函数只能有一个return语句。
注意:Dart中可以忽略掉函数的返回类型,忽略后仍能正常调用,被省略的函数返回值不是 void。
debugPrint(testReturnValue()); //null
testReturnValue() {
debugPrint(“testReturnValue”);
}
<a name = "pTwCq" ></a>
## 九、类和对象
<a name = "CKnr8" ></a>
### 9.1 类的定义
<a name = "Und9R" ></a>
#### 9.1.1 类的声明
Dart是一种面向对象的语言,使用class关键字声明一个类。 <br /> 类定义以关键字class开头,后跟类名,并且由一对花括号包围的类体。 <br /> 类定义可包括以下内容:
- 成员变量是类中声明的任何变量,表示与对象有关的数据。
- setters和getters允许程序初始化和检索类成员变量的值。
- 构造函数负责为类的对象分配内存。
- 函数表示对象可以采取的操作,它们有时也称为方法。
注意: 方法中使用属性有命名冲突时,属性前面加this。
class 类名 {
类型 成员名;
返回值类型 方法名(参数列表) {
方法体
}
}
示例:
class Person {
String? name;
int? age;
void run() {
debugPrint(“$name 跑步”);
}
}
<a name = "w2nLD" ></a>
#### 9.1.2 实例变量
实例变量为类的成员变量或者称为成员属性,当声明一个实例变量时,每一个对象实例都会有这个属性。 <br /> 如果要表示实例私有属性,直接在属性名前面加下划线_,如_name 和_age,后面使用getter和setter的时候使用的到。
<a name = "UnKay" ></a>
#### 9.1.3 类成员与方法
static关键字可以应用于类的数据成员,即字段和方法。 <br /> 静态变量保留其值,直到程序完成执行。 <br /> 静态成员由类名引用。
Person.name = “zhang san”;
Person.age = 18;
Person.run(); //zhang san 跑步
class Person {
static String? name;
static int? age;
static void run() {
debugPrint(“$name 跑步”);
}
}
<a name = "kE9T4" ></a>
### 9.2 构造方法
<a name = "F9XgP" ></a>
#### 9.2.1 普通构造方法
构造函数是类的特殊函数,负责初始化类的变量。 <br /> 当类中没有定义构造方法时,将默认拥有一个无参的构造方法。 <br /> 当类中自定义了构造方法时,默认的构造方法将会失效。 <br /> 语法:
类名(参数列表) {
函数体
}
示例:< br />在实现构造方法时,通常是通过参数列表给属性赋值, Dart 提供了一种简洁的语法糖,如下:
class Person {
String? name;
int? age;
// Person(String? name, int? age) {
// this.name = name;
// this.age = age;
// }
Person(this.name, this.age);
}
<a name = "hTZia" ></a>
#### 9.2.2 命名构造方法
开发中希望实现多个构造方法,但Dart不支持方法(函数)的重载,不能创建相同名称的构造方法。所以Dart提供了命名构造函数,以此来定义多个构造函数。 <br /> 语法:
类名.构造名(参数列表){
函数体
}
示例:
var p1 = Person(name: “zhang san”, age: 18);
debugPrint(“$p1”); //name:zhang san age:18
var p2 = Person.withArguments(name: “li si”, age: 28);
debugPrint(“$p2”); //name:li si age:28
class Person {
String? name;
int? age;
Person({this.name, this.age});
Person.withArguments({this.name, this.age});
@override
String toString() {
return “name:$name age:$age”;
}
}
<a name = "hO4Wb" ></a>
#### 9.2.3 初始化列表
Dart 中使用 final 修饰变量,表示该变量初始化后不能再被修改。 <br /> final 修饰的变量只有 getter 访问器函数,没有 setter 函数。 <br /> final 修饰的变量要在构造方法中进行初始化。 <br /> 有时候我们希望通过调用不同的构造函数来初始化某一个属性,需要在构造函数后加冒号“:”,然后再设置属性的值,称之为初始化列表
var p1 = Person.withName1(name: “zhang san”, age: 18);
debugPrint(“$p1”); //name:zhang san age:18 height:1.83
var p2 = Person.withName2(name: “li si”, age: 28);
debugPrint(“$p2”); //name:li si age:28 height:2.26
class Person {
final String? name;
final int? age;
final double height;
Person.withName1({this.name, this.age}) : height = 1.83;
Person.withName2({this.name, this.age}) : height = 2.26;
@override
String toString() {
return “name:$name age:$age height:$height”;
}
}
<a name = "LsTlD" ></a>
#### 9.2.4 重定向构造方法
如果需要在一个构造函数调用同一个类中的另一个构造函数, 可以使用重定向构造方法,重定向构造函数的主体为空,构造函数的调用出现在冒号(:)之后,使用this调用
var p = Person.withName(name: “zhang san”);
debugPrint(“$p”); //name:zhang san age:18
class Person {
String? name;
int? age;
Person({this.name, this.age});
Person.withName({String? name}) : this(name: name, age: 18);
@override
String toString() {
return “name:$name age:$age”;
}
}
<a name = "i9joO" ></a>
#### 9.2.5 常量构造方法
如果调用构造函数,在传入相同值,希望返回同一个对象,可以使用常量构造方法。构造的对象使用identical(Object? a, Object? b)函数来判断两个对象是否是同一个对象。常量构造需要注意如下几点:
- 常量构造函数需以const关键字修饰
- 常量构造方法的类中,所有的成员变量必须是final修饰
- 构建常量实例必须使用定义的常量构造函数
- 如果实例化时不加const修饰符,即使调用的是常量构造函数,实例化的对象也不是常量实例
var p1 = const Person(name: “zhang san”);
var p2 = const Person(name: “zhang san”);
debugPrint(“${identical(p1, p2)}”); //true
class Person {
final String? name;
const Person({this.name});
@override
String toString() {
return “name:$name”;
}
}
<a name = "SIQ2u" ></a>
#### 9.2.6 工厂构造方法
通常构造函数总是会创建一个新的实例对象,但有时候不是每次都需要创建新的实例,可能需要使用缓存。需要使用factory 修饰的工厂构造函数,可以从缓存中的返回已经创建实例或者返回一个新的实例。 <br /> 工厂构造函数也是一种构造函数,与普通构造函数不同,不会自动生成实例,而是通过代码来决定返回的实例对象。 <br /> 工厂构造函数类似于 static 静态成员,无法访问 this 关键字;同时需要依赖其他类型构造函数。
var p1 = Person(“zhang san”);
var p2 = Person(“zhang san”);
var p3 = Person(“li si”);
var p4 = Person._(“li si”);
debugPrint(“${identical(p1, p2)}”); //true
debugPrint(“${identical(p1, p3)}”); //false
debugPrint(“${identical(p3, p4)}”); //false
class Person {
final String name;
static final Map _cache = {};
factory Person(String name) {
bool isContain = cache.containsKey(name);
Person? tempP = _cache[name];
if (isContain && tempP != null) {
return tempP;
} else {
final p = Person. (name);
_cache[name] = p;
return p;
}
}
Person._(this.name);
}
工厂构造函数可以实现单例
var p1 = Person(“zhang san”);
var p2 = Person(“li si”);
var p3 = Person(“wang wu”);
var p4 = Person._(“zhao liu”);
debugPrint(“${identical(p1, p2)}”); //true
debugPrint(“${identical(p1, p3)}”); //true
debugPrint(“${identical(p3, p4)}”);
debugPrint(“$p1”); //zhang san
debugPrint(“$p2”); //zhang san
debugPrint(“$p3”); //zhang san
debugPrint(“$p4”); //zhao liu
class Person {
final String name;
// 静态属性存放实例化对象
static Person? instance;
factory Person(String name) {
return Person._instance ??= Person. (name);
}
Person._(this.name);
@override
String toString() {
return “name:$name”;
}
}
<a name = "OXHjO" ></a>
### 9.3 getter和setter
getter和setter(也称为访问器和更改器)允许程序分别初始化和检索类字段的值。 <br /> 使用get关键字定义getter或访问器,getter没有参数,但返回一个值。 <br /> 使用set关键字定义Setter或存取器,setter只有一个参数但不返回值。 <br /> 在给实例属性赋值或获取值时,实际上内部都是对 setter 和 getter 函数的调用。 <br /> 如果需要监控类的属性被访问的过程,可以使用setter和getter。 <br /> 注意:成员属性必须是下划线“_”私有属性,然后使用control+enter快捷键,选择生成getter和setter方法
var p = Person(name: “zhang san”);
debugPrint(“$p”); //name:zhang san
p.name = “li si”; //setName
debugPrint(“$p”); //name:li si
class Person {
late String _name;
String get name {
debugPrint(“getName”);
return _name;
}
set name(String value) {
debugPrint(“setName”);
_name = value;
}
Person({required String name}) {
_name = name;
}
@override
String toString() {
return “name:$name”;
}
}
<a name = "VIIHO" ></a>
### 9.4 类的继承
Dart支持继承的概念,它是程序从现有类创建新类的能力。 <br /> 一个类使用extends关键字从另一个类继承,子类中使用super来访问父类。 <br /> 子类继承除父类的构造函数之外的所有属性和方法。 <br /> 注意:Dart不支持多重继承。 <br /> 语法:
class 子类名 extends 父类名
示例:
var p = Person();
p.name = “zhang san”;
debugPrint(“${p.name}”); //zhang san
p.run(); //跑步
class Animal {
String? name;
void run() {
debugPrint(“跑步”);
}
}
class Person extends Animal {
}
子类可以拥有自己的成员变量, 并且可以对父类的方法进行重写:
var p = Person();
p.name = “zhang san”;
p.age = 18;
debugPrint(“${p.age}”); //18
p.run(); //zhang san 在跑步
class Animal {
String? name;
void run() {
debugPrint(“跑步”);
}
}
class Person extends Animal {
int? age;
@override
void run() {
debugPrint(“$name 在跑步”);
}
}
子类中可以调用父类的构造方法,对某些属性进行初始化:
- 子类的构造方法在执行前,将隐含调用父类的无参默认构造方法(没有参数且与类同名的构造方法)。
- 如果父类没有无参默认构造方法,则子类的构造方法必须在初始化列表中通过 super 显式调用父类的某个构造方法。
var p = Person(“zhang san”);
p.run(); //zhang san 在跑步
class Animal {
String? name;
Animal(this.name);
void run() {
debugPrint(“跑步”);
}
}
class Person extends Animal {
int? age;
Person(String? name) : super(name);
@override
void run() {
debugPrint(“$name 在跑步”);
}
}
<a name = "MBVKb" ></a>
### 9.5 抽象类
当父类本身不需要对某些方法进行具体的实现,所以父类中定义的方法可以定义为抽象方法。抽象方法是声明一个方法而不提供它的具体实现。而抽象方法必须存在于抽象类中,抽象类是使用abstract声明的类。
var p = Person(name: “zhang san”, age: 18);
p.run(); //zhang san 在跑步
abstract class Animal {
void run();
void fly() {
}
}
class Person extends Animal {
String? name;
int? age;
Person({this.name, this.age});
@override
void run() {
debugPrint(“$name 在跑步”);
}
}
注意事项:
- 抽象类不能实例化。
- 抽象类中的抽象方法必须被子类实现,抽象类中的已经被实现方法,可以不被子类重写。
< a name = "Vf7WO" ></ a >
### 9.6 隐式接口(implements)
Dart 中不支持多继承,如果想要类似的多继承该如何实现呢?< br /> Dart 中默认情况下定义的每个类都相当于默认声明了一个接口,可以由其他的类来实现。< br />如果有⼀个类 A ,你想让类 B 拥有 A 的 API ,但⼜不想拥有 A ⾥的实现,那么你就应该把 A 当做接⼝,类 B implements 类 A 。< br />在通过 implements 实现某个类时:
- 类中所有的方法都必须被重新实现,无论这个类原来是否已经实现过该方法,在子类实现的时候要加 @override 。
- 类中的成员变量也需要在子类里重新实现。在成员变量前加 @override 。
var m = SuperMan();
m.name = “zhang san”;
m.run(); //超人在奔跑
m.fly(); //超人在飞
m.jump(); //超人 zhang san 在跳
abstract class Runner {
run();
}
abstract class Flyer {
fly();
}
class Jumper {
String? name;
jump() {
debugPrint(“超人在跳”);
}
}
class SuperMan implements Runner, Flyer, Jumper {
@override
run() {
debugPrint(“超人在奔跑”);
}
@override
fly() {
debugPrint(“超人在飞”);
}
@override
jump() {
debugPrint(“超人 $name 在跳”);
}
@override
String? name;
}
<a name = "c4oRt" ></a>
### 9.7 Mixin混入
如果一个类只直接调用多个类中的原有某些方法,该怎么实现呢?
- 使用继承吗?Dart只支持单继承,只能复用一个类的实现。
- 隐式接口吗?需要实现所有的方法和属性。
- Dart提供了Mixin混入的方案
我们可以通过class定义类之外,也可以通过mixin关键字来定义一个类。通过mixin定义的类用于被其他类混入使用,需要通过with关键字进行混入。
var m = SuperMan();
m.name = “zhang san”;
m.run(); //超人在奔跑
m.fly(); //超人在飞
m.jump(); //超人 zhang san 在跳
mixin Runner {
void run() {
debugPrint(“超人在奔跑”);
}
}
mixin Flyer {
fly() {
debugPrint(“超人在飞”);
}
}
mixin Jumper {
String? name;
jump() {
debugPrint(“超人 $name 在跳”);
}
}
class SuperMan with Runner, Flyer, Jumper {
}
<a name = "peDxb" ></a>
### 9.8 枚举类型
使用enum关键字来进行定义:
enum 枚举名 {
枚举值1,
枚举值2,
枚举值3,
…
}
枚举类型中有两个比较常见的属性:
- index : 用于表示每个枚举常量的索引, 从 0 开始。
- values : 包含每个枚举值的 List 。
示例:
debugPrint(“${MyColors.red}”); //MyColors.red
debugPrint(“${MyColors.red.index}”); //0
debugPrint(“${MyColors.green.index}”); //1
debugPrint(“${MyColors.blue.index}”); //2
debugPrint(“${MyColors.values}”); //[MyColors.red, MyColors.green, MyColors.blue]
enum MyColors {
red,
green,
blue
}
```