快速入门 Dart 语言

10 阅读22分钟

基础

变量和常量

声明方式可变性可空赋值时机类型固定典型用途
var可变由初始化决定声明时局部变量,类型显而易见
Type (显式)可变默认不可空使用前公共 API、需要明确类型
Type?可变可空使用前可能缺失的值
late Type可变不可空首次使用前延迟初始化(依赖注入、Flutter)
dynamic可变可空任意时间动态语言互操作(谨慎)
final不可变默认不可空首次赋值时运行时常量(单次赋值)
final Type?不可变可空首次赋值时可能为 null 的单次赋值
const不可变不可空编译时编译时常量(字面量、数学常量)

未赋值的变量或者常量,默认是null

内置类型

类型字面量示例说明
int420x2A0b101010整数(64位)
double3.141.2e5double.nan双精度浮点数
num上述两者父类抽象数字类型
String'hello'"world"'''多行'''UTF-16 字符串
booltruefalse布尔值
List<E>[1,2,3][...list]有序列表
Set<E>{1,2,3}无序集合
Map<K,V>{'a':1, 'b':2}键值对
Runes通过字符串的 .runes 获得Unicode 码点序列
Symbol#foo标识符符号
Record(42, 'ans')(x:1, y:2)匿名聚合类型
Future<T>异步操作结果见异步章节
Stream<T>异步事件序列见异步章节
Function函数对象函数类型
Never-永不返回的类型
void-无返回值

1、 num / int / double 数字操作

// int 和 double 共享 num 的方法
int i = -42;
double d = 3.14;

// 属性
bool isNeg = i.isNegative;               // true
bool isFinite = d.isFinite;              // true
bool isInfinite = d.isInfinite;          // false
bool isNaN = d.isNaN;                    // false

// 转换
int abs = i.abs();                       // 42
double d2 = i.toDouble();                // -42.0
int i2 = d.toInt();                      // 3
int round = d.round();                   // 3
int floor = d.floor();                   // 3
int ceil = d.ceil();                     // 4
int trunc = d.truncate();                // 3

// 字符串转换
String s1 = i.toString();                // "-42"
String s2 = d.toStringAsFixed(1);        // "3.1"
String s3 = d.toStringAsPrecision(2);    // "3.1"
String hex = 255.toRadixString(16);      // "ff"
int parsed = int.parse('ff', radix: 16); // 255

// 比较
int cmp = i.compareTo(10);               // -1 (小于)
bool isEven = i.isEven;                  // true (偶数)
bool isOdd = i.isOdd;                    // false

// 位运算(仅 int)
int mask = 0b1010;
int shifted = mask << 2;                 // 0b101000
int complemented = ~mask;                // 按位非
int and = mask & 0b1100;                 // 0b1000
int or = mask | 0b0101;                  // 0b1111
int xor = mask ^ 0b1111;                 // 0b0101

2、String 字符串操作

// 创建字符串
String s1 = 'Hello';
String s2 = "World";
String s3 = '''多行
字符串''';
String s4 = r'原始字符串\n不转义';

// 属性
int len = s1.length;                  // 长度: 5
bool isEmpty = s1.isEmpty;            // 是否为空: false
bool isNotEmpty = s1.isNotEmpty;      // 是否非空: true

// 访问字符
String firstChar = s1[0];             // 'H'
int codeUnit = s1.codeUnitAt(0);      // 72 (UTF-16 码元)
List<int> codeUnits = s1.codeUnits;   // [72, 101, 108, 108, 111]
Runes runes = s1.runes;               // Unicode 码点可迭代

// 大小写转换
String upper = s1.toUpperCase();      // "HELLO"
String lower = s1.toLowerCase();      // "hello"

// 修剪
String padded = '  abc  ';
String trimmed = padded.trim();       // "abc"
String leftTrim = padded.trimLeft();  // "abc  "
String rightTrim = padded.trimRight();// "  abc"

// 填充
String padLeft = '5'.padLeft(3, '0'); // "005"
String padRight = '5'.padRight(3, '0');// "500"

// 比较与包含
bool eq = s1 == 'Hello';              // true
int compare = s1.compareTo('Hello');  // 0
bool contains = s1.contains('ell');   // true
bool starts = s1.startsWith('He');    // true
bool ends = s1.endsWith('lo');        // true

// 查找位置
int idx = s1.indexOf('l');            // 2
int lastIdx = s1.lastIndexOf('l');    // 3

// 截取
String sub = s1.substring(1, 4);      // "ell" (索引1~3)
String subToEnd = s1.substring(2);    // "llo"

// 替换
String replaced = s1.replaceAll('l', 'x');     // "Hexxo"
String firstReplaced = s1.replaceFirst('l', 'x'); // "Hexlo"
String rangeReplaced = s1.replaceRange(1, 3, 'yy'); // "Hyylo"

// 分割与连接
List<String> parts = 'a,b,c'.split(',');        // ['a','b','c']
String joined = ['a','b'].join('-');            // "a-b"

// 正则相关
RegExp reg = RegExp(r'\d+');
bool hasMatch = reg.hasMatch('abc123');         // true
String replacedAll = 'a1b2c3'.replaceAll(reg, '#'); // "a#b#c#"
Iterable<Match> matches = reg.allMatches('a1b2c3');

// 其他转换
int? intVal = int.tryParse('123');              // 123
double? doubleVal = double.tryParse('3.14');    // 3.14

3、 List<E> – 有序可重复列表

3.1、工厂构造方法(创建 List)

// 工厂构造方法
List<int> list1 = List.filled(3, 0);              // 创建固定长度列表 [0, 0, 0]
List<int> list2 = List.generate(3, (i) => i);     // 通过生成器创建 [0, 1, 2]
List<int> list3 = List.empty();                   // 创建空列表 []
List<int> list4 = List.from([1, 2, 3]);           // 从 Iterable 创建 [1, 2, 3]
List<int> list5 = List.of([1, 2, 3]);             // 从 Iterable<E> 创建 [1, 2, 3]
List<int> list6 = List.unmodifiable([1, 2, 3]);   // 创建不可修改列表 [1, 2, 3]
List<int> list7 = [1, 2, 3];                      // 字面量创建 [1, 2, 3]

3.2、实例属性(读取)

List<int> list = [1, 2, 3, 2];
int len = list.length;                // 获取长度: 4
int first = list.first;               // 第一个元素: 1
int last = list.last;                 // 最后一个元素: 2
bool empty = list.isEmpty;            // 是否为空: false
bool notEmpty = list.isNotEmpty;      // 是否非空: true
Iterable<int> rev = list.reversed;    // 逆序迭代器: (2, 3, 2, 1)
int single = list.single;             // 仅当列表只有一个元素时使用,否则抛异常
Iterator<int> it = list.iterator;     // 获取迭代器
int hashCode = list.hashCode;         // 对象的哈希码
Type type = list.runtimeType;         // 运行时类型: List<int>

3.3、操作符
List<int> list = [1, 2, 3];
int value = list[0];                  // 访问索引 0: 1
list[1] = 10;                         // 修改索引 1: [1, 10, 3]
List<int> combined = list + [4, 5];   // 连接列表: [1, 10, 3, 4, 5]
bool equal = list == [1, 2, 3];       // 判断相等: false

3.4、添加 / 插入 / 扩展元素
List<int> list = [1, 2, 3];
list.add(4);                          // 末尾添加: [1, 2, 3, 4]
list.addAll([5, 6]);                  // 添加多个: [1, 2, 3, 4, 5, 6]
list.insert(0, 0);                    // 插入到索引 0: [0, 1, 2, 3, 4, 5, 6]
list.insertAll(2, [99, 98]);          // 从索引2插入多个: [0, 1, 99, 98, 2, 3, 4, 5, 6]
list.setAll(1, [10, 20]);             // 从索引1开始覆盖: [0, 10, 20, 98, 2, 3, 4, 5, 6]

3.5、删除 / 移除元素
List<int> list = [1, 2, 3, 2, 4];
list.remove(2);                       // 删除第一个 2: [1, 3, 2, 4]
list.removeAt(2);                     // 删除索引 2 的元素: [1, 3, 4]
list.removeLast();                    // 删除最后一个: [1, 3]
list.removeWhere((n) => n.isOdd);     // 删除所有奇数: [3]? 实际测试:移除奇数后剩下 [3]? 初始 [1,3,4] 移除奇数 1,3 → [4]
// 重新初始化
list = [1, 2, 3, 4, 5];
list.removeRange(1, 3);               // 删除索引 1~2: [1, 4, 5]
list.retainWhere((n) => n.isEven);    // 只保留偶数: [4]
list.clear();                         // 清空所有元素: []

