Dart¶
学习资料¶
Dart 在有 C / C++ / Python 基础时非常容易入手,最好的方式是自己阅读官方文档。
- 结合之前已经学习掌握的 C / C++ / Python,阅读 Dart | Introduction to Dart 快速上手熟悉 Dart,了解 Dart 和 C / C++ / Python 的异同。
- 如果你熟悉 JavaScript,可以阅读 Dart | Learning Dart as a JavaScript developer。
- 如果你熟悉 Swift,可以阅读 Dart | Learning Dart as a Swift developer。
- 通过 Dart | Codelabs 中的一些案例熟悉 Dart。
- 阅读 Dart | A tour of the core libraries 了解 Dart 的核心库。
- 阅读 Dart | Asynchronous programming: futures, async, await 熟悉 Dart 的异步编程方式。
你可以使用 Dart 的在线编辑器 尝试 Dart 的语法,你也可以在安装好 Flutter 之后,在本地使用下面的命令来创建项目并编译运行:
| Bash | |
|---|---|
1 2 3 4 | |
以下内容主要提示一些 Dart 比较关键的语法。
类型¶
内建类型¶
- Numbers (
int,double) - Strings (
String) - Booleans (
bool) - Lists (
List,) - Sets (
Set) - Maps (
Map) null
声明变量和常量¶
声明变量有以下几种方式:
- 类型在前(推荐):
int n = 10;String text = "Hello, world!"; - 使用类型推断:
var n = 10;var text = "Hello, world!"; - 如果不希望在声明变量时立即赋值,也可以将变量声明为「可空」类型:
Dart 1 2 3 4 5 6
void main(List<String> arguments) { int? a; // a 的类型为「可空的整形」,默认赋值为 null print(a); // null a = 16; print(a); // 16 } - 在类中声明变量不能立即初始化,需要使用
late关键字:Dart 1 2 3 4
class A { late int a; A(this.a); }
常量声明时有两种情况:
- 某个量需要在编译器被确定且之后不会改变:使用
const关键字。eg.const double pi = 3.14; - 某个量只会在运行时被赋值一次:使用
final关键字。Dart 1 2 3 4 5 6 7 8
void main(List<String> arguments) { final List<String> listA = List.generate(8, (i) => "$i"); print(listA); // [0, 1, 2, 3, 4, 5, 6, 7] final List<String> listB; listB = List.generate(8, (i) => "$i"); print(listB); // [0, 1, 2, 3, 4, 5, 6, 7] listB = []; // error: The final variable 'listB' can only be set once. Try making 'listB' non-final. }
范型¶
以 List 为例,列表中的数据类型不一定是特定一种,因此使用范型 List<T> 来表示。当需要具体某一个类型的数组时,需要指明数据类型,如 List<int> 表示整型数组、List<String> 表示字符串数组。
函数类型¶
在 Dart 中,函数也是一种类型,如:
| Dart | |
|---|---|
1 2 3 4 5 6 7 8 | |
函数¶
参数传递¶
一种传参方式是与 C 一致的,在调用时不需要添加参数名称:
| Dart | |
|---|---|
1 2 3 4 5 6 7 | |
另一种方式调用更明确,需要写出参数名称:
| Dart | |
|---|---|
1 2 3 4 5 6 7 | |
两者也可以混用:
| Dart | |
|---|---|
1 2 3 4 5 6 7 | |
匿名函数¶
因为函数被理解为一种类型,你可将匿名函数理解为函数这个类型的值:
| Dart | |
|---|---|
1 2 3 4 5 6 7 8 9 10 | |
可以看到,这里 calculate() 的第三个参数输入的是一个函数,这个函数接收两个 int 参数,返回值为 int。
在下方,通过 () { ... } 和 () => ... 分别创建了两个匿名函数传入 calculate() 的第三个参数。
异步与错误处理¶
同步¶
要知道在 Dart 中如何用异步,首先要明白异步是什么;要明白异步是什么,首先要明白同步是什么。从代码执行的逻辑上来说,程序、函数的执行都是同步执行的
| Dart | |
|---|---|
1 2 3 4 5 | |
比如上面的例子中,funcA() 执行结束 funcB() 开始执行,funcB() 执行结束 funcC() 开始执行,funcC() 执行结束 main() 返回 void。
这个过程我们也可以理解为 funcA() 返回 void 之后,funcB() 才开始执行……
更好的同步的例子是:
| Dart | |
|---|---|
1 2 3 4 5 | |
b 的值必须等待 a,print 必须等待 b。
异步¶
现在开始对于异步的讲解,如果一个函数执行的时间特别长,比如应用中需要通过网络获取一张图片,这个过程可能花费 500ms 甚至更长的时间。我们希望在这个过程中持续显示一个进度条:
| Dart | |
|---|---|
1 2 3 4 5 | |
如果这个逻辑为上面的方式,那么你会发现,只有等待 fetchImage() 的结果返回,a 才会有值,在这个等待的过程中,没有办法执行其他的代码。比如用户界面的旋转指示也会被阻塞停住,一些后台处理的算法也会被阻塞。
要将这个代码改为异步,逻辑是:在用到结果的时候再等待:
| Dart | |
|---|---|
1 2 3 4 5 6 | |
- 有一点需要注意,
fetchImage()在定义的时候需要加上async关键字,main()也是。这表示这些函数都是异步执行的函数。 - 这里的
fetchImage()已经变成了一个异步函数,被调用时其会立即返回,返回一个未完成的Future。返回之后,这个函数会开始在后台发出网络请求开始获取图片。 - 因为
a的值已经被赋了一个未完成的Future,所以代码继续同步执行。 - 直到执行到需要展示图片的代码,这里必须要等待
a里面是一张拿到的图才能显示,所以要用await确保图片已经下载完成。也就是说,await确保a已经是一个已完成的Future,然后将其中的Image取出送入到displayImage()。 - 可以看到,这里要显示图片,所以必须要等待
fetchImage()的执行结束,但是其他的代码不需要,那就可以用异步的逻辑,使得实际的程序执行顺序和代码顺序不一致,从而提高程序运行的效率。也就是说,从调用一个async函数开始,到第一个await出现要获取这个async函数的返回值,这中间的函数都可以无需等待直接执行。
错误处理¶
使用 try-catch 处理错误即可:
| Dart | |
|---|---|
1 2 3 4 5 | |