Flutter学习三:Dart语言基础(一)

目录

0 引言

1 变量声明

1.1 var 关键字

1.2 dynamic 和 Object

1.3 final和const

1.4 空安全

2 函数

2.1 函数声明

2.2 函数作为变量

2.3 函数作为参数传递

 2.3.1 可选的位置参数

2.3.2 可选的命名参数

3 mixin

4 异步支持

 4.1  Future

 4.1.1 Future.then

 4.1.2 Future.catchError

 4.1.3 Future.whenComplete

 4.1.4 Future.wait

4.2  async/await

 4.2.1 回调地狱(Callback Hell)

4.2.2 使用Future消除回调地狱(Callback Hell)

4.2.3 使用 async/await 消除回调地狱(Callback Hell)

4.3 Stream


0 引言

Dart 是 Flutter 的基础。 Dart 作为 Flutter 应用程序的编程语言,为驱动应用运行提供了环境,同时 Dart 还支持许多核心的开发任务,例如格式化,分析和代码测试。

官方文档:Dart 语言开发文档 | Dart

学习文档:第二版序 | 《Flutter实战·第二版》 (flutterchina.club)

1 变量声明

Dart 是一个强类型语言,任何变量都是有确定类型的。

1.1 var 关键字

  • var声明的变量可以赋值任意对象,一旦赋值,就不能再改变其类型
var t = "hi world";
// 下面代码在dart中会报错,因为变量t的类型已经确定为String,
// 类型一旦确定后则不能再更改其类型。
t = 1000;

1.2 dynamic 和 Object

  • Dart 中所有类型都是Object的子类(包括Function和Null)
  • dynamicObject声明的变量都可以赋值任意对象,且后期可以改变赋值的类型
  • Object声明的对象只能使用 Object对象自己声明的属性与方法, 否则编译器会报错
  • dynamic声明的对象可以使用赋值类型自带的属性和方法,若dynamic对象使用的属性和方法不存在,编译时不会报错,运行时会报错
dynamic t;
Object x;
t = "hi world";
x = 'Hello Object';
//下面代码没有问题
t = 1000;
x = 1000;
 dynamic a;
 Object b = "";
 main() {
   a = "";
   printLengths();
 }   

 printLengths() {
 
   print(a.length);  // 正常
   
   print(b.length);// 报错 The getter 'length' is not defined for the class 'Object'

   print(a.xx); // a是字符串,没有"xx"属性,编译时不会报错,运行时会报错
 }

1.3 finalconst

  • 若从未打算更改一个变量,那么使用 final 或 const,它们只能被设置一次
  • const 变量是一个编译时常量(编译时直接替换为常量值)
  • final变量在第一次使用时被初始化
  • final或者const修饰的变量,变量类型可以省略
//可以省略String这个类型声明
final str = "hi world";
//final String str = "hi world"; 
const str1 = "hi world";
//const String str1 = "hi world";

1.4 空安全

  •  Dart 引入了空安全,定义变量时我们可以指定变量是可空还是不可空
  • 不可空变量,必须在定义时赋值,或者使用late关键字,稍后赋值
  • 可空变量,通过在变量后面加一个”?“符号来声明
  • 对于可空变量,在使用前必须使用if(变量!=null)判空
  • 对于可空变量,某些情况下即使我们赋值了,预处理器仍然有可能识别不出,需要显式申明(通过在变量后面加一个”!“符号)告诉预处理器它已经不是null了
int i = 8; //默认为不可空,必须在定义时初始化。
int? j; // 定义为可空类型,对于可空变量,我们在使用前必须判空。

// 如果我们预期变量不能为空,但在定义时不能确定其初始值,则可以加上late关键字,
// 表示会稍后初始化,但是在正式使用它之前必须得保证初始化过了,否则会报错
late int k;
k=9;
class Test{
  int? i;
  Function? fun;
  say(){
    if(i!=null) {
      print(i! * 8); //因为已经判过空,所以能走到这 i 必不为null,如果没有显式申明,则 IDE 会报错
    }
    if(fun!=null){
      fun!(); // 同上
    }
  }
}