3.6、修改 / 替换元素(不改变长度)

dart

List<int?> list = [1, 2, 3, 4, 5];
list.fillRange(1, 3, 0);              // 将索引 1~2 填充为 0: [1, 0, 0, 4, 5]
list.replaceRange(0, 2, [99, 98]);    // 替换 [0,2) 为 [99,98]: [99, 98, 0, 4, 5]
list.setRange(1, 3, [10, 20]);        // 从索引1开始复制 [10,20] 到 [1,3): [99, 10, 20, 4, 5]
list.setAll(2, [30, 40]);             // 从索引2覆盖两个元素: [99, 10, 30, 40, 5] (注意长度不变)

3.7、查询 / 检查元素
List<int> list = [1, 2, 3, 2, 4];
int idx = list.indexOf(2);            // 第一个 2 的索引: 1
int lastIdx = list.lastIndexOf(2);    // 最后一个 2 的索引: 3
bool has = list.contains(3);          // 是否包含 3: true
bool anyEven = list.any((n) => n.isEven);   // 是否有偶数: true
bool allPositive = list.every((n) => n > 0); // 是否都为正: true

3.8、获取子列表 / 部分
List<int> list = [1, 2, 3, 4, 5];
List<int> sub = list.sublist(1, 4);   // 索引 1~3: [2, 3, 4]
Iterable<int> range = list.getRange(1, 3); // 惰性视图索引 1~2: (2, 3)
Iterable<int> taken = list.take(2);   // 前两个: (1, 2)
Iterable<int> takeWhile = list.takeWhile((n) => n < 4); // 小于4的前缀: (1, 2, 3)
Iterable<int> skipped = list.skip(2); // 跳过前两个: (3, 4, 5)
Iterable<int> skipWhile = list.skipWhile((n) => n < 3); // 跳过小于3的: (3, 4, 5)

3.9、转换(产生新列表或其他类型)
List<int> list = [1, 2, 2, 3];
List<int> copy = list.toList();               // 复制新列表: [1,2,2,3]
Set<int> set = list.toSet();                  // 转为 Set: {1,2,3}
Map<int, int> map = list.asMap();             // 转为 Map: {0:1, 1:2, 2:2, 3:3}
List<num> casted = list.cast<num>();          // 类型转换视图: [1,2,2,3] 作为 List<num>
Iterable<int> mapped = list.map((n) => n * 2); // 每个元素乘2: (2,4,4,6)
Iterable<int> expanded = list.expand((n) => [n, n]); // 每个元素展开两次: (1,1,2,2,2,2,3,3)
Iterable<int> filtered = list.where((n) => n.isEven); // 筛选偶数: (2,2)
Iterable<int> typed = list.whereType<int>();   // 筛选指定类型: (1,2,2,3)
Iterable<int> reversedView = list.reversed;    // 逆序视图: (3,2,2,1)
Iterable<int> followed = list.followedBy([4,5]); // 连接后续: (1,2,2,3,4,5)

3.10、排序 / 随机化
List<int> list = [3, 1, 4, 2];
list.sort();                             // 升序排序: [1, 2, 3, 4]
list.sort((a, b) => b.compareTo(a));     // 降序排序: [4, 3, 2, 1]
list.shuffle();                          // 随机打乱顺序,例如 [2, 4, 1, 3]

3.11、归约 / 聚合操作
List<int> list = [1, 2, 3, 4];
int sum = list.reduce((a, b) => a + b);  // 累加: 10
int foldSum = list.fold(10, (p, e) => p + e); // 带初始值累加: 20
String joined = list.join('-');          // 用 '-' 连接: "1-2-3-4"

3.12、迭代 / 遍历
List<int> list = [1, 2, 3];
list.forEach((e) => print(e));           // 依次打印 1, 2, 3
Iterator<int> it = list.iterator;        // 获取迭代器
while (it.moveNext()) print(it.current); // 手动迭代

3.13 继承自 Iterable 的其他常用方法
List<int> list = [1, 2, 3, 2];
int elem = list.elementAt(2);            // 索引 2 的元素: 3
int firstEven = list.firstWhere((n) => n.isEven); // 第一个偶数: 2
int lastOdd = list.lastWhere((n) => n.isOdd);     // 最后一个奇数: 3
int singleEven = list.singleWhere((n) => n > 5, orElse: () => -1); // 无满足条件返回 -1

3.14 修改长度的操作(影响 length 属性)
List<int?> list = [1, 2, 3];
list.length = 5;                         // 扩展长度,新元素为 null: [1, 2, 3, null, null]
list.length = 2;                         // 截断长度: [1, 2]

3.15 注意:不可变列表的行为
List<int> unmod = List.unmodifiable([1, 2, 3]);
// unmod.add(4);                         // 抛出 UnsupportedError
// unmod[0] = 10;                        // 抛出 UnsupportedError

4、Set<E> – 无序不可重复

Set<int> set = {1, 2, 3};

// 属性
int len = set.length;                    // 3
bool isEmpty = set.isEmpty;              // false
bool isNotEmpty = set.isNotEmpty;        // true

// 添加/删除
set.add(4);                              // {1,2,3,4}
set.addAll([5,6]);                       // {1,2,3,4,5,6}
set.remove(3);                           // 删除成功返回 true
set.removeWhere((e) => e.isEven);        // 删除所有偶数: {1,5}
set.retainWhere((e) => e > 2);           // 只保留大于2的: {5}
set.clear();                             // {}

// 集合运算
Set<int> a = {1,2,3};
Set<int> b = {2,3,4};
Set<int> union = a.union(b);             // {1,2,3,4}
Set<int> inter = a.intersection(b);      // {2,3}
Set<int> diff = a.difference(b);         // {1}
Set<int> symDiff = a.union(b).difference(a.intersection(b)); // {1,4}

// 包含
bool contains = a.contains(2);           // true
bool containsAll = a.containsAll([1,2]); // true

// 遍历
a.forEach(print);
Iterable<int> mapped = a.map((e) => e*2);

// 转换
List<int> list = a.toList();             // [1,2,3]
Set<int> copy = Set.from(a);             // 拷贝
Set<int> of = Set.of(a);                 // 类型安全拷贝

5、Map<K, V> – 键值对

Map<String, int> map = {'a': 1, 'b': 2};

// 属性
int len = map.length;                    // 2
bool isEmpty = map.isEmpty;              // false
bool isNotEmpty = map.isNotEmpty;        // true
Iterable<String> keys = map.keys;        // ('a', 'b')
Iterable<int> values = map.values;       // (1, 2)
Iterable<MapEntry<String, int>> entries = map.entries;

// 访问
int? val = map['a'];                     // 1
int? missing = map['c'];                 // null
int valueOrDefault = map['c'] ?? 0;      // 0

// 添加/修改
map['c'] = 3;                            // 添加或更新
map.addAll({'d': 4, 'e': 5});            // 批量添加
map.putIfAbsent('f', () => 6);           // 如果不存在则添加

// 删除
map.remove('a');                         // 移除键 'a',返回被删值 1
map.removeWhere((key, value) => value.isEven); // 删除所有值为偶数的项

// 更新
map.update('b', (value) => value + 10);  // 更新已有键
map.update('missing', (v) => 0, ifAbsent: () => 99); // 不存在则添加 99

// 查询
bool containsKey = map.containsKey('b'); // true
bool containsValue = map.containsValue(2); // true

// 遍历
map.forEach((key, value) => print('$key: $value'));

// 转换
Map<String, int> copy = Map.from(map);    // 浅拷贝
Map<String, int> of = Map.of(map);        // 类型安全拷贝
Map<int, String> reversed = map.map((key, value) => MapEntry(value, key));
// 清空
map.clear();                              // {}

5、DateTime

// 创建
DateTime now = DateTime.now();
DateTime specific = DateTime(2025, 3, 15, 14, 30, 45);
DateTime utc = DateTime.utc(2025, 3, 15);
DateTime parse = DateTime.parse('2025-03-15T14:30:00');

