本工具使用的开源库

本工具代码中捆绑了 1 个开源库。

Dart 速查 — 简明参考

Dart 3 语法、类型系统、异步与最常用标准库速查手册,覆盖约 80% 日常场景。

D

Dart Dart 3

Dart SDK · OO · 泛型 · 函数式 · 异步优先 · 静态(健全空安全)

学习路径

先学会 dart create/run 与 pubspec 包结构 → 掌握变量、类型与流程控制 → 深入函数、字符串与集合 → 理解类、mixin 与错误处理 → 用 isolate 与 async/await 写并发 → 再按需学网络、时间、进程、正则与构建测试。FAQ 节适合回头避坑。

1.Hello World 与运行环境

最小程序、dart create/run 与项目结构。

最小程序

每个 Dart 程序从 void main() 入口函数开始。dart run 会执行它,print 输出到标准输出。

1
2
3
4
5
6
7
8
9
void main() {
print('Hello, world!');
}
// 运行方式:
// dart run hello.dart
// 或先编译再执行:
// dart compile exe hello.dart -o hello
// main 必须叫 main 才会被当作入口
// 返回类型 void 表示不返回值

创建与运行

dart create 生成标准项目骨架,dart run 直接运行入口文件。开发期 JIT 热重载,发布时 AOT 编译。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 创建控制台项目:
// dart create -t console myapp
// 创建 Flutter 项目:
// flutter create myapp
// 进入项目目录:
// cd myapp
// 运行入口文件:
// dart run
// 指定文件运行:
// dart run bin/main.dart
// 编译为可执行文件:
// dart compile exe bin/main.dart
// 静态分析:
// dart analyze

项目结构

pubspec.yaml 声明包名、依赖与 SDK 版本。代码放在 lib/ 与 bin/,测试放在 test/。

1
2
3
4
5
6
7
8
9
10
11
12
// pubspec.yaml 示例:
// name: myapp
// environment:
// sdk: '>=3.0.0 <4.0.0'
// dependencies:
// http: ^1.1.0
// 目录约定:
// lib/main.dart 库代码
// bin/main.dart 可执行入口
// test/ 单元测试
// 安装依赖:
// dart pub get

打印输出

print 输出到 stdout 并追加换行。$ 插值把变量嵌入字符串,${表达式} 用于复杂形式。

1
2
3
4
5
6
7
8
9
10
void main() {
print('Hello, Dart!');
final name = 'Rex';
print('Hi, $name'); // 变量插值
final age = 5;
print('Age is ${age + 1}'); // 表达式插值
print('Pi = ${3.14159.toStringAsFixed(2)}');
}
// stdout.write 不换行
// stderr.writeln 输出到错误流

命令行参数

main 可接收 List<String> args 参数。args[0] 起是命令行传入的实参,不含程序名。

1
2
3
4
5
6
7
8
9
10
11
void main(List<String> args) {
print('参数个数: ${args.length}');
for (final arg in args) {
print('参数: $arg');
}
}
// 运行:
// dart run hello.dart a b c
// args == ['a', 'b', 'c']
// 程序名本身不在 args 里
// 需要程序路径用 Platform.script

异步主函数

main 可以是 Future<void>,程序会等待所有 await 完成后再退出。异步操作常用于耗时任务。

1
2
3
4
5
6
7
8
9
Future<void> main() async {
print('开始');
await Future.delayed(Duration(seconds: 1));
print('一秒后执行');
}
// Future.delayed 模拟耗时任务
// await 会阻塞到 Future 完成
// 返回 Future<void> 让运行时等待
// 不等待则程序可能提前退出

注释

// 单行注释、/// 文档注释(生成 API 文档)、/* */ 块注释。注释不影响运行。

1
2
3
4
5
6
7
8
9
// 单行注释
/// 文档注释:供 dart doc 生成 API 文档
/* 块注释:单行闭合即可 */
void main() {
// 行尾注释
print('OK'); // 解释这行做了什么
}
// 文档注释放在声明前
// 用 dart doc 生成 HTML 文档

顶层成员

Dart 允许顶层函数与变量(不在类里)。import 导入库,顶层变量在文件间共享。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import 'dart:math';
// 顶层变量:
final appName = 'demo';
// 顶层函数:
int square(int x) => x * x;
void main() {
print(appName);
print(square(4));
print(max(1, 9));
}
// 顶层成员整个库可见
// 变量建议 final 避免全局可变
// 私有成员以下划线开头:_helper
// import 放在文件最顶部

2.变量与常量

变量声明、final/const、空安全与解构。

var 与 final

var 声明可重新赋值但类型推断固定。final 声明只能赋值一次,赋值后不可改。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
var counter = 0; // 推断为 int
counter = 1; // 可重新赋值
final name = 'Rex'; // 只能赋值一次
// name = 'New'; // 错误:final 不可改
final now = DateTime.now(); // 运行时确定
print('$counter $name $now');
}
// var 一旦推断类型就固定
// 类型不同会报错:counter = 'x'
// final 只约束赋值次数
// 对象内部仍可修改

const 常量

const 是编译期常量,必须在编译时能确定值。const 值会规范化,同一常量只存一份。

1
2
3
4
5
6
7
8
9
10
11
void main() {
const pi = 3.14159;
const greeting = 'Hello';
const list = [1, 2, 3]; // 编译期列表
const double factor = 0.5;
print('$pi $greeting $list $factor');
}
// const 的值必须在编译时已知
// DateTime.now() 不能用于 const
// const 构造创建编译期常量
// 优先用 const 提升性能

显式类型

可显式标注类型声明变量。静态类型帮助编译期检查,公共 API 建议显式标注。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
int count = 42;
double ratio = 3.14;
String name = 'Dart';
bool enabled = true;
List<int> nums = <int>[1, 2, 3];
print('$count $ratio $name $enabled $nums');
}
// 类型写在变量名前面
// 显式类型让意图更清楚
// 局部变量可用 var 让推断
// 参数与返回值建议写类型

可空变量

健全空安全下默认不可为 null。类型后加 ? 声明可空,使用前判空或用 ?? 给默认值。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
String? maybe = null; // 可空
String name = maybe ?? '匿名'; // 空合并默认值
if (maybe != null) {
print('有值: $maybe');
}
print(name);
}
// String? 可以赋 null
// String 不能赋 null(编译错误)
// ?? 在左侧为 null 时用右侧
// 判空后 Dart 会做类型提升

late 延迟初始化

late 延迟到首次访问时才初始化。late final 只能赋值一次,适合依赖运行时环境的字段。

1
2
3
4
5
6
7
8
9
10
11
12
class Config {
late final String apiKey = loadKey();
}
String loadKey() => 'abc-123';
void main() {
final config = Config();
print(config.apiKey); // 首次访问才加载
}
// late 声明时不用立即赋值
// 首次使用前必须赋值否则报错
// late final 保证只初始化一次
// 常用于单例、懒加载配置

解构赋值

Dart 3 支持记录与模式解构,把集合元素一次性赋给多个变量。解构必须匹配形状。

1
2
3
4
5
6
7
8
9
10
11
void main() {
final (x, y) = (10, 20);
final (name: n, age: a) = (name: 'Rex', age: 5);
final [first, second, ...] = [1, 2, 3];
print('$x $y $n $a $first $second');
}
// 多余元素用 _ 忽略:
// final [_, second2] = [1, 2];
// 无 rest 时列表模式长度必须匹配
// 也适用于 map 键值
// for (final MapEntry(k: k, v: v) in entries)

作用域与遮蔽

变量在声明它的块内可见,内层可以遮蔽外层同名变量。顶层变量整个库可见。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
final top = '全局';
void main() {
final inner = '函数内';
if (true) {
final block = '块内';
print(block);
print(inner); // 可见外层
print(top); // 可见全局
}
// print(block); // 错误:超出作用域
}
// 块作用域与大括号一致
// 内层可遮蔽外层同名变量
// 同作用域重复声明报错
// 建议避免深层遮蔽影响可读性

dynamic 与 Object

dynamic 跳过静态检查,类型在运行时决定。Object 要求静态类型检查,需显式转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main() {
dynamic d = 42;
d = '变成字符串'; // 运行时任意类型
Object obj = 42;
// obj.length; // 错误:Object 无此成员
if (obj is String) {
print(obj.length); // 类型提升后可访问
}
print(d);
}
// dynamic 不做静态检查,慎用
// Object 是所有类的父类型
// is 判断后触发类型提升
// 优先用明确类型或泛型

3.数据类型

内置类型、集合类型、记录与枚举。

数值类型

int 是整数,double 是浮点数,num 是二者的父类型。整数字面量可赋给 double。

1
2
3
4
5
6
7
8
9
10
11
void main() {
int i = 42;
double d = 3.14;
num n = 42; // num 可容纳整数
double whole = 42; // int 可直接赋给 double
print('$i $d $n $whole');
}
// int 在原生平台是 64 位
// double 是 64 位 IEEE 754
// 数值运算结果类型看操作数
// 类型判断用 is:if (n is int)

字符串类型

String 是不可变 UTF-16 序列。单引号双引号等价,支持插值与多行文本。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
String a = '单引号';
String b = "双引号"; // 等价
String multi = '''第一行
第二行''';
print('$a $b');
print(multi);
}
// 字符串不可修改
// 修改操作都返回新串
// 一种引号内可直接包含另一种
// 需要同种引号时用反斜杠转义

布尔类型

bool 只有 true 和 false。条件表达式必须返回 bool,不会隐式转换数字。

1
2
3
4
5
6
7
8
9
10
11
void main() {
bool ok = true;
bool done = false;
if (ok) print('成功');
final result = ok && !done;
print(result);
}
// && 逻辑与 || 逻辑或
// ! 逻辑非
// 三元:ok ? '是' : '否'
// Dart 没有 truthy/falsy 概念

列表类型

List 是有序集合,支持泛型。字面量 [a, b] 创建列表,默认可增长。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
List<int> nums = [1, 2, 3];
nums.add(4);
nums.remove(2);
final first = nums.first;
final len = nums.length;
print('$first $len $nums');
}
// List<String> 泛型限定元素类型
// 索引访问 nums[0],越界抛错
// isEmpty / isNotEmpty 判空
// first / last 取首尾元素

集合与映射

Set 是无序不重复集合,Map 是键值对映射。二者都有泛型版本与字面量语法。

