Article / 2026/05/31

Dart 语法完整参考手册

适用版本:Dart 3.x 本手册系统整理 Dart 语言的核心语法,每个知识点均配有详细说明、完整示例代码及运行输出结果,适合快速查阅与深入学习。

Dart 语法完整参考手册

适用版本:Dart 3.x 本手册系统整理 Dart 语言的核心语法,每个知识点均配有详细说明、完整示例代码及运行输出结果,适合快速查阅与深入学习。


目录

  1. 变量与类型
  2. 字符串
  3. 运算符
  4. 控制流
  5. 函数
  6. 类与面向对象
  7. 集合类型
  8. 空安全
  9. 泛型
  10. 扩展方法与扩展类型
  11. 模式匹配与解构(Dart 3.0+)
  12. Record 类型(Dart 3.0+)
  13. 异步编程
  14. 异常处理
  15. 枚举
  16. 库与导入

1. 变量与类型

Dart 是一门强类型语言,但借助类型推断,大多数情况下无需手动标注类型。Dart 的类型系统在空安全(Null Safety)的加持下,能在编译期捕获大量潜在错误。

1.1 变量声明

Dart 提供了多种变量声明方式,每种都有其适用场景:

  • var:最常用,类型由初始值推断,一旦推断后不可改变类型。
  • 显式类型:直接写出类型名(如 Stringint),代码更具自文档性。
  • final:运行时常量。变量只能赋值一次,赋值后不可再改变引用,但对象本身内容可变(如 List 可以 add)。
  • const:编译时常量。比 final 更严格,值必须在编译期就确定,整个对象都是不可变的,性能更优。
  • late:延迟初始化。告诉编译器”这个变量稍后一定会赋值”,常用于依赖注入或初始化耗时的场景。
  • dynamic:动态类型,完全跳过编译期类型检查,灵活但不安全,应尽量少用。
// var:编译器推断类型为 String,后续不可赋值为其他类型
var name = 'Dart';
var age  = 10;
print(name);              // 输出: Dart
print(age);               // 输出: 10
print(name.runtimeType);  // 输出: String

// 显式类型声明,提高可读性
String lang    = 'Dart';
int    version = 3;
double pi      = 3.14159;
bool   isAwesome = true;
print('$lang $version $pi $isAwesome');
// 输出: Dart 3 3.14159 true

// final:运行时常量,只能赋值一次
final String greeting = 'Hello';
// greeting = 'Hi';  // ❌ 编译错误:final 变量不可重赋值

// const:编译时常量,整个对象不可变
const int maxSize = 100;
const List<int> primes = [2, 3, 5, 7, 11];
print(maxSize);  // 输出: 100
print(primes);   // 输出: [2, 3, 5, 7, 11]

// late:声明时不赋值,使用前必须赋值,否则运行时报错
late String lazyValue;
lazyValue = 'initialized later';
print(lazyValue); // 输出: initialized later

// dynamic:完全动态类型,可赋值为任意类型
dynamic anything = 42;
print(anything);            // 输出: 42
anything = 'now a string';  // 可以重新赋为不同类型
print(anything);            // 输出: now a string
anything = [1, 2, 3];
print(anything);            // 输出: [1, 2, 3]

1.2 内置数据类型

Dart 的所有类型都继承自 Object(可空时为 Object?)。内置类型包括数字、布尔、字符串、列表、映射、集合、符文和符号。

// ── 整数 int ──────────────────────────────────────
// int 在 Dart VM 上为 64 位有符号整数,Web 上为 JavaScript number
int a   = 42;
int hex = 0xFF;    // 十六进制字面量
print(a);          // 输出: 42
print(hex);        // 输出: 255

// ── 浮点数 double ─────────────────────────────────
// 64 位 IEEE 754 双精度浮点
double b   = 3.14;
double sci = 1.42e5;             // 科学计数法
double inf = double.infinity;    // 正无穷
double nan = double.nan;         // 非数字
print(b);    // 输出: 3.14
print(sci);  // 输出: 142000.0
print(inf);  // 输出: Infinity
print(nan);  // 输出: NaN

// ── num:int 和 double 的公共父类 ─────────────────
num x = 10;
x = 10.5;   // 可以重新赋为 double
print(x);   // 输出: 10.5

// ── 布尔 bool ─────────────────────────────────────
// Dart 的 bool 只有 true 和 false,非零数字不是 true
bool t = true;
bool f = false;
print(t && f);  // 输出: false(与)
print(t || f);  // 输出: true (或)
print(!t);      // 输出: false(非)

// ── 数字常用方法 ───────────────────────────────────
print(42.isEven);              // 输出: true  (是否偶数)
print(42.isOdd);               // 输出: false (是否奇数)
print(-5.abs());               // 输出: 5     (绝对值)
print(255.toRadixString(16));  // 输出: ff    (转十六进制字符串)
print(3.14.ceil());            // 输出: 4     (向上取整)
print(3.14.floor());           // 输出: 3     (向下取整)
print(3.14.round());           // 输出: 3     (四舍五入)
print(3.7.round());            // 输出: 4
print(3.14.truncate());        // 输出: 3     (截断小数部分)
print(3.14.toStringAsFixed(1)); // 输出: 3.1  (保留指定小数位)

// ── 类型转换 ──────────────────────────────────────
// 字符串 → 数字(解析失败抛 FormatException)
print(int.parse('42'));         // 输出: 42
print(double.parse('3.14'));    // 输出: 3.14

// 安全解析(失败返回 null 而非抛异常)
print(int.tryParse('abc'));     // 输出: null
print(int.tryParse('99'));      // 输出: 99

// 数字 → 字符串
print(42.toString());           // 输出: 42
print(3.14.toInt());            // 输出: 3      (截断,非四舍五入)
print(42.toDouble());           // 输出: 42.0

2. 字符串

Dart 的字符串是 UTF-16 编码的不可变字符序列。支持单引号、双引号、三引号多行字符串,以及原始字符串(r 前缀)。字符串插值($${})让拼接变得简洁高效。

2.1 字符串定义与插值

// 单引号和双引号等价,选用对内容更方便的那种
var s1 = 'He said "hello"';   // 内含双引号时用单引号
var s2 = "It's fine";         // 内含单引号时用双引号
print(s1);  // 输出: He said "hello"
print(s2);  // 输出: It's fine

// 字符串插值:$ 直接跟变量名,${} 内可写任意表达式
var name = 'Dart';
print('Hello, $name!');                // 输出: Hello, Dart!
print('Length: ${name.length}');       // 输出: Length: 4
print('Upper: ${name.toUpperCase()}'); // 输出: Upper: DART
print('Sum: ${1 + 2 + 3}');           // 输出: Sum: 6

// 三引号多行字符串:保留换行和缩进
var multiLine = '''
第一行
第二行
第三行
''';
print(multiLine);
// 输出:
// 第一行
// 第二行
// 第三行

// 原始字符串(r 前缀):反斜杠不被解析为转义字符
// 常用于正则表达式和 Windows 路径
var raw = r'C:\Users\name\Documents';
var regex = r'\d+\.\d+';
print(raw);   // 输出: C:\Users\name\Documents
print(regex); // 输出: \d+\.\d+

// 相邻字符串字面量在编译期自动拼接(不需要 + 号)
var longStr = 'Hello '
              'World '
              'Dart';
print(longStr);  // 输出: Hello World Dart

// + 运算符拼接(运行时)
var s3 = 'Hello' + ', ' + 'Dart!';
print(s3);  // 输出: Hello, Dart!

2.2 字符串常用方法

字符串方法非常丰富,以下是最常用的分类整理。注意:Dart 字符串是不可变的,所有”修改”方法实际上都返回一个新字符串。

var s = 'Hello, Dart!';

// ── 基本属性 ──────────────────────────────────────
print(s.length);           // 输出: 12  (字符数量)
print(s.isEmpty);          // 输出: false
print(s.isNotEmpty);       // 输出: true

// ── 大小写转换 ────────────────────────────────────
print(s.toUpperCase());    // 输出: HELLO, DART!
print(s.toLowerCase());    // 输出: hello, dart!

// ── 查找与判断 ────────────────────────────────────
print(s.contains('Dart')); // 输出: true
print(s.startsWith('He')); // 输出: true
print(s.endsWith('!'));    // 输出: true
print(s.indexOf('Dart'));  // 输出: 7   (第一次出现的位置,没有返回 -1)
print(s.lastIndexOf('l')); // 输出: 10  (最后一次出现的位置)

// ── 截取与分割 ────────────────────────────────────
print(s.substring(7));     // 输出: Dart!       (从索引 7 到末尾)
print(s.substring(7, 11)); // 输出: Dart        (索引 7 到 11,不含 11)
print(s.split(', '));      // 输出: [Hello, Dart!]
print('a,b,,c'.split(',')); // 输出: [a, b, , c](保留空字符串)

// ── 替换 ──────────────────────────────────────────
print(s.replaceAll('l', 'L'));         // 输出: HeLLo, Dart!
print(s.replaceFirst('l', 'L'));       // 输出: HeLlo, Dart!(只替换第一个)
print(s.replaceRange(0, 5, 'Hi'));     // 输出: Hi, Dart!

// ── 去除空白 ──────────────────────────────────────
var padded = '   hello   ';
print(padded.trim());       // 输出: hello(去除两端空白)
print(padded.trimLeft());   // 输出: hello   (只去左边)
print(padded.trimRight());  // 输出:    hello(只去右边)

// ── 填充与重复 ────────────────────────────────────
print('5'.padLeft(3, '0'));    // 输出: 005 (左填充,常用于数字格式化)
print('hi'.padRight(5, '.'));  // 输出: hi...(右填充)
print('ab' * 3);               // 输出: ababab(重复)

// ── 字符操作 ──────────────────────────────────────
// codeUnitAt 返回指定位置的 UTF-16 码元值
print('A'.codeUnitAt(0));       // 输出: 65
print(String.fromCharCode(65)); // 输出: A

// Runes:处理 Unicode 补充字符(如 emoji)
var emoji = '\u{1F600}';   // 需用 \u{} 来表示 emoji
print(emoji);              // 输出: 😀
print(emoji.runes.first);  // 输出: 128512(Unicode 码点)

// ── 比较 ──────────────────────────────────────────
print('abc'.compareTo('abd'));  // 输出: -1(字典序小于)
print('abc'.compareTo('abc'));  // 输出:  0(相等)
print('abd'.compareTo('abc'));  // 输出:  1(字典序大于)

3. 运算符

Dart 的运算符大多与 C/Java 类似,但额外提供了几个非常实用的特有运算符,尤其是与空安全配合的 ?.????=,以及对同一对象进行链式操作的级联运算符 ..

3.1 算术与比较运算符