// 属性
int year = now.year;
int month = now.month;
int day = now.day;
int hour = now.hour;
int minute = now.minute;
int second = now.second;
int millisecond = now.millisecond;
int microsecond = now.microsecond;
int weekday = now.weekday;               // 1=Mon, 7=Sun
bool isUtc = now.isUtc;

// 比较
bool isAfter = now.isAfter(specific);    // true/false
bool isBefore = now.isBefore(specific);
int diffDays = now.difference(specific).inDays;

// 添加时间
DateTime later = now.add(Duration(days: 5, hours: 3));
DateTime earlier = now.subtract(Duration(minutes: 30));

// 转换
String iso = now.toIso8601String();      // "2025-03-15T14:30:45.123Z"
String localStr = now.toString();
DateTime toLocal = utc.toLocal();
DateTime toUtc = now.toUtc();

// 比较两个 DateTime
int compareResult = now.compareTo(specific);

6、Runes 是 Unicode 码点的序列,用于处理超出 UTF-16 的字符(如 emoji)。

// 字面量中的 Unicode
String emoji = '🌟';               // 单个码点 U+1F31F
String heart = '\u2665';           // ♥
String smile = '\u{1F600}';        // 😀

// 获取 Runes
Runes runes = emoji.runes;         // [127775]

// 遍历
for (int codePoint in runes) {
  print(String.fromCharCode(codePoint));
}

7、Symbol 表示 Dart 程序中的标识符,主要用于反射(尽管反射在 Flutter 中不可用)或与 JS 互操作。

Symbol sym = #foo;        // 字面量
Symbol fromStr = Symbol('foo');
print(sym == fromStr);  

8、Future 与 Stream 类型(异步相关)

虽然它们也属于数据类型,但主要涉及异步编程,这里简要提及。

Future

// 创建 Future
Future<String> f1 = Future.value('done');
Future<String> f2 = Future.error('error');
Future<String> f3 = Future.delayed(Duration(seconds: 1), () => 'result');

// 注册回调
f3.then((value) => print(value));
f3.catchError((err) => print(err));
f3.whenComplete(() => print('完成'));

// 静态方法
Future<void> wait = Future.wait([f1, f3]); // 等待多个 Future
Future<String> any = Future.any([f1, f3]); // 任何一个完成
Future<String> sync = Future.sync(() => 'sync');

// async/await 配合
void main() async {
  String res = await f3;
}

Stream

// 创建 Stream
Stream<int> stream = Stream.fromIterable([1,2,3]);
Stream<int> periodic = Stream.periodic(Duration(seconds: 1), (i) => i);

// 监听
StreamSubscription<int> sub = stream.listen(
  (data) => print(data),
  onError: (err) => print(err),
  onDone: () => print('done'),
);

// 转换
Stream<int> mapped = stream.map((e) => e * 2);
Stream<int> where = stream.where((e) => e.isEven);
Stream<int> expanded = stream.expand((e) => [e, e]);

// 聚合
Future<int> sum = stream.reduce((a, b) => a + b);
Future<int> fold = stream.fold(0, (p, e) => p + e);
Future<List<int>> list = stream.toList();
Future<Set<int>> set = stream.toSet();

// 异步循环
await for (int value in stream) {
  print(value);
}

9、Record 类型(Dart 3 新增)

记录(Record)是 Dart 3 引入的匿名、不可变、聚合类型。

// 位置字段
(int, String) pair = (42, 'answer');
print(pair.$1);   // 42
print(pair.$2);   // 'answer'

// 命名字段
({int x, int y}) point = (x: 10, y: 20);
print(point.x);   // 10

// 混合
({int x, int y, int}) triple = (x: 1, y: 2, 3);
print(triple.$1);  // 3(位置字段索引从 1 开始)
print(triple.x);   // 1

// 类型别名
typedef Point = ({int x, int y});
Point p = (x: 0, y: 0);