1
2
3
4
5
6
7
8
9
10
11
12
13
void main() {
Set<String> tags = {'a', 'b', 'a'}; // 自动去重
tags.add('c');
Map<String, int> ages = {'alice': 30};
ages['bob'] = 25;
print(tags); // {a, b, c}
print(ages['bob']); // 25
print(ages['nobody']); // null
}
// Set 判断成员用 contains
// Map 访问不存在的键返回 null
// Map 遍历顺序为插入顺序
// Set 适合去重与成员判断

记录类型

Dart 3 的 record 是无名字的轻量聚合类型。可带字段名,可配合模式解构。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
var point = (x: 1, y: 2); // 命名记录
var pair = ('left', 3); // 位置记录
print(point.x);
print(pair.$1); // 位置取值
final (a, b) = ('first', 2);
print('$a $b');
}
// 记录自动获得 == 与 hashCode
// 记录类型即形状:(int, int)
// 适合返回多个值代替小类
// 记录是不可变的

枚举

enum 定义有限的命名常量集合。增强枚举(Dart 2.17+)可带字段与方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
enum Status { pending, approved, rejected }
enum Color {
red(255), green(0), blue(0);
const Color(this.value);
final int value;
}
void main() {
final s = Status.approved;
print(s.name); // approved
print(Color.red.value);
for (final c in Status.values) {
print(c.name);
}
}
// .name 得到枚举名
// .values 遍历所有成员
// switch 处理枚举默认穷尽
// 增强枚举可附加数据与行为

类型转换

int.parse / double.parse 把字符串转数值,toString 转回字符串。as 做运行时类型转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void main() {
int n = int.parse('42');
double d = double.parse('3.14');
String s = 42.toString();
Object obj = 'hello';
if (obj is String) {
final len = obj.length; // 提升后安全
print(len);
}
print('$n $d $s');
}
// parse 失败会抛 FormatException
// toStringAsFixed(2) 保留两位小数
// as 强转失败会抛 TypeError
// 推荐用 is + 类型提升代替 as

4.引用与空安全

对象引用、null 安全、深拷贝与原生内存。

对象引用

Dart 没有裸指针,变量保存的是对象引用。赋值只复制引用,多个变量可指向同一对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
void main() {
final list1 = [1, 2, 3];
final list2 = list1; // 复制引用
list2.add(4);
print(list1); // [1, 2, 3, 4]
final copy = List.of(list1); // 真正复制
copy.add(5);
print(list1); // 不受影响
}
// 引用类型赋值共享对象
// List.of / [...list] 做浅拷贝
// 基本类型是不可变值类型
// 共享可变对象时要留意副作用

空安全

健全空安全让 null 只能出现在可空类型 String? 中。编译器在编译期保证非空安全。

1
2
3
4
5
6
7
8
9
10
11
12
13
void main() {
String name = 'Rex'; // 非空
String? nickname; // 可空,默认 null
nickname = '小雷';
if (nickname != null) {
print('$name 的昵称 $nickname');
}
print(nickname);
}
// 非空类型赋值 null 编译报错
// 判空后变量自动提升为非空
// ?? 提供默认值避免判空
// ?. 安全访问:nickname?.length

非空断言 !

在可空表达式后加 ! 断言其非空,断言失败抛出空指针异常。只在确实非空时用。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
String? maybe = '有值';
final len = maybe!.length; // 断言非空
print(len);
String? empty;
final safe = empty?.length; // null
print(safe);
}
// ! 绕过空安全检查
// 断言失败抛 Null check error
// 优先用判空与类型提升
// ?. 安全访问返回 null 而非抛错

空合并

?? 在左侧为 null 时取右侧默认值。??= 在变量为 null 时才赋值。

1
2
3
4
5
6
7
8
9
10
11
12
13
void main() {
String? user;
final name = user ?? '匿名'; // 空合并
user ??= '游客'; // 为 null 才赋值
int? count;
count ??= 0;
count++;
print('$name $user $count');
}
// ?? 只关心左侧是否为 null
// ??= 等价于 count = count ?? 0
// 可串联:a ?? b ?? c ?? '默认'
// ?? 也用于集合默认:list ?? []

late 与 const

final 运行时一次性赋值,const 编译期常量。late 延迟初始化适合大对象与循环依赖。

1
2
3
4
5
6
7
8
9
10
11
void main() {
final now = DateTime.now(); // 运行时
const pi = 3.14159; // 编译期
late final big = createBig(); // 首次访问
print('$now $pi ${big.length}');
}
List<int> createBig() => List<int>.generate(100, (i) => i);
// final 可在构造器或运行时赋值
// const 值必须在编译期可确定
// late 延迟到首次访问才执行
// 顶层与静态变量默认懒初始化

引用与相等

== 默认比较引用,identical() 判断是否同一对象。需要结构相等时用工具函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
void main() {
final a = [1, 2];
final b = [1, 2];
print(a == b); // false:不同对象
print(identical(a, b)); // false:不同引用
final c = a;
print(identical(a, c)); // true:同一引用
print(a.length == b.length); // true
}
// 字符串与数字有值语义,== 比较内容
// List/Map 默认按引用比较
// 结构比较用 listEquals(package:collection)
// 自定义类可重写 == 与 hashCode

深浅拷贝

浅拷贝只复制顶层,深层对象仍共享。深拷贝需逐层复制,不可变集合天然安全共享。

1
2
3
4
5
6
7
8
9
10
11
12
13
void main() {
final nested = [[1], [2]];
final shallow = List.of(nested); // 浅拷贝
shallow[0].add(9);
print(nested[0]); // [1, 9]
final deep = nested.map((e) => List.of(e)).toList();
deep[0].add(8);
print(nested[0]); // [1, 9] 不变
}
// 浅拷贝共享嵌套对象
// 深拷贝每层都新建对象
// Map.of / Set.of 同理
// 不可变集合避免共享副作用

原生内存

dart:ffi 提供 Pointer 与 calloc 访问原生内存,用于与 C 库互操作。指针生命周期需开发者管理。

1
2
3
4
5
6
7
8
9
10
11
12
// dart:ffi 提供原生内存访问:
// Pointer<Int32> 表示 C int 指针
// 分配与释放:
// final p = calloc<Int32>(1); // package:ffi
// p.value = 42;
// calloc.free(p);
// 与 C 库互操作:
// final lib = DynamicLibrary.open('libc.so')
// final fn = lib.lookupFunction(...)
// 字符串用 Pointer<Utf8>
// 仅原生平台可用,Web 不可用
// 指针生命周期需开发者管理

5.流程控制

if、循环、switch 与模式匹配。

if / else

if/else if/else 按条件分支执行。条件必须是 bool,可空判断会触发类型提升。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void main() {
int score = 85;
if (score >= 90) {
print('优秀');
} else if (score >= 60) {
print('及格');
} else {
print('不及格');
}
}
// 条件必须返回 bool
// 大括号可省略但建议保留
// 判空后自动类型提升:
// if (name != null) print(name.length)
// 无 else 时条件不满足则跳过

三元表达式

条件 ? 真值 : 假值 是单表达式分支。与 ?? 配合处理可空默认值。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
final n = 7;
final sign = n > 0 ? '正' : '非正';
String? maybe;
final label = maybe ?? '默认'; // 空合并
print(sign);
print(label);
}
// 三元两个分支类型应一致
// 嵌套三元可读性差,少用
// ?? 专门处理 null 默认值
// 复杂分支用 if/else 更清晰

for 循环

for 经典三段式循环,for-in 遍历可迭代对象。Dart 3 支持循环内模式解构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void main() {
for (var i = 0; i < 3; i++) {
print('i=$i');
}
final names = ['Rex', 'Ada'];
for (final name in names) {
print(name);
}
for (final (i, name) in names.indexed) {
print('$i: $name');
}
}
// i++ 等价于 i = i + 1
// for-in 可遍历 Iterable 与 Map
// .indexed 生成 (index, value) 记录
// 修改被遍历的集合要谨慎

while 循环

while 先判断再执行,do-while 至少执行一次再判断。适合未知次数的循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void main() {
var n = 3;
while (n > 0) {
print('n=$n');
n--;
}
var count = 0;
do {
print('至少执行一次');
count++;
} while (count < 1);
}
// while 条件为 false 则一次都不执行
// do-while 至少执行一次
// 条件必须是 bool
// 注意更新循环变量防止死循环

switch 表达式

Dart 3 的 switch 表达式返回一个值。case 后是模式,=> 给出结果,无需 break。

1
2
3
4
5
6
7
8
9
10
11
12
13
void main() {
final status = 404;
final label = switch (status) {
200 => '成功',
404 => '未找到',
_ => '其他',
};
print(label);
}
// 末尾必须穷尽或用 _ 兜底
// 表达式版 case 用 => 而非 :
// 支持模式:switch (value) { int n => ... }
// 比 if 链更简洁,结果可赋值

switch 语句

switch 语句按 case 执行分支。支持空 case 穿透、守卫与模式。每个非空分支要 break。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void main() {
final day = 3;
switch (day) {
case 1:
case 2:
case 3:
print('工作日');
break;
default:
print('周末');
}
switch (day) {
case int n when n > 3:
print('大于 3');
break;
default:
print('其他');
}
}
// 空 case 自动向下穿透
// 非空分支需 break 或 return
// when 添加额外条件守卫
// 模式匹配时比 if 链更强

模式匹配

if-case 与 switch 可对值做结构匹配。对象模式、记录模式与守卫组合使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Circle {
const Circle(this.radius);
final double radius;
}
void main() {
final shape = Circle(5);
if (shape case Circle(radius: final r)) {
print('半径 $r');
}
final value = (name: 'Rex', age: 5);
final msg = switch (value) {
(name: final n, age: 0) => '$n 刚出生',
(name: final n, age: final a) => '$n $a 岁',
_ => '未知',
};
print(msg);
}
// if-case 匹配成功才进入分支
// 对象模式按字段名解构
// 记录模式按位置或名字匹配
// 穷尽 switch 让编译器保证覆盖

break 与 continue

break 退出当前循环,continue 跳到下一轮。标签可控制嵌套循环的跳转目标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void main() {
for (var i = 0; i < 5; i++) {
if (i == 1) continue; // 跳过 i=1
if (i == 4) break; // 退出循环
print('i=$i'); // 0 2 3
}
outer:
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if (j == 1) break outer; // 退出外层
print('$i,$j');
}
}
}
// continue 只跳本轮剩余代码
// break 彻底结束循环
// 标签写在循环前,用标签名引用
// 标签适合深层嵌套跳出