// ── 算术运算符 ────────────────────────────────────
print(10 + 3);   // 输出: 13   (加法)
print(10 - 3);   // 输出: 7    (减法)
print(10 * 3);   // 输出: 30   (乘法)
print(10 / 3);   // 输出: 3.33...(除法,结果始终是 double)
print(10 ~/ 3);  // 输出: 3    (整除,结果是 int,舍弃小数部分)
print(10 % 3);   // 输出: 1    (取余/取模)
print(-(10));    // 输出: -10  (一元取反)

// ── 比较运算符 ────────────────────────────────────
// 所有比较运算符都返回 bool
print(1 == 1);   // 输出: true (相等,比较值;对象用 identical() 比较引用)
print(1 != 2);   // 输出: true (不等)
print(3 > 2);    // 输出: true (大于)
print(3 >= 3);   // 输出: true (大于等于)
print(2 < 3);    // 输出: true (小于)
print(2 <= 2);   // 输出: true (小于等于)

3.2 赋值与复合赋值运算符

int x = 10;

// 复合赋值:op= 是 x = x op value 的简写
x += 5;   print(x);  // 输出: 15  (等同于 x = x + 5)
x -= 3;   print(x);  // 输出: 12
x *= 2;   print(x);  // 输出: 24
x ~/= 5;  print(x);  // 输出: 4   (整除赋值)
x %= 3;   print(x);  // 输出: 1

// ??= 空值赋值:仅当变量为 null 时才赋值
// 非常适合"惰性初始化"场景
String? cache;
cache ??= computeExpensiveValue();  // 若 cache 为 null,则赋值并缓存
print(cache);  // 输出: expensive result(仅计算一次)
cache ??= 'ignored';  // cache 已有值,不覆盖
print(cache);  // 输出: expensive result

3.3 逻辑与位运算符

// ── 逻辑运算符(短路求值)────────────────────────
// && 左侧为 false 时不计算右侧;|| 左侧为 true 时不计算右侧
print(true && false);   // 输出: false
print(true || false);   // 输出: true
print(!true);           // 输出: false

// ── 位运算符(仅用于整数)────────────────────────
print(0x0F & 0xFF);   // 输出: 15  (按位与:0000_1111 & 1111_1111)
print(0x0F | 0xF0);   // 输出: 255 (按位或:0000_1111 | 1111_0000)
print(0x0F ^ 0xFF);   // 输出: 240 (按位异或:0000_1111 ^ 1111_1111)
print(~0x0F);         // 输出: -16 (按位取反,有符号结果)
print(1 << 4);        // 输出: 16  (左移:1 移 4 位)
print(256 >> 4);      // 输出: 16  (右移:256 移 4 位)
print(256 >>> 4);     // 输出: 16  (无符号右移,Dart 2.14+)

3.4 特殊运算符

这些是 Dart 独有或特别重要的运算符,需要重点掌握。

// ── 条件表达式(三元运算符)─────────────────────
int score = 85;
String grade = score >= 60 ? 'pass' : 'fail';
print(grade);  // 输出: pass

// condition ? exprIfTrue : exprIfFalse 可以嵌套,但嵌套多层时建议改用 if-else
String level = score >= 90 ? 'A' : score >= 75 ? 'B' : 'C';
print(level);  // 输出: B

// ── 级联运算符 .. 和 ?.. ─────────────────────────
// .. 允许对同一个对象连续调用多个方法或设置多个属性,最终返回该对象本身
// 等价于多次写 sb.xxx(),但无需重复写对象名
var sb = StringBuffer();
sb
  ..write('Hello')
  ..write(', ')
  ..write('Dart!')
  ..writeln();
print(sb.toString());  // 输出: Hello, Dart!\n

// ?.. 空安全级联:对象为 null 时整个级联被跳过
StringBuffer? buffer;
buffer?..write('这行不会执行');  // buffer 为 null,安全跳过
print(buffer);  // 输出: null

// ── 类型检测运算符 ───────────────────────────────
// is:检查对象是否是某类型(包括子类)
print(42 is int);         // 输出: true
print(42 is num);         // 输出: true(int 是 num 的子类)
print(42 is! String);     // 输出: true(is! 是 is 的否定)
print('hi' is Object);    // 输出: true(一切都是 Object)

// as:强制类型转换;若类型不匹配则抛 TypeError
Object obj = 'Hello';
String str = obj as String;
print(str.length);  // 输出: 5
// int wrongCast = obj as int;  // ❌ 运行时抛 TypeError

// ── 展开运算符 ... 和 ...? ───────────────────────
// 将集合内的元素展开插入另一个集合,语法简洁且高效
var list1 = [1, 2, 3];
var list2 = [4, 5, 6];
var merged = [0, ...list1, ...list2, 7];
print(merged);  // 输出: [0, 1, 2, 3, 4, 5, 6, 7]

// ...? 空安全展开:若集合为 null 则跳过,不插入任何元素
List<int>? nullable;
var safe = [0, ...?nullable, 1];
print(safe);    // 输出: [0, 1]

4. 控制流

Dart 的控制流语法与 Java/C++ 类似,但 Dart 3.0 对 switch 进行了重大升级:不仅支持 switch 语句,还引入了 switch 表达式(可直接赋值),并支持模式匹配和守卫条件(when 子句)。

4.1 if / else

// 基本 if-else if-else 结构
int score = 75;

if (score >= 90) {
  print('优秀');        // score >= 90 时执行
} else if (score >= 75) {
  print('良好');        // 输出: 良好
} else if (score >= 60) {
  print('及格');
} else {
  print('不及格');
}

// if-case(Dart 3.0+):结合模式匹配进行条件判断
// 当 value 是 int 类型且大于 0 时,将其绑定到变量 n
Object value = 42;
if (value case int n when n > 0) {
  print('正整数: $n');  // 输出: 正整数: 42
}

// 单行 if(省略花括号):仅用于简单语句,不推荐
if (score > 60) print('passed');  // 输出: passed

4.2 switch 语句与表达式

// ── 传统 switch 语句 ──────────────────────────────
// 每个 case 需要 break(或 return/throw),否则会 fallthrough
// Dart 允许多个 case 标签共享同一个分支(空 case 实现 fallthrough)
String day = 'Mon';
switch (day) {
  case 'Mon':
  case 'Tue':
  case 'Wed':
  case 'Thu':
  case 'Fri':
    print('工作日');  // 输出: 工作日
    break;
  case 'Sat':
  case 'Sun':
    print('周末');
    break;
  default:
    print('未知');
}

// ── Dart 3.0+ switch 表达式 ──────────────────────
// switch 表达式可以直接用在赋值、return 等需要值的地方
// 语法更简洁,不需要 break,用 => 代替 case 体
// _ 是通配符,匹配任何值(相当于 default)
var label = switch (day) {
  'Mon' || 'Tue' || 'Wed' || 'Thu' || 'Fri' => '工作日',  // || 匹配多个值
  'Sat' || 'Sun' => '周末',
  _ => '未知',
};
print(label);  // 输出: 工作日

// switch 表达式支持 when 守卫条件
int n = 15;
var desc = switch (n) {
  int x when x < 0  => '负数',
  0                  => '零',
  int x when x < 10 => '个位数',
  _                  => '10 及以上',   // 输出: 10 及以上
};
print(desc);

// switch 配合类型模式(Dart 3.0+)
// 编译器可以检查是否穷举了所有可能(sealed class 配合使用时)
void classify(Object obj) {
  var result = switch (obj) {
    int i    => '整数: $i',
    double d => '浮点数: $d',
    String s => '字符串: "$s"',
    bool b   => '布尔: $b',
    _        => '其他类型',
  };
  print(result);
}
classify(42);      // 输出: 整数: 42
classify(3.14);    // 输出: 浮点数: 3.14
classify('hi');    // 输出: 字符串: "hi"
classify(true);    // 输出: 布尔: true
classify([1,2]);   // 输出: 其他类型

4.3 循环

// ── for 循环(经典三段式)────────────────────────
for (var i = 0; i < 3; i++) {
  print(i);
}
// 输出: 0  1  2

// ── for-in 循环(遍历 Iterable)─────────────────
// 适用于 List、Set、Map.keys 等任何可迭代对象
for (var item in ['a', 'b', 'c']) {
  print(item);
}
// 输出: a  b  c

// 遍历 Map 的 entries,同时获取 key 和 value
var map = {'x': 1, 'y': 2};
for (var entry in map.entries) {
  print('${entry.key}: ${entry.value}');
}
// 输出: x: 1  y: 2

// ── forEach 方法(函数式风格)────────────────────
// 适合简单操作;注意:forEach 内不能使用 break/continue
[1, 2, 3].forEach((n) => print(n * 2));
// 输出: 2  4  6

// ── while 循环:先判断条件,条件满足才执行 ────────
int i = 0;
while (i < 3) {
  print('while: $i');
  i++;
}
// 输出: while: 0  while: 1  while: 2

// ── do-while 循环:先执行一次,再判断条件 ─────────
// 适合"至少执行一次"的场景,如用户输入校验
int j = 0;
do {
  print('do: $j');
  j++;
} while (j < 3);
// 输出: do: 0  do: 1  do: 2

// ── break 和 continue ────────────────────────────
// break:立即退出当前循环
// continue:跳过当前迭代,进入下一次迭代
for (var k = 0; k < 6; k++) {
  if (k == 2) continue;  // 跳过 k == 2
  if (k == 5) break;     // k == 5 时退出整个循环
  print(k);
}
// 输出: 0  1  3  4

// ── 带标签的 break:跳出嵌套循环 ─────────────────
// 标签名紧跟在循环关键字前,break/continue 后接标签名
outer:
for (var row = 0; row < 3; row++) {
  for (var col = 0; col < 3; col++) {
    if (row == 1 && col == 1) break outer;  // 直接退出外层循环
    print('($row, $col)');
  }
}
// 输出: (0,0)  (0,1)  (0,2)  (1,0)

5. 函数

在 Dart 中,函数是一等公民(first-class citizen),意味着函数可以赋值给变量、作为参数传递给另一个函数、从函数中返回。这为函数式编程风格奠定了基础。

5.1 基本函数定义

// 标准函数:指定返回类型和参数类型(推荐)
int add(int a, int b) {
  return a + b;
}
print(add(3, 4));   // 输出: 7

// 箭头函数(Arrow Function):函数体只有单个表达式时的简写
// => expr 等价于 { return expr; }
int multiply(int a, int b) => a * b;
print(multiply(3, 4));  // 输出: 12

// void 函数:无返回值(也可省略 void,但不推荐)
void greet(String name) {
  print('Hello, $name!');
}
greet('Dart');  // 输出: Hello, Dart!

// 函数是对象:可以赋值给变量
var fn = add;
print(fn(10, 20));          // 输出: 30
print(fn.runtimeType);      // 输出: (int, int) => int

// 函数可以作为参数
int applyTwice(int Function(int) f, int x) => f(f(x));
print(applyTwice((n) => n * 2, 3));  // 输出: 12(3*2=6, 6*2=12)