10、泛型类型(<>

Dart 支持泛型,允许定义类型参数化的类、函数和接口。

// 泛型class
class Box<T> {
  T content;
  Box(this.content);
}
void main() {
  Box<int> intBox = Box<int>(42);
  Box<String> strBox = Box<String>('hello');
  
  // 类型推断
  var autoBox = Box(123);   // Box<int>
}
    


// 泛型函数
T first<T>(List<T> list) => list[0];

11、泛型别名 (typedef)

typedef IntList = List<int>;
IntList numbers = [1,2,3];

// 函数类型别名
typedef Adder = int Function(int, int);
Adder add = (a, b) => a + b;

// 泛型 typedef
typedef Transformer<T> = T Function(T);
Transformer<String> upper = (s) => s.toUpperCase();

 12、Never 类型

Never 表示永远不会正常返回的表达式(例如抛出异常或无限循环)。

Never alwaysThrow() {
  throw Exception('Always');
}

运算符

/// Dart 运算符完整示例
/// 涵盖:算术、关系、逻辑、位运算、赋值、条件、类型测试、空安全、级联、索引等

void main() {
  // =========================================================================
  // 一、算术运算符
  // =========================================================================
  int a = 10, b = 3;
  print(a + b);   // 加法: 13
  print(a - b);   // 减法: 7
  print(a * b);   // 乘法: 30
  print(a / b);   // 除法: 3.3333333333333335 (double)
  print(a ~/ b);  // 整除: 3
  print(a % b);   // 取模: 1
  print(-a);      // 取负: -10

  // 自增自减
  int c = 5;
  print(c++);     // 后置自增: 输出5, c变为6
  print(++c);     // 前置自增: c变为7, 输出7
  print(c--);     // 后置自减: 输出7, c变为6
  print(--c);     // 前置自减: c变为5, 输出5

  // =========================================================================
  // 二、关系运算符(结果为 bool)
  // =========================================================================
  int x = 5, y = 8;
  print(x == y);   // 相等: false
  print(x != y);   // 不等: true
  print(x > y);    // 大于: false
  print(x < y);    // 小于: true
  print(x >= y);   // 大于等于: false
  print(x <= y);   // 小于等于: true

  // =========================================================================
  // 三、逻辑运算符(操作 bool)
  // =========================================================================
  bool p = true, q = false;
  print(!p);       // 逻辑非: false
  print(p && q);   // 逻辑与: false
  print(p || q);   // 逻辑或: true

  // 短路求值示例
  bool expensive() { print('expensive called'); return true; }
  bool result = p || expensive(); // p为true,不会调用expensive()
  print(result);  // true

  // =========================================================================
  // 四、位运算符(仅用于 int)
  // =========================================================================
  int bitA = 0b1010;  // 10
  int bitB = 0b1100;  // 12
  print(bitA & bitB);           // 按位与: 8 (0b1000)
  print(bitA | bitB);           // 按位或: 14 (0b1110)
  print(bitA ^ bitB);           // 按位异或: 6 (0b0110)
  print(~bitA);                 // 按位非: -11 (补码)
  print(bitA << 1);             // 左移: 20 (0b10100)
  print(bitA >> 1);             // 右移: 5 (0b101)
  print(bitA >>> 1);            // 无符号右移 (Dart 2.14+): 5 (0b101)

  // =========================================================================
  // 五、赋值运算符
  // =========================================================================
  int v = 10;
  v += 3;   // v = v + 3 → 13
  v -= 2;   // 11
  v *= 2;   // 22
  v ~/= 3;  // 7 (整除)
  v %= 3;   // 1
  v <<= 1;  // 2
  v >>= 1;  // 1
  v |= 4;   // 5 (0b101)
  v &= 3;   // 1 (0b001)
  v ^= 2;   // 3 (0b011)
  print(v); // 3

  // 空安全赋值 ??=
  int? nullable;
  nullable ??= 42;   // 因为 nullable 为 null,赋值为 42
  nullable ??= 99;   // 已经有值,保持不变
  print(nullable);   // 42

  // =========================================================================
  // 六、条件运算符(三元)
  // =========================================================================
  int score = 85;
  String grade = (score >= 60) ? 'Pass' : 'Fail';
  print(grade);   // Pass

  // =========================================================================
  // 七、类型测试与转换运算符
  // =========================================================================
  dynamic obj = 'Hello';
  print(obj is String);    // true
  print(obj is! int);      // true
  String str = obj as String;  // 类型转换(安全,因为运行时是 String)
  print(str);              // Hello

  // 错误示例(注释掉会抛异常):
  // int wrong = obj as int;   // 抛出 CastError

  // =========================================================================
  // 八、空安全运算符
  // =========================================================================
  // 8.1 ?? 空值合并
  int? aNull;
  int val = aNull ?? 42;    // aNull为null,取42
  print(val);   // 42

  // 8.2 ?. 安全调用
  String? name;
  int? len = name?.length;  // name为null,结果为null,不会抛出异常
  print(len);   // null

  // 8.3 空感知扩展 ...?(集合中使用)
  List<int>? maybeList;
  List<int> combined = [0, ...?maybeList];  // maybeList为null,展开为空
  print(combined);  // [0]

  // 8.4 ?[] 安全索引(Dart 2.17+)
  List<int>? list;
  int? first = list?[0];    // list为null,结果为null
  print(first);   // null

  // =========================================================================
  // 九、级联运算符
  // =========================================================================
  // 9.1 .. 级联,允许连续调用
  var sb = StringBuffer()
    ..write('Hello')
    ..write(' ')
    ..write('World')
    ..write('!');
  print(sb.toString());   // Hello World!

  // 9.2 ?.. 空感知级联(如果对象不为null才执行)
  List<int>? cascadeList;
  cascadeList?..add(1)..add(2);   // cascadeList为null,什么也不做
  print(cascadeList);   // null

  List<int> nonNullList = [];
  nonNullList?..add(1)..add(2);   // 正常执行
  print(nonNullList);   // [1, 2]

  // =========================================================================
  // 十、索引/下标运算符
  // =========================================================================
  List<String> fruits = ['apple', 'banana', 'orange'];
  print(fruits[0]);      // 访问: apple
  fruits[1] = 'blueberry';  // 修改
  print(fruits);         // [apple, blueberry, orange]

  // 可空安全索引(已见 ?[])
  // =========================================================================
  // 十一、其他运算符
  // =========================================================================
  // 成员访问 .
  print(fruits.length);  // 3

  // 函数调用 ()
  void sayHi() => print('Hi');
  sayHi();               // 调用函数

  // 逗号 , 用于分隔参数、列表元素等
  var list = [1, 2, 3];

  // =========================================================================
  // 十二、运算符优先级演示(括号改变优先级)
  // =========================================================================
  int priority = 2 + 3 * 4;        // 乘法优先: 14
  int withParen = (2 + 3) * 4;     // 括号优先: 20
  print('优先级: $priority vs $withParen');
}                                                           

函数

/// Dart 函数声明完整示例
/// 包含:命名函数、箭头函数、可选参数、匿名函数、闭包、高阶函数等

void main() {
  // 调用各种函数来演示
  print('=== 基本函数 ===');
  print(add(3, 5));           // 8
  print(multiply(4, 6));      // 24

  print('\n=== 可选参数 ===');
  greet('Alice');             // Hello, Alice! You are 0 years old.
  greet('Bob', age: 25);      // Hello, Bob! You are 25 years old.
  logMessage('Error');        // [INFO] Error
  logMessage('Warning', 'WARN'); // [WARN] Warning

  print('\n=== 返回值类型 ===');
  print(getPi());             // 3.14159
  doNothing();                // (无输出)

  print('\n=== 匿名函数 ===');
  var square = (int x) => x * x;
  print(square(5));           // 25

  print('\n=== 闭包 ===');
  var counter = makeCounter();
  print(counter());           // 0
  print(counter());           // 1

  print('\n=== 函数作为参数 ===');
  applyOperation(3, 4, add);          // 3 + 4 = 7
  applyOperation(3, 4, multiply);     // 3 * 4 = 12

  print('\n=== 函数作为返回值 ===');
  var myAdder = createAdder(10);
  print(myAdder(5));          // 15

  print('\n=== 生成器函数(同步/异步) ===');
  print(syncRange(3).toList());       // [0, 1, 2]
  asyncRange(3).listen((v) => print('async: $v')); // 异步输出 0,1,2

  print('\n=== Never 函数 ===');
  // 下面的调用会抛出异常,演示时注释掉
  // throwError();   // 抛出 Exception: Always throws!
}

// =========================================================================
// 一、基本函数声明
// =========================================================================

/// 普通函数,显式指定参数类型和返回值类型
int add(int a, int b) {
  return a + b;
}

/// 单行表达式函数(箭头函数)
int multiply(int a, int b) => a * b;

// =========================================================================
// 二、可选参数
// =========================================================================

/// 命名参数(使用 {}),可设置默认值
void greet(String name, {int age = 0, String? title}) {
  print('Hello, ${title != null ? '$title ' : ''}$name! You are $age years old.');
}

/// 位置可选参数(使用 []),可设置默认值
void logMessage(String msg, [String prefix = 'INFO']) {
  print('[$prefix] $msg');
}

// =========================================================================
// 三、返回值类型
// =========================================================================

/// 明确返回值类型
double getPi() => 3.14159;

/// void 表示无返回值(可省略 return,或 return; 或 return null;)
void doNothing() {
  // 没有 return 语句
}

/// Never 表示函数永远不会正常返回(总是抛出异常或无限循环)
Never throwError() {
  throw Exception('Always throws!');
}

// =========================================================================
// 四、匿名函数(Lambda)
// =========================================================================

// 匿名函数赋值给变量
var divide = (double a, double b) {
  if (b == 0) throw ArgumentError('Division by zero');
  return a / b;
};

// 匿名函数在集合操作中使用
var numbers = [1, 2, 3];
var doubled = numbers.map((n) => n * 2).toList(); // 箭头形式匿名函数

// =========================================================================
// 五、闭包(捕获外部变量)
// =========================================================================

/// 返回一个闭包,每次调用增加计数
Function makeCounter() {
  int count = 0;
  return () => count++;
}

// =========================================================================
// 六、函数作为一等公民(高阶函数)
// =========================================================================

/// 接收函数作为参数
void applyOperation(int a, int b, int Function(int, int) operation) {
  print('Result: ${operation(a, b)}');
}

/// 返回一个函数
Function createAdder(int addend) {
  return (int value) => value + addend;
}

// =========================================================================
// 七、生成器函数(同步和异步)
// =========================================================================

/// 同步生成器:返回 Iterable,使用 sync* 和 yield
Iterable<int> syncRange(int limit) sync* {
  for (int i = 0; i < limit; i++) {
    yield i;
  }
}

/// 异步生成器:返回 Stream,使用 async* 和 yield
Stream<int> asyncRange(int limit) async* {
  for (int i = 0; i < limit; i++) {
    await Future.delayed(Duration(milliseconds: 100));
    yield i;
  }
}

// =========================================================================
// 八、函数类型别名(typedef)
// =========================================================================

/// 为函数类型定义别名,提高可读性
typedef IntOperation = int Function(int, int);

/// 使用 typedef
int executeOperation(IntOperation op, int x, int y) => op(x, y);

// 演示 typedef
void demoTypeDef() {
  IntOperation addOp = (a, b) => a + b;
  print(executeOperation(addOp, 10, 20)); // 30
}

// =========================================================================
// 九、静态函数与实例方法(在类中)
// =========================================================================

class Calculator {
  // 实例方法
  int addInstance(int a, int b) => a + b;

  // 静态方法(类方法)
  static int multiplyStatic(int a, int b) => a * b;
}

// 调用示例(可以在 main 中取消注释)
void demoClassMethods() {
  var calc = Calculator();
  print(calc.addInstance(2, 3));        // 5
  print(Calculator.multiplyStatic(2, 3)); // 6
}

控制流程

控制流类型关键字/语法说明
条件分支ifelse ifelse布尔条件判断
多值匹配(传统)switchcasebreakdefault需要显式 break 避免穿透
多值匹配(Dart 3 表达式)switch (expr) { pattern => value }可作为值返回,无需 break
模式匹配if-casefor-in 模式、case ... when解构、类型匹配、守卫
循环forfor-inwhiledo-while标准循环与迭代
跳转breakcontinuereturn、标签控制循环退出或跳过
断言assert(condition, message)开发期检查,生产环境忽略

异常处理

特性语法/示例说明
抛出异常throw Exception('msg')可抛出任何非 null 对象,通常抛出 Exception 或 Error 子类
基本捕获try { ... } catch (e) { ... }捕获任意异常,e 是异常对象
指定类型捕获on FormatException catch (e)只捕获特定类型异常
获取堆栈catch (e, stackTrace)第二个参数是 StackTrace 对象
finallyfinally { ... }无论是否发生异常都会执行
重新抛出rethrow在 catch 块内重新抛出当前异常,保留原始堆栈
自定义异常class MyEx implements Exception通常实现 Exception 接口,可覆盖 toString()
异步捕获try { await future } catch (e)在 async 函数中使用 try-catch 捕获 Future 异常
Future 捕获future.catchError((e) => ...)链式调用处理错误
Stream 错误listen(..., onError: (e) => ...)监听时注册错误回调
await for 捕获try { await for (x in stream) } catch (e)用 try 包裹整个循环

面向对象基础与进阶(类和构造函数)

一、类的基本定义

1.1 最简单的类
class Person {
  // 实例变量(成员变量)
  String name;
  int age;

  // 构造函数(语法糖:自动将参数赋值给 this 对应的变量)
  Person(this.name, this.age);

  // 实例方法
  void sayHello() {
    print('Hello, I am $name, $age years old.');
  }
}

void main() {
  var p = Person('Alice', 30);
  p.sayHello();  // 输出:Hello, I am Alice, 30 years old.
}
1.2 带构造函数体的类
class Rectangle {
  double width, height;

  // 构造函数体中可以添加逻辑
  Rectangle(double w, double h) {
    width = w;
    height = h;
    print('创建矩形: $width x $height');
  }
}

void main() {
  var rect = Rectangle(5.0, 3.0); // 输出:创建矩形: 5.0 x 3.0
}

二、构造函数详解

2.1 默认构造函数

如果没有显式声明构造函数,Dart 会提供一个无参的默认构造函数(仅调用父类的无参构造函数)。

class Animal {
  Animal() {
    print('Animal 构造函数被调用');
  }
}

class Dog extends Animal {
  // 没有显式构造函数,默认有一个 Dog() : super()
}

void main() {
  var dog = Dog(); // 输出:Animal 构造函数被调用
}
2.2 普通构造函数(支持重载?不行,通过命名构造函数实现)

Dart 不支持多个同名构造函数(函数重载),但支持命名构造函数

class Point {
  double x, y;

  // 普通构造函数
  Point(this.x, this.y);

  // 命名构造函数
  Point.origin() : x = 0, y = 0;

  // 命名构造函数
  Point.fromJson(Map<String, double> json) : x = json['x']!, y = json['y']!;
}

void main() {
  var p1 = Point(3, 4);
  var p2 = Point.origin();
  var p3 = Point.fromJson({'x': 1.5, 'y': 2.5});
  print('${p1.x},${p1.y}'); // 3,4
  print('${p2.x},${p2.y}'); // 0,0
  print('${p3.x},${p3.y}'); // 1.5,2.5
}
2.3 重定向构造函数

一个构造函数可以委托给同一类的另一个构造函数(使用 : this(...))。

class Vector {
  double x, y;

  Vector(this.x, this.y);

  // 重定向到主构造函数 (twoD 这个是方法名称)
  Vector.twoD(double x, double y) : this(x, y);

  // 重定向到命名构造函数
  Vector.zero() : this(0, 0);
}

void main() {
  var v = Vector.twoD(2, 3);
  print('${v.x},${v.y}'); // 2,3
}
2.4 常量构造函数

使用 const 关键字创建编译时常量对象。要求所有实例变量必须是 final

class ImmutablePoint {
  final int x, y;

  const ImmutablePoint(this.x, this.y);
}

void main() {
  const p1 = ImmutablePoint(1, 2);
  const p2 = ImmutablePoint(1, 2);
  print(identical(p1, p2)); // true(同一个常量实例)
}
2.5 工厂构造函数(factory

工厂构造函数不总是创建新实例,可以从缓存中返回现有实例,或返回子类实例。工厂构造函数无法访问 this

class Logger {
  final String name;
  static final Map<String, Logger> _cache = {};

  // 工厂构造函数:返回缓存中的实例或新实例
  factory Logger(String name) {
    return _cache.putIfAbsent(name, () => Logger._internal(name));
  }

  // 私有构造函数
  Logger._internal(this.name);
}

void main() {
  var logger1 = Logger('UI');
  var logger2 = Logger('UI');
  print(identical(logger1, logger2)); // true(同一个实例)
}

三、初始化列表与 final 变量

3.1 初始化列表

在构造函数体执行前初始化实例变量,常用于初始化 final 变量或进行断言。

class Circle {
  final double radius;
  static const double pi = 3.14159;

  // 初始化列表(: 后跟表达式)
  Circle(double r) : radius = r, assert(r > 0);

  // 也可以调用父类构造函数
  Circle.fromDiameter(double diameter) : radius = diameter / 2;

  double get area => pi * radius * radius;
}

void main() {
  var c = Circle(5.0);
  print(c.area); // 78.53975
}
3.2 使用 late 延迟初始化
class Database {
  late String _connectionString;

  void init(String conn) {
    _connectionString = conn;
  }

  void query() {
    print('Querying $_connectionString');
  }
}

四、成员变量

4.1 实例变量与 getter/setter

每个实例变量隐式包含一个 getter 和 setter(final 变量只有 getter)。也可以自定义 getter/setter。

class Temperature {
  double _celsius; // 私有变量(以下划线开头)

  Temperature(this._celsius);

  // 自定义 getter
  double get celsius => _celsius;

  set celsius(double value) {
    _celsius = value;
  }

  // 计算属性(只有 getter)
  double get fahrenheit => _celsius * 9 / 5 + 32;

  set fahrenheit(double value) {
    _celsius = (value - 32) * 5 / 9;
  }
}

void main() {
  var temp = Temperature(25);
  print(temp.fahrenheit); // 77.0
  temp.fahrenheit = 100;
  print(temp.celsius);    // 37.77777777777778
}
4.2 静态变量与静态方法

静态变量属于类而非实例,在首次访问时初始化。

class AppConfig {
  static const String appName = 'MyApp';
  static int _counter = 0;

  static int get counter => _counter;

  static void increment() {
    _counter++;
  }
}

void main() {
  print(AppConfig.appName); // MyApp
  AppConfig.increment();
  print(AppConfig.counter); // 1
}
4.3 私有成员(库可见性)

使用下划线 _ 前缀声明的成员仅在当前库(文件)内可见。

// 在 my_class.dart 文件中
class _InternalHelper {
  void _hiddenMethod() {}
}

class PublicClass {
  String _privateField = 'secret';

  void _internalLogic() {}
}

五、方法

5.1 实例方法、静态方法、操作符重载
class Vector2 {
  final double x, y;

  Vector2(this.x, this.y);

  // 实例方法
  double dot(Vector2 other) => x * other.x + y * other.y;

  // 静态方法
  static Vector2 zero() => Vector2(0, 0);

  // 操作符重载
  Vector2 operator +(Vector2 other) => Vector2(x + other.x, y + other.y);

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

void main() {
  var v1 = Vector2(1, 2);
  var v2 = Vector2(3, 4);
  print(v1 + v2);      // (4.0, 6.0)
  print(v1.dot(v2));   // 11
  print(Vector2.zero());// (0.0, 0.0)
}

六、继承

6.1 extends 与 @override
class Animal {
  void eat() => print('Animal eats');

  Animal() {
    print('Animal constructor');
  }
}

class Dog extends Animal {
  @override
  void eat() {
    super.eat();      // 调用父类方法
    print('Dog eats bone');
  }

  Dog() : super() {
    print('Dog constructor');
  }
}

void main() {
  var dog = Dog();
  dog.eat();
}
// 输出:
// Animal constructor
// Dog constructor
// Animal eats
// Dog eats bone
6.2 继承中的构造函数顺序

子类构造函数体执行前必须调用父类构造函数(默认调用无参构造,或通过 : super(...) 显式调用)。

class Parent {
  String name;
  Parent(this.name);
}

class Child extends Parent {
  int age;
  Child(String name, this.age) : super(name); // 必须显式调用父类构造函数
}

七、抽象类与接口

7.1 抽象类(abstract

抽象类不能实例化,可以包含抽象方法(无方法体)。

abstract class Shape {
  double get area; // 抽象 getter
  void draw();     // 抽象方法
}

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

  @override
  double get area => 3.14159 * radius * radius;

  @override
  void draw() {
    print('Drawing a circle');
  }
}
7.2 隐式接口(implements

每个类都隐式定义了一个接口,其他类可以通过 implements 实现该接口(必须提供所有成员实现)。

class Printable {
  void printData() => print('Printing...');
}

class Document implements Printable {
  @override
  void printData() {
    print('Document printing');
  }
}

八、混入(Mixin)

8.1 基本混入(mixin + with

混入用于复用多个类的代码。

mixin Flyable {
  void fly() => print('Flying');
}

mixin Swimmable {
  void swim() => print('Swimming');
}

class Duck with Flyable, Swimmable {
  void quack() => print('Quack');
}

void main() {
  var duck = Duck();
  duck.fly();   // Flying
  duck.swim();  // Swimming
  duck.quack(); // Quack
}
8.2 限制混入的使用范围(on 关键字)

mixin 可以指定只能被特定类的子类使用。

class Animal {}

mixin Walker on Animal {
  void walk() => print('Walking');
}

class Dog extends Animal with Walker {} // 正确
// class Table with Walker {} // 错误:Table 不是 Animal 的子类
8.3 mixin class(Dart 3)

Dart 3 允许将 mixin 声明为 mixin class,既可以作为普通类使用,也可以作为混入使用。

mixin class Serializable {
  void serialize() => print('Serializing');
}

class MyClass extends Serializable {} // 作为普通类继承
class Other with Serializable {}      // 作为混入使用

九、Dart 3 类修饰符

Dart 3 引入了新的类修饰符,用于限制类的使用方式。

修饰符含义
sealed禁止在库外继承或实现,用于详尽 switch
base禁止在库外继承或实现
final禁止在库外继承或实现,但允许扩展(仅本库)
interface禁止在库外实现,但可以继承
mixin只能作为混入使用,不能实例化
mixin class既可以作类也可以作混入
// sealed 示例:详尽 switch
sealed class Result {}

class Success extends Result {
  final int value;
  Success(this.value);
}

class Failure extends Result {
  final String error;
  Failure(this.error);
}

String describe(Result result) => switch (result) {
  Success(value: var v) => 'Success with $v',
  Failure(error: var e) => 'Failure: $e',
}; // 不需要 default,因为 sealed 确保所有子类都已处理

十、 extension 类扩展

扩展方法(Extension Methods)是 Dart 2.7 引入的特性,它允许你在不修改原始类不继承该类的情况下,为现有类型添加新的方法、getter、setter 或操作符。这对于为第三方库或内置类型(如 StringList)增加便捷功能非常有用

语法:

extension <扩展名> on <类型> {
  // 新增的方法、getter、setter、操作符
}
扩展中可定义的成员

扩展中可以定义:

  • 实例方法
  • getter / setter
  • 操作符(如 +-[]
  • 静态方法(不能直接定义,但可以通过类名调用?注意:扩展中不能定义 static 成员,但可以定义普通方法,这些方法仍然需要通过实例调用)
extension NumberParsing on String {
  // 方法
  int toInt() => int.parse(this);

  // getter
  bool get isNumeric => double.tryParse(this) != null;

  // setter(一般用于可修改的上下文,但字符串不可变,这里仅演示语法)
  set asInt(int value) => throw UnsupportedError('Strings are immutable');
}

void main() {
  print('123'.toInt());      // 123
  print('abc'.isNumeric);    // false
}
extension 总结
要点说明
目的为现有类型添加新成员,无需修改源码或继承
语法extension Name on Type { ... }
可定义内容方法、getter、setter、操作符
不可定义实例变量、静态成员、构造函数
优先级实例成员 > 扩展成员
解析时机编译时静态解析(基于声明类型)
冲突处理使用 show/hide/as 或扩展名限定
典型用例为内置类型或第三方库添加便捷方法

十一、call

任何类只要定义了 call 方法,它的实例就可以像函数那样被调用(使用 对象() 语法)。call 方法可以定义参数和返回值,调用时就会执行 call 方法体。

11.1 基本示例
class Greeter {
  String call(String name) {
    return 'Hello, $name!';
  }
}

void main() {
  var greeter = Greeter();
  
  // 像函数一样调用实例
  print(greeter('Alice')); // Hello, Alice!
}

十二、类总结与最佳实践

概念关键字/语法要点
类定义class Name { ... }实例变量、方法
构造函数ClassName(...)命名、重定向、常量、工厂
初始化列表: field = value在构造函数体前执行
final 变量final type name只能赋值一次
静态成员static属于类,类名访问
私有成员_name库内可见
getter/setterget name / set name自定义属性访问
继承extends单继承,调用 super
抽象类abstract class不能实例化
接口实现implements多个接口
混入mixin + with代码复用
类修饰符sealedbasefinalinterfaceDart 3 控制继承/实现

异步编程

一、Future 基础

Future 表示一个延迟计算的结果,它可能是未完成(pending)、成功(completed with value)或失败(completed with error)。

1.1 创建 Future
import 'dart:async';

void main() {
  // 1. 直接返回值的 Future
  Future<String> future1 = Future.value('Hello');

  // 2. 立即出错的 Future
  Future<void> future2 = Future.error(Exception('出错了'));

  // 3. 延迟完成的 Future(常用)
  Future<String> future3 = Future.delayed(Duration(seconds: 2), () {
    return '2秒后的结果';
  });

  // 4. 同步执行的 Future(立即执行,但仍为 Future)
  Future<String> future4 = Future.sync(() => '同步计算');

  // 5. 使用 Completer 手动控制(进阶)
  Completer<int> completer = Completer();
  Future<int> future5 = completer.future;
  completer.complete(42); // 手动完成
}
1.2 使用 then 和 catchError
void main() {
  Future.delayed(Duration(seconds: 1), () => '数据')
      .then((value) {
        print('成功: $value');
        return '处理后的数据';
      })
      .then((processed) => print('最终: $processed'))
      .catchError((error) {
        print('捕获错误: $error');
      })
      .whenComplete(() {
        print('无论成功或失败都执行');
      });
}
// 输出:
// 成功: 数据
// 最终: 处理后的数据
// 无论成功或失败都执行
1.3 async / await(推荐)
Future<void> fetchData() async {
  try {
    print('开始获取数据');
    String data = await Future.delayed(Duration(seconds: 2), () => '网络数据');
    print('数据: $data');
  } catch (e) {
    print('出错: $e');
  } finally {
    print('清理资源');
  }
}

void main() async {
  await fetchData();
  print('完成');
}
// 输出:
// 开始获取数据
// 数据: 网络数据
// 清理资源
// 完成

二、Stream 基础

Stream 是一系列异步事件的序列,可以随时间接收多个值(类似 Rx 中的 Observable)。

2.1 创建 Stream
import 'dart:async';

void main() {
  // 1. 从 Iterable 创建
  Stream<int> stream1 = Stream.fromIterable([1, 2, 3]);

  // 2. 周期性事件
  Stream<int> stream2 = Stream.periodic(Duration(seconds: 1), (count) => count).take(5);

  // 3. 单次事件
  Stream<int> stream3 = Stream.value(42);

  // 4. 错误流
  Stream<int> stream4 = Stream.error(Exception('出错'));

  // 5. 空流
  Stream<int> stream5 = Stream.empty();

  // 6. 使用 StreamController 手动控制(进阶)
  StreamController<int> controller = StreamController();
  Stream<int> stream6 = controller.stream;
  controller.add(1);
  controller.add(2);
  controller.close();
}
2.2 监听 Stream
void main() {
  Stream<int> numberStream = Stream.periodic(Duration(milliseconds: 500), (i) => i).take(5);

  // 方式1:listen(常用)
  StreamSubscription<int> subscription = numberStream.listen(
    (value) => print('收到: $value'),        // 数据回调
    onError: (error) => print('错误: $error'),
    onDone: () => print('流结束'),
    cancelOnError: false,                    // 出错后是否自动取消
  );

  // 稍后可以取消订阅
  // subscription.cancel();

  // 方式2:await for(推荐,需在 async 函数中)
  // 见下文
}
// 输出(每0.5秒):
// 收到: 0
// 收到: 1
// 收到: 2
// 收到: 3
// 收到: 4
// 流结束
2.3 使用 await for
Future<void> readStream() async {
  Stream<int> stream = Stream.periodic(Duration(milliseconds: 500), (i) => i).take(3);
  await for (int value in stream) {
    print('await for: $value');
  }
  print('循环结束');
}

void main() async {
  await readStream();
}
// 输出:
// await for: 0
// await for: 1
// await for: 2
// 循环结束

三、生成器(Generator)

生成器函数可以惰性地产出多个值,分为同步生成器和异步生成器。

3.1 同步生成器(sync* + yield

返回 Iterable,每次迭代时产生一个值。

dart

Iterable<int> countUpTo(int n) sync* {
  for (int i = 1; i <= n; i++) {
    yield i;          // 产生一个值
  }
}

void main() {
  for (int val in countUpTo(5)) {
    print(val);
  }
}
// 输出:1 2 3 4 5

递归生成器:

Iterable<int> countDown(int from) sync* {
  if (from <= 0) return;
  yield from;
  yield* countDown(from - 1);   // yield* 委托给另一个生成器
}

void main() {
  print(countDown(3).toList()); // [3, 2, 1]
}
3.2 异步生成器(async* + yield

返回 Stream,可异步产出值(支持 await)。

Stream<int> asyncCount(int max) async* {
  for (int i = 1; i <= max; i++) {
    await Future.delayed(Duration(milliseconds: 500));
    yield i;
  }
}

void main() async {
  await for (int val in asyncCount(3)) {
    print(val);
  }
}
// 每0.5秒输出:1 2 3

使用 yield* 委托给另一个异步生成器:

Stream<int> numbers() async* {
  yield 1;
  yield* asyncCount(2);   // 委托
  yield 4;
}

四、Future 高级组合操作

4.1 Future.wait:等待多个 Future 全部完成
Future<void> fetchUsers() async {
  var futures = [
    Future.delayed(Duration(seconds: 1), () => 'User 1'),
    Future.delayed(Duration(seconds: 2), () => 'User 2'),
    Future.delayed(Duration(seconds: 1), () => 'User 3'),
  ];

  List<String> results = await Future.wait(futures);
  print(results); // [User 1, User 2, User 3] 约2秒后输出
}
4.2 Future.any:任意一个完成即返回
Future<void> fastestResponse() async {
  var futures = [
    Future.delayed(Duration(seconds: 3), () => '慢速'),
    Future.delayed(Duration(seconds: 1), () => '快速'),
  ];
  String result = await Future.any(futures);
  print(result); // '快速'(约1秒后)
}
4.3 Future.timeout:超时处理
Future<void> withTimeout() async {
  try {
    String data = await Future.delayed(Duration(seconds: 5), () => '数据')
        .timeout(Duration(seconds: 2));
    print(data);
  } catch (e) {
    print('超时: $e');
  }
}
4.4 其他实用方法
方法作用
Future.value创建已完成的 Future
Future.error创建立即出错的 Future
Future.delayed延迟执行
Future.sync同步执行,但返回 Future
Future.microtask在微任务队列中执行(高优先级)

五、Stream 高级操作

5.1 转换 Stream
void main() async {
  Stream<int> source = Stream.fromIterable([1, 2, 3, 4]);

  // map
  Stream<int> doubled = source.map((x) => x * 2);
  await for (int val in doubled) print(val); // 2,4,6,8

  // where
  Stream<int> evens = source.where((x) => x.isEven);
  await for (int val in evens) print(val);   // 2,4

  // expand
  Stream<int> expanded = source.expand((x) => [x, x]);
  await for (int val in expanded) print(val); // 1,1,2,2,3,3,4,4
}
5.2 合并多个 Stream
import 'dart:async';

void main() async {
  Stream<int> streamA = Stream.periodic(Duration(milliseconds: 300), (i) => i).take(3);
  Stream<int> streamB = Stream.periodic(Duration(milliseconds: 500), (i) => i * 10).take(3);

  // 合并(交错输出)
  Stream<int> merged = StreamGroup.merge([streamA, streamB]);
  await for (int val in merged) {
    print(val);
  }
  // 可能输出:0,0,1,10,2,20
}
5.3 其他常用 Stream 方法
方法作用
stream.take(n)取前 n 个事件
stream.skip(n)跳过前 n 个事件
stream.takeWhile(condition)满足条件时取,一旦不满足就结束
stream.distinct()去重(连续重复的只发一次)
stream.handleError处理错误并可能继续
stream.timeout超时后触发错误或执行备选
stream.toList()收集所有事件到 List(返回 Future<List>
stream.fold / stream.reduce归约

六、错误处理

6.1 Future 中的错误
Future<void> risky() async {
  throw FormatException('格式错误');
}

void main() async {
  // try-catch 方式
  try {
    await risky();
  } on FormatException catch (e) {
    print('捕获: $e');
  }

  // catchError 方式
  risky().catchError((e) => print('catchError: $e'));
}
6.2 Stream 中的错误
void main() {
  Stream<int> stream = Stream.fromIterable([1, 2, 3])
      .map((x) {
        if (x == 2) throw Exception('值2非法');
        return x;
      })
      .handleError((e) {
        print('处理错误: $e');
        return -1; // 可返回替代值,但注意类型必须匹配
      });

  stream.listen(
    (val) => print('收到: $val'),
    onError: (err) => print('监听器收到错误: $err'),
  );
}
// 输出:
// 收到: 1
// 处理错误: Exception: 值2非法
// 监听器收到错误: Exception: 值2非法
// 注意:错误之后流通常终止,除非使用 `handleError` 且返回相同类型且流未取消

七、微任务与事件循环

Dart 事件循环有多个队列:微任务队列(Microtask)和事件队列(Event)。Future 默认放入事件队列,但可以通过 Future.microtask 放入微任务队列(优先级更高)。

dart

void main() {
  print('开始');

  Future(() => print('事件队列 Future'));
  Future.microtask(() => print('微任务 Future'));

  print('结束');
}
// 输出:
// 开始
// 结束
// 微任务 Future
// 事件队列 Future

注意:通常不需要手动使用微任务,但了解它有助于理解异步顺序。

八、总结与最佳实践
概念适用场景代码特征
Future单次异步操作(如 HTTP 请求)async/await 或 .then()
Stream多次异步事件(如用户点击、定时器)async*/yield 或 StreamController
sync*惰性同步序列Iterable<T> + yield
async*异步事件序列Stream<T> + yield

元数据

1 内置注解

是指注解(Annotations) ,用于为代码添加额外信息(如 @deprecated@override),供工具或运行时使用

  • @override:表示子类方法覆盖了父类的方法。
  • @deprecated / @Deprecated('message'):标记元素已过时,使用时会发出警告。
  • @required(已废弃,使用 required 关键字代替)。
  • @doNotStore(用于 dart:ffi)。
  • @literal(用于预期为字面量的参数)。

2 自定义注解

你可以定义自己的注解类,只需要创建一个类,并在其前添加 @ 即可使用。通常将注解类的构造函数声明为 const

// 定义注解类
class Todo {
  final String who;
  final String what;
  const Todo(this.who, this.what);
}

// 使用自定义注解
@Todo('Alice', 'Refactor this function')
void legacyFunction() {
  // ...
}

// 注解可以包含多个值,也可以单独使用(不需要参数)
class Experimental {
  const Experimental();
}

@Experimental()
void newFeature() {}

枚举

从 Dart 2.17 开始,枚举可以拥有字段构造函数方法和 getter。每个枚举值可以携带不同的数据。

1 带字段的枚举

enum Planet {
  mercury(0.4, 0.055),
  venus(0.7, 0.815),
  earth(1.0, 1.0),
  mars(1.5, 0.107);

  final double distance;   // 距离太阳(AU)
  final double mass;       // 质量(地球质量单位)

  // 构造函数必须是 const
  const Planet(this.distance, this.mass);

  // 计算属性
  double get gravity => mass / (distance * distance);
}

void main() {
  Planet earth = Planet.earth;
  print('地球距离: ${earth.distance} AU');  // 1.0
  print('地球质量: ${earth.mass}');        // 1.0
  print('地球重力: ${earth.gravity}');     // 1.0
}

2 带方法的枚举

enum Status {
  loading,
  success,
  failure;

  // 实例方法
  bool get isSuccess => this == Status.success;

  // 静态方法
  static Status fromString(String str) {
    return Status.values.firstWhere(
      (e) => e.name == str,
      orElse: () => throw ArgumentError('Unknown status: $str'),
    );
  }
}

void main() {
  Status s = Status.success;
  print(s.isSuccess);  // true

  Status parsed = Status.fromString('loading');
  print(parsed);       // Status.loading
}

3 实现接口的枚举

abstract class Describable {
  String describe();
}

enum Animal implements Describable {
  cat('meow'),
  dog('bark'),
  cow('moo');

  final String sound;
  const Animal(this.sound);

  @override
  String describe() => 'This animal says $sound';
}

void main() {
  Animal animal = Animal.cat;
  print(animal.describe());  // This animal says meow
}

4、枚举与 switch 语句

枚举最常见的使用场景是与 switch 配合,且推荐使用 switch 表达式(Dart 3)实现详尽检查。

enum TrafficLight { red, yellow, green }

String getAction(TrafficLight light) {
  return switch (light) {
    TrafficLight.red => 'Stop',
    TrafficLight.yellow => 'Caution',
    TrafficLight.green => 'Go',
  };
}

void main() {
  print(getAction(TrafficLight.red));     // Stop
  print(getAction(TrafficLight.green));   // Go
}

传统 switch 语句需要 break

void handleLight(TrafficLight light) {
  switch (light) {
    case TrafficLight.red:
      print('Stop');
      break;
    case TrafficLight.yellow:
      print('Caution');
      break;
    case TrafficLight.green:
      print('Go');
      break;
  }
}

5、枚举的局限性

  1. 不能动态添加值:枚举值在编译时就固定。
  2. 不能继承或混入:枚举类默认是 final,不能作为父类。
  3. 自动生成 values 列表,但顺序与声明顺序一致。
  4. 每个枚举值都是单例,因此可以使用 identical 比较。
enum Size { small, medium, large }

void main() {
  Size s1 = Size.small;
  Size s2 = Size.small;
  print(identical(s1, s2));  // true
}

6、枚举与泛型

枚举类型可以作为泛型参数:

enum Priority { low, medium, high }

class Task<T extends Enum> {
  final T priority;
  Task(this.priority);
}

void main() {
  Task<Priority> task = Task(Priority.high);
  print(task.priority);  // Priority.high
}

7、实用技巧

7.1 获取枚举值的下一个/上一个
enum Direction { north, east, south, west }

extension DirectionExtension on Direction {
  Direction get next {
    int nextIndex = (index + 1) % Direction.values.length;
    return Direction.values[nextIndex];
  }

  Direction get previous {
    int prevIndex = (index - 1 + Direction.values.length) % Direction.values.length;
    return Direction.values[prevIndex];
  }
}

void main() {
  Direction dir = Direction.north;
  print(dir.next);     // Direction.east
  print(dir.previous); // Direction.west
}
7.2 枚举与 JSON 序列化

枚举值通常序列化为字符串名称:

enum Role { admin, editor, viewer }

String roleToJson(Role role) => role.name;
Role roleFromJson(String json) => Role.values.firstWhere((e) => e.name == json);

一、基本导入语法

1.1 导入核心库(dart: 前缀)

Dart 平台内置的核心库,如 dart:core(自动导入)、dart:mathdart:iodart:convertdart:async 等。

dart

import 'dart:math';      // 导入数学库
import 'dart:io';        // 导入文件/网络/进程库(仅命令行/Flutter 桌面端)
import 'dart:convert';   // 导入 JSON/UTF-8 编解码库
import 'dart:async';     // 导入异步库(Future/Stream 已自动部分导入,但显式导入无害)

void main() {
  print(sqrt(16)); // 来自 dart:math,输出 4.0
}

1.2 导入自己写的文件(相对路径或绝对路径)

dart

// 假设项目结构:
// lib/
//   utils/
//     math_helper.dart
//   main.dart

// 在 main.dart 中:
import 'utils/math_helper.dart';   // 相对路径
import '/absolute/path/to/file.dart'; // 绝对路径(不推荐)

1.3 导入第三方包(package: 前缀)

通过 pubspec.yaml 添加依赖后,使用 package:包名/路径 导入。

dart

// 假设 pubspec.yaml 中有:dependencies: http: ^0.13.0
import 'package:http/http.dart' as http;

void fetch() async {
  var response = await http.get(Uri.parse('https://api.example.com'));
  print(response.body);
}

二、导入时的修饰符

2.1 使用 as 指定前缀(避免命名冲突)

当两个库有相同名称的类时,可以使用前缀区分。

dart

import 'package:math_expressions/math_expressions.dart' as mathExpr;
import 'dart:math' as math;

void main() {
  // math.sin 来自 dart:math
  print(math.sin(3.14159 / 2)); // 1.0

  // mathExpr.Parser 来自第三方包
  var parser = mathExpr.Parser();
}

2.2 使用 show 只导入部分成员

dart

// 只导入 sqrtsin,其他如 cos、max 等不可见
import 'dart:math' show sqrt, sin;

void main() {
  print(sqrt(25));   // 5.0
  // print(cos(0));  // 错误:cos 未导入
}

2.3 使用 hide 排除部分成员

dart

// 导入所有成员,但排除 sqrt
import 'dart:math' hide sqrt;

void main() {
  print(sin(0));     // 0.0
  // print(sqrt(4)); // 错误:sqrt 被隐藏
}

2.4 组合使用

可以同时使用 show 和 as,但通常 show/hide 与 as 不常混用(容易混淆)。一般建议:避免冲突用 as,限制导入用 show

dart

import 'dart:math' as math show sin, cos;

三、库的可见性与 part / export

3.1 私有成员(_ 下划线)

Dart 中以下划线开头的标识符在库内可见,库外不可见。每个 .dart 文件是一个独立的库。

dart

// 文件:math_utils.dart
int _internalCounter = 0;      // 库私有

int add(int a, int b) => a + b; // 公开

class Calculator {
  void _reset() {}              // 私有方法
}

dart

// 文件:main.dart
import 'math_utils.dart';

void main() {
  print(add(1, 2));            // 可以
  // print(_internalCounter);   // 错误:私有
}

3.2 part 指令(拆分一个库到多个文件)

part 允许将一个库拆分为多个文件,所有 part 文件共享同一个库的私有作用域。不推荐使用,建议使用 export 和独立库代替。

dart

// 文件:my_library.dart
library my_library;          // 显式声明库名(可选)
part 'utils.dart';
part 'models.dart';

// 公开 API
void doSomething() => _helper();

dart

// 文件:utils.dart(与 my_library.dart 在相同目录)
part of my_library;

void _helper() {
  print('内部帮助函数');
}

现在使用 import 'my_library.dart' 即可同时获得 utils.dart 和 models.dart 中的内容(且私有成员互通)。

3.3 export 指令(组合多个库为一个公开 API)

export 用于创建一个“聚合库”,将多个库的公开成员重新导出。

dart

// 文件:shapes.dart
export 'circle.dart';
export 'rectangle.dart' hide RectanglePrivateHelper; // 可选择性隐藏
export 'triangle.dart' show Triangle;

使用者只需 import 'shapes.dart' 即可获得三个库的所有公开成员。


四、延迟加载(Deferred Loading)

使用 deferred as 可以实现按需加载,减少应用初始启动时间。仅在 Web 和 Dart VM 中支持(Flutter 不支持延迟加载)。

dart

import 'package:heavy/analysis.dart' deferred as heavy;

Future<void> runAnalysis() async {
  // 手动触发库的加载
  await heavy.loadLibrary();
  var analyzer = heavy.Analyzer();
  analyzer.analyze();
}

注意:

  • 延迟加载的库只能通过 loadLibrary() 加载一次,之后可重复使用。
  • 加载返回 Future,可以等待完成。
  • 延迟库中的常量、类型注解等不能在使用前直接引用(因为尚未加载)。

五、内置核心库列表

库名功能常用类/函数
dart:core自动导入,基础类型StringintListMapprint()
dart:async异步编程FutureStreamTimer
dart:math数学函数sqrtsinRandomPoint
dart:convert编解码jsonDecodejsonEncodeutf8
dart:io文件/网络/进程(非 Web)FileHttpClientProcess
dart:html浏览器 API(Web 专用)documentwindowWebSocket
dart:typed_data高效字节数组Uint8ListByteBuffer
dart:ffi调用 C 语言接口PointerDynamicLibrary
dart:isolate并发(Isolate)Isolate.spawnReceivePort
dart:mirrors反射(VM 专用,Flutter 禁用)reflectClassMirror