6.函数与 Lambda

函数定义、可选参数、闭包与异步函数。

函数定义

函数由返回类型、名称、参数与函数体构成。无返回值的函数用 void。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int add(int a, int b) {
return a + b;
}
void greet(String name) {
print('你好,$name');
}
void main() {
print(add(1, 2));
greet('Rex');
}
// 返回类型在函数名前面
// 参数必须写类型
// 没有返回值用 void
// 函数是对象,可赋给变量

箭头函数

单表达式函数体可用 => 简写,返回该表达式的值。适合纯函数与回调。

1
2
3
4
5
6
7
8
9
10
11
12
int square(int x) => x * x;
bool isEven(int n) => n % 2 == 0;
void main() {
print(square(4));
print(isEven(3));
final doubled = [1, 2, 3].map((n) => n * 2).toList();
print(doubled);
}
// => 函数体必须是单个表达式
// 与 { return expr; } 等价
// 常用于回调与高阶函数
// void 返回:() => print('x')

可选位置参数

方括号 [] 声明可选位置参数,未传时用默认值或为 null。默认值必须是编译期常量。

1
2
3
4
5
6
7
8
9
10
11
12
String join(String a, [String b = '无', int times = 1]) {
return '$a $b 重复 $times 次';
}
void main() {
print(join('你好'));
print(join('你好', '世界'));
print(join('你好', '世界', 3));
}
// [] 内参数按位置可选
// 默认值在参数后写 =
// 无默认值则类型为可空
// 可空可选参数可用 ?? 给默认

命名参数

花括号 {} 声明命名参数,调用时按名传值。required 强制必须传入。

1
2
3
4
5
6
7
8
9
10
11
12
13
String describe(String name,
{int age = 0, String? city, required bool married}) {
return '$name 年龄 $age 城市 $city 已婚 $married';
}
void main() {
final s = describe('Rex',
age: 5, city: '上海', married: false);
print(s);
}
// 命名参数顺序无关
// 不传时用默认值或为 null
// required 参数必须显式传入
// 长调用建议换行增强可读性

高阶函数

函数是一等公民,可作参数传入或作为返回值。map/where/fold 是集合的常用高阶函数。

1
2
3
4
5
6
7
8
9
10
11
void main() {
final nums = [1, 2, 3, 4];
final doubled = nums.map((n) => n * 2).toList();
final evens = nums.where((n) => n.isEven).toList();
final sum = nums.fold(0, (acc, n) => acc + n);
print('$doubled $evens $sum');
}
// map 逐元素变换
// where 按条件过滤
// fold 累积成单个结果
// 结果是惰性 Iterable,用 toList 固化

闭包

闭包捕获定义环境中的变量。即使外层函数已返回,闭包仍能访问与修改这些变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Function makeCounter() {
var count = 0;
return () => ++count;
}
void main() {
final counter = makeCounter();
print(counter()); // 1
print(counter()); // 2
final counter2 = makeCounter();
print(counter2()); // 1:独立状态
}
// 闭包保存环境与状态
// 每次调用 makeCounter 生成独立闭包
// 变量在闭包间共享还是独立看位置
// 常用来实现状态封装

异步函数

async 函数返回 Future,内部用 await 等待异步结果。await 只能用于 async 函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
Future<String> fetchData() async {
await Future.delayed(Duration(milliseconds: 100));
return '数据';
}
Future<void> main() async {
print('开始');
final data = await fetchData();
print(data);
}
// async 函数隐式返回 Future<T>
// await 挂起直到 Future 完成
// 异常会传回 await 处可 try/catch
// 忘记 await 会拿到 Future 对象本身

生成器

sync* 生成惰性 Iterable,async* 生成 Stream。yield 每次产出一个值,yield* 委托另一个生成器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Iterable<int> countUp(int max) sync* {
for (var i = 0; i < max; i++) {
yield i;
}
}
Stream<int> streamCount(int max) async* {
for (var i = 0; i < max; i++) {
await Future.delayed(Duration(milliseconds: 10));
yield i;
}
}
void main() {
print(countUp(3).toList());
}
// sync* 逐次生成不占内存
// async* 配合 await 生成流
// yield* 展开子序列:yield* countUp(2)
// 生成器按需执行,首次访问才跑

7.字符串

字面量、插值、子串、编码与格式化。

字符串字面量

单引号与双引号都表示字符串。可用反斜杠转义特殊字符,r 前缀表示原始字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
void main() {
String s1 = '单引号';
String s2 = "双引号";
String esc = '换行符\n制表符\t';
String raw = r'不转义\n直接显示';
print('$s1 $s2');
print(esc);
print(raw);
}
// \n 换行 \t 制表符 \\ 反斜杠
// r'...' 原始字符串不处理转义
// 适合正则表达式、Windows 路径
// 引号内可使用另一种引号避免转义

字符串插值

$变量 与 ${表达式} 把值嵌入字符串。任何对象都会调用 toString。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
final name = 'Rex';
final age = 5;
print('$name 今年 $age 岁');
print('十年后 ${age + 10} 岁');
final list = [1, 2];
print('共 ${list.length} 个元素');
}
// $name 直接插入变量
// ${...} 插入表达式与调用
// 对象自动 toString 转换
// 比字符串拼接更清晰高效

多行字符串

