Dart SDK installation
If you are developing with Flutter, the Flutter SDK comes with it, so you don't need to install the Dart SDK separately.
If you need to debug and run Dart code independently, you need to install the Dart SDK independently.
Official documentation: https://dart.dev/get-dart
-
windows (recommended): http://www.gekorm.com/dart-windows
-
mac: If the mac computer does not have
brew
this tool installed, the first step is to install it: https://brew.sh and then executebrew tap dart-lang/dart
,brew install dart
Dart development tools:
- There are many development tools for Dart: IntelliJ IDEA, WebStorm, Atom, Vscode, Android Studio, etc.
- Run Dart online without using IDE: https://dartpad.cn
If Dart is configured in Vscode:
-
Find the vscode plugin to install dart
-
Find the vscode plugin to install code runner, Code Runner can run our files
Base
Dart is a strongly typed language , any variable has a definite type .
Official Documentation: An Overview of the Dart Programming Language
Variables and Naming
Automatic type inference:
var name = 'lisa'; // name 变量的类型被推断为 String
In Dart, after var
declaring a variable, Dart will infer its type based on the type of the first assigned data at compile time, and its type has been determined after compilation. For example, the above name has been determined to be of String type, and other types cannot be assigned to it, otherwise an error will be reported. (This behavior is consistent with kotlin)
var name = 'lisa';
name = 123; // 报错
name = '张三'; // 不报错
Declare variables with definite types whenever possible
String name = '张三';
You can use dynamic
dynamic typing if you don't know the type
dynamic name ="张三";
name = 666;
Dart naming rules:
- Variable names must consist of numbers, letters, underscores, and dollar signs ($).
- Note: Identifiers cannot begin with a number
- Identifiers cannot be reserved words and keywords.
- Variable names are case sensitive eg: age and Age are different variables. In actual use, it is also recommended not to use a word case to distinguish two variables.
- Identifiers (variable names) must see the meaning of the name: it is recommended to use nouns for variable names, and verbs for method names
List of Dart language keywords:
In Dart, uninitialized variables default to null
. The default value is true even if the variable is a number , because everything is an objectnull
in Dart , and numbers are no exception.
int? num;
print(num); //null
dart entry function:
// void 可以省略
void main(){
print('你好dart');
}
constant
Variables that are never modified during use can use final
or const
, not var
or other types. The difference between the two is: a const variable is a compile-time constant, and a final variable is initialized when it is first used . For used final
or const
modified variables, the variable type can be omitted.
const
You have to assign a value at the beginning, final
and you can assign it once without assigning a value; final
not only const
has the characteristics of a compile-time constant, the most important thing is that it is a runtime constant, and it final
is lazy initialization, that is, it is initialized before it is used for the first time at runtime .
// 使用final定义变量。
final String name = '李四';
final int age = 24;
// 使用const定义变量。
const String gender = '男';
const int height = 165;
final name = 'Bob'; // Without a type annotation
const bar = 1000000;
The same const
constant will not be created repeatedly in memory, but final
will:
final finalList1 = [1, 2];
final finalList2 = [1, 2];
print(identical(finalList1, finalList2)); // false identical 用于检查两个引用是否指向同一个对象
const constList1 = [1, 2];
const constList2 = [1, 2];
print(identical(constList1, constList2)); // true
built-in type
The Dart language supports the following built-in types:
- Number :
int
、double
- String:
String
- Boolean:
bool
- List (also known as Array)
- Map
- Set
- Rune (for representing Unicode characters in strings)
- Symbol
These types can all be initialized as literals.
Because all variables in Dart are ultimately an object (an instance of a class), variables can be initialized using constructor functions. Some built-in types have their own constructors. For example, Map()
construct a map variable by .
string
Several ways of string definition
var str1='this is str1';
var str2="this is str2";
String str1='this is str1';
String str2="this is str2";
String str1='''this is str1
this is str1
this is str1
''';
String str1="""
this is str1
this is str1
this is str1
""";
concatenation of strings
String str1='你好';
String str2='Dart';
print("$str1 $str2");
print(str1 + str2);
print(str1 +" "+ str2);
String to other types are through parse
the method:
String str = '123';
var myNum = int.parse(str);
print(myNum is int);
String str2 = '123.1';
var myNum2 = double.parse(str2);
print(myNum2 is double);
String price = '12';
var myNum3 = double.parse(price);
print(myNum3);
print(myNum3 is double);
Other types of String
transfer toString
methods
print(myNum3.toString());
int? bbb;
print(bbb.toString()); //正常 其他类型的null可以转成null字符串
String? ccc;
print(double.parse(ccc));//报错,null类型String不能转为其他类型
string split
var list = str.split('-'); // 字符串分割转化成List
Runes
// 表示符文的意思,用于在字符串中表示Unicode字符。
// 使用String.fromCharCodes显示字符图形。如果非4个数值,需要把编码值放到大括号中。
Runes runes = Runes('\u{1f605} \u6211');
var str1 = String.fromCharCodes(runes);
print(str1);
List (array/collection)
The objects in the List can be different, several ways to define the List
// 1、第一种定义List的方式
var l1 = ["张三",20,true];
print(l1); //[张三, 20, true]
print(l1[0]); //张三
print(l1[1]); //20
print(l1.length); //3
// 2、第二种定义List的方式 指定类型
var l2 = <String>["张三","李四"];
var l3 = <int>[12, 30];
print(l2);
print(l3);
// 3、第三种定义List的方式 增加数据 ,通过[]创建的集合它的容量可以变化
var l4 = [];
print(l4);
print(l4.length);
l4.add("张三");
l4.add("李四");
l4.add(20);
print(l4);
print(l4.length);
var l5 = ["张三", 20, true];
l5.add("李四");
l5.add("zhaosi");
print(l5);
// 4、第四种定义List的方式,通过List.filled创建的集合长度是固定
var l6 = List.filled(2, "");
print(l6.length);
l6.length = 0; // 修改集合的长度 报错
var l7 = <String>["张三","李四"];
print(l7.length); //2
l7.length=0; //可以改变的
print(l7); //[]
var l8 = List<String>.filled(2, "");
l8[0]="string";
// l8[0]=222;
print(l8);
// 通过List.from创建
final numbers = <num>[1, 2, 3];
final listFrom = List<int>.from(numbers);
print(listFrom); // [1, 2, 3]
// 通过List.of创建
final numbers = <int>[1, 2, 3];
final listOf = List<num>.of(numbers);
print(listOf); // [1, 2, 3]
// 通过List.generate创建
final growableList =
List<int>.generate(3, (int index) => index * index, growable: true);
print(growableList); // [0, 1, 4]
final fixedLengthList =
List<int>.generate(3, (int index) => index * index, growable: false);
print(fixedLengthList); // [0, 1, 4]
// 通过List.unmodifiable创建
final numbers = <int>[1, 2, 3];
final unmodifiableList = List.unmodifiable(numbers); // [1, 2, 3]
unmodifiableList[1] = 87; // 报错 不可修改
Various operation methods of List:
void listAdd() {
List<String> list = ["周一", "周二", "周三"];
list.add("周四");
list.add("周五");
print(list);
List<String> l1 = ["周一", "周二", "周三"];
List<String> l2 = ["周四", "周五", "周六", "周日"];
l1.addAll(l2);
print(l1); // [周一, 周二, 周三, 周四, 周五, 周六, 周日]
}
void listInsert() {
List<String> l1 = ["周一", "周二", "周三"];
l1.insert(3, "周四");
print(l1); // [周一, 周二, 周三, 周四]
l1.insert(0, "周日");
print(l1); // [周日, 周一, 周二, 周三, 周四]
}
void listInsert2() {
List<String> l1 = ["周一", "周二", "周三"];
List<String> l2 = ["周四", "周五", "周六", "周日"];
l1.insertAll(1, l2);
print(l1); // [周一, 周四, 周五, 周六, 周日, 周二, 周三]
}
void listRemove() {
List<String> l1 = ["周一", "周二", "周三", "周一"];
l1.remove("周一");
print(l1); // [周二, 周三, 周一]
l1.removeAt(1);
print(l1);
l1.removeLast();
print(l1);
// l1.clear();
}
//根据条件删除元素
void listRemoveWhere() {
List<String> l1 = ["周一", "周二", "周三"];
l1.removeWhere((e) => e == "周二");
print(l1); // [周一, 周三]
List<String> l2 = ["周一", "周二", "周三", "星期四"];
l2.removeWhere((e) => e.contains("周"));
print(l2); // [星期四]
List<String> l3 = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
l3.removeRange(2, 5); //包前不包后
print(l3); // [周一, 周二, 周六, 周日]
}
void listSet() {
List<String> l1 = ["周一", "周二", "周三", "星期四"];
l1[3] = "周四";
print(l1); // [周一, 周二, 周三, 周四]
List<String> l2 = ["周一", "周二", "周三", "周四", "周五"];
l2.fillRange(1, 4, "AllenSu"); //用相同的值替换指定索引范围内的所有元素(含头不含尾)
print(l2); // [周一, AllenSu, AllenSu, AllenSu, 周五]
List<String> l3 = ["周一", "周二", "周三"];
l3.replaceRange(1, 2, ["周四", "周五", "周六", "周日"]); // 用某一数组替换指定索引范围内的所有元素(含头不含尾)
print(l3); // [周一, 周四, 周五, 周六, 周日, 周三]
List<String> l4 = ["周一", "周二", "周三", "周四", "周五"];
l4.replaceRange(0, 4, ["周六", "周日"]);
print(l4); // [周六, 周日, 周五]
List<String> l5 = ["11", "22", "33"];
List<String> l6 = ["aa", "bb", "cc"];
l5.setRange(0, 2, l6); // 用 l6 数组中索引为 0 1 位置处元素的值,替换掉 l5 数组中索引 0 1 位置处元素的值
print(l5); // [aa, bb, 33]
List<String> l7 = ["周一", "周二", "周三", "周四"];
List<String> l8 = ["周五", "周六", "周日"];
l7.setAll(1,
l8); // 从指定索引位置开始,使用第二个数组内的元素依次替换掉第一个数组中的元素 注意: index + l2.length <= l1.length ,否则会报错。
print(l7); // [周一, 周五, 周六, 周日]
}
void listSearch() {
List<String> l1 = ["周一", "周二", "周三", "周四"];
String str = l1.elementAt(2);
print(str); // 周三
print(l1[2]); // 周三
print(l1.first); // 周一
print(l1.last); // 周四
List<String> l2 = ["周一", "周二", "周三", "周四", "周三"];
bool a = l2.contains("周一");
bool b = l2.contains("周日");
print(a); // true
print(b); // false
print(l2.indexOf("周三")); //2
print(l2.indexOf("周日")); //-1
print(l2.lastIndexOf("周三")); //4
print(l2.lastIndexOf("周日")); //-1
print(l2.indexWhere((e) => e == "周三")); //2 返回第一个满足条件的元素的索引
print(l2.lastIndexWhere((e) => e == "周三")); //4 从后往前找 返回第一个满足条件的元素的索引
List<int> l3 = [8, 12, 4, 1, 17, 33, 10];
Iterable<int> l4 =
l3.where((e) => e > 10); //根据条件函数筛选每个元素,符合条件的元素组成一个新的 Iterable
print(l4); // (12, 17, 33)
List<int> l5 = l3.where((e) => e >= 10).toList();
print(l5); // [12, 17, 33, 10]
print(l3.firstWhere((e) => e > 10)); // 12 返回第一个满足条件的元素
print(l3.lastWhere((e) => e < 10)); // 1 从后往前找 返回第一个满足条件的元素
int res1 = l3.singleWhere((e) => e > 30); //获取满足指定条件的唯一元素
int res2 = l3.singleWhere((e) => e > 33, orElse: () => -1);
// int res3 = l3.singleWhere((e) => e > 10, orElse: () => null);
print(res1); // 33
print(res2); // -1。数组中没有满足条件的元素,返回 orElse 方法指定的返回值(-1是自己填的,你也可以设置为其它值)
// print(res3); // 报错。当数组中有多个满足条件的元素时,即使你设置了 orElse 的返回值,用 singleWhere 也会报错
List<int> l6 = [8, 12, 4, 1, 17, 33, 10];
l6.retainWhere((e) => e > 10); // 保留满足条件的元素(改变了原数组)
print(l6); // [12, 17, 33]
List<int> l7 = [8, 12, 4, 1, 17, 33, 10];
print(l7.any((e) => e > 30)); //true 判断数组中是否有满足指定条件的元素
print(l7.every((e) => e > 5)); //false 判断数组中是否每个元素都满足指定的条件
List<int> l8 = [8, 12, 4, 1, 17, 33, 10];
Iterable<int> l9 = l8.take(3); // 从索引 0 位置处,取指定个数的元素
print(l9); // (8, 12, 4)
//从索引 0 位置处,查询满足指定条件的元素,直到出现第一个不符合条件的元素,然后返回前面符合条件的元素
Iterable<int> l10 = l8.takeWhile((e) => e > 1);
print(l10); // (8, 12, 4)
Iterable<int> l11 = l8.skip(4); // 跳过指定个数的元素,返回后面的元素
print(l11); // (17, 33, 10)
Iterable<int> l12 =
l8.skipWhile((e) => e < 17); //跳过满足指定条件的元素,直到不满足条件的元素,然后将该元素后面的所有元素返回
print(l12); // (17, 33, 10)
List<int> l13 = l8.sublist(5); //从指定索引处截取数组
print(l13); // [33, 10]
List<int> l14 = l8.sublist(2, 5);
print(l14); // [4, 1, 17] 含头不含尾
Iterable<int> l15 = l8.getRange(2, 5); //截取指定索引范围内的元素
print(l15); // (4, 1, 17)
List list = [
"a",
15,
"b",
false,
true,
20,
"c",
{
"name": "AllenSu"}
];
Iterable<String> list1 = list.whereType(); // 从混合类型的数组中,筛选出指定类型的元素
Iterable<int> list2 = list.whereType();
Iterable<bool> list3 = list.whereType();
Iterable<Map> list4 = list.whereType();
print(list1); // (a, b, c)
print(list2); // (15, 20)
print(list3); // (false, true)
print(list4); // ({name: AllenSu})
}
void listOperate() {
List<int> l1 = [8, 12, 4, 6, 22, 35, 10];
l1.forEach((e) => print(e));
for(var i = 0; i < l1.length; i++) {
print(l1[i]);
}
for (var item in l1) {
print(item);
}
Iterable<bool> l2 =
l1.map((e) => e > 10); //遍历数组中的所有元素,可以对元素进行处理,并返回新的 Iterable
Iterable<String> l3 = l1.map((e) => e > 10 ? "大" : "小");
Iterable<num> l4 = l1.map((e) => e > 12 ? e / 2 : e * 2);
List<String> l5 = l1.map((e) => e % 2 == 0 ? "even" : "odd").toList();
print(l2); // (false, true, false, false, true, true, false)
print(l3); // (小, 大, 小, 小, 大, 大, 小)
print(l4); // (16, 24, 8, 12, 11.0, 17.5, 20)
print(l5); // [even, even, even, even, even, odd, even]
List<int> l7 = [8, 12, 8, 6, 22, 12, 10];
Set<int> l8 = l7.toSet(); //去重
print(l8); // {8, 12, 6, 22, 10}
List<String> l9 = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
Map map = l9.asMap(); // 将 List 转换为 Map,key 为原数组的索引,value 为原数组的元素
print(map); // {0: 周一, 1: 周二, 2: 周三, 3: 周四, 4: 周五, 5: 周六, 6: 周日}
List<int> sortList1 = [8, 12, 8, 6, 22, 12, 10];
sortList1.sort(); //排序
List<String> sortList2 = ["f", "d", "b", "c", "a", "e"];
sortList2.sort();
print(sortList1); // [6, 8, 8, 10, 12, 12, 22]
print(sortList2); // [a, b, c, d, e, f]
print(sortList2.reversed); //反序
List<int> list1 = [8, 12, 8, 6, 22, 12, 10];
String str1 = list1.join();
String str2 = list1.join("");
String str3 = list1.join(" ");
String str4 = list1.join(","); //用指定字符连接数组中每个元素,返回 String
String str5 = list1.join("-");
print(str1); // 81286221210
print(str2); // 81286221210
print(str3); // 8 12 8 6 22 12 10
print(str4); // 8,12,8,6,22,12,10
print(str5); // 8-12-8-6-22-12-10
List<int> intList = [8, 12, 8];
var anotherList = intList.cast(); // 指定 l2 的数据类型和 l1 的一样,都是 int 类型
anotherList.add(6);
print(intList); // [8, 12, 8, 6] 二者是同一个数组引用
// anotherList.add("ddd");
// print(intList); // 报错,提示无法将 String 类型的数添加到 int 类型的数组中
// generate 快速生产 Flutter 中的 Widget
// children: List.generate(l1.length, (index){
// return Text("$index");
// })
List<String> strList =
List.generate(l1.length, (index) => "$index ==> ${l1[index]}");
print(strList);
//reduce 用指定的函数方式对数组中的所有元素做连续操作,并将结果返回
List<int> list = [1, 2, 3, 4];
int res1 = list.reduce((a, b) => (a * b)); // 元素依次相乘
int res2 = list.reduce((a, b) => (a + b)); // 元素依次相加
print(res1); // 1*2*3*4 = 24
print(res2); // 1+2+3+4 = 10
//fold 根据一个现有数组和一个初始参数值 initValue,指定参数条件操作现有数组的所有元素,并返回处理的结果
// 2 为初始参数值,后面定义的方法为该初始值和原数组之间的处理方式
int res3 = list.fold(2, (a, b) => (a * b)); // 相乘
int res4 = list.fold(2, (a, b) => (a + b)); // 相加
print(res3); // 2*(1*2*3*4) = 48
print(res4); // 2+(1+2+3+4) = 12
// 将 l1 数组内的每一个元素都和指定的表达式组相操作
Iterable<int> a2 = list.expand((e) => [e + 1]);
Iterable<int> a3 = list.expand((e) => [e + 1, e + 2]);
Iterable<int> a4 = list.expand((e) => [e + 2, e * 2]);
Iterable<num> a5 = list.expand((e) => [e * 2, e / 2]);
Iterable<int> a6 = list.expand((e) => [e, e + 1, e + 2]);
print(a2); // (2, 3, 4, 5)
print(a3); // (2, 3, 3, 4, 4, 5, 5, 6)
print(a4); // (3, 2, 4, 4, 5, 6, 6, 8)
print(a5); // (2, 0.5, 4, 1.0, 6, 1.5, 8, 2.0)
print(a6); // (1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6)
list.shuffle(); //随机变换数组元素顺序
print(list); // [1, 4, 2, 3]
print(list.isEmpty);
print(list.isNotEmpty);
}
Map
How to define a Map
//第一种定义Map的方式 有点像js定义json对象的方式
var person = {
"name": "张三",
"age": 20,
"work": ["程序员", "送外卖"]
};
print(person);
print(person["name"]);
print(person["age"]);
print(person["work"]);
//第二种定义Map的方式
var p = new Map();
p["name"] = "李四";
p["age"] = 22;
p["work"] = ["程序员", "送外卖"];
print(p);
print(p["age"]);
Common properties of Map:
Map person = {
"name": "张三",
"age": 20,
"sex": "男"
};
print(person.keys.toList());
print(person.values.toList());
print(person.isEmpty);
print(person.isNotEmpty);
Map common methods:
Map person = {
"name": "张三",
"age": 20,
"sex": "男"
};
// 合并映射 给映射内增加属性
person.addAll({
"work": ['敲代码', '送外卖'], "height": 160});
print(person);
person.remove("sex"); // 删除指定key的数据
print(person);
print(person.containsValue('张三')); // 查看映射内的值 返回true/false
person.forEach((key, value) {
print("$key---$value");
});
Set
Its main function is to remove duplicates from arrays. Set is an unordered and non-repeatable collection, so values cannot be obtained through indexes.
var s = new Set();
s.add('香蕉');
s.add('苹果');
s.add('苹果');
print(s); //{香蕉, 苹果}
print(s.toList());
List myList=['香蕉','苹果','西瓜','香蕉','苹果','香蕉','苹果'];
var s = new Set();
s.addAll(myList);
print(s);
print(s.toList());
s.forEach((value)=>print(value));
// set1.difference(set2):返回set1集合里有但set2里没有的元素集合
// set1.intersection(set2):返回set1和set2的交集
// set1.union(set2):返回set1和set2的并集
// set1.retainAll():set1只保留某些元素(要保留的元素要在原set中存在)
type judgment
var str=123;
if (str is String) {
print('是string类型');
} else if(str is int) {
print('int');
} else {
print('其他类型');
}
Person p = Person('张三', 20);
if (p is Person) {
p.name = "李四";
}
p.printInfo();
print(p is Object); //true
Cast type:
var p1;
p1 = '';
p1 = new Person('张三1', 20);
p1.printInfo();
(p1 as Person).printInfo(); // 强转
operators and operators
Arithmetic operators:
The division in Dart is a real division, which is different from java's rounding
int a = 13;
int b = 5;
print(a + b); // 加
print(a - b); // 减
print(a * b); // 乘
print(a / b); // 2.6 除
print(a ~/ b); // 2 取整
print(a % b); // 3 取余
??
Assignment operator:
bool? b;
b ??= false; // 如果b为空时,将false赋值给b,否则,b的值保持不变。
print(b);
// a ?? b
// 如果 a != null,返回 a 的值; 否则返回 b 的值。
// 如果赋值是基于判定是否为 null, 考虑使用 ??。
String playerName = p.name ?? 'Guest'; // 等价于:name != null ? name : 'Guest';
Compound assignment operator: +=
-=
*=
/=
%=
~/=
use the same as java
Relational operators: ==
!=
>
<
>=
<=
use the same as java
Logical operators: ||
&&
!
use the same as java
Conditional expressions: if-else
, switch-case
, 三目运算符 c = flg ?a :b
use the same as java
Self-increment and self-decrement operators: ++
--
use the same as java
?.
non-null operator:
Person p = Person('张三', 20);
// p不为null才执行右边的
p?.printInfo();
..
Cascading operators:
Person p1 = new Person('张三1', 20)
..name = "李四"
..age = 30
..printInfo();
// 上面等价下面的
// Person p1 = new Person('张三1', 20);
// p1.printInfo();
// p1.name='张三222';
// p1.age=40;
// p1.printInfo();
function
Define a method and print user information:
String printUserInfo(String username, int age) {
//行参
return "姓名:$username---年龄:$age";
}
print(printUserInfo('张三', 20)); //实参
Define a method with optional parameters or default parameter values[]
(optional parameters use specified), the latest dart definition optional parameters need to specify the type default value:
String printUserInfo(String username,[String sex = '男',int age = 0]) {
if (age != 0) {
return "姓名:$username---性别:$sex--年龄:$age";
}
return "姓名:$username---性别:$sex--年龄保密";
}
print(printUserInfo('张三'));
print(printUserInfo('小李','女'));
print(printUserInfo('小李','女',30));
Define a method with named parameters (named parameters are {}
specified), the latest dart defines named parameters that need to specify the default value of the type:
String printUserInfo(String username, {
int age = 0, String sex = '男'}) {
return "姓名:$username-性别:$sex-年龄:${age}";
}
print(printUserInfo('张三', age: 20, sex: '女'));
Define a method that takes a method as a function parameter :
//fn1方法
fn1() {
print('fn1');
}
//fn2方法
fn2(fn) {
fn();
}
//调用fn2这个方法 把fn1这个方法当做参数传入
fn2(fn1);
Anonymous method :
var printNum = (int n, int m) => print(m * n); // 匿名方法
printNum(1, 2); // 调用
var printNum2 = () => print("aaaaa"); // 无参匿名函数
printNum2 (); // 调用
Define a function that returns a function type :
/// 返回一个函数,返回的函数参数与 [addBy] 相加。
Function makeAdder(num addBy) {
return (num i) => addBy + i;
}
void testMakeAdder() {
// 创建一个加 2 的函数。
var add2 = makeAdder(2);
// 创建一个加 4 的函数。
var add4 = makeAdder(4);
print(add2(3) == 5); //true
print(add4(3) == 7); //true
}
Arrow function :
// 需求:使用forEach打印下面List里面的数据
List list = ['苹果','香蕉','西瓜'];
list.forEach((value){
print(value);
});
// 注意和方法的区别: 箭头函数内只能写一条语句,并且语句后面没有分号(;)
list.forEach((value) => print(value) );
// 需求:修改下面List里面的数据,让数组中大于2的值乘以2
List list = [4,1,2,3,4];
var newList = list.map((value) {
if (value > 2) {
return value * 2;
}
return value;
});
print(newList.toList());
var newList = list.map((value) => value> 2 ? value * 2 : value);
print(newList.toList());
Function alias :
//函数别名 使用typedef给函数定义一个名字代替使用
typedef Fun1(int a, int b);
typedef Fun2<T, K>(T a, K b);
int add(int a, int b) {
return a + b;
}
class Demo1 {
Demo1(int f(int a, int b), int x, int y) {
var sum = f(x, y);
print("sum1 = $sum");
}
}
class Demo2 {
Demo2(Fun1 f, int x, int y) {
var sum = f(x, y);
print("sum2 = $sum");
}
}
class Demo3 {
Demo3(Fun2<int, int> f, int x, int y) {
var sum = f(x, y);
print("sum3 = $sum");
}
}
void main() {
Demo2 demo2 = Demo2(add, 2, 3);
Demo3 demo3 = Demo3(add, 4, 5);
}
Closure :
- Features of global variables: Global variables are resident in memory and pollute the whole world
- Features of local variables: non-resident memory will be recycled by the garbage mechanism and will not pollute the global
Problems solved by closures:
- resident memory
- Do not pollute the whole world
The essence of closure: function nesting function, the inner function will call the variables or parameters of the outer function, the variables or parameters will not be recycled by the system (the memory will not be released)
The way of writing the closure: the function nests the function, and returns the function inside, thus forming a closure.
void main() {
fn() {
var a = 123; /*不会污染全局 常驻内存*/
return () {
a++;
print(a);
};
}
var b = fn();
b();
b();
b();
// print(a); // 其他地方访问不到a
}
Null safety
Null safety translated into Chinese means empty safety. Null safety can help developers avoid some hard-to-find mistakes in daily development, and the added benefit is that it can improve performance.
Versions after Flutter2.2.0 (released on May 19, 2021) require the use of null safety.
Dart uses ?
to represent the nullable type, with after the type ?
to represent the nullable type, and without it to represent the non-nullable type. This is the same as kotlin.
null
cannot be assigned to a non-null type
String username = "张三";
username = null; // 报错
List<String> l1 = ["张三","李四","王五"];
l1 = null; // 报错
String? username = "张三"; // String? 表示username是一个可空类型
username = null; // ok
print(username);
int? a = 123; // int? 表示a是一个可空类型
a = null; // ok
print(a);
List<String>? l1 = ["张三","李四","王五"];
l1 = null; // ok
print(l1);
String? getData(apiUrl){
if(apiUrl!=null){
return "this is server data";
}
return null;
}
//调用方法
print(getData("http://www.itying.com"));
print(getData(null));
Dart uses !
to represent type assertion
void printLength(String? str){
try {
//类型断言: 如果str不等于null 会打印str的长度,如果等于null会抛出异常
print(str!.length);
} catch (e) {
print("str is null");
}
}
printLength("str");
printLength(null);
late Lazy initialization
It is very similar to kotlin's lateinit, but kotlin's lateinit cannot modify basic types, while dart can.
class Person {
late String name;
late int age;
void setName(String name, int age) {
this.name = name;
this.age = age;
}
String getName() {
return "${this.name}---${this.age}";
}
}
void main(args) {
Person p = new Person();
p.setName("张三", 20);
print(p.getName());
}
required
required
Translated into Chinese means need, dependence
required
Keywords: At first @required
it was an annotation, and now it has been used as a built-in modifier.
Mainly used to allow any named parameters (function or class) to be marked as required such that they are not null. Because there must be a required parameter among the optional parameters or the parameter has a default value.
String printUserInfo(String username, {
int age=10, String sex="男"}) {
return "姓名:$username---性别:$sex--年龄:$age";
}
String printInfo(String username, {
required int age, required String sex}) {
return "姓名:$username---性别:$sex--年龄:$age";
}
void main(args) {
// 不加required的命名参数,可以不传,可以只传部分
print(printUserInfo('张三'));
print(printUserInfo('张三', age: 20));
//加required:age 和 sex必须传入
print(printInfo('张三',age: 22,sex: "女"));
}
Used in the constructor parameters of the class required
(constructors in Flutter components are often written like this):
class Person {
String name;
int age;
// {}表示 name 和age 是必须传入的命名参数
// required表示 name 和age 必须传入
Person({
required this.name, required this.age});
String getName() {
return "${this.name}---${this.age}";
}
}
void main(args) {
Person p = new Person(name: "张三", age: 20);
print(p.getName());
}
class Person {
String? name; //可空属性
int age;
Person({
this.name, required this.age}); //可空的参数不用加required
String getName() {
return "${this.name}---${this.age}";
}
}
void main(args) {
Person p = new Person(age: 20);
print(p.getName()); //张三---20
Person p1 = new Person(age: 20);
print(p1.getName()); //null---20
}
object oriented
Everything in Dart is an object, and all objects inherit from the Object class.
kind
Dart is an object-oriented language that uses classes and single inheritance. All objects are instances of classes, and all classes are subclasses of Object.
A class usually consists of properties and methods.
class Person{
String name = "张三";
int age = 23;
void getInfo() {
// print("$name----$age");
print("${this.name}----${this.age}");
}
void setInfo(int age) {
this.age=age;
}
}
void main(){
//实例化
Person p1=new Person();
p1.setInfo(28);
p1.getInfo();
print(p1.name);
p1.getInfo();
}
Constructor
Default no-argument constructor :
class Person{
String name='张三';
int age=20;
//默认构造函数
Person(){
print('这是构造函数里面的内容 这个方法在实例化的时候触发');
}
void printInfo(){
print("${this.name}----${this.age}");
}
}
Ordinary constructor:
// 注意:最新版本的dart中需要初始化不可为null的实例字段,如果不初始化的话需要在属性前面加上late
class Person{
late String name;
late int age;
Person(String name,int age){
this.name=name;
this.age=age;
}
void printInfo(){
print("${this.name}----${this.age}");
}
}
Shorthand for the constructor :
class Person{
late String name;
late int age;
Person(this.name,this.age);
void printInfo(){
print("${this.name}----${this.age}");
}
}
class Container{
int width;
int height;
Container({
required this.width, required this.height});
}
void main(){
var c1 = new Container(width: 100,height: 100);
var c2 = new Container(width: 100,height: 100);
}
Named constructor:
class Person {
late String name;
late int age;
//构造函数的简写
Person(this.name, this.age);
Person.now() {
print('我是命名构造函数');
}
//命名构造函数传参
Person.setInfo(String name, int age) {
this.name = name;
this.age = age;
}
void printInfo() {
print("${this.name}----${this.age}");
}
}
void main() {
Person p1 = new Person('张三', 20); //默认实例化类的时候调用的是 默认构造函数
var d = new DateTime.now(); //实例化DateTime调用它的命名构造函数
print(d);
Person p1 = new Person.now(); //调用命名构造函数
Person p1 = new Person.setInfo('李四', 30); //调用命名构造函数
p1.printInfo();
}
There can be multiple constructors in dart.
Factory constructor :
The factory constructor is a kind of constructor. Unlike ordinary constructors, the factory function does not automatically generate an instance, but determines the returned instance object through code.
If a constructor does not always return a new object (singleton), use factory
to define the constructor. The factory constructor cannot be accessed this
.
class Singleton {
String name;
//工厂构造函数无法访问this,所以这里要用static
static Singleton? _cache;
//工厂方法构造函数,关键字factory
factory Singleton([String name = 'singleton']) =>
Singleton._cache ??= Singleton._newObject(name);
//定义一个命名构造函数用来生产实例
Singleton._newObject(this.name);
}
constructor initializer list
In Dart we can also initialize instance variables before the constructor body runs
class Rect {
int height;
int width;
Rect(): height = 2, width = 10 {
print("${this.height}---${this.width}");
}
getArea() {
return this.height * this.width;
}
}
void main() {
Rect r = new Rect();
print(r.getArea());
}
class Point {
//final变量不能被修改,必须被构造函数初始化
final num x;
final num y;
final num distanceFromOrigin;
//初始化列表构造函数
Point(x, y)
: x = x,
y = y,
distanceFromOrigin = sqrt(x * x + y * y);
}
import class
You can put the class .dart
in a separate file and import and use it in other files
import 'lib/Person.dart';
void main(){
Person p1=new Person.setInfo('李四1',30);
p1.printInfo();
}
Private Methods and Private Properties
Unlike other object-oriented languages, Dart does not have public private protected
these access modifiers in Data
But we can use underscore _
to define a property or method as private .
// Animal.dart
class Animal{
late String _name; //私有属性
late int age;
//默认构造函数的简写
Animal(this._name,this.age);
void printInfo(){
print("${this._name}----${this.age}");
}
String getName(){
return this._name;
}
void _run(){
print('这是一个私有方法');
}
execRun(){
this._run(); //类里面方法的相互调用
}
}
import 'lib/Animal.dart';
void main() {
Animal a=new Animal('小狗', 3);
print(a.getName());
a.execRun(); //间接的调用私有方法
// a._run(); // 访问不到私有方法
// print(a._name); // 访问不到私有成员
print(a.age);
}
getter和setter
// 常规方式
class Rect {
num height;
num width;
Rect(this.height, this.width);
area() {
return this.height * this.width;
}
}
void main() {
Rect r = new Rect(10,4);
print("面积:${r.area()}");
}
// getter和setter 方式
class Rect {
late num height;
late num width;
Rect(this.height, this.width);
get area {
return this.height * this.width;
}
set areaHeight(value) {
this.height = value;
}
}
void main() {
Rect r=new Rect(10,4);
//注意调用直接通过访问属性的方式访问area
r.areaHeight = 6;
print("面积:${r.area}");
}
static members and static methods
There is no difference in use from java
class Person {
static String name = '张三';
static void show() {
print(name);
}
}
main(){
print(Person.name);
Person.show();
}
class Person {
static String name = '张三';
int age=20;
static void show() {
print(name);
}
void printInfo(){
/*非静态方法可以访问静态成员以及非静态成员*/
// print(name); //访问静态属性
// print(this.age); //访问非静态属性
show(); //调用静态方法
}
static void printUserInfo(){
//静态方法
print(name); //静态属性
show(); //静态方法
//print(this.age); //静态方法没法访问非静态的属性
// this.printInfo(); //静态方法没法访问非静态的方法
// printInfo();
}
}
main(){
// print(Person.name);
// Person.show();
// Person p=new Person();
// p.printInfo();
Person.printUserInfo();
}
inherit
The three major characteristics of object-oriented: encapsulation, inheritance, polymorphism
Inheritance of classes in Dart:
- 1. The subclass uses the extends keyword to inherit the parent class
- 2. The subclass will inherit the properties and methods visible in the parent class but will not inherit the constructor
- 3. Subclasses can override the method getter and setter of the parent class
class Person {
String name='张三';
num age=20;
void printInfo() {
print("${this.name}---${this.age}");
}
}
class Man extends Person {
}
main() {
Man m = new Man();
print(m.name);
m.printInfo();
}
Call the parent class constructor:
class Person {
late String name;
late num age;
Person(this.name,this.age);
void printInfo() {
print("${this.name}---${this.age}");
}
}
class Man extends Person {
Man(String name, num age) : super(name, age) {
}
}
class Person {
String name;
num age;
Person(this.name, this.age);
Person.from(this.name, this.age);
void printInfo() {
print("${this.name}---${this.age}");
}
}
class Man extends Person {
late String sex;
// 调用父类命名构造函数
Man(String name, num age, String sex) : super.from(name, age) {
this.sex = sex;
}
run() {
print("${this.name}---${this.age}--${this.sex}");
}
}
main() {
Man m = new Man('张三', 12, "男");
m.printInfo();
m.run();
}
Override the method of the parent class:
class Person {
String name;
num age;
Person(this.name,this.age);
void printInfo() {
print("${this.name}---${this.age}");
}
work(){
print("${this.name}在工作...");
}
}
class Man extends Person{
Man(String name, num age) : super(name, age);
run(){
print('run');
}
// 可以写也可以不写 建议在覆写父类方法的时候加上 @override
void printInfo(){
print("姓名:${this.name}---年龄:${this.age}");
}
work(){
print("${this.name}的工作是写代码");
}
}
main(){
Man m = new Man('李四',20);
m.printInfo();
m.work();
}
The subclass calls the method of the superclass:
class Person {
String name;
num age;
Person(this.name,this.age);
void printInfo() {
print("${this.name}---${this.age}");
}
work(){
print("${this.name}在工作...");
}
}
class Man extends Person {
Man(String name, num age) : super(name, age);
run(){
print('run');
super.work(); //子类调用父类的方法
}
}
main(){
Man m = new Man('李四',20);
m.run();
}
abstract class
Dart abstract classes are mainly used to define standards, subclasses can inherit abstract classes, and can also implement abstract class interfaces.
-
1. Abstract classes
abstract
are defined by keywords -
2. Abstract methods in Dart cannot be
abstract
declared. Methods without method bodies in Dart are called abstract methods. -
3. If the subclass inherits the abstract class, it must implement the abstract method inside
-
4. If you implement an abstract class as an interface, you must implement all the properties and methods defined in the abstract class.
-
5. An abstract class cannot be instantiated, only subclasses that inherit it can
extends
The difference between an abstract class and implements
:
-
1. If we want to reuse the methods in the abstract class and use the abstract method to constrain the self-class, we use
extends
the inheritance abstract class -
2. If we only regard abstract classes as standards, we will use
implements
the implementation of abstract classes
Case: Defining a Animal
class requires that its subclasses must contain eat
methods
abstract class Animal {
eat(); //抽象方法
run(); //抽象方法
printInfo(){
print('我是一个抽象类里面的普通方法');
}
}
class Dog extends Animal{
eat() {
print('小狗在吃骨头');
}
run() {
// TODO: implement run
print('小狗在跑');
}
}
class Cat extends Animal{
eat() {
// TODO: implement eat
print('小猫在吃老鼠');
}
run() {
// TODO: implement run
print('小猫在跑');
}
}
main(){
Dog d=new Dog();
d.eat();
d.printInfo();
Cat c=new Cat();
c.eat();
c.printInfo();
// Animal a=new Animal(); //抽象类没法直接被实例化
}
polymorphism
Like java, it is allowed to use the reference of the parent type to receive instances of the subtype. Polymorphism means that the parent class first defines a method but does not implement it, and let the subclass implement it. Different subclasses have different implementations, so the reference of the parent type The function call will have different execution effects at runtime, and its behavior is determined by the subclass actually pointed to at runtime.
abstract class Animal{
eat(); //抽象方法
}
class Dog extends Animal{
eat() {
print('小狗在吃骨头');
}
run(){
print('run');
}
}
class Cat extends Animal{
eat() {
print('小猫在吃老鼠');
}
run(){
print('run');
}
}
main(){
Animal d = new Dog();
d.eat();
Animal c = new Cat();
c.eat();
}
interface
Like Java, dart also has interfaces, but it is still different from Java.
-
First of all, dart's interface does not have
interface
a keyword to define the interface, but ordinary classes or abstract classes can be implemented as interfaces. -
implements
The same is done using keywords.
But the interface of dart is a bit strange. If the implemented class is an ordinary class, all the methods of the ordinary class and the attributes in the abstract will need to be overwritten.
And because abstract classes can define abstract methods, ordinary classes cannot, so generally if you want to implement a method like a Java interface, you will generally use abstract classes. It is recommended to define interfaces using abstract classes.
/**
* 定义一个DB库 支持 mysql mssql mongodb
* mysql mssql mongodb三个类里面都有同样的方法
*/
abstract class Db {
//当做接口 接口:就是约定 、规范
late String uri; //数据库的链接地址
add(String data);
save();
delete();
}
class Mysql implements Db {
String uri;
Mysql(this.uri);
add(data) {
print('这是mysql的add方法'+data);
}
delete() {
return null;
}
save() {
return null;
}
remove(){
}
}
class MsSql implements Db {
late String uri;
add(String data) {
print('这是mssql的add方法'+data);
}
delete() {
return null;
}
save() {
return null;
}
}
main() {
Mysql mysql=new Mysql('xxxxxx');
mysql.add('1243214');
}
A class in Dart implements multiple interfaces:
abstract class A {
late String name;
printA();
}
abstract class B {
printB();
}
class C implements A, B {
late String name;
printA() {
print('printA');
}
printB() {
return null;
}
}
void main(){
C c = new C();
c.printA();
}
Use ordinary classes as interfaces:
class D {
printD() {
print("aa");
}
}
// dart中的implements可以实现普通类,它把普通类的方法当做实现接口
class C implements D {
printD() {
}
}
mixedin
The Chinese meaning of mixins is to mix in, which is to mix other functions into the class.
In Dart, mixins can be used to implement functions similar to multiple inheritance
Because the conditions used by mixins (may change with Dart version updates):
- Classes that are mixins must use the mixin modifier
- Classes used as mixins can only inherit from Object and cannot inherit from other classes
- Classes that are mixins cannot have constructors
- A class can mixins multiple mixins classes
- Mixins are by no means inheritance, nor interface, but a brand new feature
mixin class A {
String info="this is A";
void printA(){
print("A");
}
}
mixin class B {
void printB(){
print("B");
}
}
class C with A,B {
}
void main(){
var c = new C();
c.printA();
c.printB();
print(c.info);
// 下面三个都是true说明c是A和B的子类型,效果上是多继承
print(c is C); //true
print(c is A); //true
print(c is B); //true
}
You can also write directly like this:
mixin A {
String info="this is A";
}
class C with A {
}
Use extends
and with
implement multiple inheritance at the same time:
class Person{
String name;
num age;
Person(this.name,this.age);
printInfo(){
print('${this.name}----${this.age}');
}
void run(){
print("Person Run");
}
}
mixin class A {
String info="this is A";
void printA(){
print("A");
}
void run(){
print("A Run");
}
}
mixin class B {
void printB(){
print("B");
}
void run(){
print("B Run");
}
}
class C extends Person with B,A {
C(String name, num age) : super(name, age);
}
void main(){
var c=new C('张三',20);
c.printInfo();
// c.printB();
// print(c.info);
c.run();
}
generic
//只能返回string类型的数据
String getData(String value){
return value;
}
//同时支持返回 string类型 和int类型 (代码冗余)
String getData1(String value){
return value;
}
int getData2(int value){
return value;
}
// 同时返回 string类型 和number类型 不指定类型可以解决这个问题
getData(value){
return value;
}
This problem can be solved by not specifying the type
// 同时返回 string类型 和number类型
getData(value){
return value;
}
Not specifying a type forgoes type checking. What we want to achieve now is to return what is passed in. For example: the incoming number type must return the number type and the incoming string type must return the string type
T getData<T>(T value) {
return value;
}
void main(){
getData<String>('你好');
print(getData<int>(12));
}
Basically similar to java.
Collection List
generic class usage:
//案例:把下面类转换成泛型类,要求MyList里面可以增加int类型的数据,也可以增加String类型的数据。
// 但是每次调用增加的类型要统一
/**
class MyList {
List list = <int>[];
void add(int value) {
this.list.add(value);
}
List getList() {
return list;
}
}
*/
class MyList<T> {
List list = <T>[];
void add(T value) {
this.list.add(value);
}
List getList() {
return list;
}
}
main() {
MyList l1 = new MyList(); // 不指定具体类型时,可以任何类型
l1.add("张三");
l1.add(12);
l1.add(true);
print(l1.getList());
MyList l2 = new MyList<String>(); // 指定泛型为String类型
l2.add("张三1");
// l2.add(11); //错误的写法 会报错
print(l2.getList());
MyList l3 = new MyList<int>(); // 指定泛型为int类型
l3.add(11);
l3.add(12);
// l3.add("aaaa"); //错误的写法 会报错
print(l3.getList());
}
System built-in List generic usage:
List list = List.filled(2, "");
list[0] = "张三";
list[1] = "李四";
print(list);
List list = List<String>.filled(2, "");
list[0] = "张三1111";
list[1] = "李四";
print(list);
List list2 = List<int>.filled(2, 0);
list2[0] = 12;
list2[1] = 13;
print(list2);
Generic interface:
/**
Dart中的泛型接口:
实现数据缓存的功能:有文件缓存、和内存缓存。内存缓存和文件缓存按照接口约束实现。
1、定义一个泛型接口 约束实现它的子类必须有getByKey(key) 和 setByKey(key,value)
2、要求setByKey的时候的value的类型和实例化子类的时候指定的类型一致
*/
abstract class Cache<T> {
getByKey(String key);
void setByKey(String key, T value);
}
class FileCache<T> implements Cache<T> {
getByKey(String key) {
return null;
}
void setByKey(String key, T value) {
print("我是文件缓存 把key=${key} value=${value}的数据写入到了文件中");
}
}
class MemoryCache<T> implements Cache<T> {
getByKey(String key) {
return null;
}
void setByKey(String key, T value) {
print("我是内存缓存 把key=${key} value=${value} -写入到了内存中");
}
}
void main() {
MemoryCache m1 = new MemoryCache<String>();
m1.setByKey('index', '首页数据');
MemoryCache m = new MemoryCache<Map>();
m.setByKey('index', {
"name": "张三", "age": 20});
}
import library library
When introducing the basic knowledge of Dart, the Dart code is basically written in a file, but it is impossible to write like this in actual development. Modularization is very important, so this requires the use of the concept of the library.
In Dart, library usage import
is introduced through keywords.
library
A directive can create a library, and every Dart file is a library , even if no library
directive is used to specify it.
There are three main types of libraries in Dart:
- Our custom library
import 'lib/xxx.dart';
- System built-in library
import 'dart:math';
import 'dart:io';
import 'dart:convert';
- Libraries in the Pub package management system
https://pub.dev/packages
https://pub.flutter-io.cn/packages
https://pub.dartlang.org/flutter/- 1. You need to create a new pubspec.yaml in the root directory of your project
- 2. Configure the name, description, dependencies and other information in the pubspec.yaml file
- 3. Then run pub get to download the package to the local
- 4. Introduce the library into the project import 'package:http/http.dart' as http; see the documentation for usage
// Dart中导入自己本地库
import 'lib/Animal.dart';
main(){
var a=new Animal('小黑狗', 20);
print(a.getName());
}
// 导入系统内置库
// import 'dart:io';
import "dart:math";
main(){
print(min(12,23));
print(max(12,25));
}
// 导入系统内置库实现请求数据httpClient
import 'dart:io';
import 'dart:convert';
void main() async{
var result = await getDataFromZhihuAPI();
print(result);
}
//api接口: http://news-at.zhihu.com/api/3/stories/latest
getDataFromZhihuAPI() async{
//1、创建HttpClient对象
var httpClient = new HttpClient();
//2、创建Uri对象
var uri = new Uri.http('news-at.zhihu.com','/api/3/stories/latest');
//3、发起请求,等待请求
var request = await httpClient.getUrl(uri);
//4、关闭请求,等待响应
var response = await request.close();
//5、解码响应的内容
return await response.transform(utf8.decoder).join();
}
Import library naming conflict resolution
When importing two libraries with the same name identifier, if it is java, we usually specify the specific identifier used by writing the complete package name path, even without import, but it is necessary in Dart import
. When there is a conflict, as
keywords can be used to specify the prefix of the library. As shown in the following example:
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
Element element1 = new Element(); // Uses Element from lib1.
lib2.Element element2 = new lib2.Element(); // Uses Element from lib2.
partial import
If only part of the library needs to be imported, there are two modes:
Mode 1: Import only the required parts and use show
keywords, as shown in the following example:
import 'package:lib1/lib1.dart' show foo;
Mode 2: Hide unnecessary parts and use hide
keywords, as shown in the following example:
import 'package:lib2/lib2.dart' hide foo;
In the Java coding process, it often happens that a class calls a method with the same name in two classes. At this time, a fully qualified name is required to call it, but it is not required in Dart. Just use the library prefix when importing.
import 'MyLib1.dart' as lib1;
import 'MyLib2.dart' as lib2 hide test;
import 'MyLib3.dart' as lib3 show test;
void main() {
var compute = lib1.compute();
var compute2 = lib2.compute();
var test = lib3.test();
}
Split the library into multiple files
// mylib.dart
library mylib;
//使用part 把一个库分开到多个 Dart 文件中。
part 'util.dart';
part 'tool.dart';
void printMyLib() => print('mylib');
// util.dart
part of mylib;
void printUtil() => print('util');
// tool.dart
part of mylib;
void printTool() => print('tool');
lazy loading
Also known as lazy loading, it can be loaded when needed. The biggest benefit of lazy loading is that it can reduce the startup time of APP.
Lazy loading is deferred as
specified using keywords, as shown in the following example:
import 'package:deferred/hello.dart' deferred as hello;
When you need to use it, you need to use loadLibrary()
the method to load:
greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
identical
dart:core
identical
Usage of the function in the library : bool identical(Object? a, Object? b)
Check if two references point to the same object.
void main() {
var o1 = new Object();
var o2 = new Object();
print(identical(o1, o2)); // alse, different objects.
print(identical(o1, o1)); // true, same object
//表示实例化常量构造函数
//o1 和 o2共享了存储空间
var o1 = const Object();
var o2 = const Object();
print(identical(o1, o2)); //true 共享存储空间
print(identical(o1, o1)); //true 共享存储空间
print(identical([2],[2])); //false 不共享存储空间
print(identical(const [2],const [2])); //true
print(identical(const [1],const [2])); //false
print(identical(2, 1 + 1)); // true
}
Discovery: const
When keywords create the same object in multiple places, only one object is kept in memory
Shared storage space conditions: 1. Constant, 2. Equal value
//常量构造函数
class Container{
final int width;
final int height;
const Container({
required this.width,required this.height});
}
// 实例化常量构造函数的时候,多个地方创建这个对象,如果传入的值相同,只会保留一个对象。
void main(){
var c1 = Container(width: 100, height: 100);
var c2 = Container(width: 100, height: 100);
print(identical(c1, c2)); //false
var c3 = const Container(width: 100, height: 100);
var c4 = const Container(width: 100, height: 100);
print(identical(c3, c4)); //true
var c5 = const Container(width: 100, height: 110);
var c6 = const Container(width: 120, height: 100);
print(identical(c5, c6)); //false
}
extension function
Similar to kotlin, dart also supports the use of extension functions. The extension function is mainly for the convenience of dynamically adding methods to a class without having to inherit the class for modification (for example, if you have a class from a third-party library, it lacks a certain method you expect, and you want to add one yourself) .
Syntax for defining extensions:
extension [扩展函数名称] on <type> {
// 具体的扩展函数内容定义
}
String
For example, the following code implements an extension for a system class:
extension NumberParsing on String {
int parseInt() {
return int.parse(this);
}
double parseDouble() {
return double.parse(this);
}
}
Then you can use the extension function like this:
'42'.parseInt();
'42.0'.parseDouble();
When declaring an extension, the name can also be omitted. Unnamed extensions are only visible in the library in which they are declared.
extension on String {
bool get isBlank => trim().isEmpty;
}
Extensions can have generic type parameters. For example, here is List<T>
code that extends a built-in type:
extension MyFancyList<T> on List<T> {
int get doubleLength => length * 2;
List<T> operator -() => reversed.toList();
List<List<T>> split(int at) => [sublist(0, at), sublist(at)];
}
Extension functions are very useful, such as the commonly used code for serializing Json entity classes:
class User {
final String name;
final String email;
User(this.name, this.email);
User.fromJson(Map<String, dynamic> json)
: name = json['name'],
email = json['email'];
Map<String, dynamic> toJson() => {
'name': name,
'email': email,
};
}
If this User
class is not our own, but a third-party, then it is impossible to directly write serialized template code in it. At this time, we can use the extended implementation:
// 原始类不用动
class User {
final String name;
final String email;
User(this.name, this.email);
}
// 自己工程中动态为其添加扩展
extension on User {
User.fromJson(Map<String, dynamic> json)
: name = json['name'],
email = json['email'];
Map<String, dynamic> toJson() => {
'name': name,
'email': email,
};
}
It can be seen that the two implementations are separated and do not interfere with each other. This actually gives us a solution that can dynamically add functions without modifying the original code.
Many dart package libraries on pub.dev use dart's extension feature to define extension functionality for system classes.
Handling Extension Function Conflicts
If an extension member conflicts with an interface or another extension member, there are two workarounds.
One is to use show
or hide
restrict the exposed API:
// 两个库中都定义了 parseInt() 扩展方法
import 'string_apis.dart';
import 'string_apis_2.dart' hide NumberParsing2; // 使用 hide 隐藏该库中的扩展
// ···
print('42'.parseInt());
Another workaround is to explicitly specify the name when calling, which causes the extension code to look like a wrapper class:
// 两个库中都定义了 parseInt() 扩展方法,但是使用了不同名称
import 'string_apis.dart'; // Contains NumberParsing extension.
import 'string_apis_2.dart'; // Contains NumberParsing2 extension.
// ···
// print('42'.parseInt()); // Doesn't work.
print(NumberParsing('42').parseInt());
print(NumberParsing2('42').parseInt());
If two extensions have the same name, then you may need to import with a prefix:
import 'string_apis.dart';
import 'string_apis_3.dart' as rad;
// ···
// print('42'.parseInt()); // Doesn't work.
// Use the ParseNumbers extension from string_apis.dart.
print(NumberParsing('42').parseInt());
// Use the ParseNumbers extension from string_apis_3.dart.
print(rad.NumberParsing('42').parseInt());
// Only string_apis_3.dart has parseNum().
print('42'.parseNum());
external keyword
external
The main role of is to separate the declaration from the implementation. It is exactly the same as Kotlin in KMM expect / actual
.
The method of modifying the declaration is usually implemented by the underlying sdk according to different platforms (vm, web, etc.) ; external
if it is not implemented externally, it will return null; in this way, the same set of external API declarations can be reused, and then multiple corresponding to different platforms A set of implementation; so whether it is dart for web or dart for vm, it is the same set of API for upper-level development. ( external
The declared method class
does not need to be declared as abstract class
, so class
it can be instantiated directly)
external
The implementation of the declaration method is @patch
implemented through annotations:
class 类名 {
...
external声明的方法名
...
}
How to find the implementation of the flutter external declaration method
external
Declare the path of the method implementation file, the mobile terminal is in vm
the directory:
flutter sdk目录/bin/cache/dart-sdk/lib/_internal/vm/lib
The web side is in js_runtime
the directory:
flutter sdk目录/bin/cache/dart-sdk/lib/_internal/js_runtime/lib
external
The naming of the file implemented by the declaration method is generally named as xxx_patch.dart
, such as in vm/lib
the directory, you can see various xxx_patch.dart
files.
For example, the commonly used Api for obtaining the current time:
var now = new DateTime.now();
print(now);
View its source code and find that it is external
a statement:
// date_time.dart
external DateTime._now();
DateTime.now() : this._now();
Its real implementation is in flutter sdk安装目录\flutter\bin\cache\dart-sdk\lib\_internal\vm_shared\lib\date_patch.dart
the file:
._now()
: isUtc = false,
_value = _getCurrentMicros();
DateTime
Dart asynchronous programming
Why Asynchronous Code Matters
Asynchronous operations let your program do work while waiting for another operation to complete. Here are some common asynchronous operations:
- Get data over the network.
- Write to the database.
- Read data from a file.
Such asynchronous computations typically Future
provide their results in the form of or if the result has multiple parts Stream
. These calculations will be introduced into the program asynchronously. To accommodate initial async, other normal Dart functions also need to be made async.
To interact with these asynchronous results, you can use async
the and await
keywords. Most asynchronous functions are just asynchronous Dart functions, and they may depend deeply on inherently asynchronous computations.
Future
Future represents the result of an asynchronous operation, which is equivalent to JS Promise
. Future has two states: unfinished or completed .
- outstanding : When you call an async function, it returns an outstanding
Future
. ThisFuture
is waiting for the function's asynchronous operation to complete or throw an error. - Completed
Future
: Returns a value if the asynchronous operation was successful . Otherwise, it will complete with an error.
Returns a value: completes Future<T>
with T
a value of type . For example, Future<String>
a string value is produced. If the Future does not yield a usable value, then the Future's type is Future<void>
.
Completed with Error: If the asynchronous operation performed by the function fails for any reason, the Future will complete with an error.
// 2s后打印,不返回任何值
Future.delayed(Duration(seconds: 2),() => "hi world");
// 返回一个值
Future.delayed(Duration(seconds: 2), () {
return "hi world!";
}).then((data) {
print(data);
});
// 返回一个异常
Future.delayed(Duration(seconds: 2), () {
//return "hi world!";
throw AssertionError("Error");
}).then((data) {
//执行成功会走到这里
print("success");
}).catchError((e) {
//执行失败会走到这里
print(e);
});
// 同上,另一种写法
Future.delayed(new Duration(seconds: 2), () {
//return "hi world!";
throw AssertionError("Error");
}).then((data) {
print("success");
}, onError: (e) {
print(e);
});
whenComplete : Execute regardless of success or failure
Future.delayed(new Duration(seconds: 2), () {
return "hi world!";
}).then((data) {
//执行成功会走到这里
print(data);
}).catchError((e) {
//执行失败会走到这里
print(e);
}).whenComplete(() {
//无论成功或失败都会走到这里
print("Complete");
});
wait : Wait for multiple asynchronous tasks to complete before executing
Future.wait([
// 2秒后返回结果
Future.delayed(new Duration(seconds: 2), () {
return "hello";
}),
// 4秒后返回结果
Future.delayed(new Duration(seconds: 4), () {
return " world";
})
]).then((results) {
print(results[0] + results[1]);
}).catchError((e) {
print(e);
});
async and await
These two usages are completely consistent with JS.
Just two things to remember:
- To define an asynchronous function, add
async
the keyword before the function body await
Keywords can only appear inasync
decorated methods
async
It is to make the method asynchronous. await
Is to wait for the asynchronous method execution to complete. Synchronize asynchronous code.
Define an async method
foo() async {
return '假设这里是某种异步任务';
}
await
Calling an async method using
void main() async {
var result = await foo();
print(result);
}
If the function has a declared return type, update the type to Future<T>
, where T
is the type of the function's return value. If the function does not explicitly return a value, the return type is Future<void>
:
Future<void> foo() async {
··· }
An example of bad asynchronous code:
String createOrderMessage() {
var order = fetchUserOrder();
return 'Your order is: $order';
}
Future<String> fetchUserOrder() = Future.delayed(const Duration(seconds: 2), () => 'Large Latte');
void main() {
print('Fetching user order...');
print(createOrderMessage());
}
Here's why the example fails to print the resulting value:
fetchUserOrder()
is an asynchronous function that, after a delay, provides a string describing the user's order: "Large Latte".- To get the user's order,
createOrderMessage()
you should callfetchUserOrder()
and wait for it to complete. Failed to get the final provided string value becausecreateOrderMessage()
there was no waitfetchUserOrder()
to complete .createOrderMessage()
fetchUserOrder()
- Instead,
createOrderMessage()
get a representation of work to be done: an unfinished Future. - Because
createOrderMessage()
the value describing the user's order cannot be obtained, the example fails to“Large Latte”
print to the console, instead it prints“Your order is: Instance of '_Future<String>'”
.
The correct way to write it is to use async
and await
:
Future<String> createOrderMessage() async {
var order = await fetchUserOrder();
return 'Your order is: $order';
}
Future<String> fetchUserOrder() = Future.delayed(const Duration(seconds: 2), () => 'Large Latte');
Future<void> main() async {
print('Fetching user order...');
print(await createOrderMessage());
}
Use multiple await
to ensure that multiple asynchronous tasks are executed sequentially :
task() async {
try {
String id = await login("alice", "******");
String userInfo = await getUserInfo(id);
await saveUserInfo(userInfo);
//执行接下来的操作
} catch (e) {
//错误处理
print(e);
}
}
getUserInfo(String id) async {
return Future.delayed(Duration(seconds: 2),() => "hi world");
}
saveUserInfo(String id) async {
return Future.delayed(Duration(seconds: 2),() => "hi world");
}
login(String s, String t) async {
return Future.delayed(Duration(seconds: 2), () => "hi world");
}
In an async function body, the code before await will be executed synchronously and immediately, and the code after await will be blocked until await returns.
Handling errors: To handle async
errors in a function, usetry-catch
try {
var order = await fetchUserOrder();
} catch (err) {
print('Caught error: $err');
}
Future<void> printOrderMessage() async {
try {
print('Awaiting user order...');
var order = await fetchUserOrder();
print(order);
} catch (err) {
print('Caught error: $err');
}
}
Future<String> fetchUserOrder() = Future.delayed(const Duration(seconds: 2), () => 'Large Latte');
void main() async {
await printOrderMessage();
}
Stream
The Future and Stream classes are at the heart of Dart's asynchronous programming.
- Future represents a computation that does not complete immediately. Unlike ordinary functions that return results directly, asynchronous functions return a Future that will contain the result. This Future will notify the caller when the result is ready.
- A Stream is a sequence of asynchronous events. It is similar to an asynchronous one
Iterable
, the difference is that when you getIterable
the next event to it will give you immediately, but Stream will not give you immediately but tell you when it is ready.
Create Stream
Here is an example of a function that periodically sends integers:
Stream<int> timedCounter(Duration interval, [int? maxCount]) async* {
int i = 0;
while (true) {
await Future.delayed(interval);
yield i++;
if (i == maxCount) break;
}
}
The function returns a Stream
. The function body Stream
starts running when it should be monitored and continuously generates an incremental number within the specified number range at a certain periodic interval. Stream
If the count parameter is omitted, the loop will execute endlessly, generating more and more numbers unless unsubscribed .
timedCounter()
You can use the Stream returned by the function like this :
var counterStream = timedCounter(const Duration(seconds: 1), 15);
counterStream.listen(print); // Print an integer every second, 15 times.
When the listener is canceled ( the method in the object listen()
returned by the method is called ), if the next loop body executes to the statement, then the statement acts like the statement. And any statement block executed at this time will cause the function to exit. If the function tries to reset a value before exiting , the attempt will fail with an effect similar to the statement.StreamSubscription
cancel()
yield
return
finally
yield
return
cancel()
The returned by the method completes when the function finally exits Future
. If the function exited due to an error, it Future
will complete with the corresponding error, otherwise it will carry one null
.
Alternatively, a more useful example is a function that converts a sequence of Futures into a Stream :
Stream<T> streamFromFutures<T>(Iterable<Future<T>> futures) async* {
for (final future in futures) {
var result = await future;
yield result;
}
}
This function loops to request a Future from the Future sequence and waits for the Future to complete obtaining its result before submitting it to the Stream. If a Future completes with an error, the error is also committed to the Stream.
In practical applications, it is rare to construct a Stream from zero through an async* function. async* functions usually create a Stream from some data source, which is often another Stream. For example, like the Future sequence in the above example, its data often comes from other asynchronous event sources. However, in many cases, asynchronous functions are too simple to handle multiple data sources easily. And that's where the StreamController class comes in.
Another way to convert a Future to a Stream is via Stream.fromFutures()
the method:
Stream.fromFutures([
// 1秒后返回结果
Future.delayed(Duration(seconds: 1), () {
return "hello 1";
}),
// 抛出一个异常
Future.delayed(Duration(seconds: 2),(){
throw AssertionError("Error");
}),
// 3秒后返回结果
Future.delayed(Duration(seconds: 3), () {
return "hello 3";
})
]).listen((data){
print(data);
}, onError: (e){
print(e.message);
},onDone: (){
});
Use StreamController
If the events of your Stream come not only from the Stream and Future that can be traversed by asynchronous functions, but also from different parts of your program, it will be difficult to generate Stream using the above two methods. Faced with this situation, we can use a StreamController to create and fill the Stream.
StreamController can generate a Stream for you and provide methods to add events to the Stream anytime and anywhere. This Stream has all the logic needed to handle listeners and pauses. The controller object you can handle yourself and just return the Stream required by the caller.
The version of timedCounter() implemented in the code below (from stream_controller.dart) implements the pause functionality by using the onListen, onPause, onResume, and onCancel callbacks in the StreamController.
Stream<int> timedCounter(Duration interval, [int? maxCount]) {
late StreamController<int> controller;
Timer? timer;
int counter = 0;
void tick(_) {
counter++;
controller.add(counter); // Ask stream to send counter values as event.
if (counter == maxCount) {
timer?.cancel();
controller.close(); // Ask stream to shut down and tell listeners.
}
}
void startTimer() {
timer = Timer.periodic(interval, tick);
}
void stopTimer() {
timer?.cancel();
timer = null;
}
controller = StreamController<int>(
onListen: startTimer,
onPause: stopTimer,
onResume: startTimer,
onCancel: stopTimer);
return controller.stream;
}
We change the code using Stream as follows:
void listenWithPause() {
var counterStream = timedCounter(const Duration(seconds: 1), 15);
late StreamSubscription<int> subscription;
subscription = counterStream.listen((int counter) {
print(counter); // Print an integer every second.
if (counter == 5) {
// After 5 ticks, pause for five seconds, then resume.
subscription.pause(Future.delayed(const Duration(seconds: 5)));
}
});
}
Use the above timedCounter function in the listenWithPause() function, after running you can see that when the subscription is paused, the count of the printout will also pause, and then resume correctly.
You must use all callbacks onListen, onCancel, onPause and onResume to notify the pause state change, otherwise only onListen or onCancel callback will be called if subscription state and pause state both changed at the same time.
You need to be very careful when using StreamController, and it is prone to problems. For more information about StreamController, please refer to: Using Stream in Dart
Receive Stream events
Like using a for loop to iterate an Iterable, we can use an asynchronous for loop (usually we call it directly await for
) to iterate the events in the Stream. For example:
The code simply takes each event in the stream of integer events, adds them together, and returns (wrapped by Future
) the added integer value. When the loop body ends, the function pauses until the next event arrives or the Stream completes.
await for
Functions that use loops internally need to be async
marked with the keyword.
The following example uses async* functions to generate a simple stream of integers to test the previous code snippet:
Future<int> sumStream(Stream<int> stream) async {
var sum = 0;
await for (final value in stream) {
sum += value;
}
return sum;
}
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
yield i;
}
}
void main() async {
var stream = countStream(10);
var sum = await sumStream(stream);
print(sum); // 55
}
error event
When the Stream has no more events to be processed, it will become completed. At the same time, the caller can receive the Stream completed event callback just like receiving a new event callback. When reading events with an await for loop, the loop stops when the Stream completes.
Sometimes an error occurs before the Stream is complete; for example, a network request fails when fetching a file from a remote server, or a bug occurs when creating an event. Although errors are always possible, users should be notified when they occur.
Streams can provide error events as well as data events. Most Streams will stop after the first error, but it is also possible to provide multiple errors and continue to provide data events after an error occurs. In this document we only discuss the case where a Stream occurs at most and provides an error event.
When using await for to read the Stream, if an error occurs, it will be thrown by the loop statement, and the loop ends at the same time. You can try-catch
catch errors using the statement. The following example throws an error when the loop iterates until the parameter value equals 4:
Future<int> sumStream(Stream<int> stream) async {
var sum = 0;
try {
await for (final value in stream) {
sum += value;
}
} catch (e) {
return -1;
}
return sum;
}
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
if (i == 4) {
throw Exception('Intentional exception');
} else {
yield i;
}
}
}
void main() async {
var stream = countStream(10);
var sum = await sumStream(stream);
print(sum); // -1
}
Use of Streams
The Stream class contains many Iterable
helper methods like the class to help you implement some common operations. For example, you can use the method in the Stream API lastWhere()
to find out the last positive integer from a Stream.
Future<int> lastPositive(Stream<int> stream) =>
stream.lastWhere((x) => x >= 0);
Two types of Stream
Stream of type Single-Subscription
The most common type is a Stream that contains only one of some sequence of events. And these events need to be provided in order and cannot be lost. This type of Stream is used when you read a file or receive a web page request.
This kind of Stream can only be set to monitor once. Repeated settings will lose the original event, and the remaining other events you monitor will be meaningless. When you start listening, data will be provided and retrieved in chunks.
Stream of type Broadcast
Another stream is for single messages, which can process one message at a time. For example it can be used for browser mouse events.
You can listen to this Stream at any time, and after that you can get any triggered events. This kind of stream can set multiple different listeners to listen at the same time, and you can also start listening to it again after canceling the previous subscription.
Methods for handling Streams
The methods in the following Stream<T>
classes can process the Stream and return the result:
Future<T> get first;
Future<bool> get isEmpty;
Future<T> get last;
Future<int> get length;
Future<T> get single;
Future<bool> any(bool Function(T element) test);
Future<bool> contains(Object? needle);
Future<E> drain<E>([E? futureValue]);
Future<T> elementAt(int index);
Future<bool> every(bool Function(T element) test);
Future<T> firstWhere(bool Function(T element) test, {
T Function()? orElse});
Future<S> fold<S>(S initialValue, S Function(S previous, T element) combine);
Future forEach(void Function(T element) action);
Future<String> join([String separator = '']);
Future<T> lastWhere(bool Function(T element) test, {
T Function()? orElse});
Future pipe(StreamConsumer<T> streamConsumer);
Future<T> reduce(T Function(T previous, T element) combine);
Future<T> singleWhere(bool Function(T element) test, {
T Function()? orElse});
Future<List<T>> toList();
Future<Set<T>> toSet();
All of the above methods, except drain()
the and pipe()
method, Iterable
have corresponding similar methods in the class. If you're using await for
a loop inside an async function (or just inside another method), it's much easier to use the methods above. For example, some code implementations might look like this:
Future<bool> contains(Object? needle) async {
await for (final event in this) {
if (event == needle) return true;
}
return false;
}
Future forEach(void Function(T element) action) async {
await for (final event in this) {
action(event);
}
}
Future<List<T>> toList() async {
final result = <T>[];
await forEach(result.add);
return result;
}
Future<String> join([String separator = '']) async =>
(await toList()).join(separator);
Convert an existing Stream
The following methods can process the original Stream and return a new Stream. When these methods are called, the listener set on the original Stream will first listen to the converted new Stream, and will turn back to listen to the original Stream after the processing of the new Stream is completed.
Stream<R> cast<R>();
Stream<S> expand<S>(Iterable<S> Function(T element) convert);
Stream<S> map<S>(S Function(T event) convert);
Stream<T> skip(int count);
Stream<T> skipWhile(bool Function(T element) test);
Stream<T> take(int count);
Stream<T> takeWhile(bool Function(T element) test);
Stream<T> where(bool Function(T event) test);
There are also methods in the Iterable class that transform one iterable into another iterable, and these methods above are similar to those in the Iterable class. If you use await for loop in async function, it will be easier to use these methods above.
You can use the conversion class methods provided by the Stream class, such as map()、where()、expand()
and take()
to deal with most common conversion needs.
For example, suppose you have a counterStream
Stream named that prints out an incrementing integer every second. Its implementation process may be as follows:
var counterStream =
Stream<int>.periodic(const Duration(seconds: 1), (x) => x).take(15);
You can quickly view events using the following code:
counterStream.forEach(print); // Print an integer every second, 15 times.
map()
You can call a similar conversion method to convert the Stream's events before listening to the Stream . This method will return a new Stream.
// Double the integer in each event.
var doubleCounterStream = counterStream.map((int x) => x * 2);
doubleCounterStream.forEach(print);
You can use any other transformation method instead map()
, such as the following:
.where((int x) => x.isEven) // Retain only even integer events.
.expand((var x) => [x, x]) // Duplicate each event.
.take(5) // Stop after the first five events.
Generally speaking, using various conversion methods is enough to meet your simple usage needs. However, if you need more control over the conversion, you can transform()
specify one using the Stream class's method StreamTransformer
. The Dart platform library provides Stream converters for many common task needs. For example, the code below uses the and converters dart:convert
provided by the library .utf8.decoder
LineSplitter
Stream<List<int>> content = File('someFile.txt').openRead();
List<String> lines = await content
.transform(utf8.decoder)
.transform(const LineSplitter())
.toList();
The following code example reads a file and performs two transformations on its Stream. The first conversion is to convert the file data into UTF-8 encoding format, and then transform the converted data into a LineSplitter for execution. All lines in the file except the lines beginning with # will be printed.
import 'dart:convert';
import 'dart:io';
void main(List<String> args) async {
var file = File(args[0]);
var lines = utf8.decoder
.bind(file.openRead())
.transform(const LineSplitter());
await for (final line in lines) {
if (!line.startsWith('#')) print(line);
}
}
listen() method
This is a "bottom" method against which all other Stream methods are listen()
defined.
StreamSubscription<T> listen(void Function(T event)? onData,
{
Function? onError, void Function()? onDone, bool? cancelOnError});
You just need to inherit the Stream class and implement the listen() method to create a subclass of the Stream type. All other methods in the Stream class depend on listen()
calling the method.
The listen() method allows you to monitor a Stream. Until you listen to a Stream, it's just a lazy object that describes the events you want to watch. When you listen to it, it will return a StreamSubscription object, which is used to represent an active Stream that produces events. This is similar to the implementation of the Iterable object, except that the Iterable object can return an iterator and perform actual iteration operations.
Stream allows you to pause, resume or even completely cancel a subscription. You can also set a callback for it, which will notify the caller on every data event, error event, and when the Stream itself is closed.
Microtask micro task
Dart is a single-threaded model , which will continuously fetch tasks from the two queues of the event queue and the microtask queue .
//执行一个微任务
scheduleMicrotask(() {
print('a microtask');
});
Dart VM model
Each isolate
zone is isolated, and each isolate
zone consists of a Mutator Thread + N Helper Threads (processing GC)
Dart event loop mechanism
Similar to the message loop in Android Handler
, MessageQueue
Dart is also event loop
driven by it. event loop
It will continuously obtain the message events sent by the isolate from the message queue to process. Unlike Android, which has only one Looper+one message queue for one thread, there are two queues in Dart: one is called event queue (event queue), and one is called event queue . Called microtask queue (microtask queue) . The latter has higher priority.
- Event queue (event queue) , including all external events: information transfer between I/O, mouse events, drawing events, timers, and isolate.
- Microtask queue (microtask queue) represents an asynchronous task that will be completed in a short time. It has the highest priority and is higher than the event queue. As long as there are tasks in the queue, it can always occupy the event loop. The tasks added by the microtask queue are mainly generated internally by Dart.
After Dart executes main()
the method, Event-Loop will execute the events in the two task queues. In each event loop, it always checks whether there are executable tasks in the micro-task queue , and executes them sequentially if there are any. Event, and then the Event execution of the event queue will be queried , and when the Event of the event queue is executed, each time an Event is executed, the microtask queue will be checked again . Therefore, the microtask queue has a higher priority and can be used to jump in the queue.
Because the microtask queue has a higher priority than the event queue, if the microtask queue has too many microtasks, it may occupy the current event loop. As a result, external events such as touch and drawing in the event queue are blocked and stuck.