5.2 参数类型详解

Dart 函数参数分为三种:位置必需参数、命名参数和位置可选参数,可以灵活组合。

// ── 位置必需参数(默认)────────────────────────────
// 调用时必须按顺序传入,不能省略
int subtract(int a, int b) => a - b;
print(subtract(10, 3));   // 输出: 7
// subtract(b: 3, a: 10);  // ❌ 位置参数不能用名字

// ── 命名参数(用 {} 包裹)────────────────────────
// 调用时通过 参数名: 值 传入,顺序无关
// required 标注的命名参数必须传,否则编译错误
// 不带 required 的命名参数可选,但需要提供默认值或声明为可空类型
void createUser({
  required String name,        // 必须传
  required int age,            // 必须传
  String role = 'user',        // 可选,有默认值
  String? email,               // 可选,可空类型
}) {
  print('$name ($age) [$role] ${email ?? "无邮箱"}');
}

createUser(name: 'Alice', age: 30);
// 输出: Alice (30) [user] 无邮箱

createUser(name: 'Bob', age: 25, role: 'admin', email: 'bob@example.com');
// 输出: Bob (25) [admin] bob@example.com

// ── 位置可选参数(用 [] 包裹)────────────────────
// 调用时可以按顺序传入,也可以省略;需要提供默认值或声明为可空
String describe(String item, [String? size, int count = 1]) {
  var sizeStr = size != null ? '$size ' : '';
  return '$count x ${sizeStr}$item';
}
print(describe('apple'));                // 输出: 1 x apple
print(describe('apple', 'large'));       // 输出: 1 x large apple
print(describe('apple', 'large', 3));   // 输出: 3 x large apple

// ── 混用规则 ──────────────────────────────────────
// 位置必需参数必须在最前面
// 命名参数用 {},位置可选参数用 [],二者只能用其中一种
void example(int required, {String? named}) {}     // ✅
void example2(int required, [String? optional]) {} // ✅
// void example3(int req, {String? named}, [int opt]) {} // ❌ 不合法

5.3 匿名函数、闭包与高阶函数

// ── 匿名函数(Lambda)────────────────────────────
// 没有名字的函数,常用于传递给高阶函数
var double = (int x) { return x * 2; };
var triple = (int x) => x * 3;    // 箭头简写
print(double(5));   // 输出: 10
print(triple(5));   // 输出: 15

// ── 闭包(Closure)───────────────────────────────
// 闭包是捕获了外部变量的函数。外部函数执行完毕后,被捕获的变量依然存活
// 常见用途:计数器、记忆化(memoization)、工厂模式
Function makeCounter({int start = 0}) {
  int count = start;  // 这个变量被闭包捕获
  return () => ++count;
}
var counter1 = makeCounter();
var counter2 = makeCounter(start: 10);
print(counter1());  // 输出: 1
print(counter1());  // 输出: 2
print(counter2());  // 输出: 11  (独立的 count 变量)
print(counter1());  // 输出: 3   (counter1 的状态不受 counter2 影响)

// 闭包工厂:生成不同参数的函数
Function makeMultiplier(int factor) => (int x) => x * factor;
var double2 = makeMultiplier(2);
var triple2 = makeMultiplier(3);
print(double2(5));  // 输出: 10
print(triple2(5));  // 输出: 15

// ── 高阶函数(Higher-Order Functions)────────────
// 接受函数作为参数,或返回函数的函数
// Dart 的集合类提供了丰富的高阶方法

var nums = [1, 2, 3, 4, 5, 6];

// map:将每个元素转换为新值,返回惰性 Iterable
print(nums.map((n) => n * n).toList());
// 输出: [1, 4, 9, 16, 25, 36]

// where:过滤满足条件的元素(类似 filter)
print(nums.where((n) => n.isEven).toList());
// 输出: [2, 4, 6]

// reduce:将所有元素聚合为单个值(集合不能为空)
print(nums.reduce((acc, n) => acc + n));
// 输出: 21

// fold:带初始值的聚合(比 reduce 更安全,可用于空集合)
print(nums.fold(0, (acc, n) => acc + n));
// 输出: 21
print(nums.fold(1, (acc, n) => acc * n));
// 输出: 720(阶乘:1×2×3×4×5×6)

// any:检查是否存在满足条件的元素(有一个 true 即返回 true)
print(nums.any((n) => n > 5));   // 输出: true
print(nums.any((n) => n > 10));  // 输出: false

// every:检查是否所有元素都满足条件
print(nums.every((n) => n > 0));  // 输出: true
print(nums.every((n) => n > 3));  // 输出: false

// firstWhere / lastWhere:找到第一个/最后一个满足条件的元素
print(nums.firstWhere((n) => n > 3));  // 输出: 4
print(nums.lastWhere((n) => n < 4));   // 输出: 3

// takeWhile / skipWhile:前缀取/跳
print(nums.takeWhile((n) => n < 4).toList());  // 输出: [1, 2, 3]
print(nums.skipWhile((n) => n < 4).toList());  // 输出: [4, 5, 6]

// 链式调用:组合多个操作,形成数据处理管道
var result = nums
    .where((n) => n.isEven)       // 过滤出偶数: [2, 4, 6]
    .map((n) => n * n)             // 平方: [4, 16, 36]
    .where((n) => n > 10)          // 过滤大于 10 的: [16, 36]
    .toList();
print(result);  // 输出: [16, 36]

5.4 生成器函数

生成器函数用于惰性产生一系列值,避免一次性生成全部数据占用大量内存。

// ── 同步生成器(sync*)────────────────────────────
// 返回 Iterable,用 yield 逐个产生值
// 优势:惰性求值,只有被消费时才计算下一个值
Iterable<int> range(int start, int end, [int step = 1]) sync* {
  for (var i = start; i <= end; i += step) {
    yield i;  // 每次 yield 暂停函数并产出一个值
  }
}
print(range(1, 5).toList());        // 输出: [1, 2, 3, 4, 5]
print(range(0, 10, 2).toList());    // 输出: [0, 2, 4, 6, 8, 10]

// yield*:将另一个 Iterable 的所有值依次产出(相当于展开)
Iterable<int> concat(Iterable<int> a, Iterable<int> b) sync* {
  yield* a;
  yield* b;
}
print(concat([1, 2], [3, 4]).toList());  // 输出: [1, 2, 3, 4]

// 实际应用:生成斐波那契数列(惰性无限序列)
Iterable<int> fibonacci() sync* {
  var a = 0, b = 1;
  while (true) {
    yield a;
    var next = a + b;
    a = b;
    b = next;
  }
}
// take(10) 只取前 10 个,不会无限循环
print(fibonacci().take(10).toList());
// 输出: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

6. 类与面向对象

Dart 是一门真正的面向对象语言,所有值都是对象(包括数字和 null)。Dart 支持单继承、接口(所有类都可作为接口)、Mixin(灵活的代码复用机制)和抽象类。Dart 3.0 进一步引入了密封类(sealed class)用于穷举式模式匹配。

6.1 基础类定义

class Person {
  // ── 实例变量(Instance Variables)────────────────
  // 每个实例拥有独立的副本
  String name;
  int age;

  // ── 类变量(静态变量)────────────────────────────
  // 所有实例共享,通过类名访问
  static int totalCount = 0;

  // ── 构造函数参数简写(Initializing Formals)──────
  // this.name 等价于在构造体中写 this.name = name;
  Person(this.name, this.age) {
    totalCount++;
    print('创建了 $name');
  }

  // ── 命名构造函数(Named Constructor)─────────────
  // 用于提供多种创建对象的方式,增强可读性
  Person.anonymous() : name = 'Anonymous', age = 0;

  // 从 Map 创建(常见于 JSON 解析场景)
  Person.fromMap(Map<String, dynamic> map)
      : name = map['name'] as String,
        age  = map['age']  as int;

  // ── 工厂构造函数(Factory Constructor)───────────
  // 不一定创建新对象(可返回缓存),也可用于实现单例
  // factory 构造函数不能访问 this
  factory Person.adult(String name) {
    return Person(name, 18);  // 必须返回 Person 实例(或子类)
  }

  // ── getter:计算属性,像访问字段一样使用 ─────────
  String get info => '$name ($age 岁)';
  bool   get isAdult => age >= 18;

  // ── setter:赋值时执行自定义逻辑(如校验)────────
  set setAge(int val) {
    if (val < 0 || val > 150) throw ArgumentError('年龄不合法: $val');
    age = val;
  }

  // ── 实例方法 ──────────────────────────────────────
  void introduce() {
    print('我是 $name,今年 $age 岁。${isAdult ? "已成年。" : "未成年。"}');
  }

  // ── 静态方法:不能访问实例成员 ────────────────────
  static void showTotal() => print('共创建了 $totalCount 人');

  // ── 重写 toString:print 时自动调用 ───────────────
  @override
  String toString() => 'Person(name: $name, age: $age)';

  // ── 重写 == 和 hashCode:使对象支持值相等比较 ─────
  @override
  bool operator ==(Object other) =>
      other is Person && other.name == name && other.age == age;

  @override
  int get hashCode => Object.hash(name, age);
}

// 使用示例
var p1 = Person('Alice', 30);     // 输出: 创建了 Alice
p1.introduce();   // 输出: 我是 Alice,今年 30 岁。已成年。
print(p1.info);   // 输出: Alice (30 岁)
print(p1);        // 输出: Person(name: Alice, age: 30)

var p2 = Person.fromMap({'name': 'Bob', 'age': 25}); // 输出: 创建了 Bob
var p3 = Person.adult('Charlie');  // 输出: 创建了 Charlie
print(p3.age);    // 输出: 18

Person.showTotal(); // 输出: 共创建了 3 人
print(p1 == Person('Alice', 30));  // 输出: true(值相等)

6.2 继承(Inheritance)

// Dart 使用单继承,extends 关键字继承父类
// 子类自动继承父类的所有非私有成员

class Animal {
  String name;
  Animal(this.name);

  // 父类的普通方法(可被子类覆盖)
  void speak() => print('$name: ...');

  @override
  String toString() => '$runtimeType($name)';
}

class Dog extends Animal {
  final String breed;

  // 调用父类构造函数用 super(),在初始化列表中进行
  Dog(String name, this.breed) : super(name);

  // 命名构造 + 父类构造
  Dog.puppy(String name) : breed = 'Mixed', super(name);

  // @override 覆盖父类方法(加注解有助于编译器检查)
  @override
  void speak() => print('$name: Woof! Woof!');

  // super.xxx() 显式调用父类实现
  void fullInfo() {
    super.speak();                     // 调用 Animal.speak
    print('品种: $breed');
  }
}

var dog = Dog('Buddy', 'Labrador');
dog.speak();   // 输出: Buddy: Woof! Woof!
dog.fullInfo();
// 输出: Buddy: ...
// 输出: 品种: Labrador

