Dart 是 Google 公司开发的编程语言,最开始的目标是成为下一代 Web 开发语言,设计之初就吸收了其它编程语言的优点,入门还是比较简单的。

特点

  • 一切皆对象:包括基本类型(数值、布尔值)、函数都是对象
  • 面向接口:没有 final 方法,允许重写除了内置操作符之外的所有方法
  • 封装性:外部操作都是使用 get / set 方法来改变对象的状态
  • 类型可选
    • 类型在语法层面可选
    • 类型对运行时的语义没有影响
    • 存在类型不一致,会警告,但不会报错

HelloWorld

1
2
3
void main() {
print("Hello, World");
}

变量与类型

Dart 使用 var 关键字定义实例变量,这点上和 JavaScript ES5 定义方式是一致的。如果定义变量,没有赋值,默认为 null

Dart 是强类型语言。 如果第一次已经赋值了,之后代码就不能将变量更改为其它类型。JavaScript 作为弱类型语言,则可以随意改变类型。如果使用 Dart 语言,定义一个变量之后需要改变类型,可以使用 dynamic 关键字。

final 与 const

constfinal 的共同点是初始化后无法更改。区别在于,const 在编译时会检查值,而 final 值在运行时才检查值。

数值类型

Dart 提供了 intdouble ,但没有 float 类型。

  • -0.00.0 相等但不相同
  • NaN(非数值)与自身不相等但相同

字符串类型

