前言
Flutter官网:flutterchina.club/
Dart官网:www.dartcn.com/
背景
写这篇文章的初衷是用来是巩固和温习flutter相关知识,说起来也惭愧。其实在19年的时候,就已经接触并使用过一段时间的flutter。后面由于公司业务调整,flutter项目被砍掉了,后续再未去深入研究(其实是懒吧)。正好现在有时间,重新梳理温习一遍。
Flutter介绍
Flutter是谷歌的高性能、跨端UI框架,可以通过一套代码,支持iOS、Android、Windows/MAC/Linux等多个平台,且能达到原生性能。 Flutter也可以与平台原生代码进行混合开发。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费、开源的。
上述是官方对Flutter的介绍,当我们想要实现跨平台,完全可以使用Flutter。
Flutter环境配置
官网有详细的Flutter环境配置文档,大家可以按需进行,这里就不过多介绍了。注意由于国内网络限制的原因,最好先配置网络环境变量:
export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
Dart语法
Dart是谷歌开发的计算机编程语言,它被用于web、服务器、移动应用和物联网等领域的开发。
Dart是面向对象的、类定义的、单继承的语言。它的语法类似C语言,可以转译为JavaScript,支持接口(interfaces)、混入(mixins)、抽象类(abstract classes)、具体化泛型(reified generics)、可选类型(optional typing)和sound type system。
注意
- 任何保存在变量中的都是一个对象,并且所有的对象都是对应一个类的实例。 无论是数字,函数和null都是对象。所有对象继承自Object类。
- 尽管Dart是强类型的,但是Dart可以推断类型,所以类型注释是可选的。如果要明确说明不需要任何类型,需要使用特殊类型 dynamic 。
变量
声明变量
- 创建一个变量并初始化
var name = "Cob";
- 变量仅存储对象引用,这里的变量是name存储了一个String类型的对象引用。 “Cob” 是这个 String 类型对象的值。name变量的类型被推断为String。但是也可以通过指定类型的方式,来改变变量类型。 如果对象不限定为单个类型,可以指定为对象类型或动态类型。
dynamic name = "Cob";
Object name = "Cob";
- 也可以直接显示声明变量的类型
String name = "Cob";
Final 和 Const
- 使用过程中从来不会被修改的变量, 可以使用final或const, 而不是var或者其他类型,Final变量的值只能被设置一次;const变量在编译时就已经固定(const变量是隐式final的类型) 最高级final变量或类变量在第一次使用时被初始化。简单来讲,final修饰的变量在运行时才会赋值,const修饰的变量在编译期就已经赋值。
final name = "Cob";
const name = "Coby";
空安全
-
Dart是可以保证空安全,但是我们可以定义为空,在使用时必须判空,不然会爆出异常。
-
默认为不可空,必须在定义时初始化。
int i = 8;
- 定义为可空类型,对于可空变量,我们在使用前必须判空。
int? j;
- 如果我们预期变量不能为空,但在定义时不能确定其初始值,则可以加上late关键字,表示会稍后初始化,但是在正式使用它之前必须得保证初始化过了,否则会报错
late int k;
k=9;
内建类型
| 类型 | 说明 |
|---|---|
| Number | Dart 的 Number 类型只有 int 和 double,支持更多类型请参考dart:math |
| String | Dart 字符串是一组 UTF-16 单元序列,字符串通过单引号或者双引号创建 |
| Boolean | Dart 使用 bool 类型表示布尔值。Dart 只有字面量 true 和 false 是布尔类型,这两个对象都是编译时常量 |
| List | 在 Dart 中的 Array 就是 List 对象,通常称之为 List |
| Set | 在 Dart 中 Set 是一个元素唯一且无序的集合,Dart 中 Map 通过 Map 字面量和 Map 类型来实现 |
| Map | 通常来说,Map 是用来关联 keys 和 values 的对象。keys和values可以是任何类型的对象。在一个Map对象中一个key只能出现一次。但是value可以出现多次。Dart中Map通过Map字面量和Map类型来实现 |
| Rune | 在Dart中,Rune用来表示字符串中的UTF-32编码字符 |
| Symbol | 一个 Symbol 对象表示 Dart 程序中声明的运算符或者标识符 |
方法
- Dart 是一门真正面向对象的语言, 甚至其中的函数也是对象,并且有它的类型 Function 。 这也意味着函数可以被赋值给变量或者作为参数传递给其他函数。 也可以把 Dart 类的实例当做方法来调用。
- 下面三种方式都是定义同一种方法
//显示声明返回值
bool isReal() {
return 1 == 2;
}
//不显示声明
isReal() {
return 1 == 2;
}
//lambda表达式
isReal() => 1 == 2;
- 注意:所有函数都会返回一个值。如果没有明确指定返回值,函数体会被隐式的添加return null; 语句。
控制流程语句
| 运算符 | 说明 |
|---|---|
| if 和 else | Dart 支持 if-else 语句,其中 else 是可选的 |
| for 循环 | 进行迭代操作,可以使用标准 for 语句 |
| while 和 do-while | while 循环在执行前判断执行条件,do-while 循环在执行后判断执行条件 |
| break 和 continue | 使用 break 停止程序循环,使用 continue 跳转到下一次迭代 |
| switch 和 case | 在 Dart 中 switch 语句使用 == 比较整数,字符串,或者编译时常量。 比较的对象必须都是同一个类的实例(并且不可以是子类), 类必须没有对 == 重写。 枚举类型 可以用于 switch 语句 |
| assert | 如果 assert 语句中的布尔条件为 false , 那么正常的程序执行流程会被中断 |
类
-
Dart 是一种基于类和 mixin 继承机制的面向对象的语言。 每个对象都是一个类的实例,所有的类都继承于 Object. 。 基于 * Mixin 继承* 意味着每个类(除 Object 外) 都只有一个超类, 一个类中的代码可以在其他多个继承类中重复使用。
-
在这里要特别说明一下接口,每个类都隐式的定义了一个接口,接口包含了该类所有的实例成员及其实现的接口。 如果要创建一个 A 类,A 要支持 B 类的 API ,但是不需要继承 B 的实现, 那么可以通过 A 实现 B 的接口
// person 类。 隐式接口里面包含了 greet() 方法声明。
class Person {
// 包含在接口里,但只在当前库中可见。
final String _name;
// 不包含在接口里,因为这是一个构造函数。
Person(this._name);
// 包含在接口里。
String greet(String who) => 'Hello, $who. I am $_name.';
}
String greetBob(Person person) => person.greet('Bob');
// person 接口的实现。
class Impostor implements Person {
@override
get _name => '';
@override
String greet(String who) => 'Hi $who. Do you know who I am?';
}
void main() {
print(greetBob(Person('Kathy')));
print(greetBob(Impostor()));
}
- Dart 是不支持多继承的,但是它支持 mixin,简单来讲 mixin 可以 “组合” 多个类,我们通过一个例子来理解。
class Fruits {
say() {
print("all Fruits");
}
}
mixin Apple {
say() {
print("i am Apple");
}
}
mixin Banana {
say() {
print("i am Banana");
}
}
class Pear extends Fruits with Apple, Banana {}
异步操作
- Dart 库中包含许多返回 Future 或 Stream 对象的函数. 这些函数在设置完耗时任务(例如 I/O 操作)后, 就立即返回了,不会等待耗任务完成。 使用 async 和 await 关键字实现异步编程。 可以让你像编写同步代码一样实现异步操作。
Future
- 在 Dart 库中随处可见 Future 对象,通常异步函数返回的对象就是一个 Future。 当一个 future 完成执行后,future 中的值就已经可以使用了。
- 注意:在直接使用 Future API 前,首先应该考虑
await来替代。 代码中使用await表达式会比直接使用 Future API 更容易理解。 - 简单示例
main() {
//方式一:
Future future = Future.value(20);
future.then((value) => print(value));
//方式二:可以直接创建Future
Future(() => print("start")).then((value) => print("complete")).onError((error, stackTrace) => print("error"));
}
async 和 await
- 使用 async 和 await 关键字的代码是异步的,要使用 await , 代码必须在 异步函数(使用 async 标记的函数)中
main() {
start();
}
start() async {
print("start");
await request();
print("end");
}
request() {
print("request");
}
Stream
- Stream 也是用于接收异步事件数据,和 Future 不同的是,它可以接收多个异步操作的结果(成功或失败)。 也就是说,在执行异步任务时,可以通过多次触发成功或失败事件来传递结果数据或错误异常。 Stream 常用于会多次读取数据的异步任务场景,如网络内容下载、文件读写等。
Stream.fromFutures([
Future.delayed(Duration(seconds: 1), () {
return "start";
}),
Future.delayed(Duration(seconds: 3), () {
return AssertionError("timeout");
})
]).listen((event) {
print(event);
}, onError: (e) {
print(e.toString());
});