2 函数

Dart是一种真正的面向对象的语言,所以即使是函数也是对象,并且有一个类型Function。这意味着函数可以赋值给变量或作为参数传递给其他函数,这是函数式编程的典型特征。

2.1 函数声明

  • Dart函数声明如果没有显式声明返回值类型,则默认返回值类型类型是dynamic
typedef bool CALLBACK();

//不指定返回类型,此时默认为dynamic,不是bool
isNoble(int atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

//void 表示没有返回值
void test(CALLBACK cb){
   print(cb()); 
}
//报错,isNoble不是bool类型
test(isNoble);

2.2 函数作为变量

var say = (str){
  print(str);
};
say("hi world");

2.3 函数作为参数传递

  • 不能同时使用可选的位置参数和可选的命名参数
//定义函数execute,它的参数类型为函数
void execute(var callback) {
    callback(); //执行传入的函数
}
//调用execute,将箭头函数作为参数传递
execute(() => print("xxx"))

 2.3.1 可选的位置参数

  • 用[]标记为可选的位置参数,需要放在参数列表的最后面
String say(String from, String msg, [String? device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}

//不带可选参数调用这个函数的例子:
say('Bob', 'Howdy'); //结果是: Bob says Howdy

//带可选参数调用这个函数的例子:
say('Bob', 'Howdy', 'smoke signal'); //结果是:Bob says Howdy with a smoke signal

2.3.2 可选的命名参数

  • 定义函数时,使用{param1, param2, …},放在参数列表的最后面,用于指定命名参数
  • 调用函数时,可以使用指定命名参数。例如:paramName: value
//定义函数
void enableFlags({bool bold, bool hidden}) {
    // ... 
}

//调用函数
enableFlags(bold: true, hidden: false);

3 mixin

  • Dart 是不支持多继承的,但是它支持 mixin
  •  mixin 可以 “组合” 多个类:定义几个 mixin,通过 with 关键字将它们组合成不同的类
  • 如果多个mixin 中有同名方法,with 时,会默认使用最后面的 mixin 的方法
  • mixin 方法中可以通过 super 关键字调用之前 mixin 或类中的方法
//例如:
//定义一个 Person 类,实现吃饭、说话、走路和写代码功能,
//同时定义一个 Dog 类,实现吃饭、走路功能
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{}

4 异步支持

  • 同步函数:该函数被调用时不会立即返回,直到该函数所要做的事情全都做完了才返回。比如在银行排队办理业务,要等到前面一个人办完才能到下一个。(一步一步做
  • 异步函数:该函数会立即返回,尽管该函数规定的操作任务还没有完成。比如一个人边吃饭,边看手机。(同时做
  • Dart类库有非常多的返回Future或者Stream对象的函数,这些函数被称为异步函数
  • Dart支持asyncawait关键词,用法和功能同JavaScript中的是一样的

 4.1  Future

  • Future与JavaScript中的Promise非常相似,是用于处理异步操作的,异步处理成功了就执行成功的操作,异步处理失败了就捕获错误或者停止后续操作
  • 一个Future只会对应一个结果,要么成功,要么失败
  • Future 的所有API的返回值仍然是一个Future对象

 4.1.1 Future.then

  • 执行耗时任务,比如一次网络请求时,在then中接收异步处理成功的结果
/* 使用Future.delayed 创建了一个延时任务,2秒后返回结果字符串"hi world!",
   然后在then中接收异步结果并打印结果,代码如下:
*/
Future.delayed(Duration(seconds: 2),(){
   return "hi world!";
}).then((data){
   //执行成功会走到这里  
   print(data);
});

 4.1.2 Future.catchError

  • 如果异步任务发生错误,可以在catchError中捕获错误
  • then方法还有一个可选参数onError,也可以用它来捕获异常
Future.delayed(Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");  
}).then((data){
   //执行成功会走到这里  
   print("success");
}).catchError((e){
   //执行失败会走到这里  
   print(e);
});
Future.delayed(Duration(seconds: 2), () {
	//return "hi world!";
	throw AssertionError("Error");
}).then((data) {
    //执行成功会走到这里  
	print("success");
}, onError: (e) {
    //执行失败会走到这里 
	print(e);
});

 4.1.3 Future.whenComplete

  • 遇到无论异步任务执行成功或失败都需要做一些事的场景时,用whenComplete处理
Future.delayed(Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");
}).then((data){
   //执行成功会走到这里 
   print(data);
}).catchError((e){
   //执行失败会走到这里   
   print(e);
}).whenComplete((){
   //无论成功或失败都会走到这里
});

 4.1.4 Future.wait

  • 接受一个Future数组参数,只有数组中所有Future都执行成功后,才会触发then的成功回调,只要有一个Future执行失败,就会触发错误回调
//执行下面代码,4秒后你会在控制台中看到“hello world”
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.2  async/await

  • Dart中的async/await 和JavaScript中的async/await功能是一样的:异步任务串行化。

 4.2.1 回调地狱(Callback Hell)

  • 当大量异步任务依赖其他异步任务的结果时,出现Future.then回调中套回调的情况,过多的嵌套会导致的代码可读性下降以及出错率提高,并且非常难维护,这个问题被形象的称为回调地狱(Callback Hell)
/*
  现在有个需求场景是用户先登录,登录成功后会获得用户ID,
  然后通过用户ID,再去请求用户个人信息,获取到用户个人信息后,
  为了使用方便,我们需要将其缓存在本地文件系统
*/

//先分别定义各个异步任务
Future<String> login(String userName, String pwd){
	...
    //用户登录
};
Future<String> getUserInfo(String id){
	...
    //获取用户信息 
};
Future saveUserInfo(String userInfo){
	...
	// 保存用户信息 
}; 

//调用这些异步任务,实现需求
login("alice","******").then((id){
 //登录成功后,通过id获取用户信息    
 getUserInfo(id).then((userInfo){
    //获取用户信息后保存 
    saveUserInfo(userInfo).then((){
       //保存用户信息,接下来执行其他操作
        ...
    });
  });
})

4.2.2 使用Future消除回调地狱(Callback Hell)

  • Future 的所有API的返回值仍然是一个Future对象,所以可以很方便的进行链式调用
  • 如果在then 中返回的是一个Future的话,该future会执行,执行结束后会触发后面的then回调,这样依次向下,就避免了层层嵌套
login("alice","******").then((id){
  	return getUserInfo(id);
}).then((userInfo){
    return saveUserInfo(userInfo);
}).then((e){
   //执行接下来的操作 
}).catchError((e){
  //错误处理  
  print(e);
});

4.2.3 使用 async/await 消除回调地狱(Callback Hell)

  • async用来表示函数是异步的,定义的函数会返回一个Future对象,可以使用 then 方法添加回调函数。

  • await 后面是一个Future,表示等待该异步任务完成,异步完成后才会往下走;await必须出现在 async 函数内部。

  • 可以通过async/await将一个异步流用同步的代码表示出来

  • 为了保持代码的健壮性使用async/await的时候,使用try catch来处理错误

  • async/await 只是一个语法糖,编译器最终都会将其转化为一个 Future的调用链

task() async {
   try{
    String id = await login("alice","******");
    String userInfo = await getUserInfo(id);
    await saveUserInfo(userInfo);
    //执行接下来的操作   
   } catch(e){
    //错误处理   
    print(e);   
   }  
}

4.3 Stream

  • Stream 可以接收多个异步操作的结果(成功或失败)
  • Stream 常用于会多次读取数据的异步任务场景,如网络内容下载、文件读写等。
Stream.fromFutures([
  // 1秒后返回结果
  Future.delayed(Duration(seconds: 1), () {
    return "hello 1";
  }),
  // 2秒后抛出一个异常
  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

猜你喜欢

转载自blog.csdn.net/D_lunar/article/details/131391709