编码 · 编程语言
Dart 入门教程
简短回答
Dart 是 Google 推出的静态类型、面向对象语言,因 Flutter 应用框架而广为人知。它具备健全的空安全,可编译为原生代码以及 JavaScript 或 WebAssembly,开发时支持快速热重载。本页依次讲解环境搭建、类型、空安全、流程控制、函数、集合、类、异步代码和错误处理。
Dart 是什么,用来做什么
Dart 是 Google 开发的、面向客户端优化的编程语言。它是带类型推断的静态类型语言,拥有健全的空安全,既能编译为原生机器码(用于移动端、桌面和服务器),也能编译为 JavaScript 或 WebAssembly(用于 Web)。开发时它运行在带热重载的虚拟机上,修改后几秒内就能在运行中的应用里看到效果。
大多数人学 Dart 是为了用 Flutter 开发应用。Flutter 是 Google 的 UI 工具包,一套代码即可构建 Android、iOS、Web、Windows、macOS 和 Linux 应用。Dart 也可用于命令行工具和后端服务。
安装 Dart 并运行 Hello, world
从 dart.dev 安装 Dart SDK(或安装自带 Dart 的 Flutter)。每个 Dart 程序都从顶层的 main 函数开始执行。
// hello.dart
void main() {
print('Hello, world!');
}dart run hello.dart
# Hello, world!
dart compile exe hello.dart # builds a native executable
dart create my_app # creates a new console project本页的所有示例都可以在 DartPad 中试运行,它是 dartpad.dev 上基于浏览器的编辑器。
变量与基本类型
用 var 让编译器推断类型,用 final 声明只赋值一次的变量,用 const 声明编译期常量。你也可以始终显式写出类型。
void main() {
var name = 'Dart'; // inferred as String
int year = 2011; // explicit type
final now = DateTime.now(); // set once, at run time
const pi = 3.14159; // compile-time constant
double ratio = 2; // int literal becomes 2.0
bool ok = year > 2000;
print('$name since $year, pi=${pi.toStringAsFixed(2)}, ok=$ok, ratio=$ratio');
// Dart since 2011, pi=3.14, ok=true, ratio=2.0
print(now.year > 2000); // true
}| 类型 | 示例 | 说明 |
|---|---|---|
int | 42 | 整数;在原生平台上为 64 位 |
double | 3.14 | 64 位浮点数 |
num | 7 或 7.5 | int 和 double 的父类型 |
bool | true | 条件中只允许 true 和 false |
String | 'hi' | UTF-16;支持 $x 和 ${expr} 插值 |
dynamic | 任意类型 | 关闭静态检查;大多数代码中应避免使用 |
在 Web 上,int 值用 JavaScript 数字表示,因此非常大的整数可能丢失精度。
健全空安全
从 Dart 3 起,所有 Dart 代码都使用健全空安全。String 类型的变量永远不会为 null;若要允许为空,写成 String?。编译器会检查每一处使用,因此空值错误在程序运行之前就能发现。
int lengthOf(String? s) {
// return s.length; // compile error: s might be null
if (s == null) return 0;
return s.length; // promoted to String after the check
}
void main() {
String? nickname;
print(nickname?.length); // null (null-aware access)
print(nickname ?? 'guest'); // guest (default if null)
nickname ??= 'dee'; // assign only if null
print(lengthOf(nickname)); // 3
late String config; // initialized later, checked at run time
config = 'loaded';
print(config); // loaded
}流程控制
Dart 有常见的 if、for、while 和 do-while,以及可遍历任意可迭代对象的 for-in 循环。从 Dart 3 起,switch 支持模式匹配,并且可以作为表达式使用。
void main() {
var scores = [72, 95, 48];
for (var s in scores) {
if (s >= 90) {
print('$s excellent');
} else if (s >= 50) {
print('$s pass');
} else {
print('$s fail');
}
}
for (var i = 0; i < 3; i++) {
print('i = $i');
}
var day = 6;
var kind = switch (day) {
6 || 7 => 'weekend',
>= 1 && <= 5 => 'weekday',
_ => 'invalid',
};
print(kind); // weekend
}函数
函数是一等对象。参数可以是位置参数、放在 [] 中的可选位置参数,或放在 {} 中的命名参数。命名参数默认可选,除非标记为 required。箭头语法 => 是只返回一个表达式的函数的简写。
int add(int a, int b) => a + b;
String greet(String name, {String greeting = 'Hello', required bool loud}) {
var text = '$greeting, $name';
return loud ? text.toUpperCase() : text;
}
String label(String text, [int? width]) =>
width == null ? text : text.padRight(width, '.');
void main() {
print(add(2, 3)); // 5
print(greet('Ana', loud: false)); // Hello, Ana
print(greet('Ana', greeting: 'Hi', loud: true)); // HI, ANA
print(label('id', 5)); // id...
var twice = (int x) => x * 2; // anonymous function
print([1, 2, 3].map(twice).toList()); // [2, 4, 6]
}集合:List、Set、Map 与记录
核心集合有 List(有序)、Set(元素唯一)和 Map(键值对)。集合字面量支持 if、for 和展开运算符 ...。Dart 3 新增的记录(Record)无需声明类即可把多个值组合在一起。
void main() {
var nums = [3, 1, 2]; // List<int>
nums.add(4);
nums.sort();
print(nums); // [1, 2, 3, 4]
var tags = {'dart', 'flutter', 'dart'};
print(tags.length); // 2
var ages = {'Ann': 31, 'Bo': 25}; // Map<String, int>
ages['Cy'] = 40;
print(ages['Bo']); // 25
var showAll = false;
var menu = ['Home', if (showAll) 'Admin', for (var n in nums) 'Item $n'];
print(menu); // [Home, Item 1, Item 2, Item 3, Item 4]
(String, int) pair = ('Ann', 31); // a record
var (who, age) = pair; // destructuring
print('$who is $age'); // Ann is 31
}类与 async/await
Dart 是面向对象的:每个值都是对象。类支持带初始化形参(this.x)的构造函数、命名构造函数、getter、mixin 和接口。异步代码使用 Future 和 Stream 配合 async 与 await,使 Flutter 应用在等待文件或网络时保持响应。
class Point {
final double x, y;
const Point(this.x, this.y);
Point.origin() : x = 0, y = 0; // named constructor
double get sum => x + y; // getter
@override
String toString() => 'Point($x, $y)';
}
Future<String> fetchUser() async {
await Future.delayed(Duration(milliseconds: 100));
return 'ana';
}
Future<void> main() async {
print(Point(1, 2).sum); // 3.0
print(Point.origin()); // Point(0.0, 0.0)
var user = await fetchUser();
print('user: $user'); // user: ana
}错误处理
用 throw 抛出异常,用 try、on、catch 和 finally 处理。用 on SomeType 捕获特定类型。Dart 区分 Exception(程序可以处理的预期问题)和 Error(程序缺陷,例如断言失败或越界错误),后者通常不应捕获。
int parseAge(String s) {
var n = int.tryParse(s); // null instead of throwing
if (n == null || n < 0) {
throw FormatException('Not a valid age: $s');
}
return n;
}
void main() {
for (var input in ['42', 'abc']) {
try {
print(parseAge(input));
} on FormatException catch (e) {
print('Error: ${e.message}');
} finally {
print('checked $input');
}
}
}
// 42 / checked 42 / Error: Not a valid age: abc / checked abc一个完整的小程序:购物车合计
这个程序用类表示商品,用映射保存购物车,并打印小票。它综合运用了类、映射、字符串格式化和集合方法。
class Product {
final String name;
final double price;
const Product(this.name, this.price);
}
void main() {
const apple = Product('Apple', 0.5);
const bread = Product('Bread', 2.25);
final cart = <Product, int>{apple: 6, bread: 2};
var total = 0.0;
cart.forEach((product, qty) {
final line = product.price * qty;
total += line;
print('${product.name.padRight(6)} x$qty ${line.toStringAsFixed(2)}');
});
final count = cart.values.reduce((a, b) => a + b);
print('$count items, total ${total.toStringAsFixed(2)}');
}
// Apple x6 3.00
// Bread x2 4.50
// 8 items, total 7.50接下来学什么
- dart.dev 上的 Dart 语言导览和教程。
- Effective Dart:官方的风格与设计指南。
- 如果想开发应用,请阅读 flutter.dev 上的 Flutter 文档。
- pub.dev:Dart 和 Flutter 的包仓库。
常见问题
Dart 只能用于 Flutter 吗?
不是。Dart 是通用语言,可以开发命令行工具、服务器和 Web 应用。Flutter 只是学习它最热门的理由。
Dart 是编译型还是解释型?
两种模式都有。开发时 Dart 在虚拟机上以即时编译方式运行并支持热重载;发布时则提前编译为原生机器码,Web 端则编译为 JavaScript 或 WebAssembly。
Dart 中的 final 和 const 有什么区别?
final 变量只赋值一次,可以在运行时赋值。const 值必须在编译期已知,并且是深度不可变的,因此 Flutter 可以复用常量 widget。
Dart 和 Java 或 JavaScript 相似吗?
它的 C 风格语法和类对 Java、C# 和 JavaScript 开发者来说很熟悉。与 JavaScript 不同的是,Dart 是静态类型语言,并具备健全的空安全。
Dart 中的 ?? 运算符有什么作用?
a ?? b 在 a 不为 null 时返回 a,否则返回 b。与之相关的 a ??= b 仅在 a 为 null 时才把 b 赋给 a。