// 多态:父类引用指向子类对象,调用时使用子类的实现
Animal animal = Dog('Rex', 'Poodle');
animal.speak();          // 输出: Rex: Woof! Woof!(动态分发到 Dog.speak)
print(animal is Dog);    // 输出: true
print(animal is Animal); // 输出: true

// 向下转型
if (animal is Dog) {
  print(animal.breed);  // 输出: Poodle(类型已提升为 Dog)
}

6.3 抽象类与接口

// 抽象类:包含抽象方法,不能被直接实例化
// 用于定义一组规范(接口),子类必须实现所有抽象成员
abstract class Shape {
  // 抽象成员:只有声明,没有实现
  double get area;
  double get perimeter;

  // 具体方法:抽象类可以包含有实现的方法
  void describe() {
    print('${runtimeType}: 面积=${area.toStringAsFixed(2)}, '
          '周长=${perimeter.toStringAsFixed(2)}');
  }
}

class Circle extends Shape {
  final double radius;
  Circle(this.radius);

  @override double get area      => 3.14159 * radius * radius;
  @override double get perimeter => 2 * 3.14159 * radius;
}

class Rectangle extends Shape {
  final double width, height;
  Rectangle(this.width, this.height);

  @override double get area      => width * height;
  @override double get perimeter => 2 * (width + height);
}

Circle(5).describe();      // 输出: Circle: 面积=78.54, 周长=31.42
Rectangle(4, 6).describe(); // 输出: Rectangle: 面积=24.00, 周长=20.00

// implements:以另一个类作为接口来实现
// 与 extends 不同,implements 不继承任何实现,必须自己实现所有成员
// 一个类可以 implements 多个接口
class Square implements Shape {
  final double side;
  Square(this.side);

  @override double get area      => side * side;
  @override double get perimeter => 4 * side;
}
Square(5).describe();  // 输出: Square: 面积=25.00, 周长=20.00

6.4 Mixin

// Mixin 是一种代码复用机制,可以将一组方法"混入"到类中
// 解决了单继承无法复用多个类功能的问题

mixin Flyable {
  int _altitude = 0;
  void fly() {
    _altitude = 1000;
    print('$runtimeType 正在飞行,高度: ${_altitude}m');
  }
  void land() {
    _altitude = 0;
    print('$runtimeType 已降落');
  }
}

mixin Swimmable {
  void swim() => print('$runtimeType 正在游泳');
}

mixin Singable {
  void sing() => print('$runtimeType 正在唱歌');
}

class Animal { String name; Animal(this.name); }
class Bird   extends Animal with Flyable           { Bird(super.name); }
class Fish   extends Animal with Swimmable         { Fish(super.name); }
class Duck   extends Animal with Flyable, Swimmable, Singable { Duck(super.name); }

Bird('老鹰').fly();    // 输出: Bird 正在飞行,高度: 1000m
Fish('金鱼').swim();   // 输出: Fish 正在游泳
var duck = Duck('唐老鸭');
duck.fly();            // 输出: Duck 正在飞行,高度: 1000m
duck.swim();           // 输出: Duck 正在游泳
duck.sing();           // 输出: Duck 正在唱歌

// on 约束:限制 mixin 只能用于特定类或其子类
mixin Musical on Animal {
  // 可以访问 Animal 的成员(因为约束了 on Animal)
  void sing() => print('${name} 唱起了歌');
}
class Parrot extends Animal with Musical { Parrot(super.name); }
Parrot('鹦鹉').sing();  // 输出: 鹦鹉 唱起了歌

6.5 密封类(Dart 3.0+)

密封类用 sealed 修饰,其所有子类必须在同一个文件中定义。这让编译器能够在 switch 中检查是否穷举了所有可能的子类,从而避免遗漏分支。

// 密封类常用于表示有限的状态集合(类似 Kotlin sealed class、Rust enum)
sealed class Result<T> {}

class Success<T> extends Result<T> {
  final T data;
  const Success(this.data);
}

class Failure<T> extends Result<T> {
  final String error;
  final int code;
  const Failure(this.error, this.code);
}

class Loading<T> extends Result<T> {
  const Loading();
}

// 处理 Result:编译器检查穷举性,若漏写某个子类会警告
String handleResult(Result<int> result) => switch (result) {
  Success(data: var d) => '成功,数据: $d',
  Failure(error: var e, code: var c) => '失败[$c]: $e',
  Loading() => '加载中...',
};

print(handleResult(Success(42)));            // 输出: 成功,数据: 42
print(handleResult(Failure('Not Found', 404))); // 输出: 失败[404]: Not Found
print(handleResult(Loading()));              // 输出: 加载中...

7. 集合类型

Dart 的集合系统丰富而强大,核心类型有三种:List(有序列表)、Map(键值映射)、Set(无重复集合)。三者都支持泛型、字面量语法、以及 collection if/for 等编译时构建特性。

7.1 List(有序列表)

// ── 创建 List ─────────────────────────────────────
var list  = [1, 2, 3];           // 字面量,类型推断为 List<int>
var typed = <int>[1, 2, 3];      // 显式指定元素类型
var empty = <String>[];          // 空列表

// fixed-length:固定长度(不能 add/remove),适合性能敏感场景
var fixed = List<int>.filled(3, 0);      // [0, 0, 0]
print(fixed);  // 输出: [0, 0, 0]

// generate:用函数生成元素
var gen = List<int>.generate(5, (i) => i * i);
print(gen);   // 输出: [0, 1, 4, 9, 16]

// ── 增删操作 ──────────────────────────────────────
var fruits = ['apple', 'banana'];
fruits.add('cherry');                 // 末尾添加一个
fruits.addAll(['date', 'elderberry']); // 末尾添加多个
fruits.insert(0, 'avocado');          // 指定位置插入
print(fruits);
// 输出: [avocado, apple, banana, cherry, date, elderberry]

fruits.remove('banana');     // 移除第一个匹配的元素(按值)
fruits.removeAt(0);          // 移除指定索引的元素
fruits.removeWhere((f) => f.length > 6);  // 移除所有满足条件的元素
print(fruits);  // 输出: [apple, date](cherry/elderberry 长度 > 6 被删除)

// ── 查询与访问 ────────────────────────────────────
var nums = [3, 1, 4, 1, 5, 9, 2, 6];
print(nums.length);           // 输出: 8
print(nums.isEmpty);          // 输出: false
print(nums.first);            // 输出: 3
print(nums.last);             // 输出: 6
print(nums[2]);               // 输出: 4  (索引访问,从 0 开始)
print(nums.contains(5));      // 输出: true
print(nums.indexOf(1));       // 输出: 1  (第一次出现的索引)
print(nums.lastIndexOf(1));   // 输出: 3  (最后一次出现的索引)
print(nums.sublist(2, 5));    // 输出: [4, 1, 5]  (子列表,不含末索引)

// ── 排序 ──────────────────────────────────────────
var sorted = [...nums]..sort();  // 复制后升序排序
print(sorted);  // 输出: [1, 1, 2, 3, 4, 5, 6, 9]

// 自定义排序:降序
sorted.sort((a, b) => b.compareTo(a));
print(sorted);  // 输出: [9, 6, 5, 4, 3, 2, 1, 1]

// 按对象属性排序
var people = [('Alice', 30), ('Bob', 25), ('Charlie', 35)];
people.sort((a, b) => a.$2.compareTo(b.$2));  // 按年龄升序
print(people.map((p) => p.$1).toList());  // 输出: [Bob, Alice, Charlie]

// ── 集合构建语法 ──────────────────────────────────
// collection if:条件添加元素
bool includeExtra = true;
var conditional = [1, 2, if (includeExtra) 3, if (!includeExtra) 99];
print(conditional);  // 输出: [1, 2, 3]

// collection for:循环生成元素
var squares = [for (var i = 1; i <= 5; i++) i * i];
print(squares);  // 输出: [1, 4, 9, 16, 25]

// 展开运算符:合并多个列表
var a = [1, 2, 3];
var b = [4, 5, 6];
var merged = [...a, 0, ...b];
print(merged);  // 输出: [1, 2, 3, 0, 4, 5, 6]

7.2 Map(键值映射)

// ── 创建 Map ──────────────────────────────────────
var map  = {'name': 'Dart', 'version': 3};  // 字面量
var typed = <String, int>{'a': 1, 'b': 2};   // 显式类型
var empty = <String, dynamic>{};              // 空 Map

// ── 增删改查 ──────────────────────────────────────
map['author'] = 'Google';            // 新增键值对
map['version'] = 4;                  // 修改已有键的值
print(map);  // 输出: {name: Dart, version: 4, author: Google}

map.remove('author');                // 删除指定键
print(map.containsKey('name'));      // 输出: true
print(map.containsValue(4));        // 输出: true

print(map['name']);                  // 输出: Dart
print(map['missing']);               // 输出: null(键不存在时返回 null)

// 安全访问:提供默认值
print(map['missing'] ?? 'default'); // 输出: default

// ── 遍历 ──────────────────────────────────────────
// forEach 同时拿到 key 和 value
map.forEach((k, v) => print('$k: $v'));
// 输出: name: Dart  version: 4

// for-in 遍历 entries(MapEntry 对象)
for (var entry in map.entries) {
  print('${entry.key} => ${entry.value}');
}

// 只遍历 keys 或 values
print(map.keys.toList());    // 输出: [name, version]
print(map.values.toList());  // 输出: [Dart, 4]

// ── 高级操作 ──────────────────────────────────────
// putIfAbsent:键不存在时才插入(避免覆盖已有值)
map.putIfAbsent('lang', () => 'Dart');
print(map['lang']);  // 输出: Dart

// update:更新已有键的值(键不存在时可提供 ifAbsent 函数)
map.update('version', (v) => (v as int) + 1);
print(map['version']);  // 输出: 5

// map 转换:创建新 Map,每个 entry 通过函数转换
var upper = map.map((k, v) => MapEntry(k.toUpperCase(), v.toString()));
print(upper);  // 输出: {NAME: Dart, VERSION: 5, LANG: Dart}

// 从两个 List 构造 Map
var keys   = ['a', 'b', 'c'];
var values = [1, 2, 3];
var zipped = Map.fromIterables(keys, values);
print(zipped);  // 输出: {a: 1, b: 2, c: 3}

7.3 Set(不重复集合)

// ── 创建 Set ──────────────────────────────────────
// 注意:{} 是空 Map 的字面量,空 Set 需要 <Type>{} 或 Set()
var s1 = {1, 2, 3};                // 字面量,自动去重
var s2 = <String>{};               // 空 Set
var s3 = Set.from([1, 2, 2, 3, 3]); // 从列表构造,自动去重
print(s3);  // 输出: {1, 2, 3}

