第一章 起步

弹射起步

变量声明

  • var 声明变量,但是类型后面不可以改变了,根据第一次赋值数据的类型来推断其类型,编译结束后其类型就已经被确定
  • dynamic 和 Object 声明动态变量,后面类型也可以改变,不同点在于dynamic可以调用可以用的属性(有运行时风险),Object 只能调用Object提供的属性
  • final const final是运行时常量,const是编译时常量,同时用final或者const修饰的变量可以不加类型
  • 空类型 安全
1
2
3
4
5
6
7
int i = 8; //默认为不可空,必须在定义时初始化。
int? j; // 定义为可空类型,对于可空变量,我们在使用前必须判空。

// 如果我们预期变量不能为空,但在定义时不能确定其初始值,则可以加上late关键字,
// 表示会稍后初始化,但是在正式使用它之前必须得保证初始化过了,否则会报错
late int k;
k=9;

函数

  • 函数式编程
  • Dart函数声明如果没有显式声明返回值类型时会默认当做dynamic处理,返回值没有类型推断
  • 支持箭头函数,只有一个语句的函数,使用箭头函数简写
  • 函数做为变量
1
2
3
4
var say = (str){
  print(str);
};
say("hi world");
  • 函数作为参数传递
1
2
3
4
void execute(var callback) {
    callback();
}
execute(() => print("xxx"))
  • 可选的位置参数 包装一组函数参数,用[]标记为可选的位置参数,并放在参数列表的最后面:
1
2
3
4
5
6
7
String say(String from, String msg, [String? device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}
  • 可选的命名参数 定义函数时,使用{param1, param2, …},放在参数列表的最后面,用于指定命名参数。例如:
1
2
3
4
//设置[bold]和[hidden]标志
void enableFlags({bool bold, bool hidden}) {
    // ... 
}
  • mixin Dart 是不支持多继承的,但是它支持 mixin,简单来讲 mixin 可以 “组合” 多个类,我们通过一个例子来理解。
 1
 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
class Person {
  say() {
    print('say');
  }
}

mixin Eat {
  eat() {
    print('eat');
  }
}

mixin Walk {
  walk() {
    print('walk');
  }
}

mixin Code {
  code() {
    print('key');
  }
}

class Dog with Eat, Walk{}
class Man extends Person with Eat, Walk, Code{}

ps:

  • 不能同时使用位置参数和命名参数
  • 我们定义了几个 mixin,然后通过 with 关键字将它们组合成不同的类。有一点需要注意:如果多个mixin 中有同名方法,with 时,会默认使用最后面的 mixin 的,mixin 方法中可以通过 super 关键字调用之前 mixin 或类中的方法。

异步支持

Future

Future.then

为了方便示例,在本例中我们使用Future.delayed 创建了一个延时任务(实际场景会是一个真正的耗时任务,比如一次网络请求),即2秒后返回结果字符串"hi world!",然后我们在then中接收异步结果并打印结果,代码如下:

1
2
3
4
5
Future.delayed(Duration(seconds: 2),(){
   return "hi world!";
}).then((data){
   print(data);
});

Future.catchError

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Future.delayed(Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");  
}).then((data){
   //执行成功会走到这里  
   print("success");
}).catchError((e){
   //执行失败会走到这里  
   print(e);
});

或者使用then的onError可选参数

1
2
3
4
5
6
7
8
Future.delayed(Duration(seconds: 2), () {
	//return "hi world!";
	throw AssertionError("Error");
}).then((data) {
	print("success");
}, onError: (e) {
	print(e);
});

Future.whenComplete

无论成功失败都走的块

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Future.delayed(Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");
}).then((data){
   //执行成功会走到这里 
   print(data);
}).catchError((e){
   //执行失败会走到这里   
   print(e);
}).whenComplete((){
   //无论成功或失败都会走到这里
});

Future.wait

类似于GCD group

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
Future.wait([
  // 2秒后返回结果  
  Future.delayed(Duration(seconds: 2), () {
    return "hello";
  }),
  // 4秒后返回结果  
  Future.delayed(Duration(seconds: 4), () {
    return " world";
  })
]).then((results){
  print(results[0]+results[1]);
}).catchError((e){
  print(e);
});

执行上面代码,4秒后你会在控制台中看到“hello world”。 里面的两个任务都执行完成后才会走到then

async/await

避免回调低于,用同步的写法写出异步的代码 回调地狱

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
login("alice","******").then((id){
 //登录成功后通过,id获取用户信息    
 getUserInfo(id).then((userInfo){
    //获取用户信息后保存 
    saveUserInfo(userInfo).then((){
       //保存用户信息,接下来执行其他操作
        ...
    });
  });
})
  • 使用Future写出 Callback Hell
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
login("alice","******").then((id){
  	return getUserInfo(id);
}).then((userInfo){
    return saveUserInfo(userInfo);
}).then((e){
   //执行接下来的操作 
}).catchError((e){
  //错误处理  
  print(e);
});

Future 的所有API的返回值仍然是一个Future对象,所以可以很方便的进行链式调用” ,如果在then 中返回的是一个Future的话,该future会执行,执行结束后会触发后面的then回调,这样依次向下,就避免了层层嵌套。

  • 使用 async/await 消除callback hell
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
task() async {
   try{
    String id = await login("alice","******");
    String userInfo = await getUserInfo(id);
    await saveUserInfo(userInfo);
    //执行接下来的操作   
   } catch(e){
    //错误处理   
    print(e);   
   }  
  • async用来表示函数是异步的,定义的函数会返回一个Future对象,可以使用 then 方法添加回调函数。
  • await 后面是一个Future,表示等待该异步任务完成,异步完成后才会往下走;await必须出现在 async 函数内部

其实,无论是在 JavaScript 还是 Dart 中,async/await 都只是一个语法糖, 译器或解释器最终都会将其转化为一个 Promise(Future)的调用链。

Stream

Stream 也是用于接收异步事件数据,和 Future 不同的是,它可以接收多个异步操作的结果(成功或失败)。 也就是说,在执行异步任务时,可以通过多次触发成功或失败事件来传递结果数据或错误异常。 Stream 常用于会多次读取数据的异步任务场景,如网络内容下载、文件读写等。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
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: (){

});

输出如下: I/flutter (17666): hello 1 I/flutter (17666): Error I/flutter (17666): hello 3