Dart
# Dart
[TOC]
- dart文档:https://dart.dev/samples (opens new window)
- 线上palyground:https://dartpad.dartlang.org/ (opens new window)
- 学习教程:https://www.dartcn.com/guides/language/language-tour
- 学习教程:http://codingdict.com/article/21928
# 一、基础知识
# 1.1 程序入口
每个app都必须有一个顶级的main()函数作为应用程序的入口点。
main() {}
# 1.2 控制台输出
print('Hello world!');
# 1.3 变量
参考教程:Flutter 知识梳理 (Dart) - Dart 中 static, final, const 区别 (opens new window)
String name = 'dart';
只能是字符串类型。var otherName = 'Dart';
接收任何类型的变量,一旦赋值,类型就会确定,不再改变。Object x;
所有类型都是Object的子类,即Dart一切皆对象,可赋值任意类型,后期也可改变赋值类型。dynamic t
;同Object,不同在于,dynamic声明的对象编译器会提供所有可能的组合, 而Object声明的对象只能使用Object的属性与方法, 否则编译器会报错。故使用dynamics时要小心引入运行时错误。动态类型final str='str'// final String str = 'str'
、const str='str'// const String str = 'str'
final和const只能被设置一次,并且变量类型可以省略。两者区别在于,const
变量是一个编译时常量,final
变量在第一次使用时被初始化。- 同JS一样,有词法作用域。
# 1.4 检查null或零
未初始化的变量的初始值为null。数字在Dart中也被当成对象,所以只要是带有数字类型的未初始化变量的值都是“null"。
只有布尔值”true"被视为true。
var name = null;
if(name == null){
print('yes');
}else{
print('no');
}
// no
2
3
4
5
6
7
null-aware运算符:null检查最佳实践
- ?. 运算符在左边为null的情况下会阻断右边的调用。(与js的&&很像)
- ?? 运算符在左边为null时为其设置默认值。(||)
var name; if(name??true){ print('yes'); } // yes
1
2
3
4
5
# 1.5 Functions
- Dart函数声明如果没有显式声明返回值类型时会默认当做
dynamic
处理,注意,函数返回值没有类型推断:
// 声明
fn() {
return true;
}
bool fn() {
return true;
}
2
3
4
5
6
7
8
- 可选的位置参数。包装一组函数参数,用[]标记为可选的位置参数,并放在参数列表的最后面
String say(String from, String msg, [String device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
2
3
4
5
6
7
- 可选的命名参数
//设置[bold]和[hidden]标志
void enableFlags({bool bold, bool hidden}) {
// ...
}
enableFlags(bold: true, hidden: false);
2
3
4
5
不能同时使用可选的位置参数和可选的命名参数。
- 默认参数值
- 默认值只能是编译时常量
void enableFlags ({bool bold = false, bool hidden = false}) {...}
- 一个函数可以作为另一函数的参数。(同JS)
void printElement(int element) {
print(element);
}
var list = [1, 2, 3];
// 将 printElement 函数作为参数传递。
list.forEach(printElement);
// 1 2 3
2
3
4
5
6
7
8
9
10
- 果没有明确指定返回值, 函数体会被隐式的添加
return null;
语句。
# 1.6 异步编程
# 1.6.1 Future
类似于JS的Promise。
Future
的所有API的返回值仍然是一个Future
对象,所以可以很方便的进行链式调用。
- 用于表示未来某个时间可能会出现的可用值或错误。
Future.delayed(new Duration(seconds: 2),(){
//return "hi world!";
throw AssertionError("Error");
}).then((data){
//执行成功会走到这里
print("success");
}).catchError((e){
//执行失败会走到这里
print(e);
}).whenComplete((){
//无论成功或失败都会走到这里
});
2
3
4
5
6
7
8
9
10
11
12
- Future.wait
- 等待多个异步任务都执行结束后才进行一些操作
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);
});
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1.6.2 async 和 await
功能和用法:于JS一致。
# 1.6.3 Stream
Stream
也是用于接收异步事件数据,和Future
不同的是,它可以接收多个异步操作的结果(成功或失败)。 也就是说,在执行异步任务时,可以通过多次触发成功或失败事件来传递结果数据或错误异常。 Stream
常用于会多次读取数据的异步任务场景,如网络内容下载、文件读写等。
Stream.fromFutures([
// 1秒后返回结果
Future.delayed(new Duration(seconds: 1), () {
return "hello 1";
}),
// 抛出一个异常
Future.delayed(new Duration(seconds: 2),(){
throw AssertionError("Error");
}),
// 3秒后返回结果
Future.delayed(new Duration(seconds: 3), () {
return "hello 3";
})
]).listen((data){
print(data);
}, onError: (e){
print(e.message);
},onDone: (){
});
I/flutter (17666): hello 1
I/flutter (17666): Error
I/flutter (17666): hello 3
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 1.7 内建类型
Number String Boolean List Set Map Rune Symbol
- 是 Set 还是 Map ? Map 字面量语法同 Set 字面量语法非常相似。 因为先有的 Map 字母量语法,所以
{}
默认是Map
类型。 如果忘记在{}
上注释类型或赋值到一个未声明类型的变量上, 那么 Dart 会创建一个类型为Map
的对象。
# 1.8 运算符
/
和~/
都是除,前者正常返回,后者返回整数部分。优先级自左向右 >= 、==、&&、|| 、? :
算术运算符
assert(5 / 2 == 2.5); // 结果是双浮点型
assert(5 ~/ 2 == 2); // 结果是整型
assert(5 % 2 == 1); // 余数
2
3
赋值运算符
- 使用
??=
运算符时,只有当被赋值的变量为 null 时才会赋值给它。
- 使用
条件表达式
- expr1 ?? expr2 :如果 expr1 是 non-null, 返回 expr1 的值; 否则, 执行并返回 expr2 的值。
级联运算符
querySelector('#confirm') // 获取对象。
..text = 'Confirm' // 调用成员变量。
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
// 等价于
var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));
2
3
4
5
6
7
8
9
10
# 1.9 控制流程语句
和 JavaScript 不同, Dart 的判断条件必须是布尔值,不能是其他类型。
闭包在 Dart 的
for
循环中会捕获循环的 index 索引值, 来避免 JavaScript 中常见的陷阱。
var callbacks = [];
for (var i = 0; i < 2; i++) {
callbacks.add(() => print(i));
}
callbacks.forEach((c) => c());
// 0 1
2
3
4
5
6
如果不需要使用当前计数值, 使用
forEach()
是非常棒的选择。现了 Iterable 的类(比如, List 和 Set)同样也支持使用
for-in
进行迭代操作。在非空
case
中实现 fall-through 形式, 可以使用continue
语句结合lable
的方式实现:
var command = 'CLOSED';
switch (command) {
case 'CLOSED':
executeClosed();
continue nowClosed;
// Continues executing at the nowClosed label.
nowClosed:
case 'NOW_CLOSED':
// Runs for both CLOSED and NOW_CLOSED.
executeNowClosed();
break;
}
2
3
4
5
6
7
8
9
10
11
12
13
case
语句可以拥有局部变量, 这些局部变量只能在这个语句的作用域中可见。如果
assert
语句中的布尔条件为 false , 那么正常的程序执行流程会被中断,只在开发环境中有效。assert 的第二个参数可以为其添加一个字符串消息。
# 1.10 类
class class_name {
<fields> // 声明的任何变量
<getters/setters> // 显示覆盖默认值
<constructors> // 负责为类的对象分配内存
<functions> // 方法
}
2
3
4
5
6
与 Java 不同,Dart 没有关键字 “public” , “protected” 和 “private” 。 如果标识符以下划线(_)开头,则它相对于库是私有的。
- 使用
?.
来代替.
, 可以避免因为左边对象可能为 null , 导致的异常。
// 如果 p 为 non-null,设置它变量 y 的值为 4。
p?.y = 4;
2
- 通常模式下,会将构造函数传入.的参数的值赋值给对应的实例变量。
class Point {
num x, y;
Point(num x, num y) {
// 还有更好的方式来实现下面代码,敬请关注。
this.x = x;
this.y = y;
}
}
// 等价于
class Point {
num x, y;
// 在构造函数体执行前,
// 语法糖已经设置了变量 x 和 y。
Point(this.x, this.y);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1.10.1 扩展类(继承)
- 使用
extends
关键字来创建子类, 使用super
关键字来引用父类。 - 子类继承除父类的构造函数之外的所有属性和方法。(构造函数是类的特殊函数)
- dart不支持多重继承,即一个类可以从多个类继承。
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
// ···
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
// ···
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 重写类成员
- 使用
@override
注解指出想要重写的成员。 - 重写方法时,函数参数的数量和类型必须和父级匹配。如果参数数量或其数据类型不匹配,Dart编译器会抛出错误。
- 使用
class SmartTelevision extends Television {
void turnOn() {...}
// ···
}
2
3
4
5
# 1.10.2 枚举类型
- 不需要示例化,可以直接拿来用。
enum Color {red, green, blue}
- 枚举中的每个值都有一个
index
getter 方法。
print(Color.blue.index); // 2
# 1.10.3 getter和setter
- getter没有参数并返回一个值。
- setter有一个参数并不返回值。
class Student {
String name;
int age;
String get stud_name {
return name;
}
void set stud_name(String name) {
this.name = name;
}
void set stud_age(int age) {
if(age<= 0) {
print("Age should be greater than 5");
} else {
this.age = age;
}
}
int get stud_age {
return age;
}
}
void main() {
Student s1 = new Student();
s1.stud_name = 'MARK';
s1.stud_age = 0;
print(s1.stud_name);
print(s1.stud_age);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 1.10.4 关键字
# 1.10.4.1 this
this
关键字指向类的当前实例。这里,参数名称和类字段的名称是相同的。
# 1.10.4.2 static
static
关键字可以应用于一类,即,数据成员字段和方法。静态变量保留其值,直到程序完成执行。静态成员由类名引用。
class StaticMem {
static int num;
static disp() {
print("The value of num is ${StaticMem.num}") ;
}
}
void main() {
StaticMem.num = 12;
// initialize the static variable }
StaticMem.disp();
// invoke the static method
}
2
3
4
5
6
7
8
9
10
11
12
# 1.10.4.3 super
super
关键字用来指一类的直接父类。关键字可用于引用变量,属性或方法的超类。
void main() {
Child c = new Child();
c.m1(12);
}
class Parent {
String msg = "message variable from the parent class";
void m1(int a){ print("value of a ${a}");}
}
class Child extends Parent {
void m1(int b) {
print("value of b ${b}");// value of b 12
super.m1(13);// value of a 13
print("${super.msg}");// message variable from the parent class
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1.11 异常处理
参考教程:https://www.jianshu.com/p/f331898f3183
# 1.11.1 抛出异常
- 可以抛出任意对象。
throw FormatException('Expected at least 1 section');
throw 'Out of llamas!';
2
# 1.11.2 捕捉异常
- 按类型捕捉异常。
try {
breedMoreLlamas();
} on OutOfLlamasException {
// 一个指定的异常
buyMoreLlamas();
} on Exception catch (e) {
// 捕获任意一个异常
print('Unknown exception: $e');
} catch (e,s) { // 第一个是抛出的异常,第二个是堆栈跟踪( StackTrace对象)。
// 没有指定类型,处理所有
print('Something really unknown: $e');
} finally {
// 最后执行清理
}
2
3
4
5
6
7
8
9
10
11
12
13
14
- 部分处理异常,允许传播。
try {
dynamic foo = true;
print(foo++); // 运行时错误
} catch (e) {
print('misbehave() partially handled ${e.runtimeType}.');
rethrow; // 允许调用者看到异常
}
2
3
4
5
6
7
# 二、常用API
# 2.1字符串与数字互转
// String -> int
var one = int.parse('1');
// String -> double
var onePointOne = double.parse('1.1');
// int -> String
String oneAsString = 1.toString();
// double -> String
String piAsString = 3.14159.toStringAsFixed(2);
2
3
4
5
6
7
8
9
10
11
# 2.2 List
# 2.2.1 初始化
List list = List(); // list.length == 0
List list = List(2); // list.length == 2
List list = List<String>(); // list.add(String)
List list = [1,2]; // list.add('1') -> error
List list = ['1',2,true]; // list.add(1.1)
2
3
4
5
# 2.2.2 常用属性
list.length;
list.isEmpty;
list.isNotEmpty;
list.first;
2
3
4
# 2.2.3 添加数据
List list = [1,2,3];
// 末尾添加
list.add(4); // [1,2,3,4]
list.add([5,6]); // [1,2,3,4,5,6]
// 向指定位置添加
list.insert(0, 66); // [66,1,2,3,4,5,6]
list.insertAll(1,[22,22]); // [66,22,22,1,2,3,4,5,6]
2
3
4
5
6
7
8
9
# 2.2.4 删除数据
List list = [1,2,3];
// 删除指定元素
list.remove(1); // [2,3]
// 删除末尾元素
list.removeLast(); // [2]
// 删除指定位置的元素
list.removeAt(0); // []
list = [1,2,3];
// 删除指定区域的元素
list.removeRange(0,1); // [2,3]
// 删除指定要求的元素,返回void
list.removeWhere((item) => item == 2); // [3]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 2.2.5 遍历
List list = [1,2,3];
list.forEach((e) => print(e)); // 1 2 3
// 返回一个新集合
list.map((e) => e+1); // (2,3,4)
// 是否包含某个元素
list.contains(1); // true
list.sort((n1,n2) => n2 - n1); // [3,2,1]
// reduce() 将数组中的每一个值与前面返回的值相加,最后返回相加的总和
list.reduce((cur, next) => cur+next); // 6
// fold() 用法跟 reduce() 基本一样,只不过是可以提供一个初始值
list.fold(4, (cur, next) => cur+next); // 10
// 判断数组中的每一项是否均达到了某个条件
list.every((e) => e < 4)); // true
// where() 返回数组中满足给定条件的元素集合
list.where((e) => e < 3); // (1,2)
// firstWhere() 返回数组中满足给定条件的第一个元素
list.firstWhere((e) => e == 3, orElse: ()=> null); // 3
// singleWhere() 返回数组中满足给定条件的唯一一个元素,若有多个元素满足条件会抛出异常
list.singleWhere((e) => e !=3, orElse: ()=> null); // Uncaught Error: Bad state: Too many elements
// 扁平化遍历
list = [[1,2],[3]];
list.expand((e) => e); // (1,2,3)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 2.2.6 其他
List list = [1,2,3];
// 克隆(深拷贝)
List list1 = List.from(list);
// 取n个元素
list.take(2); // (1,2)
// 跳过n个元素
list.skip(2); // (3)
// 转map
list.asMap(); // {0:1, 1:2, 2:3}
// 获取下标集合
list.asMap().keys; // (0,1,2)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 2.3 DateTime
参考教程:https://www.cnblogs.com/lxlx1798/p/11267411.html
# 三、小case
# 3.1 Timer创建循环执行与取消
_startTimer() {
/*创建循环*/
_timer = new Timer.periodic(new Duration(seconds: 2), (timer) {
setState(() {
/* do something */
});
});
}
_cancelTimer() {
_timer?.cancel();
}
2
3
4
5
6
7
8
9
10
11
12
# 3.2 处理异步操作
Future openImagePicker () {
// dart:async提供了Completer类,通过实例这个类生成Future
Complete completer = new Completer();
// ImagePicker 是一个图片选择插件
ImagePicker.singlePicker(
context,
singleCallback: (data) {
// 控制completer.future的成功状态
completer.complete(data);
},
failCallback:(err) {
// 控制completer.future的失败状态
completer.catchError(err);
}
);
return completer.future;
}
// 使用
openImagePicker().then((data) {}).catchError((err){});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22