// ── 增删操作 ──────────────────────────────────────
s1.add(4);
s1.addAll([5, 5, 6]);  // 重复元素会被忽略
print(s1);  // 输出: {1, 2, 3, 4, 5, 6}
s1.remove(1);
print(s1.contains(2));  // 输出: true

// ── 集合运算 ──────────────────────────────────────
var a = {1, 2, 3, 4};
var b = {3, 4, 5, 6};

print(a.union(b));          // 输出: {1, 2, 3, 4, 5, 6}  (并集:所有元素)
print(a.intersection(b));   // 输出: {3, 4}               (交集:共有元素)
print(a.difference(b));     // 输出: {1, 2}               (差集:a 有 b 没有的)
print(b.difference(a));     // 输出: {5, 6}               (差集:b 有 a 没有的)
print(a.isSubsetOf({1,2,3,4,5}));   // 输出: true(a 是否是子集)
print(a.isSupersetOf({1,2}));        // 输出: true(a 是否是超集)

8. 空安全

Dart 2.12 引入了健全的空安全(Sound Null Safety)系统,这是 Dart 语言最重要的特性之一。它将空指针错误从运行时提前到编译时检测,从根本上消除了 Null was not expected 这类运行时崩溃。

核心思想:类型默认不可为空。String 永远不会是 null;只有 String? 才可能为 null。这与大多数语言(Java/Kotlin/Swift 等)中的 Optional 概念类似。

8.1 可空类型基础

// 非空类型(默认):编译器保证这些变量不会是 null
String  nonNull = 'must have a value';
int     count   = 42;
// nonNull = null;  // ❌ 编译错误:String 类型不能为 null

// 可空类型(? 后缀):可以持有该类型的值或 null
String? nullable = null;     // ✅ 合法
print(nullable);              // 输出: null
nullable = 'now has value';
print(nullable);              // 输出: now has value

int?   maybeInt;             // 未初始化的可空变量默认为 null
print(maybeInt);              // 输出: null

8.2 空安全操作符

这些操作符让处理可空值变得简洁而安全,是空安全体系的核心工具。

String? s;

// ── ?. 条件成员访问 ───────────────────────────────
// s?.length:若 s 为 null,整个表达式返回 null;否则返回 s.length
// 避免了手动 if (s != null) 判断,链式调用时特别方便
print(s?.length);          // 输出: null(s 为 null,短路)
print(s?.toUpperCase());   // 输出: null

s = 'hello';
print(s?.length);          // 输出: 5(s 非 null,正常访问)

// 链式条件访问
class Address { String? city; }
class User    { Address? address; }
var user = User();
print(user.address?.city?.toUpperCase());  // 输出: null(任一为 null 则短路)

// ── ?? 空值合并运算符 ──────────────────────────────
// 若左侧为 null,使用右侧的默认值;否则使用左侧的值
String? name;
print(name ?? 'Anonymous');  // 输出: Anonymous(name 为 null)
name = 'Dart';
print(name ?? 'Anonymous');  // 输出: Dart(name 非 null,忽略右侧)

// 可以链式使用
String? a, b;
String c = a ?? b ?? 'default';
print(c);  // 输出: default

// ── ??= 空值赋值运算符 ────────────────────────────
// 仅当变量为 null 时才赋值,已有值时不覆盖
// 常用于缓存初始化
String? cache;
cache ??= 'first assignment';
print(cache);  // 输出: first assignment
cache ??= 'second assignment';  // cache 已非 null,忽略
print(cache);  // 输出: first assignment

// ── ! 非空断言运算符 ──────────────────────────────
// 告诉编译器"我确定这不是 null",若运行时为 null 则抛出 Null check operator
// 谨慎使用,尽量用 ?? 或 if 检查代替
String? value = 'hello';
print(value!.length);   // 输出: 5(断言非 null,正常访问)

// ── late 延迟初始化 ───────────────────────────────
// 声明时不初始化,但向编译器承诺使用前一定会赋值
// 常用于 init() 方法中的初始化,或依赖注入场景
late String database;
// print(database);  // 若此时访问会抛运行时错误
database = 'connected';
print(database);  // 输出: connected

// late final:只能赋值一次,之后不可修改
late final int expensiveResult;
expensiveResult = 6 * 7;
print(expensiveResult);  // 输出: 42
// expensiveResult = 99;  // ❌ 已赋值的 late final 不可再赋值

8.3 类型提升(Type Promotion)

Dart 的流敏感类型分析能够在代码控制流中自动将可空类型”提升”为非空类型,无需手动转型,非常便利。

// ── if 检查后自动提升 ─────────────────────────────
String? value = getValue();

if (value != null) {
  // 此处编译器知道 value 不为 null,类型从 String? 提升为 String
  print(value.length);       // ✅ 直接访问,无需 value?.length
  print(value.toUpperCase()); // ✅
}

// ── 早返回模式(Guard Clause)────────────────────
// 非常推荐的写法:在函数开头检查 null,之后无需重复判断
void processUser(String? username, int? age) {
  if (username == null) return;  // 提前返回
  if (age == null) return;
  // 此后 username 和 age 都已提升为非空类型
  print('${username.toUpperCase()}$age 岁');
}
processUser(null, 30);     // 无输出
processUser('dart', null); // 无输出
processUser('dart', 10);   // 输出: DART 是 10 岁

// ── is 检查触发类型提升 ───────────────────────────
Object obj = 'Hello, Dart!';
if (obj is String) {
  // obj 已从 Object 提升为 String
  print(obj.split(', '));  // 输出: [Hello, Dart!]
}

// ── pattern 匹配触发类型提升(Dart 3.0+)─────────
Object x = 42;
if (x case int n when n > 0) {
  print('正整数: ${n.isEven ? "偶" : "奇"}');  // 输出: 正整数: 偶
}

9. 泛型

泛型(Generics)允许我们编写与类型无关的通用代码,同时保留类型安全。Dart 的泛型是具体化的(reified),即运行时依然携带类型信息,与 Java 的类型擦除不同。

9.1 泛型类

// 泛型容器:T 是类型参数,调用时用实际类型替换
class Box<T> {
  T _value;
  Box(this._value);

  T get value => _value;
  set value(T v) => _value = v;

  // 泛型方法:可以有自己独立的类型参数 R
  Box<R> transform<R>(R Function(T) mapper) => Box(mapper(_value));

  bool contains(T item) => _value == item;

  @override
  String toString() => 'Box<$T>($_value)';
}

var strBox = Box<String>('hello');
print(strBox);               // 输出: Box<String>(hello)
print(strBox.contains('hello')); // 输出: true

var intBox = Box<int>(21);
var doubled = intBox.transform<int>((n) => n * 2);
print(doubled);              // 输出: Box<int>(42)

// 转换成不同类型
var strFromInt = intBox.transform<String>((n) => 'value is $n');
print(strFromInt);           // 输出: Box<String>(value is 21)

// ── 泛型约束(Bounded Type Parameters)──────────
// extends 限制类型参数必须是某类型或其子类型
// 约束后可以在泛型代码中使用该类型的方法
class NumericBox<T extends num> {
  T value;
  NumericBox(this.value);

  T doubled() => (value * 2) as T;
  bool isPositive() => value > 0;
  T clamp(T min, T max) => value.clamp(min, max) as T;
}

var intNb = NumericBox<int>(5);
print(intNb.doubled());       // 输出: 10
print(intNb.isPositive());    // 输出: true
print(intNb.clamp(0, 3));     // 输出: 3

var doubleNb = NumericBox<double>(3.14);
print(doubleNb.doubled());    // 输出: 6.28

// NumericBox<String>('hi');  // ❌ 编译错误:String 不是 num 的子类

9.2 泛型函数与多类型参数

// 泛型函数:函数自身带类型参数
T identity<T>(T value) => value;
print(identity<int>(42));       // 输出: 42
print(identity<String>('hi'));  // 输出: hi
print(identity([1, 2, 3]));     // 输出: [1, 2, 3](类型推断)

// 多类型参数
class Pair<A, B> {
  final A first;
  final B second;
  const Pair(this.first, this.second);

  // 交换两个元素的类型和值
  Pair<B, A> swap() => Pair(second, first);

  @override
  String toString() => '($first, $second)';
}

var pair = Pair<String, int>('age', 30);
print(pair);          // 输出: (age, 30)
print(pair.swap());   // 输出: (30, age)

// 泛型函数结合集合
List<T> repeat<T>(T value, int count) =>
    List.generate(count, (_) => value);

print(repeat('x', 4));   // 输出: [x, x, x, x]
print(repeat(0, 3));     // 输出: [0, 0, 0]

10. 扩展方法与扩展类型

Dart 提供了两种在不修改原始类的情况下扩展其功能的机制:extension(扩展方法,可访问实例)和 extension type(扩展类型,零运行时开销的类型包装)。

10.1 扩展方法(Extension Methods)

// 扩展 String:添加业务相关的实用方法
extension StringUtils on String {
  // getter 扩展
  bool get isPalindrome {
    var s = toLowerCase().replaceAll(RegExp(r'[^a-z0-9]'), '');
    return s == s.split('').reversed.join();
  }

  // 方法扩展
  String capitalize() =>
      isEmpty ? this : '${this[0].toUpperCase()}${substring(1).toLowerCase()}';

  String truncate(int maxLen, {String ellipsis = '...'}) =>
      length <= maxLen ? this : '${substring(0, maxLen)}$ellipsis';

  bool get isEmail => RegExp(r'^[\w.-]+@[\w-]+\.\w+$').hasMatch(this);
}

print('racecar'.isPalindrome);         // 输出: true
print('hello world'.isPalindrome);     // 输出: false
print('hello world'.capitalize());    // 输出: Hello world
print('Hello, Dart!'.truncate(7));    // 输出: Hello, ...
print('user@example.com'.isEmail);    // 输出: true
print('not-an-email'.isEmail);        // 输出: false

// 扩展 int:添加数学工具方法
extension IntUtils on int {
  bool get isPrime {
    if (this < 2) return false;
    for (var i = 2; i * i <= this; i++) {
      if (this % i == 0) return false;
    }
    return true;
  }

  // 生成 [this, end] 范围的整数列表
  List<int> to(int end) =>
      List.generate((end - this).abs() + 1, (i) => this <= end ? this + i : this - i);

  // 重复执行某个操作 n 次
  void times(void Function(int i) action) {
    for (var i = 0; i < this; i++) action(i);
  }
}

print(7.isPrime);        // 输出: true
print(10.isPrime);       // 输出: false
print(1.to(5));          // 输出: [1, 2, 3, 4, 5]
print(5.to(1));          // 输出: [5, 4, 3, 2, 1]
3.times((i) => print('第 ${i+1} 次'));
// 输出: 第 1 次  第 2 次  第 3 次