三个单引号 ''' 创建多行字符串,保留换行与缩进。适合长文本与模板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void main() {
String poem = '''
床前明月光,
疑是地上霜。''';
print(poem);
String html = '''
<div>
<p>你好</p>
</div>''';
print(html);
}
// 多行串保留实际换行
// 首尾换行会被包含
// 可与插值组合使用
// 适合 SQL、HTML、模板文本

常用方法

contains/hasPrefix 判断包含,replaceAll 替换,toUpperCase/toLowerCase 转换大小写。

1
2
3
4
5
6
7
8
9
10
11
12
13
void main() {
final text = 'Hello Dart';
print(text.contains('Dart')); // true
print(text.startsWith('Hello')); // true
print(text.toLowerCase()); // hello dart
print(text.replaceAll('Dart', 'Flutter'));
print(text.replaceFirst('l', 'L')); // HeLlo Dart
print(text.length);
}
// 字符串方法返回新串,不修改原串
// trim() 去除首尾空白
// isEmpty / isNotEmpty 判空
// 方法可以链式调用

子串与查找

substring 按索引截取,indexOf 查找子串位置,split 按分隔符拆分成列表。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
final text = 'hello world';
print(text.substring(0, 5)); // hello
print(text.substring(6)); // world
print(text.indexOf('world')); // 6
print(text.indexOf('x')); // -1 未找到
print(text.split(' ')); // [hello, world]
}
// substring 参数为起始(含)与结束(不含)
// indexOf 找不到返回 -1
// lastIndexOf 从后往前找
// split 结果不含分隔符本身

字符与编码

Dart 字符串是 UTF-16 序列,codeUnits 给编码单元,runes 给码点。处理 emoji 需 runes。

1
2
3
4
5
6
7
8
9
10
11
void main() {
final s = 'A中😀';
print(s.length); // 4 个 UTF-16 单元
print(s.codeUnits); // 编码单元列表
print(s.runes.toList()); // 码点列表
print(String.fromCharCodes(s.runes));
}
// 中文与 emoji 占多个 UTF-16 单元
// length 返回编码单元数不是字数
// runes 返回 Unicode 码点
// 需要真实字数时用 runes.length

高效拼接

循环拼接字符串会创建大量临时对象。StringBuffer 先累积再一次性转成字符串。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
final buffer = StringBuffer();
for (var i = 0; i < 1000; i++) {
buffer.write('第 $i 行\n');
}
final result = buffer.toString();
print('${result.length} 个字符');
}
// write 追加不换行
// writeln 追加并换行
// toString 一次性产出字符串
// 比 + 拼接高效得多

格式化与解析

toStringAsFixed 固定小数位,padLeft 补位,int.parse/double.parse 解析数字字符串。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
final pi = 3.14159;
print(pi.toStringAsFixed(2)); // 3.14
print(7.toString().padLeft(3, '0')); // 007
final n = int.parse('42');
final d = double.parse('3.14');
print('$n $d');
}
// toStringAsFixed 四舍五入
// padLeft/padRight 补字符
// parse 失败抛 FormatException
// 指定进制:int.parse('ff', radix: 16)

8.集合

列表、映射、集合运算与排序。

列表操作

add 追加、insert 插入、remove 删除、sort 排序。默认 List 可变且可增长。

1
2
3
4
5
6
7
8
9
10
11
12
13
void main() {
final nums = [3, 1, 2];
nums.add(4); // [3, 1, 2, 4]
nums.insert(0, 0); // [0, 3, 1, 2, 4]
nums.remove(3); // 移除元素 3
nums.sort(); // 升序
print(nums);
print(nums.reversed.toList());
}
// remove 移除首个相等元素
// removeAt 按索引移除
// contains 判断是否包含
// indexOf 查找首个匹配下标

列表高阶

map 变换、where 过滤、reduce/fold 聚合、expand 展平。返回的 Iterable 多为惰性。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
final nums = [1, 2, 3, 4];
final doubled = nums.map((n) => n * 2).toList();
final evens = nums.where((n) => n.isEven).toList();
final sum = nums.reduce((a, b) => a + b);
final flat = [[1, 2], [3]].expand((e) => e).toList();
print('$doubled $evens $sum $flat');
}
// map/where 返回惰性 Iterable
// 惰性序列只在遍历时计算
// reduce 要求列表非空
// fold 可提供初始值更安全

展开运算符

... 展开一个集合的元素并入字面量。...? 在源为 null 时安全跳过。

1
2
3
4
5
6
7
8
9
10
11
12
13
void main() {
final a = [1, 2];
final b = [3, 4];
final merged = [...a, ...b, 5]; // [1,2,3,4,5]
List<int>? maybe;
final safe = [...a, ...?maybe]; // 空安全
final copy = [...a]; // 浅拷贝
print('$merged $safe $copy');
}
// 展开运算符并入元素而非子列表
// ...? 自动处理 null 源
// 也可用于 Map 与 Set 字面量
// 常用于合并与拷贝集合

集合内 if/for

集合字面量内可用 if 条件包含元素,用 for 批量生成元素。Dart 3 还支持模式解构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void main() {
final includeZero = true;
final nums = [
1,
if (includeZero) 0,
for (var i = 2; i < 4; i++) i,
];
print(nums); // [1, 0, 2, 3]
final pairs = {
for (var i = 0; i < 3; i++) 'k$i': i,
};
print(pairs);
}
// if 不满足则省略该元素
// for 循环体产出多个元素
// 同样适用于 Set 与 Map
// 让数据描述更声明式

映射操作

Map 的键、值、条目可迭代。putIfAbsent 惰性填充,update 更新,remove 删除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main() {
final ages = {'alice': 30, 'bob': 25};
ages['carol'] = 22; // 新增
ages.update('bob', (v) => v + 1); // 更新
ages.putIfAbsent('dave', () => 40);
ages.remove('alice');
for (final entry in ages.entries) {
print('${entry.key}: ${entry.value}');
}
}
// 访问不存在的键返回 null
// putIfAbsent 仅在缺失时计算
// entries 提供键值对
// containsKey 判断键是否存在

集合操作

Set 保证元素唯一。union/intersection/difference 做集合运算,toSet 从列表去重。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main() {
final a = {1, 2, 3};
final b = {3, 4, 5};
print(a.union(b)); // {1,2,3,4,5}
print(a.intersection(b)); // {3}
print(a.difference(b)); // {1,2}
final nums = [1, 1, 2, 3];
final unique = nums.toSet();
print(unique);
}
// Set 无序、元素唯一
// contains 判断成员高效
// 可用 Set 做去重与成员检查
// add 已存在元素则无操作

排序与查找

sort 默认升序,可传比较器。indexOf 线性查找,contains 判断存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void main() {
final names = ['bob', 'alice', 'carol'];
names.sort(); // 字母序
print(names);
final people = [
('bob', 30),
('alice', 20),
];
people.sort((x, y) => x.$2.compareTo(y.$2));
print(people);
print(names.indexOf('bob'));
}
// 默认按 Comparable 顺序
// 比较器返回负数/零/正数
// sort 原地修改列表
// 需要原顺序先拷贝再排序

只读集合

List.unmodifiable 创建只读列表,修改抛 UnsupportedError。List.of 复制可变列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
void main() {
final frozen = List.unmodifiable([1, 2, 3]);
// frozen.add(4); // 抛 UnsupportedError
final editable = List.of([1, 2, 3]); // 可变副本
editable.add(4);
print(editable);
final constList = const [1, 2, 3]; // 编译期常量
print(frozen.length);
}
// unmodifiable 运行时只读
// const 编译期常量且规范化
// 传给 API 时只读更安全
// Map.unmodifiable / Set.unmodifiable 同理

9.内存与性能

垃圾回收、const 规范化与缓冲复用。

垃圾回收

Dart VM 自动垃圾回收,无需手动释放内存。对象不再被引用后会被回收。

1
2
3
4
5
6
7
8
9
10
void main() {
// 对象失去引用后成为回收候选:
var data = List<int>.generate(10000, (i) => i);
data = [1, 2, 3]; // 旧列表不再被引用
print(data);
}
// 无引用对象由 GC 自动清理
// 不需要也不能手动 delete
// 短生命周期对象多不担心
// Flutter 使用分代式 GC

const 规范化

const 值会被规范化,相同常量只存在一份。使用 const 减少重复分配。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
const a = [1, 2, 3];
const b = [1, 2, 3];
print(identical(a, b)); // true:同一实例
final c = [1, 2, 3];
final d = [1, 2, 3];
print(identical(c, d)); // false
}
// const 相同值规范化共享
// 非 const 每次都是新对象
// 热路径尽量用 const 字面量
// 编译期常量不会重复构建

惰性初始化

顶层与静态变量是懒初始化,首次访问才赋值。late 提供同样的延迟语义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main() {
print('开始');
print(cache); // 首次访问触发计算
print(cache); // 复用已有值
}
final cache = buildCache();
String buildCache() {
print('构建缓存中');
return 'heavy-data';
}
// 顶层变量首次访问才初始化
// 之后复用同一实例
// late 字段同理
// 避免启动时不必要的开销

列表容量

可变列表按需扩容,频繁 add 会多次重新分配。已知大小建议预分配。

1
2
3
4
5
6
7
8
9
10
11
12
13
void main() {
final nums = <int>[];
for (var i = 0; i < 100; i++) {
nums.add(i); // 自动扩容
}
final sized = List<int>.filled(100, 0);
final growable = List<int>.generate(100, (i) => i);
print('${nums.length} ${sized.length} ${growable.length}');
}
// 默认列表可增长,扩容有代价
// filled 固定长度初始化
// generate 用函数生成元素
// 频繁追加时批量构造更高效

缓冲复用

Uint8List 等 typed data 适合二进制缓冲。复用缓冲避免反复分配,StringBuffer 拼接字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import 'dart:typed_data';
void main() {
final bytes = Uint8List(4); // 固定大小
bytes[0] = 255;
final buffer = StringBuffer();
for (var i = 0; i < 10; i++) {
buffer.write(i);
}
print('$bytes ${buffer.toString()}');
}
// Uint8List 按字节访问高效
// 大二进制用 Uint8List 而非 List<int>
// 复用一个缓冲减少 GC 压力
// 大数据流式处理别整段载入

弱关联

Expando 在不修改对象的前提下附加数据,键是弱引用。WeakReference 不阻止对象回收。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
final tags = Expando<String>();
final obj = Object();
tags[obj] = '元数据'; // 关联数据
print(tags[obj]); // 元数据
final weak = WeakReference(obj);
print(weak.target == obj); // true
}
// Expando 键是弱引用
// 对象回收后关联数据一并消失
// 适合缓存、调试、框架扩展
// 不占用对象本身的字段

对象生命周期

对象在 isolate 独立堆中创建。isolate 间无法共享可变对象,只能复制或消息传递。

1
2
3
4
5
6
7
8
9
10
11
void main() {
// 每个 isolate 有独立堆
final local = [1, 2, 3];
// 发送给其它 isolate 的是副本:
// final result = await Isolate.run(() => local);
print(local);
}
// isolate 间不共享内存
// 共享状态需消息传递或端口
// 全局可变对象危险,慎用
// 适合数据处理的独立任务

原生内存

与 C 互操作时用 dart:ffi 分配原生内存。指针必须手动释放,否则内存泄漏。

1
2
3
4
5
6
7
8
9
10
import 'dart:ffi';
// 声明指针类型:
// typedef NativeBuf = Pointer<Uint8>;
// 分配原生内存:
// final p = calloc<Uint8>(1024); // package:ffi
// 使用 p[i] 读写字节
// 释放:
// calloc.free(p);
// 忘记释放会造成泄漏
// ffi 仅原生平台可用

10.面向对象

类、继承、mixin 与接口。

类与构造

class 定义对象蓝图。构造器与类同名,this 参数把参数直接赋给字段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Animal {
String name;
int age;
Animal(this.name, this.age); // this 参数赋值
void speak() {
print('$name 叫了一声');
}
}
void main() {
final dog = Animal('旺财', 3);
dog.speak();
print('${dog.name} ${dog.age} 岁');
}
// this.name 把构造参数赋给字段
// 字段默认非空必须初始化
// 方法可访问实例字段
// 实例化不需要 new 关键字

继承

extends 继承父类,@override 重写方法,super 调用父类构造器与方法。Dart 单继承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Animal {
Animal(this.name);
final String name;
String sound() => '...';
}
class Dog extends Animal {
Dog(String name) : super(name);
@override
String sound() => '汪汪';
}
void main() {
final dog = Dog('旺财');
print(dog.sound());
}
// 子类继承父类字段与方法
// @override 标记重写
// super(name) 传参给父类构造
// 一个类只能有一个父类

mixin

mixin 是可复用的行为片段,用 with 混入。比继承更灵活,避免单一继承限制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mixin Swimmer {
void swim() => print('游泳');
}
mixin Flyer {
void fly() => print('飞行');
}
class Duck with Swimmer, Flyer {}
void main() {
final duck = Duck();
duck.swim();
duck.fly();
}
// with 关键字混入多个 mixin
// mixin 不能单独实例化
// 可限定父类:mixin X on BaseClass {}
// 组合行为优于深层继承

抽象类

abstract class 不能被实例化,只定义契约。子类必须实现抽象方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
abstract class Shape {
double area();
}
class Circle extends Shape {
Circle(this.r);
final double r;
@override
double area() => 3.14159 * r * r;
}
void main() {
final c = Circle(2);
print(c.area().toStringAsFixed(2));
}
// 抽象方法没有方法体
// 抽象类可含具体实现
// implements 实现接口也可
// 统一类型后可按父类使用

接口

每个类都隐式是一个接口,用 implements 实现。implements 要求重写所有成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Greeter {
String greet(String name) => '你好,$name';
}
class EnglishGreeter implements Greeter {
@override
String greet(String name) => 'Hello, $name';
}
void main() {
final g = EnglishGreeter();
print(g.greet('Rex'));
}
// implements 不继承实现
// 接口的所有成员必须重写
// 一个类可实现多个接口
// 用 implements 表达类型契约

访问器

get 定义只读属性访问,set 定义可写属性。访问器调用时无需括号,隐藏内部存储。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Rectangle {
Rectangle(this.width, this.height);
double width;
double height;
double get area => width * height;
set scale(double factor) {
width *= factor;
height *= factor;
}
}
void main() {
final r = Rectangle(2, 3);
print(r.area); // 6.0
r.scale = 2;
print(r.area); // 24.0
}
// get 像字段一样读取
// set 像字段一样赋值
// 可在 get/set 中加校验逻辑
// 计算属性用 get 派生

静态成员

static 成员属于类本身而非实例。static 方法不能访问实例成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MathUtils {
static const pi = 3.14159;
static int square(int x) => x * x;
static int _counter = 0; // 私有静态
static int get count => _counter;
}
void main() {
print(MathUtils.pi);
print(MathUtils.square(5));
}
// 通过类名直接访问静态成员
// static 不能访问实例字段
// 私有成员下划线开头,库内可见
// static 常量常用于共享配置

sealed 类

sealed 类限定子类必须在同一库内,switch 可穷尽匹配而无需 default。Dart 3 特性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sealed class Shape {}
class Circle extends Shape {}
class Square extends Shape {}
String describe(Shape shape) => switch (shape) {
Circle() => '圆',
Square() => '方',
};
void main() {
print(describe(Circle()));
print(describe(Square()));
}
// sealed 子类限同库声明
// 穷尽 switch 无需 default
// 新增子类会触发编译警告
// 配合模式匹配很安全

11.错误处理

try/catch、自定义异常与异步错误。

try / catch

try 包裹可能抛异常的代码,catch 捕获异常。catch (e) 取异常对象。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
try {
final n = int.parse('abc');
print(n);
} catch (e) {
print('解析失败: $e');
}
}
// catch (e) 捕获所有异常
// 异常抛出后 try 内剩余代码跳过
// 未捕获异常会导致程序崩溃
// catch (e, st) 可拿堆栈 st

on 子句

on 指定只捕获特定类型的异常,与 catch 组合使用。按异常类型分流处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void main() {
try {
throw FormatException('格式错误');
} on FormatException catch (e) {
print('格式: ${e.message}');
} on ArgumentError {
print('参数错误');
} catch (e) {
print('其他: $e');
}
}
// on 类型 捕获匹配的异常
// 不带 catch 的 on 分支不取对象
// 顺序匹配,前面的优先
// 兜底 catch 捕获其余异常

finally 与 rethrow

finally 无论是否异常都会执行,适合清理资源。rethrow 重新抛出保留原堆栈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void main() {
try {
outer();
} catch (e) {
print('最终处理: $e');
}
}
void outer() {
try {
throw Exception('原始错误');
} catch (e) {
print('中间捕获');
rethrow; // 保留堆栈继续抛
} finally {
print('清理资源');
}
}
// finally 总会执行
// rethrow 必须位于 catch 内
// rethrow 保留原始堆栈
// 常用于关闭资源与连接

抛出异常

throw 关键字抛出异常对象。可抛任意对象,但惯例是抛 Exception 或 Error 子类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void main() {
try {
checkAge(-1);
} catch (e) {
print('拒绝: $e');
}
}
void checkAge(int age) {
if (age < 0) {
throw ArgumentError('年龄不能为负');
}
}
// throw 中断当前控制流
// 异常可以是任意对象
// 惯例用 Exception 或 Error
// 不满足业务条件建议抛异常

自定义异常

implements Exception 定义业务异常。重写 toString 提供友好信息,可携带字段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class ValidationError implements Exception {
ValidationError(this.field, this.message);
final String field;
final String message;
@override
String toString() => '字段 $field 无效: $message';
}
void main() {
try {
throw ValidationError('email', '格式错误');
} on ValidationError catch (e) {
print(e.field); // email
print(e);
}
}
// implements Exception 标记为异常
// 可携带结构化字段
// on 子句精确捕获自定义类型
// 便于上层按类型分别处理

异步错误

async 函数中 await 抛出的异常可被 try/catch 捕获。未处理的 Future 错误会静默丢失。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Future<void> load() async {
throw Exception('加载失败');
}
Future<void> main() async {
try {
await load();
} catch (e) {
print('异步捕获: $e');
}
}
// 不 await 的错误很难捕获
// 用 catchError 链式处理:
// load().catchError(print);
// 未处理错误可能进入 zone
// 建议统一错误处理策略

流错误

Stream 监听可传 onError 回调处理错误。StreamController.addError 向流注入错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import 'dart:async';
void main() {
final controller = StreamController<int>();
controller.stream.listen(
(data) => print('数据: $data'),
onError: (e) => print('流错误: $e'),
onDone: () => print('流结束'),
);
controller.add(1);
controller.addError('意外错误');
controller.close();
}
// addError 注入错误到流
// onError 回调处理流错误
// onDone 在关闭后触发
// 流错误不会中断其它数据

断言

assert 在开发模式检查条件,失败抛 AssertionError。发布模式断言被移除。

1
2
3
4
5
6
7
8
9
void main() {
final score = 85;
assert(score >= 0, '分数不能为负'); // 通过
print('断言通过');
}
// 条件为 false 抛 AssertionError
// 第二个参数是失败消息
// 发布模式 dart compile 移除断言
// 适合检查不变量,非用户输入

12.文件与 I/O

文件读写、JSON 与标准输入输出。

读取文件

dart:io 的 File 类读写文件。readAsString 读整个文本文件,需 import 'dart:io'。

1
2
3
4
5
6
7
8
9
10
11
import 'dart:io';
Future<void> main() async {
final text = await File('data.txt').readAsString();
print(text);
final exists = await File('data.txt').exists();
print(exists);
}
// readAsString 一次性读入内存
// 大文件用流式读取
// 路径不存在抛 FileSystemException
// exists 返回布尔判断存在

写入文件

writeAsString 写入文本,writeAsBytes 写入二进制。追加模式用 mode 参数。

1
2
3
4
5
6
7
8
9
10
11
12
import 'dart:io';
Future<void> main() async {
await File('out.txt').writeAsString('Hello\n');
await File('out.txt').writeAsString('World\n',
mode: FileMode.append); // 追加
final content = await File('out.txt').readAsString();
print(content);
}
// 默认模式覆盖原文件
// FileMode.append 追加到末尾
// FileMode.writeOnly 只写覆盖
// 中文写入默认 UTF-8 编码

逐行读取

readAsLines 把文件按行拆成列表。大文件用 openRead 流式逐行处理,避免整文件载入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import 'dart:io';
import 'dart:convert';
Future<void> main() async {
final lines = await File('log.txt').readAsLines();
for (final line in lines) {
print(line);
}
final stream = File('big.log').openRead()
.transform(utf8.decoder)
.transform(const LineSplitter());
await for (final line in stream) {
print(line);
}
}
// readAsLines 适合小文件
// 大文件用 openRead 流式处理
// LineSplitter 按行切分
// 流式处理不占满内存

JSON 处理

dart:convert 的 jsonEncode/jsonDecode 序列化与反序列化。JSON 数字解析为 int 或 double。

1
2
3
4
5
6
7
8
9
10
11
12
import 'dart:convert';
void main() {
final data = {'name': 'Rex', 'age': 5};
final encoded = jsonEncode(data);
print(encoded);
final decoded = jsonDecode(encoded) as Map<String, dynamic>;
print(decoded['name']);
}
// jsonDecode 返回 dynamic,需转型
// 数字解析为 int 或 double
// 未知字段保留在 Map 中
// 复杂对象需手动映射

标准输入输出

stdin.readLineSync 同步读一行输入,stdout.writeln 输出。适合命令行交互程序。

1
2
3
4
5
6
7
8
9
10
import 'dart:io';
void main() {
stdout.write('请输入姓名: ');
final name = stdin.readLineSync() ?? '';
stdout.writeln('你好,$name');
}
// readLineSync 读一行返回 String?
// 输入结束返回 null,需默认值
// stdout.write 不换行
// stderr.writeln 输出到错误流

目录操作

Directory.list 遍历目录内容,create 建目录,delete 删除。返回 FileSystemEntity。

1
2
3
4
5
6
7
8
9
10
11
12
import 'dart:io';
Future<void> main() async {
await Directory('data/sub').create(recursive: true);
await for (final entity in Directory('.').list()) {
print('${entity.path} ${entity is Directory ? '目录' : '文件'}');
}
await Directory('data').delete(recursive: true);
}
// create recursive 创建多级目录
// list 惰性迭代目录内容
// delete recursive 递归删除
// entity 统一处理文件与目录

二进制字节

readAsBytes 读二进制,Uint8List 表示字节。writeAsBytes 写入。适合图片、音频等。

1
2
3
4
5
6
7
8
9
10
11
12
13
import 'dart:io';
import 'dart:typed_data';
Future<void> main() async {
final bytes = await File('image.png').readAsBytes();
print('大小: ${bytes.length} 字节');
final copy = Uint8List.fromList(bytes);
await File('copy.png').writeAsBytes(copy);
print('已复制');
}
// readAsBytes 返回 Uint8List
// 字节适合非文本数据
// fromList 复制字节序列
// 大文件用 openWrite 流式写

路径操作

File/Directory 的 path 属性给完整路径。absolute 解析绝对路径,uri 转 file:// 形式。

1
2
3
4
5
6
7
8
9
10
11
12
import 'dart:io';
void main() {
final file = File('a/b/data.txt');
print(file.path); // 完整路径
print(file.absolute.path); // 绝对路径
print(file.uri); // file:// URI
print(Platform.pathSeparator);
}
// path 属性返回完整路径字符串
// absolute 解析为绝对路径
// uri 转为 file:// URI
// Windows 与 Unix 分隔符不同

13.常见坑

Dart 日常开发最容易踩的坑与正确写法。

滥用非空断言

用 ! 绕过空检查容易运行时崩溃。判空、?? 与类型提升更安全。

1
2
3
4
5
6
7
8
9
10
11
// BAD:用 ! 掩盖可空设计
String? name = 'Rex';
final len = name!.length; // 依赖运行时断言
print(len);
// GOOD:利用类型提升
String? nick = '小雷';
if (nick != null) {
print(nick.length);
}
// GOOD:?? 提供默认值
print(name?.toUpperCase() ?? '未知');

集合 == 比较

List/Map 的 == 比较引用而非内容。相同元素的列表不相等。需要结构比较用工具函数。

1
2
3
4
5
6
7
8
9
// BAD:== 比较的是引用
final a = [1, 2];
final b = [1, 2];
print(a == b); // false
// GOOD:比较内容而非引用
print(a.join(',') == b.join(',')); // true
// GOOD:结构比较推荐
// listEquals(a, b) package:collection
// 字符串与数字直接 == 比较内容

const 与 final

const 必须是编译期常量,final 运行时确定。把运行时值当 const 写会编译失败。

1
2
3
4
5
6
7
8
9
// BAD:运行时值不能作 const
// const now = DateTime.now(); // 编译错误
// GOOD:运行时值用 final
final now = DateTime.now();
// GOOD:编译期常量用 const
const pi = 3.14159;
// const 值会规范化共享
// final 只是不能重新赋值
// 集合元素也可用 const

惰性 Iterable

map/where 返回惰性 Iterable,遍历时重新计算,底层变化会反映到结果。需要快照用 toList。

1
2
3
4
5
6
7
8
9
10
11
// BAD:惰性 Iterable 每次遍历重新计算
final nums = [1, 2, 3];
final lazy = nums.where((n) => n.isOdd);
nums.add(5);
print(lazy.toList()); // [1, 3, 5] 含新元素
// GOOD:需要快照用 toList
final snapshot = nums.where((n) => n.isOdd).toList();
nums.add(7);
print(snapshot); // 仍是 [1, 3, 5]
// 底层变化不影响已固化结果
// 流(Stream)只能监听一次

忘写 await

调用 async 函数不 await 会拿到 Future 而不是结果。并发任务用 Future.wait。

1
2
3
4
5
6
7
8
9
10
11
12
// BAD:忘记 await 得到 Future 对象
Future<String> fetch() async => '数据';
Future<void> main() async {
final result = fetch(); // Future 非字符串
print(result);
// GOOD:await 后才是结果
final text = await fetch();
print(text);
// 并发等待多个:
final all = await Future.wait([fetch(), fetch()]);
print(all);
}

级联返回原对象

.. 级联对同一对象连续调用并返回该对象,不是最后表达式的结果。需要返回值时别用级联。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class User {
String name = '';
int age = 0;
}
void main() {
// BAD:级联返回的是接收者
final result = [1, 2]..add(3);
print(result); // [1, 2, 3] 而非 3
// GOOD:级联就地修改并返回原对象
final user = User()
..name = 'Rex'
..age = 5;
print(user.name);
// 需要函数结果用普通调用
print('abc'.toUpperCase());
}

遍历时修改

遍历集合时增删元素会抛 ConcurrentModificationError。用 removeWhere 或收集后处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// BAD:遍历时移除元素会抛错
final items = [1, 2, 3, 4];
// for (final n in items) { items.remove(n); }
// 抛 ConcurrentModificationError
// GOOD:removeWhere 安全过滤
final nums = [1, 2, 3, 4];
nums.removeWhere((n) => n.isEven);
print(nums); // [1, 3]
// GOOD:先收集再统一处理
final keep = <int>[];
for (final n in nums) {
if (n.isOdd) keep.add(n);
}
print(keep);

字符串按编码单元

length 返回 UTF-16 单元数,中文与 emoji 会多于字符数。按字符处理要遍历 runes。

1
2
3
4
5
6
7
8
9
10
11
12
// BAD:用 length 数中文字符
final s = '你好';
print(s.length); // 2 个单元
// emoji 情况:
final emoji = '👍';
print(emoji.length); // 2!一个符号
// GOOD:按 runes 数码点
print(emoji.runes.length); // 1
// GOOD:按 runes 逐字处理
for (final r in emoji.runes) {
print(String.fromCharCode(r));
}

14.并发与异步

isolate、Future、Stream 与事件循环。

isolate 并发

isolate 是 Dart 并发单元,各自有独立内存与事件循环。Isolate.run 或 Isolate.spawn 启动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import 'dart:isolate';
Future<void> main() async {
final result = await Isolate.run(() {
var sum = 0;
for (var i = 0; i < 100000; i++) {
sum += i;
}
return sum;
});
print('结果: $result');
}
// Isolate.run 返回 Future<结果>
// 隔离内存,不共享可变状态
// 适合 CPU 密集计算
// 计算完成自动销毁

spawn 与端口

Isolate.spawn 以入口函数启动新 isolate,SendPort 发送消息,ReceivePort 接收消息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import 'dart:isolate';
void entry(SendPort sendPort) {
sendPort.send('来自新 isolate');
}
Future<void> main() async {
final receive = ReceivePort();
await Isolate.spawn(entry, receive.sendPort);
final msg = await receive.first;
print(msg);
receive.close();
}
// spawn 的入口须是顶层函数或静态方法
// 消息通过端口传递
// receive.first 等待第一条消息
// 用完记得 close 端口

Future 基础

Future 表示将来完成的结果。Future.value 直接成功,await 等待完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Future<int> addLater(int a, int b) async {
await Future.delayed(Duration(milliseconds: 10));
return a + b;
}
Future<void> main() async {
final future = Future.value(42);
print(await future);
final result = await addLater(1, 2);
print(result);
}
// async 函数返回 Future
// await 挂起直到 Future 完成
// Future.delayed 定时延迟
// Future.value 包装立即结果

async / await

async 标记异步函数,await 等待异步操作。await 只能在 async 函数内使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Future<String> fetchUser() async {
await Future.delayed(Duration(milliseconds: 50));
return 'Rex';
}
Future<void> main() async {
print('开始加载');
final name = await fetchUser();
print('加载到: $name');
final results = await Future.wait([
fetchUser(),
fetchUser(),
]);
print(results);
}
// await 暂停当前流程但主线程不阻塞
// 多个 await 默认串行
// Future.wait 并发等待多个
// 异常会传到 await 处可捕获

流 Stream

Stream 是异步事件序列。listen 订阅事件,await for 逐个处理。与 Future 的单次不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import 'dart:async';
Stream<int> tick(int max) async* {
for (var i = 0; i < max; i++) {
await Future.delayed(Duration(milliseconds: 10));
yield i;
}
}
Future<void> main() async {
await for (final n in tick(3)) {
print('tick $n');
}
tick(2).listen((n) => print('listen $n'));
await Future.delayed(Duration(milliseconds: 100));
}
// await for 顺序消费流事件
// listen 注册监听回调
// 流只产生一次,不能重放
// 每事件依次到达

StreamController

StreamController 手动控制流,add 发数据,addError 发错误,close 关闭。broadcast 支持多订阅。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import 'dart:async';
void main() {
final controller = StreamController<int>();
controller.stream.listen((n) => print('收到 $n'));
controller.add(1);
controller.add(2);
controller.close();
final broadcast = StreamController<int>.broadcast();
broadcast.stream.listen((n) => print('A $n'));
broadcast.stream.listen((n) => print('B $n'));
broadcast.add(5);
broadcast.close();
}
// 默认流只允许一个监听者
// broadcast 允许多个监听
// add 向流推送数据
// 记得 close 释放资源

等待多个 Future

Future.wait 并发等待全部完成,Future.any 等待最先完成的结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Future<int> slow() async {
await Future.delayed(Duration(milliseconds: 30));
return 1;
}
Future<int> fast() async {
await Future.delayed(Duration(milliseconds: 10));
return 2;
}
Future<void> main() async {
final all = await Future.wait([slow(), fast()]);
print(all); // [1, 2]
final first = await Future.any([slow(), fast()]);
print(first); // 2
}
// wait 全部成功后返回结果列表
// 任一失败默认传播错误
// any 返回最先完成的
// eagerError 控制是否立即失败

Completer

Completer 手动控制 Future 完成时机,适合把回调风格 API 转成 async/await 风格。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import 'dart:async';
Future<String> fromCallback() {
final completer = Completer<String>();
Future.delayed(Duration(milliseconds: 20), () {
completer.complete('回调结果');
});
return completer.future;
}
Future<void> main() async {
final result = await fromCallback();
print(result);
}
// complete 让 Future 成功
// completeError 让 Future 失败
// 只能 complete 一次
// 用于包装第三方回调

15.网络

HTTP 请求、WebSocket 与 TCP 套接字。

HTTP 客户端

dart:io 的 HttpClient 发起 HTTP 请求。getUrl 得到请求对象,关闭后读取响应体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import 'dart:io';
import 'dart:convert';
Future<void> main() async {
final client = HttpClient();
final request = await client.getUrl(Uri.parse('https://example.com'));
final response = await request.close();
final body = await response.transform(utf8.decoder).join();
print('状态码: ${response.statusCode}');
print(body.substring(0, 50));
client.close();
}
// HttpClient 底层且需手动关闭
// 响应体是字节流,用 utf8.decoder
// statusCode 200 表示成功
// 简单场景优先用 package:http

package:http

package:http 封装常用请求。http.get/post 返回 Response,body 直接是字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import 'dart:convert';
import 'package:http/http.dart' as http;
Future<void> main() async {
final response = await http.get(
Uri.parse('https://example.com'),
);
print('状态码: ${response.statusCode}');
print(response.body.substring(0, 50));
final post = await http.post(
Uri.parse('https://example.com/api'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({'name': 'Rex'}),
);
print(post.statusCode);
}
// 需在 pubspec 添加 http 依赖
// Response.body 已解码字符串
// headers 传请求头
// 状态码判断成功与否

URI 解析

Uri.parse 解析 URL,queryParameters 读取查询参数,Uri.http 构建请求地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
void main() {
final uri = Uri.parse('https://api.com/users?id=5&page=2');
print(uri.scheme); // https
print(uri.host); // api.com
print(uri.path); // /users
print(uri.queryParameters); // {id: 5, page: 2}
final built = Uri.http('api.com', '/users', {'id': '5'});
print(built);
}
// queryParameters 自动解码
// Uri.https 走 TLS
// queryParametersAll 处理重复键
// replace 可派生新 URI

JSON API

请求 JSON 接口并用 jsonDecode 解析响应体。结合 http 包与 Future 等待数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import 'dart:convert';
import 'package:http/http.dart' as http;
Future<void> main() async {
final response = await http.get(
Uri.parse('https://api.example.com/users'),
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body) as List;
for (final item in data) {
final user = item as Map<String, dynamic>;
print(user['name']);
}
}
}
// 检查状态码再解析
// JSON 数组转 List,对象转 Map
// 字段不存在返回 null
// 真实项目可用模型类封装

WebSocket

WebSocket 全双工通信。WebSocket.connect 连接,add 发送,流式接收消息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import 'dart:io';
Future<void> main() async {
final ws = await WebSocket.connect('wss://example.com/ws');
ws.add('你好');
await for (final message in ws) {
print('收到: $message');
if (message == 'bye') {
await ws.close();
}
}
}
// connect 建立连接
// add 发送消息
// 流式接收返回消息
// close 优雅关闭连接

TCP 套接字

Socket.connect 建立 TCP 连接,写入请求,读取响应。适合底层协议与自定义服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import 'dart:io';
Future<void> main() async {
final socket = await Socket.connect('example.com', 80);
socket.write('GET / HTTP/1.0\r\n\r\n');
await for (final data in socket) {
final text = String.fromCharCodes(data);
if (text.contains('HTTP')) print(text);
}
socket.destroy();
}
// Socket 提供字节流
// write 发送请求内容
// 流式读取响应分片
// destroy 立即关闭

HTTP 服务端

HttpServer.bind 启动本地服务,监听请求并返回响应。适合开发调试与工具。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import 'dart:io';
Future<void> main() async {
final server = await HttpServer.bind('localhost', 8080);
print('监听 8080 端口');
await for (final request in server) {
request.response.headers.set('Content-Type', 'text/plain; charset=utf-8');
request.response.write('你好,路径 ${request.uri.path}');
await request.response.close();
}
}
// bind 绑定地址与端口
// 每个请求进入 await for
// response 对象返回内容
// close 结束本次响应

超时与错误

网络请求可能超时或失败。timeout 设置超时,异常用 try/catch 捕获并处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import 'dart:async';
import 'package:http/http.dart' as http;
Future<void> main() async {
try {
final response = await http
.get(Uri.parse('https://example.com'))
.timeout(const Duration(seconds: 5));
print(response.statusCode);
} on TimeoutException {
print('请求超时');
} catch (e) {
print('网络错误: $e');
}
}
// timeout 超时后抛 TimeoutException
// 网络异常多为 SocketException
// 按错误类型分别处理
// 生产环境可加指数退避重试

16.时间与日期

DateTime、Duration、格式化与时间戳。

当前时间

DateTime.now() 获取当前本地时间。各字段可直接访问,毫秒部分也可取。

1
2
3
4
5
6
7
8
9
10
11
void main() {
final now = DateTime.now();
print('${now.year}-${now.month}-${now.day}');
print('${now.hour}:${now.minute}:${now.second}');
print(now.weekday); // 周一=1 周日=7
print(now.millisecondsSinceEpoch);
}
// now 是本地时区时间
// month/day 从 1 开始
// weekday 1-7,周一为 1
// 各字段都是 int

构造时间

DateTime(年, 月, 日, ...) 构造指定时间。DateTime.utc 构造 UTC 时间。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
final birthday = DateTime(2020, 1, 15);
print(birthday);
final launch = DateTime(2026, 8, 2, 10, 30);
print(launch);
final utc = DateTime.utc(2026, 1, 1);
print(utc.isUtc); // true
}
// 未传的部分默认 0 或 1
// 月日不合法会自动进位
// UTC 构造用 DateTime.utc
// 比较用 isBefore/isAfter

时长

Duration 表示一段时间,支持时、分、秒与微秒。可加减、比较、转各时间单位。

1
2
3
4
5
6
7
8
9
10
11
12
13
void main() {
final halfHour = Duration(minutes: 30);
final fiveMin = Duration(minutes: 5);
print(halfHour.inSeconds); // 1800
print(halfHour + fiveMin); // 35 分钟
print(halfHour > fiveMin); // true
final d = Duration(days: 1, hours: 2);
print('${d.inHours} 小时'); // 26
}
// inSeconds/inMinutes 等转单位
// 支持 + - * / 运算
// 可比较大小
// 常用 Duration(milliseconds: n) 做延迟

格式化输出

Dart 没有内置 strftime,用 padLeft 补零拼格式,或用 intl 包的 DateFormat。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void main() {
final now = DateTime.now();
final y = now.year.toString();
final m = now.month.toString().padLeft(2, '0');
final d = now.day.toString().padLeft(2, '0');
print('$y-$m-$d');
print(now.toIso8601String());
// intl 包提供日期模板:
// import 'package:intl/intl.dart';
// print(DateFormat('yyyy-MM-dd').format(now));
}
// padLeft 补足位数
// toIso8601String 输出 ISO 格式
// intl 支持本地化日期模板
// 简单场景手写拼接即可

解析时间

DateTime.parse 解析 ISO 格式字符串。toIso8601String 生成标准格式便于解析与传输。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main() {
final parsed = DateTime.parse('2026-08-02 10:30:00');
print(parsed.year); // 2026
final iso = DateTime.parse('2026-08-02T10:30:00Z');
print(iso.isUtc); // true
final parts = '2026-08-02'.split('-');
final d = DateTime(
int.parse(parts[0]), int.parse(parts[1]), int.parse(parts[2]));
print(d.day);
}
// 支持多种 ISO 形式
// 无时区默认本地时间
// 含 Z 表示 UTC
// 格式不合法抛 FormatException

时间运算

add/subtract 加减时长,difference 计算两时间差,返回 Duration。

1
2
3
4
5
6
7
8
9
10
11
12
13
void main() {
final start = DateTime(2026, 8, 2, 10);
final later = start.add(Duration(hours: 5));
print(later.hour); // 15
final earlier = start.subtract(const Duration(days: 1));
print(earlier.day); // 1
final diff = later.difference(start);
print(diff.inHours); // 5
}
// add/subtract 返回新对象
// 原对象不可变
// difference 总是非负
// 需要带方向判断用 isBefore

时间戳

毫秒/微秒时间戳便于存储与排序。fromMillisecondsSinceEpoch 从时间戳还原。

1
2
3
4
5
6
7
8
9
10
11
12
13
void main() {
final now = DateTime.now();
final ms = now.millisecondsSinceEpoch;
print(ms);
final back = DateTime.fromMillisecondsSinceEpoch(ms);
print(back.toIso8601String());
final day = DateTime(now.year, now.month, now.day);
print(day.millisecondsSinceEpoch);
}
// 时间戳是自 1970-01-01 起的毫秒数
// 存储用 UTC 时间戳避免时区混乱
// fromMicrosecondsSinceEpoch 对应微秒
// 相同时间戳还原的时间相等

时区

DateTime 默认本地时区。isUtc 判断,toUtc/toLocal 转换。存储建议用 UTC。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
final now = DateTime.now();
print(now.isUtc); // false
final utc = now.toUtc();
print(utc.isUtc); // true
final local = utc.toLocal();
print(local == now); // 时间一致
}
// toUtc/toLocal 互转不改变时刻
// DateTime 没有自带时区名称
// 需要 IANA 时区用 package:timezone
// 持久化推荐存 UTC

计时器

Stopwatch 测量代码执行耗时。start/stop/reset 控制,elapsed 给出耗时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main() {
final watch = Stopwatch()..start();
var sum = 0;
for (var i = 0; i < 1000000; i++) {
sum += i;
}
watch.stop();
print('耗时 ${watch.elapsedMilliseconds} ms');
print('微秒 ${watch.elapsedMicroseconds}');
}
// start 开始计时
// elapsed 返回 Duration
// elapsedMilliseconds 毫秒数
// reset 清零后重新开始

17.进程与环境

子进程、环境变量、标准流与信号。

运行子进程

Process.run 运行外部命令并等待结果。result.stdout 与 stderr 捕获输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import 'dart:io';
Future<void> main() async {
final result = await Process.run('dart', ['--version']);
print('退出码: ${result.exitCode}');
print('输出: ${result.stdout}');
final ls = await Process.run(
Platform.isWindows ? 'cmd' : 'ls',
Platform.isWindows ? ['/c', 'dir'] : ['-la'],
);
print(ls.stdout);
}
// run 等待进程结束返回结果
// stdout/stderr 是字符串或字节
// exitCode 0 表示成功
// 需要交互用 Process.start

流式进程

Process.start 启动进程立即返回,可实时读 stdout 与写入 stdin。适合长时运行任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import 'dart:io';
import 'dart:convert';
Future<void> main() async {
final process = await Process.start('dart', ['--version']);
process.stdout
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen((line) => print('子进程输出: ${line.trim()}'));
final exitCode = await process.exitCode;
print('退出码: $exitCode');
}
// start 不等待进程结束
// stdout 是流可实时监听
// 需要交互时用 stdin 写入
// 可用 environment 定制环境

环境变量

Platform.environment 读取环境变量,返回只读 Map。不存在返回 null。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import 'dart:io';
void main() {
final env = Platform.environment;
final path = env['PATH'] ?? '未设置';
print('PATH: $path');
print('有 HOME: ${env.containsKey('HOME')}');
for (final entry in env.entries) {
if (entry.key.startsWith('FLUTTER')) {
print('${entry.key}=${entry.value}');
}
}
}
// environment 是只读 Map
// 不存在返回 null
// containsKey 判断存在
// 创建进程时传 environment 可注入

退出码

exitCode 设置进程退出码。0 成功,非 0 失败。命令行程序据此判断结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import 'dart:io';
void main(List<String> args) {
if (args.isEmpty) {
stderr.writeln('用法: app <参数>');
exitCode = 1;
return;
}
print('处理 ${args[0]}');
exitCode = 0; // 成功
}
// exitCode 0 表示成功
// 非 0 表示错误类型
// exit() 立即结束进程
// 脚本依赖退出码判断结果

参数与脚本

args 是命令行参数,Platform.script 给出脚本路径。Directory.current 给当前目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
import 'dart:io';
void main(List<String> args) {
print('参数个数: ${args.length}');
for (var i = 0; i < args.length; i++) {
print('参数 $i: ${args[i]}');
}
print('脚本路径: ${Platform.script}');
print('当前目录: ${Directory.current.path}');
}
// args 不含程序名
// Platform.script 是入口文件路径
// Directory.current 当前工作目录
// 复杂参数可用 args 包解析

标准流

stdout/stderr 输出,stdin 输入。flush 刷新缓冲,stderr 不参与管道正常输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import 'dart:io';
void main() {
stdout.write('请输入:');
stdout.flush();
final input = stdin.readLineSync() ?? '';
stdout.writeln('你输入了: $input');
if (input.isEmpty) {
stderr.writeln('警告:输入为空');
}
}
// stdout.write 不换行
// flush 立即输出缓冲
// stderr 输出到错误流
// 管道处理时 stdout 被重定向

文件系统

File/Directory 处理文件系统。create 建目录,delete 删,rename 移动,exists 判断存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import 'dart:io';
Future<void> main() async {
final dir = Directory('data/sub');
await dir.create(recursive: true);
final file = File('data/note.txt');
await file.writeAsString('内容');
print(await file.exists()); // true
print(file.lengthSync()); // 字节数
await file.rename('data/new.txt');
await Directory('data').delete(recursive: true);
}
// create recursive 建多级目录
// exists 判断是否存在
// lengthSync 同步取大小
// rename 移动或改名

信号处理

ProcessSignal 响应系统信号(SIGINT/SIGTERM)。用于优雅关闭服务或清理资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import 'dart:io';
Future<void> main() async {
ProcessSignal.sigint.watch().listen((_) {
print('收到中断,清理退出');
exit(0);
});
print('运行中,按 Ctrl+C 退出');
await Future.delayed(Duration(seconds: 30));
}
// sigint 是 Ctrl+C
// sigterm 是 kill 默认信号
// watch 返回流监听信号
// 处理后调用 exit 结束
// Windows 仅部分信号可用

18.正则表达式

RegExp 的匹配、替换、分割与分组。

创建与判断

RegExp 表示正则表达式。hasMatch 判断是否匹配。r 前缀避免转义。

1
2
3
4
5
6
7
8
9
10
11
void main() {
final digits = RegExp(r'\d+');
print(digits.hasMatch('abc123')); // true
print(digits.hasMatch('abc')); // false
final email = RegExp(r'^[\w.-]+@[\w-]+\.\w+$');
print(email.hasMatch('[email protected]'));
}
// r 前缀表示原始字符串
// \d 数字 \w 单词字符 \s 空白
// ^ 行首 $ 行尾
// hasMatch 只判断是否存在

首个匹配

firstMatch 返回第一个匹配对象。group 取分组,start/end 给位置,无匹配返回 null。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main() {
final re = RegExp(r'\d+');
final match = re.firstMatch('编号 42,编号 99');
if (match != null) {
print(match.group(0)); // 42
print(match.start); // 匹配起点
print(match.end); // 匹配终点
print(match.matched); // 42
}
}
// firstMatch 返回第一个匹配
// 无匹配返回 null
// group(0) 是整个匹配
// group(n) 是第 n 个分组

全部匹配

allMatches 返回所有匹配的迭代器。配合模式可提取所有符合条件的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void main() {
final re = RegExp(r'\d+');
final matches = re.allMatches('a1 b22 c333');
final numbers = <String>[];
for (final m in matches) {
numbers.add(m.group(0)!);
}
print(numbers); // [1, 22, 333]
final list = RegExp(r'\d+')
.allMatches('a1 b22')
.map((m) => m.group(0)!)
.toList();
print(list);
}
// 遍历每个匹配对象
// group(0) 取整段匹配
// map 组合提取更简洁
// 惰性迭代按需计算

替换

replaceAll 替换所有匹配,replaceFirst 只换第一个。回调形式按匹配内容动态生成替换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main() {
final re = RegExp(r'\d+');
print(re.replaceAll('a1b2', '#')); // a#b#
print(re.replaceFirst('a1b2', '#')); // a#b2
final result = re.replaceAllMapped('a1b2', (m) {
final n = int.parse(m.group(0)!);
return '${n + 1}';
});
print(result); // a2b3
}
// replaceAll 全部替换
// replaceFirst 只换首个
// 回调模式可按内容计算
// 原字符串不变,返回新串

分割

String.split 可接受 RegExp 按模式分割。分隔符不会进入结果列表。

1
2
3
4
5
6
7
8
9
10
11
void main() {
final text = 'a, b, c';
final parts = text.split(RegExp(r',\s*'));
print(parts); // [a, b, c]
print('one two three'.split(RegExp(r'\s+')));
print(text.replaceAll(RegExp(r',\s*'), ' | '));
}
// split 按匹配处切分
// 分隔符不进入结果
// 复杂分隔用正则
// 简单分隔直接传字符串

分组捕获

圆括号定义捕获组。group(n) 取分组,命名组用 (?<name>...) 且按名访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void main() {
final re = RegExp(r'(\d{4})-(\d{2})-(\d{2})');
final m = re.firstMatch('日期 2026-08-02');
if (m != null) {
print(m.group(1)); // 2026
print(m.group(2)); // 08
print(m.group(3)); // 02
}
final named = RegExp(r'(?<year>\d{4})');
final nm = named.firstMatch('2026');
print(nm?.namedGroup('year'));
}
// group(1) 起是捕获组
// 非捕获组 (?:...) 不占编号
// 命名组提高可读性
// namedGroup 按名取值

标志参数

RegExp 构造器第二个参数设标志:caseSensitive 忽略大小写,multiLine 多行匹配。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
final re = RegExp('dart', caseSensitive: false);
print(re.hasMatch('Hello DART')); // true
final multiline = RegExp('^a', multiLine: true);
print(multiline.hasMatch('x\na\nb')); // true
final dotAll = RegExp('a.b', dotAll: true);
print(dotAll.hasMatch('a\nb')); // true
}
// caseSensitive: false 忽略大小写
// multiLine 让 ^ 匹配每行开头
// dotAll 让 . 匹配换行
// unicode: true 启用 Unicode 模式

常用模式

常见验证模式:邮箱、手机号、URL、IP。写成可复用常量便于维护。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main() {
final email = RegExp(r'^[\w.+-]+@[\w-]+\.\w+$');
final phone = RegExp(r'^1[3-9]\d{9}$');
final url = RegExp(r'^https?://\S+$');
final ip = RegExp(r'^\d{1,3}(\.\d{1,3}){3}$');
print(email.hasMatch('[email protected]'));
print(phone.hasMatch('13800138000'));
print(url.hasMatch('https://dart.dev'));
print(ip.hasMatch('192.168.1.1'));
}
// 邮箱:常规校验
// 手机号:中国 11 位
// URL:http/https
// IP:四段点分十进制

19.构建与调试

pub、静态检查、格式化与测试。

pubspec 配置

pubspec.yaml 是包配置文件,声明名称、版本、SDK 与依赖。dart pub get 解析依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// pubspec.yaml 示例:
// name: myapp
// description: 示例应用
// version: 1.0.0
// environment:
// sdk: '>=3.0.0 <4.0.0'
// dependencies:
// http: ^1.1.0
// dev_dependencies:
// test: ^1.24.0
// 版本约束:
// ^1.1.0 表示 >=1.1.0 <2.0.0
// 安装依赖:
// dart pub get

pub 命令

dart pub get 安装、upgrade 升级、outdated 查看过期、add/remove 管理依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 安装依赖:
// dart pub get
// 升级所有依赖:
// dart pub upgrade
// 查看过期依赖:
// dart pub outdated
// 添加依赖:
// dart pub add http
// 移除依赖:
// dart pub remove http
// 搜索包:
// dart pub search json
// 发布:
// dart pub publish
// 锁定版本在 pubspec.lock

静态检查

dart analyze 静态分析代码,报告错误与警告。运行在 CI 中保证代码质量。

1
2
3
4
5
6
7
8
9
10
11
12
// 分析当前包:
// dart analyze
// 只分析指定文件:
// dart analyze lib/main.dart
// 常见检查项:
// 未使用的导入
// 类型错误
// 不推荐的写法
// 应用 lint 修复:
// dart fix --apply
// CI 集成:
// dart analyze && dart test

格式化

dart format 统一代码风格。自动整理缩进、引号与换行,团队协作必备。

1
2
3
4
5
6
7
8
9
10
11
// 格式化当前目录:
// dart format .
// 格式化指定文件:
// dart format lib/main.dart
// 检查不修改:
// dart format --output=none --set-exit-if-changed .
// 风格约定:
// 2 空格缩进
// 优先单引号
// 行尾不空格
// CI 检查格式一致性

单元测试

package:test 提供 test() 与 expect 编写单元测试。dart test 运行。测试放 test/ 目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// test/calculator_test.dart:
import 'package:test/test.dart';
int add(int a, int b) => a + b;
void main() {
test('加法正确', () {
expect(add(1, 2), 3);
expect(add(-1, 1), 0);
});
group('边界情况', () {
test('大数相加', () {
expect(add(1000000, 1), 1000001);
});
});
}
// 运行测试:
// dart test
// 只跑单个文件:
// dart test test/calculator_test.dart
// 名称过滤:
// dart test -n 加法

编译部署

dart compile 生成可执行文件。exe 原生可执行,js 编译到 JS,aot-snapshot 快照。

1
2
3
4
5
6
7
8
9
10
11
12
// 编译原生可执行文件:
// dart compile exe bin/main.dart
// 指定输出:
// dart compile exe bin/main.dart -o myapp
// 编译到 JS(浏览器):
// dart compile js lib/main.dart
// AOT 快照:
// dart compile aot-snapshot bin/main.dart
// 编译核:
// dart compile kernel bin/main.dart
// 编译后无需 Dart SDK 运行
// 原生 exe 启动快

lint 规则

analysis_options.yaml 配置 lint 规则。lints 包提供推荐规则集,约束代码风格。

1
2
3
4
5
6
7
8
9
10
11
12
13
// analysis_options.yaml 示例:
// include: package:lints/recommended.yaml
// linter:
// rules:
// - prefer_final_locals
// - avoid_print
// analyzer:
// language:
// strict-casts: true
// 推荐规则集:
// package:lints/recommended.yaml
// Flutter 项目用 flutter_lints
// dart analyze 会应用这些规则

调试

print 打点调试最常用。assert 检查不变量,IDE 断点支持逐步执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void main() {
final total = calculate(3, 4);
print('调试: total=$total');
assert(total > 0, '结果应为正');
print(total);
}
int calculate(int a, int b) {
final sum = a + b;
// dart:developer 的 log 支持分级:
// log('中间值 $sum', name: 'calc');
return sum;
}
// print 输出到控制台
// dart:developer 的 log 支持分级
// 断言在发布模式被移除
// 断点调试支持逐步执行
// debugPrint 用于 Flutter 大量日志

官方链接

直达官方文档与资源。

关于本速查

本页是 Dart 3 的自包含速查手册,覆盖语言核心与最常用标准库在真实项目中约 80% 的常见用法。内容偏向现代惯用法:健全空安全、record 与 pattern、switch 表达式、sealed class、级联运算符,以及基于 isolate 的并发模型。Dart 由 Google 设计,是 Flutter 的底层语言,支持 JIT 快速开发与 AOT 高效发布两种执行方式。权威参考见官方 Dart 语言指南与 Effective Dart。 19 个章节各自聚焦一个主题——从第一个程序到 isolate、常见误区与构建测试。每节拆成 8 个带示例的小节(每个 5–15 行),共约 152 个主题。代码片段刻意短小、自解释,注释用中文便于对照学习。 所有处理都在浏览器中完成——无上传、无追踪。本页是 GuruToolkit 免费开发者工具集的一部分;代码片段可自由使用,无任何担保。

版本 2.1.0