创建字符串

  • ' 或者 "
  • 多行字符串:''' 或者 """
  • 原始字符串:r

操作

  • 运算符操作:+*==[]
  • 插值 ${name}
  • 字符串属性:lengthisEmptyisNotEmpty
  • 常用方法:contains()subString()replaceAll()split()

列表类型

1
2
3
4
// 赋值
var list = [1, 2, 3];
// 通过 new 创建
var list = new List();

常用方法:add()length()remove()insert()indexOf()sublist()forEachshuffle()

键值对类型

1
2
3
4
5
6
7
8
9
Map map = {'name': 'lin', 'age': '21' };
Map map = new Map()
..[1] = 'one'
..[2] = 'two'
..[3] = 'three';
// 删除某个键的数据
map.remove('name');
// 清空键值对
map.clear();

动态类型与 Object

在 Dart 语言中,一切皆对象,而这些对象的父类都是 Object。

在实际的开发中,还是要尽量为变量确定一个类型,这样才能提高程序的安全性,也能加快程序的运行速度。

进行赋值操作的时候,最好使用 isas 进行判断。

1
2
3
4
5
6
7
dynamic map = {"name": "John", "age": "25"};
if (map is Map<String, String>) {
print("Map...");
}

var map2 = map as Map<String, String>;
print(map2);

is 是判断类型的时候使用的;as 是转换类型的时候使用的。is!is 的功能正好相反。

符号字符

Dart 提供的 UTF-32 编码的字符串,可以通过这些编码直接转换成表情包与特定的文字。

1
2
Runes input = new Runes('\u2665  \u{1f605}  \u{1f60e}');
print(new String.fromCharCodes(input)); // ♥ 😅 😎

符号

符号用来表示程序中声明的名称,使用 # 作为开头,后面跟着一个或多个用点分隔的符号或运算符。

在实际的项目中,基本用不到这个内置类型。

运算符

三目运算符

三目运算符通常与状态管理结合使用,用于判断 Flutter 组件的状态

1
2
3
4
var number = null;
int number2 = 5;
var result = number ?? number2;
print(result);

Dart语言也支持常规的三目运算符:

1
2
3
4
int a = 200;
var b = a > 10 ? 1 : 2;
print(b);
}

取商运算符

~/ 是 Dart 语言中的取商运算符,返回一个整数。

1
2
3
int c = 20;
print(c ~/ 11); // 1
print(c % 11); // 9

自定义类操作符

Dart 支持运算符重载,可以自定义运算符操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Point {
double x, y;
Point(this.x, this.y);
// 自定义 + 操作符
operator +(Point p) => new Point(x + p.x, y + p.y);

@override
String toString() => '($x, $y)';
}

void main() {
Point p1 = new Point(2, 2);
Point p2 = new Point(3, 3);
print(p1 + p2);
}

级联操作符

级联操作符非常类似于程序的链式调用。

1
2
3
4
5
String fruits = (new StringBuffer()
..write('apples')
..write('oranges'))
toString();
print(fruits);

get 和 set 方法

在 Dart 语言类的 setget 方法中,不要调用自身类的方法,因为 Dart 语言有层级树的概念,递归调用会导致 Stack Overflow(堆栈溢出)异常。如果属性是公开的,那么,可以直接通过 类.属性 访问,或者通过 类.属性=某值 设置值,这样的调用方法其实就是默认调用了 getset 方法。

如果用 _ 将属性设为私有,例如上面的 xy,代码中设置为 var _x, _y,那么默认的 getset 方法就失效了。

1
2
3
4
5
6
class Point {
double _x, y;
Point(this._x, this.y);

get x => _x;
// ...

异常捕获

在 Dart 语言中,可以抛出异常或者捕获异常,如果没有捕获异常,就会和其他编程语言一样,程序会终止。与 Java 相比,所有 Dart 异常都是没有被终止的,可以继续传递。

throw

在 Dart 语言中,可以抛出任何类型的异常,并不要求被抛出的对象是某个特殊异常类的实例或子类。在 Dart 语言中,throw 是一个表达式,并不像在 Java 中那样是一个语句。

1
throw Exception('error');

try-catch

try 语句由多个部分组成。首先是一条可能抛出异常的语句,然后是一个或者多个 catch 子句,以及一个 finally 子句,当然也可以省略这两种子句中的某一种。

finally 子句用于定义异常捕获处理结束后需要做什么,不论异常是否发生,finally 子句都会运行。

rethrow:捕获的异常经检查之后,如果我们发现本地不需要处理异常,并且异常应该在调用链中向上传播,那么可以使用 rethrow,它在 catch 子句中将捕获的异常重新抛出。(基本用不到)

循环语句

Dart 支持 3 种形式的循环:

  • for
  • while
  • do-while

for

在 Dart 语言中,同时支持两种传统的 for 循环,分别为 for 循环和 for-in 循环。建议在开发中尽可能使用 for-in 循环。

1
for(int i in [1,2,3,4]) print(i);

传统 for 循环:

1
for(int i = 1;i <= 100;i++) print(i);

while

1
2
3
4
5
6
int i = 1;
while(i <= 100)
{
print(i);
i++;
}

do-while

1
2
3
4
5
6
int i = 1;
do
{
print(i);
i++;
} while(i <= 100);

switch 语句

1
2
3
4
5
6
7
8
9
10
11
var name = 'Bob';
switch (name) {
case 'Bob':
print('Hello Bob');
break;
case 'Alice':
print('Hello Alice');
break;
default:
print('Hello who is that?');
}

函数

在 Dart 语言中,一切皆对象,所以函数也是对象,并且函数的对象类型为 Function。Dart 的函数作为一等公民,可以作为参数传递、保存在变量中,也能作为参数和函数的返回值。

可选参数

在 Dart 语言中,通过 {} 声明可选参数:

1
2
3
void setUser({var name, var age}) {
print('name: ${name ?? "name"}, age: ${age ?? "age"}');
}

使用这个函数的时候,可以不传入参数,也可以只传入 name 或者 age,或者两者都传入。

必选参数

必选参数有两种定义方法,一种就是直接定义:

1
2
3
void setUser(String name,int age) {
// ...
}

还可以在引入 package:meta/meta.dart 时使用 @required

可选位置参数

在函数的定义中,我们还可以使用 [] 定义可选位置参数:

1
2
3
4
void printSomething(int a, int b, [int c = 99]) {
print(a + b + c);
}

默认参数

1
2
3
void setUser({String name: "zhao"}) {
print(name);
}

函数作为参数传递

Dart 的函数可以作为参数传递。

1
2
List list = [1, 2, 3];
list.forEach(print);

也可以传入匿名函数:

1
list.forEach((element) { print(element); });

函数作为变量

1
2
3
List list = [1, 2, 3];
var sum = (List elem) => elem.reduce((a, b) => a + b);
print(sum(list));

级联

函数也是对象,所以它也可以使用级联操作。

级联操作返回的还是对象,并不是返回值:

1
2
print("Hello".length.toString()); // 5
print("Hello"..length.toString()); // Hello

异步编程

大多数 Flutter 项目是并发的。Dart 与 JavaScript 语言有一个共同点,它们都是单线程的。Dart 语言与 JavaScript 语言有一个共同点,它们都是单线程的,如果代码中直接出现同步代码,就会阻塞线程。

Future

Future 表示未来或者将来,也就是说,它代表将来运算结果的对象,结果可能在未来某个时刻知道(可以理解为先占一个位置,后面等到结果就填进去)。

Future 本身也是一个泛型对象,程序中大多不会单独使用 Future,而是使用 Future<T>,运算返回的结果对象就是 T。如果返回结果不可用,或者没有返回结果,Future 类型就会是 Future<void>

在 Dart 语言中,Futuredart:async 库提供,如果返回 Future 函数,将会发生以下两件事情。

  1. 这个函数加入待完成的队列并且返回一个未完成的 Future 对象。
  2. 当这个操作结束时,Future 会返回一个值或者返回错误。

当然,单独使用 Future 的情况非常少,往往需要搭配 then() 方法使用。then() 方法接收一个 onValue 闭包作为参数,该闭包在 Future 成功完成时被调用。

async 和 await

Future 开始工作后,有成功处理、错误处理,以及后续的任务处理,任务相当繁重。为了减轻使用异步操作的工作量,Dart 语言为异步函数提供了 async 关键字。函数体可以使用 async 进行操作。注意,await 必须被包裹在 async 里面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int frac(int n) {
if (n < 2) return 1;

return frac(n - 1) + frac(n - 2);
}

void tasks(n) async {
try {
int a = await frac(n);
print(" -->: ${a}");
} catch (e) {
print(e);
}
}

抽象方法和抽象类

在 Dart 语言中,如果简单地声明一个方法而不提供它的实现,这种方法被称为抽象方法。一个抽象方法本身属于一个抽象类,抽象类与 Java 语言一样都是通过 abstract 关键字进行声明的。

1
2
3
4
5
6
7
8
9
10
11
12
13
abstract class Point {
void add();
}

class Point2D extends Point {
double x, y;
Point2D(this.x, this.y);

@override
void add() {
print(x + y);
}
}

在 Dart 语言中,抽象类同样不能被实例化,因为它缺失部分实现。对抽象类进行实例化,会产 abstractClassInstantiationError 错误,Dart 解析器也会发出警告。

接口

在 Dart 语言中,每个类都隐含地定义了一个接口,此接口描述了类的实例具有哪些方法。不过,Dart 语言虽然有接口,但没有接口声明。Dart 语言的设计者在设计之初就觉得这是不必要的,因为我们始终可以定义一个抽象类来描述所需的接口,使用过 Java 的开发人员应该很清楚这一点。

换句话说,Dart 语言并不关心对象是如何实现的,而只在意它支持哪些接口(面向接口)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
abstract class Point {
get x;
get y;

void add();
}

class XYPoint implements Point {
var x, y;

@override
void add() {
print('add');
}
}

void main() {
XYPoint p = new XYPoint();
p.add();
}

继承

Dart 语言和 Java 语言一样都是支持继承操作的。不过,在 Flutter 中的继承只能是单继承。

当继承一个类之后,子类不仅可以通过 @override 关键字来重写父类的方法,还可以使用 super 来调用超类中的方法。不过,需要注意的一点是构造方法不能被继承。另外,Dart 语言中也没有公有与私有的访问修饰符,所以子类可以访问超类中的所有方法与属性。

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
27
28
29
30
abstract class Point {
get x;
get y;

void add();
}

class XYPoint implements Point {
var x, y;

@override
void add() {
print('add');
}
}

class Point3D extends XYPoint {
var z;

@override
void add() {
super.add();
print('Point3D add');
}
}

void main() {
Point p = new Point3D();
p.add();
}

mixin

mixin 的出现是为了解决多继承问题。假如有一个 Widget 包含很多个子 Widget,那么它肯定会继承 Collection 集合,同时它是一个组件,所以它肯定也会继承 Widget。这样就会导致同一个类继承多个父类。前面已经说了,Dart 不能多继承。

mixin 是一个可以把自己的方法提供给其他类,而不用成为其父类的类。它以非继承的方式来复用类中的方法。在 Dart 语言中使用 mixin 时需要用到关键字 with

如果你继承某个抽象类,那么你就必须重写其方法,而对于通过 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
27
28
29
30
31
32
33
34
35
36
abstract class Animal {
void printAnminalName() {
print("Animal");
}
}

abstract class Food {
void printFoodName() {
print("Food");
}
}

abstract class Fruits {
void printFruitsName();
}

class Apple extends Fruits {
@override
void printFruitsName() {
print("Apple");
}
}

class Dog extends Fruits with Animal, Food {
@override
void printFruitsName() {
print("Dog");
}
}

void main() {
Dog()
..printFruitsName()
..printFoodName()
..printAnminalName();
}

泛型

1
2
3
4
class Animal{}
class Tiger extends Animal{}
class Lion extends Animal{}
class MyAnimal<T extends Animal>{}

导入库出现命名冲突可以进行重新命名:

1
import 'package:http/http.dart' as htp;

如果需要显示 / 隐藏某些成员,可以使用 show / hide

1
2
import 'package:http/http.dart' show http;
import 'package:http/http.dart' hide http;