// 泛型扩展 List
extension ListUtils<T> on List<T> {
  T get second => this[1];
  List<T> get shuffledCopy => [...this]..shuffle();
  List<List<T>> chunked(int size) {
    return [for (var i = 0; i < length; i += size) sublist(i, (i + size).clamp(0, length))];
  }
}

print([10, 20, 30, 40].second);               // 输出: 20
print([1, 2, 3, 4, 5].chunked(2));            // 输出: [[1, 2], [3, 4], [5]]

10.2 扩展类型(Extension Types,Dart 3.3+)

扩展类型提供了零运行时开销的类型包装。与普通类不同,扩展类型在运行时会被直接转换为其底层类型,因此没有任何性能损耗,但在编译期提供了完整的类型安全。

// 使用扩展类型为不同单位的数值添加类型区分
// 防止将"米"和"千克"的数值混用(否则两者都是 double)
extension type Meters(double value) implements double {
  Meters operator +(Meters other) => Meters(value + other.value);
  Meters operator -(Meters other) => Meters(value - other.value);
  Meters operator *(double factor) => Meters(value * factor);
  String get display => '${value.toStringAsFixed(2)}m';
}

extension type Kilograms(double value) implements double {
  String get display => '${value.toStringAsFixed(2)}kg';
}

var dist1 = Meters(1.5);
var dist2 = Meters(2.8);
var total = dist1 + dist2;
print(total.display);   // 输出: 4.30m

// 类型安全:不同单位之间无法混用
// Meters m = Kilograms(10); // ❌ 编译错误
// var wrong = dist1 + Kilograms(5); // ❌ 编译错误

// 运行时:扩展类型与底层类型完全等价
// print(total == 4.3);  // ✅ 因为 implements double,可以比较

11. 模式匹配与解构(Dart 3.0+)

Dart 3.0 引入了完整的模式匹配系统,这是语言历史上最大的一次语法扩展。模式(Pattern)可以在 switch、if-case、变量声明、for 循环等多处使用,支持类型匹配、解构、守卫条件等功能。

11.1 switch 模式匹配

// ── 字面量模式(Literal Pattern)────────────────
// 匹配特定的常量值
Object val = 42;
var r = switch (val) {
  0    => '零',
  1    => '一',
  42   => '宇宙的答案',  // 输出: 宇宙的答案
  _    => '其他',
};
print(r);

// ── 类型模式(Type Pattern)──────────────────────
// 匹配特定类型,并自动将变量绑定为该类型(类型提升)
void describeValue(Object obj) {
  switch (obj) {
    case int n when n < 0:
      print('负整数: $n');
    case int n:
      print('非负整数: $n');
    case double d:
      print('浮点数: ${d.toStringAsFixed(2)}');
    case String s when s.isEmpty:
      print('空字符串');
    case String s:
      print('字符串: "$s" (长度 ${s.length})');
    case List<int> list:
      print('整数列表,共 ${list.length} 个元素');
    case null:
      print('null 值');
    default:
      print('未知类型: ${obj.runtimeType}');
  }
}

describeValue(-5);        // 输出: 负整数: -5
describeValue(42);        // 输出: 非负整数: 42
describeValue(3.14159);   // 输出: 浮点数: 3.14
describeValue('');        // 输出: 空字符串
describeValue('Dart');    // 输出: 字符串: "Dart" (长度 4)
describeValue([1, 2, 3]); // 输出: 整数列表,共 3 个元素

// ── 或模式(Or Pattern)── 用 | 匹配多个值 ────────
var day = 'Sat';
var type = switch (day) {
  'Mon' | 'Tue' | 'Wed' | 'Thu' | 'Fri' => '工作日',
  'Sat' | 'Sun' => '周末',  // 输出: 周末
  _ => '无效日期',
};
print(type);

// ── 守卫条件(Guard Clause)── when ───────────────
// 在模式匹配成功的基础上,额外附加布尔条件
var numbers = [1, -2, 3, -4, 5];
for (var n in numbers) {
  switch (n) {
    case int x when x > 0:
      print('$x 是正数');
    case int x when x < 0:
      print('$x 是负数');
    case 0:
      print('零');
  }
}
// 输出: 1 是正数  -2 是负数  3 是正数  -4 是负数  5 是正数

11.2 解构(Destructuring)

解构允许将复合数据结构的值”拆包”并绑定到变量,让代码更简洁清晰。

// ── Record 解构 ──────────────────────────────────
var (x, y) = (10, 20);
print('x=$x, y=$y');  // 输出: x=10, y=20

var (name: userName, age: userAge) = (name: 'Dart', age: 10);
print('$userName: $userAge');  // 输出: Dart: 10

// ── List 解构 ──────────────────────────────────────
// 精确匹配:列表元素数量必须和模式一致
var [a, b, c] = [1, 2, 3];
print('$a $b $c');  // 输出: 1 2 3

// 剩余模式 ...:捕获剩余元素为列表
var [first, ...rest] = [1, 2, 3, 4, 5];
print('first=$first, rest=$rest');  // 输出: first=1, rest=[2, 3, 4, 5]

// 捕获头尾,中间剩余
var [head, ...middle, tail] = [1, 2, 3, 4, 5];
print('head=$head, tail=$tail, middle=$middle');
// 输出: head=1, tail=5, middle=[2, 3, 4]

// 忽略某些元素(用 _ 占位符)
var [_, second, _, fourth] = [10, 20, 30, 40];
print('$second $fourth');  // 输出: 20 40

// ── Map 解构 ──────────────────────────────────────
var {'name': n, 'age': a} = {'name': 'Alice', 'age': 30, 'city': 'BJ'};
print('$n 今年 $a 岁');  // 输出: Alice 今年 30 岁(city 被忽略)

// ── 对象解构 ──────────────────────────────────────
class Point {
  final double x, y;
  const Point(this.x, this.y);
}

var p = Point(3.0, 4.0);

// 对象模式:ClassName(field: variable, ...)
var Point(x: px, y: py) = p;
print('($px, $py)');  // 输出: (3.0, 4.0)

// 简写:当变量名与字段名相同时
var Point(:x, :y) = p;   // 等价于 Point(x: x, y: y)
print('($x, $y)');        // 输出: (3.0, 4.0)

// 在 switch 中对对象解构并加守卫条件
switch (p) {
  case Point(x: 0.0, y: 0.0):
    print('原点');
  case Point(:var x, y: 0.0):
    print('X 轴上,x=$x');
  case Point(x: var px, y: var py) when px == py:
    print('对角线上,($px, $py)');
  case Point(:var x, :var y):
    print('一般点 ($x, $y)');  // 输出: 一般点 (3.0, 4.0)
}

// ── 嵌套解构 ─────────────────────────────────────
var [(a1, b1), (a2, b2)] = [(1, 2), (3, 4)];
print('($a1,$b1) ($a2,$b2)');  // 输出: (1,2) (3,4)

12. Record 类型(Dart 3.0+)

Record 是 Dart 3.0 引入的匿名、不可变的复合值类型。与 ListMap 不同,Record 的结构(字段数量和类型)在编译期固定,因此具有完整的类型安全。Record 最常见的用途是让函数返回多个值。

// ── 创建 Record ───────────────────────────────────
// 位置 Record:用 () 包裹多个值
var point = (3, 4);
print(point);      // 输出: (3, 4)
print(point.$1);   // 输出: 3   (位置访问,从 $1 开始)
print(point.$2);   // 输出: 4

// 命名 Record:字段有名字,访问更清晰
var user = (name: 'Alice', age: 30);
print(user);       // 输出: (name: Alice, age: 30)
print(user.name);  // 输出: Alice
print(user.age);   // 输出: 30

// 混合 Record:既有位置字段,也有命名字段
var mixed = ('point', x: 1.0, y: 2.0);
print(mixed.$1);   // 输出: point(位置字段用 $n)
print(mixed.x);    // 输出: 1.0
print(mixed.y);    // 输出: 2.0

// ── 类型标注 ──────────────────────────────────────
(int, String) pair = (1, 'hello');
({String name, int age}) person = (name: 'Dart', age: 10);

// ── 函数返回多个值(最典型的用途)────────────────
// 以前只能用 List、Map 或自定义类;现在 Record 更简洁类型安全
(int quotient, int remainder) divmod(int a, int b) {
  return (a ~/ b, a % b);
}

var (q, r) = divmod(17, 5);
print('17 ÷ 5 = $q$r');  // 输出: 17 ÷ 5 = 3 余 2

// 命名字段更清晰
({double min, double max, double avg}) stats(List<double> data) {
  data.sort();
  return (
    min: data.first,
    max: data.last,
    avg: data.reduce((a, b) => a + b) / data.length,
  );
}

var result = stats([3.0, 1.0, 4.0, 1.5, 9.0, 2.6]);
print('min=${result.min}, max=${result.max}, avg=${result.avg.toStringAsFixed(2)}');
// 输出: min=1.0, max=9.0, avg=3.52

// ── Record 相等(结构相等)───────────────────────
// 两个 Record 相等当且仅当类型、字段名和值都相等
print((1, 'a') == (1, 'a'));   // 输出: true
print((x: 1) == (x: 1));      // 输出: true
print((x: 1) == (y: 1));      // 输出: false(字段名不同)
print((1, 2) == (2, 1));      // 输出: false(值顺序不同)

// ── 在集合中使用 Record ───────────────────────────
var coords = [(0, 0), (1, 2), (3, 4), (5, 6)];
for (var (x, y) in coords) {
  var dist = (x * x + y * y);
  print('($x,$y) 距原点²=$dist');
}
// 输出:
// (0,0) 距原点²=0
// (1,2) 距原点²=5
// (3,4) 距原点²=25
// (5,6) 距原点²=61

13. 异步编程

Dart 使用事件循环(Event Loop)实现非阻塞异步编程。Future 代表一个未来会完成的单个异步操作,Stream 代表随时间产生的一系列异步事件。async/await 让异步代码看起来像同步代码,大大提高了可读性。

13.1 Future 与 async/await

import 'dart:async';

// ── 声明异步函数 ──────────────────────────────────
// async 函数始终返回 Future,即使函数体是同步的
// 返回类型 Future<T> 中的 T 是实际值的类型
Future<String> fetchUserName(int id) async {
  // await:暂停当前函数等待 Future 完成,但不阻塞整个程序
  await Future.delayed(Duration(milliseconds: 100));  // 模拟网络请求
  if (id <= 0) throw ArgumentError('无效的用户 ID: $id');
  return 'User_$id';
}

Future<int> fetchScore(String user) async {
  await Future.delayed(Duration(milliseconds: 50));
  return user.length * 10;  // 模拟计算
}

// ── 使用 async/await ─────────────────────────────
void main() async {
  // 顺序等待(适合有依赖关系的操作)
  var name  = await fetchUserName(1);
  var score = await fetchScore(name);
  print('$name 的分数: $score');
  // 输出: User_1 的分数: 60

  // ── 并行等待(适合互相独立的操作,节省时间)─────
  // Future.wait 同时启动所有 Future,等待全部完成
  var results = await Future.wait([
    fetchUserName(1),
    fetchUserName(2),
    fetchUserName(3),
  ]);
  print(results);  // 输出: [User_1, User_2, User_3]

  // ── 取最先完成的 ──────────────────────────────
  var fastest = await Future.any([
    Future.delayed(Duration(seconds: 2), () => 'slow server'),
    Future.delayed(Duration(milliseconds: 10), () => 'fast server'),
  ]);
  print(fastest);  // 输出: fast server

  // ── 立即完成的 Future ─────────────────────────
  var immediate = await Future.value(42);
  print(immediate);  // 输出: 42
}

// ── then/catchError 链式 API ─────────────────────
// 适合不想用 async/await 的场景,或需要链式转换的场景
fetchUserName(1)
  .then((name) {
    print('获取到用户名: $name');  // 输出: 获取到用户名: User_1
    return fetchScore(name);
  })
  .then((score) => print('分数: $score'))   // 输出: 分数: 60
  .catchError((e) => print('错误: $e'))
  .whenComplete(() => print('请求完毕'));   // 输出: 请求完毕

13.2 Stream

// ── async* 创建 Stream ────────────────────────────
// async* 函数是异步生成器,用 yield 产生事件,用 yield* 委托另一个 Stream
Stream<int> ticker(int from, int to) async* {
  for (var i = from; i <= to; i++) {
    await Future.delayed(Duration(milliseconds: 100));
    yield i;  // 每隔 100ms 产生一个数字
  }
}

// ── await for 消费 Stream ─────────────────────────
Future<void> consumeStream() async {
  await for (var n in ticker(1, 5)) {
    print('收到: $n');
  }
  print('Stream 已完成');
}
// 输出: 收到: 1  收到: 2  收到: 3  收到: 4  收到: 5  Stream 已完成

// ── Stream 变换操作 ──────────────────────────────
// Stream 也支持 map、where、take 等操作(与 Iterable 类似)
Stream<String> processed = ticker(1, 10)
    .where((n) => n.isOdd)          // 只要奇数: 1,3,5,7,9
    .map((n) => 'item_$n')          // 转换: item_1, item_3...
    .take(3);                        // 只取前 3 个: item_1, item_3, item_5

processed.listen((s) => print(s));
// 输出: item_1  item_3  item_5

// ── StreamController:手动控制 Stream ────────────
// 适合需要在外部往 Stream 推送数据的场景
var controller = StreamController<String>();

// 订阅 Stream
controller.stream.listen(
  (data) => print('收到: $data'),         // 每次有数据时调用
  onError: (e) => print('错误: $e'),      // 发生错误时调用
  onDone: () => print('Stream 关闭了'),   // Stream 关闭时调用
);

// 向 Stream 推送数据
controller.sink.add('消息 1');   // 输出: 收到: 消息 1
controller.sink.add('消息 2');   // 输出: 收到: 消息 2
controller.sink.addError('出错了'); // 输出: 错误: 出错了
controller.close();               // 输出: Stream 关闭了

// ── 广播 Stream(Broadcast Stream)──────────────
// 普通 Stream 只允许一个监听者;广播 Stream 允许多个监听者
var broadcast = StreamController<int>.broadcast();

broadcast.stream.listen((n) => print('监听者 1: $n'));
broadcast.stream.listen((n) => print('监听者 2: $n'));

broadcast.sink.add(42);
// 输出: 监听者 1: 42
// 输出: 监听者 2: 42

14. 异常处理

Dart 的异常分为两类:Exception(可预期的运行时错误,应该被捕获处理)和 Error(编程错误,通常不应该被捕获)。Dart 允许抛出任意对象(包括字符串),但最佳实践是使用 ExceptionError 的子类。

14.1 基础异常处理

// ── try / on / catch / finally ───────────────────
// on:按类型捕获,不需要异常对象
// catch:获取异常对象(第一个参数)和栈跟踪(第二个参数,可选)
// finally:无论是否发生异常都会执行(常用于资源释放)

void riskyOperation(int n) {
  switch (n) {
    case 0: throw FormatException('数字不能为零', 'input=$n');
    case -1: throw RangeError.range(n, 0, 100, '参数 n');
    case -2: throw ArgumentError.notNull('n');
    default: print('结果: ${100 / n}');
  }
}

for (var i in [5, 0, -1, -2]) {
  try {
    riskyOperation(i);
  } on FormatException catch (e) {
    // 捕获特定类型:FormatException
    print('格式异常: ${e.message}(来源: ${e.source})');
  } on RangeError catch (e) {
    print('范围异常: ${e.message}');
  } catch (e, stackTrace) {
    // 兜底捕获所有未处理的异常类型
    // stackTrace 包含完整的调用栈信息(调试时非常有用)
    print('未知异常: $e');
    // print(stackTrace);  // 打印完整栈跟踪
  } finally {
    // 始终执行:适合关闭文件、释放数据库连接等清理工作
    print('--- 处理完毕 ---');
  }
}
// 输出:
// 结果: 20.0  --- 处理完毕 ---
// 格式异常: 数字不能为零(来源: input=0)  --- 处理完毕 ---
// 范围异常: RangeError (index): ...  --- 处理完毕 ---
// 未知异常: Invalid argument (n): ...  --- 处理完毕 ---

// ── rethrow:重新抛出 ─────────────────────────────
// 捕获异常后做些记录,然后将其原封不动地继续向上抛出
// 注意:rethrow 不会重置栈跟踪(与 throw e 不同)
void withLogging(void Function() fn) {
  try {
    fn();
  } catch (e) {
    print('[LOG] 捕获到异常: ${e.runtimeType}');
    rethrow;  // 继续向上传播,让调用者处理
  }
}

14.2 自定义异常体系

// 自定义异常类:实现 Exception 接口即可
// 通常建议设计成不可变(所有字段用 final)
class AppException implements Exception {
  final String message;
  final int    code;
  final DateTime timestamp;

  AppException(this.message, this.code)
      : timestamp = DateTime.now();

  @override
  String toString() => '[$timestamp] AppException($code): $message';
}

// 继承扩展:建立异常层次结构
class NetworkException extends AppException {
  final String url;
  NetworkException(String message, {required this.url})
      : super(message, 503);

  @override
  String toString() => 'NetworkException: $message (URL: $url)';
}

class AuthException extends AppException {
  AuthException(String message) : super(message, 401);
}

class NotFoundException extends AppException {
  final String resource;
  NotFoundException(this.resource)
      : super('$resource 不存在', 404);
}

// 使用异常层次结构
void fetchResource(String type) {
  switch (type) {
    case 'network': throw NetworkException('连接超时', url: 'https://api.example.com');
    case 'auth':    throw AuthException('Token 已过期');
    case 'missing': throw NotFoundException('用户 ID=999');
    default:        print('成功获取: $type');
  }
}

for (var type in ['data', 'network', 'auth', 'missing']) {
  try {
    fetchResource(type);
  } on NetworkException catch (e) {
    print('网络错误: $e');
  } on AuthException catch (e) {
    print('认证错误: ${e.message},错误码: ${e.code}');
  } on AppException catch (e) {
    // 捕获所有 AppException 的子类(兜底)
    print('应用错误(${e.code}): ${e.message}');
  }
}
// 输出:
// 成功获取: data
// 网络错误: NetworkException: 连接超时 (URL: https://api.example.com)
// 认证错误: Token 已过期,错误码: 401
// 应用错误(404): 用户 ID=999 不存在

15. 枚举

Dart 的枚举从简单的值枚举演变为功能丰富的增强枚举(Dart 2.17+),支持构造函数、字段、方法和接口实现,几乎具备普通类的全部能力。

15.1 基础枚举

// 最简单的枚举:定义一组具名常量
enum Direction { north, south, east, west }

var dir = Direction.north;
print(dir);              // 输出: Direction.north
print(dir.name);         // 输出: north(枚举值的名字,String)
print(dir.index);        // 输出: 0   (枚举值的序号,从 0 开始)
print(Direction.values); // 输出: [Direction.north, ...](所有值的列表)

// 从字符串解析枚举(Dart 2.15+)
var parsed = Direction.values.byName('east');
print(parsed);           // 输出: Direction.east

// 在 switch 中使用枚举:编译器可以检查穷举性
switch (dir) {
  case Direction.north: print('向北走'); break;
  case Direction.south: print('向南走'); break;
  case Direction.east:  print('向东走'); break;
  case Direction.west:  print('向西走'); break;
  // 若漏写某个 case,编译器会产生警告
}
// 输出: 向北走

15.2 增强枚举(Dart 2.17+)

// 增强枚举:可以有构造函数、字段、方法,用法与类相似
// 每个枚举值就像一个类的常量实例
enum Planet {
  // 每个枚举值像调用构造函数一样传入参数
  mercury(mass: 3.303e+23, radius: 2.4397e6),
  venus  (mass: 4.869e+24, radius: 6.0518e6),
  earth  (mass: 5.976e+24, radius: 6.37814e6),
  mars   (mass: 6.421e+23, radius: 3.3972e6);

  // 枚举的构造函数必须是 const
  const Planet({required this.mass, required this.radius});

  // 字段(必须是 final)
  final double mass;    // 千克
  final double radius;  // 米

  // 常量
  static const double G = 6.67430e-11;

  // 计算属性
  double get surfaceGravity => G * mass / (radius * radius);

  // 方法
  double surfaceWeight(double bodyMass) => bodyMass * surfaceGravity;

  // 重写 toString
  @override
  String toString() => 'Planet.$name (g=${surfaceGravity.toStringAsFixed(2)})';
}

// 计算在各星球上的体重
var earthWeight = 75.0;
var mass = earthWeight / Planet.earth.surfaceGravity;
print('地球质量: ${mass.toStringAsFixed(2)} kg');
// 输出: 地球质量: 7.65 kg

for (var p in Planet.values) {
  print('${p.name}: ${p.surfaceWeight(mass).toStringAsFixed(2)} N');
}
// 输出(近似):
// mercury: 28.32 N
// venus:   67.87 N
// earth:   75.00 N
// mars:    28.46 N

15.3 枚举实现接口

// 枚举可以 implements 接口,甚至实现 mixin
enum Status implements Comparable<Status> {
  draft    ('草稿',   0xFF9E9E9E),
  pending  ('待审核', 0xFFFFA726),
  active   ('进行中', 0xFF66BB6A),
  completed('已完成', 0xFF42A5F5),
  cancelled('已取消', 0xFFEF5350);

  const Status(this.label, this.color);

  final String label;
  final int    color;  // ARGB 颜色值

  // 实现 Comparable 接口
  @override
  int compareTo(Status other) => index.compareTo(other.index);

  // 判断是否为终态(不可再转换到其他状态)
  bool get isTerminal => this == completed || this == cancelled;

  // 可转换的下一个状态列表
  List<Status> get nextStates => switch (this) {
    draft     => [pending, cancelled],
    pending   => [active, cancelled],
    active    => [completed, cancelled],
    completed => [],
    cancelled => [],
  };

  @override
  String toString() => label;
}

print(Status.active.label);          // 输出: 进行中
print(Status.active.isTerminal);     // 输出: false
print(Status.completed.isTerminal);  // 输出: true
print(Status.pending.nextStates);    // 输出: [进行中, 已取消]

// 排序(利用 Comparable 接口)
var statuses = [Status.completed, Status.draft, Status.active];
statuses.sort();
print(statuses);  // 输出: [草稿, 进行中, 已完成](按 index 排序)

16. 库与导入

Dart 的包管理系统以 pubspec.yaml 为核心,dart pub 为命令行工具。标准库涵盖 IO、网络、异步、集合、数学、JSON 等各个领域。

16.1 导入语法

// ── 导入标准库(dart: 前缀)──────────────────────
import 'dart:math';        // 数学函数和常量
import 'dart:convert';     // JSON、UTF-8、Base64 等编解码
import 'dart:async';       // Future、Stream、Timer 等异步工具
import 'dart:collection';  // Queue、LinkedHashMap、SplayTreeMap 等
import 'dart:io';          // 文件、套接字、进程(不支持 Web)

// ── 导入第三方包(package: 前缀)────────────────
import 'package:http/http.dart';
import 'package:flutter/material.dart';

// ── as:给导入的库取别名(解决命名冲突)──────────
import 'dart:math' as math;
print(math.pi);          // 输出: 3.141592653589793
print(math.sqrt(16));    // 输出: 4.0
print(math.max(3, 7));   // 输出: 7
print(math.min(3, 7));   // 输出: 3
print(math.pow(2, 10));  // 输出: 1024.0
print(math.log(math.e)); // 输出: 1.0

// ── show:只导入指定的名称 ────────────────────────
import 'dart:math' show Random, pi, sqrt;
// 现在只有 Random、pi、sqrt 可用

// ── hide:排除指定的名称 ──────────────────────────
import 'dart:math' hide Random;
// 除了 Random 之外的所有名称都可用

// ── deferred as:延迟/懒加载(减少初始加载时间)──
import 'package:heavy_lib/heavy_lib.dart' deferred as heavy;
// 使用时调用 await heavy.loadLibrary(); 再使用

16.2 dart:math 常用功能

import 'dart:math';

// ── 数学常量 ──────────────────────────────────────
print(pi);   // 输出: 3.141592653589793
print(e);    // 输出: 2.718281828459045(自然常数)
print(ln2);  // 输出: 0.6931471805599453

// ── 常用数学函数 ──────────────────────────────────
print(sqrt(2));         // 输出: 1.4142135623730951(平方根)
print(pow(2, 10));      // 输出: 1024.0(幂运算:2^10)
print(log(e));          // 输出: 1.0(自然对数)
print(log(100) / log(10)); // 输出: 2.0(以 10 为底的对数)

// 三角函数(参数为弧度)
print(sin(pi / 2));     // 输出: 1.0
print(cos(0));          // 输出: 1.0
print(tan(pi / 4));     // 输出: 0.9999999999999999(约等于 1)
print(asin(1));         // 输出: 1.5707963267948966(≈ π/2)

// 取整函数(注意各自语义)
print(3.7.ceil());      // 输出: 4  (向上取整)
print(3.7.floor());     // 输出: 3  (向下取整)
print(3.5.round());     // 输出: 4  (四舍五入)
print(-3.7.ceil());     // 输出: -3 (向上取整:往正方向)
print(-3.7.floor());    // 输出: -4 (向下取整:往负方向)

// ── 随机数 ─────────────────────────────────────────
var rng = Random();
print(rng.nextInt(100));    // 输出: 0~99 的随机整数(不含 100)
print(rng.nextDouble());    // 输出: 0.0~1.0 之间的随机浮点数
print(rng.nextBool());      // 输出: 随机的 true 或 false

// 指定种子(相同种子产生相同序列,适合测试)
var seeded = Random(42);
print(seeded.nextInt(100)); // 输出: 固定值(取决于实现)

// 密码学安全随机数(不可预测,但较慢)
var secure = Random.secure();
print(secure.nextInt(256));

16.3 dart:convert 编解码

import 'dart:convert';

// ── JSON 编解码 ───────────────────────────────────
// JSON 是 Web 开发中最常见的数据交换格式
var data = {
  'name': 'Dart',
  'version': 3,
  'features': ['null-safety', 'records', 'patterns'],
  'stable': true,
  'releaseDate': null,
};

// 编码:Dart 对象 → JSON 字符串
String jsonStr = jsonEncode(data);
print(jsonStr);
// 输出: {"name":"Dart","version":3,"features":["null-safety","records","patterns"],"stable":true,"releaseDate":null}

// 解码:JSON 字符串 → Dart 对象(返回 dynamic)
var decoded = jsonDecode(jsonStr);
print(decoded['name']);           // 输出: Dart
print(decoded['version']);        // 输出: 3
print(decoded['features']);       // 输出: [null-safety, records, patterns]
print(decoded['features'][0]);    // 输出: null-safety

// 格式化输出(带缩进)
import 'dart:convert';
var encoder = JsonEncoder.withIndent('  ');
print(encoder.convert({'a': 1, 'b': [2, 3]}));
// 输出:
// {
//   "a": 1,
//   "b": [
//     2,
//     3
//   ]
// }

// ── Base64 编解码 ─────────────────────────────────
// 常用于将二进制数据编码为可打印的 ASCII 字符串(如图片、证书)
var bytes = [72, 101, 108, 108, 111];  // "Hello" 的 ASCII 码
print(base64Encode(bytes));     // 输出: SGVsbG8=
print(base64Decode('SGVsbG8=')); // 输出: [72, 101, 108, 108, 111]

// URL 安全的 Base64(用 - 和 _ 替换 + 和 /)
print(base64Url.encode(bytes));  // 输出: SGVsbG8=

// ── UTF-8 编解码 ──────────────────────────────────
// UTF-8 是最常见的文本编码,每个 ASCII 字符 1 字节,中文通常 3 字节
var text = '你好,Dart!';
var utf8bytes = utf8.encode(text);
print(utf8bytes.length);          // 输出: 13(3+3+3+4+1+3 = 中文3字节,ASCII1字节)
print(utf8.decode(utf8bytes));    // 输出: 你好,Dart!(往回解码)

16.4 dart:collection 高级集合

import 'dart:collection';

// ── Queue:双端队列 ──────────────────────────────
// 两端都可以高效地插入和删除,适合队列/栈/双端操作
var q = Queue<int>();
q.add(1);          // 从后端加入
q.add(2);
q.addFirst(0);     // 从前端加入
print(q);          // 输出: {0, 1, 2}
q.removeFirst();   // 从前端移除(队列出队)
q.removeLast();    // 从后端移除(栈弹出)
print(q);          // 输出: {1}

// ── LinkedHashMap:保持插入顺序的 Map ────────────
// 标准 Map 的插入顺序在 Dart 中也是保证的(但 LinkedHashMap 是明确保证)
var lhm = LinkedHashMap<String, int>();
lhm['c'] = 3;
lhm['a'] = 1;
lhm['b'] = 2;
print(lhm.keys.toList());   // 输出: [c, a, b](保持插入顺序)

// ── SplayTreeMap:键自动排序的 Map ───────────────
// 基于 Splay 树实现,键总是保持有序状态
// 适合需要按键顺序遍历的场景
var stm = SplayTreeMap<String, int>();
stm['banana'] = 2;
stm['apple']  = 1;
stm['cherry'] = 3;
print(stm.keys.toList());   // 输出: [apple, banana, cherry](字典序)
print(stm.firstKey());      // 输出: apple
print(stm.lastKey());       // 输出: cherry

// 自定义排序(降序)
var descMap = SplayTreeMap<int, String>((a, b) => b.compareTo(a));
descMap[1] = 'one';
descMap[3] = 'three';
descMap[2] = 'two';
print(descMap.keys.toList());  // 输出: [3, 2, 1](降序)

// ── HashSet:哈希集合(查找 O(1),但无序)────────
var hs = HashSet<String>();
hs.addAll(['dart', 'flutter', 'dart', 'android']);
print(hs.length);       // 输出: 3(dart 重复被忽略,顺序不保证)
print(hs.contains('flutter')); // 输出: true

快速参考

Dart 内置类型总览

类型说明示例字面量
int64 位整数(VM)/ JS number(Web)42, 0xFF, -10
double64 位 IEEE 754 浮点数3.14, 1.5e3, double.infinity
numintdouble 的父类
bool布尔值true, false
StringUTF-16 字符串(不可变)'hi', "hello", r'\n'
List<T>有序可变列表[1, 2, 3]
Map<K,V>键值映射{'a': 1}
Set<T>无重复集合{1, 2, 3}
Symbol标识符的符号表示#name
RunesUnicode 码点序列Runes('\u{1F600}')
dynamic动态类型(跳过类型检查)
Object所有非空类型的根类
Object?所有类型的根(含 null)
void无返回值
Never永不正常返回(抛异常的函数)
Nullnull 的类型null

关键操作符速查

操作符说明示例
?.条件成员访问(null 时短路返回 null)obj?.method()
??空值合并(左侧为 null 则用右侧)x ?? 'default'
??=空值赋值(仅当 null 时赋值)x ??= 'init'
!非空断言(断言非 null,否则运行时报错)x!.length
..级联(对同一对象多次操作,返回对象本身)list..add(1)..add(2)
?..空安全级联(null 时跳过)obj?..method()
is类型检查(包含子类)x is String
is!类型排除x is! int
as强制类型转换(失败抛 TypeError)x as String
~/整除(结果为 int)10 ~/ 3 == 3
...展开运算符(集合合并)[...list1, ...list2]
...?空安全展开(null 时不展开)[...?nullableList]
@override标注覆盖父类成员@override void build()
=>箭头函数(单表达式函数体简写)int double(int x) => x * 2

Dart 3 新特性速览

特性关键字一句话说明
模式匹配switchcasewhen在 switch 中对数据结构进行解构和类型匹配
Record 类型(a, b)(x: 1, y: 2)匿名不可变多值结构,替代简单的类或 Map
密封类sealed class子类有限且已知,switch 可穷举检查
解构赋值var (a, b) = ...直接从 Record/List/Map/对象中提取值
switch 表达式switch (x) { ... => ... }switch 可作为表达式直接产生值
扩展类型extension type零开销的类型包装,提供编译期类型安全

本手册涵盖 Dart 3.x 全部核心语法,建议结合官方文档 dart.dev/language 深入学习。

Giscus 未启用:请在 src/site.config.ts 中配置 repoId 与 categoryId。