基础
变量和常量
| 声明方式 | 可变性 | 可空 | 赋值时机 | 类型固定 | 典型用途 |
|---|---|---|---|---|---|
var | 可变 | 由初始化决定 | 声明时 | 是 | 局部变量,类型显而易见 |
Type (显式) | 可变 | 默认不可空 | 使用前 | 是 | 公共 API、需要明确类型 |
Type? | 可变 | 可空 | 使用前 | 是 | 可能缺失的值 |
late Type | 可变 | 不可空 | 首次使用前 | 是 | 延迟初始化(依赖注入、Flutter) |
dynamic | 可变 | 可空 | 任意时间 | 否 | 动态语言互操作(谨慎) |
final | 不可变 | 默认不可空 | 首次赋值时 | 是 | 运行时常量(单次赋值) |
final Type? | 不可变 | 可空 | 首次赋值时 | 是 | 可能为 null 的单次赋值 |
const | 不可变 | 不可空 | 编译时 | 是 | 编译时常量(字面量、数学常量) |
未赋值的变量或者常量,默认是null
内置类型
| 类型 | 字面量示例 | 说明 |
|---|---|---|
int | 42, 0x2A, 0b101010 | 整数(64位) |
double | 3.14, 1.2e5, double.nan | 双精度浮点数 |
num | 上述两者父类 | 抽象数字类型 |
String | 'hello', "world", '''多行''' | UTF-16 字符串 |
bool | true, false | 布尔值 |
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
}
控制流程
| 控制流类型 | 关键字/语法 | 说明 |
|---|---|---|
| 条件分支 | if、else if、else | 布尔条件判断 |
| 多值匹配(传统) | switch、case、break、default | 需要显式 break 避免穿透 |
| 多值匹配(Dart 3 表达式) | switch (expr) { pattern => value } | 可作为值返回,无需 break |
| 模式匹配 | if-case、for-in 模式、case ... when | 解构、类型匹配、守卫 |
| 循环 | for、for-in、while、do-while | 标准循环与迭代 |
| 跳转 | break、continue、return、标签 | 控制循环退出或跳过 |
| 断言 | assert(condition, message) | 开发期检查,生产环境忽略 |
异常处理
| 特性 | 语法/示例 | 说明 |
|---|---|---|
| 抛出异常 | throw Exception('msg') | 可抛出任何非 null 对象,通常抛出 Exception 或 Error 子类 |
| 基本捕获 | try { ... } catch (e) { ... } | 捕获任意异常,e 是异常对象 |
| 指定类型捕获 | on FormatException catch (e) | 只捕获特定类型异常 |
| 获取堆栈 | catch (e, stackTrace) | 第二个参数是 StackTrace 对象 |
finally | finally { ... } | 无论是否发生异常都会执行 |
| 重新抛出 | 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 或操作符。这对于为第三方库或内置类型(如 String、List)增加便捷功能非常有用
语法:
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/setter | get name / set name | 自定义属性访问 |
| 继承 | extends | 单继承,调用 super |
| 抽象类 | abstract class | 不能实例化 |
| 接口实现 | implements | 多个接口 |
| 混入 | mixin + with | 代码复用 |
| 类修饰符 | sealed, base, final, interface | Dart 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、枚举的局限性
- 不能动态添加值:枚举值在编译时就固定。
- 不能继承或混入:枚举类默认是
final,不能作为父类。 - 自动生成
values列表,但顺序与声明顺序一致。 - 每个枚举值都是单例,因此可以使用
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:math、dart:io、dart:convert、dart: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
// 只导入 sqrt 和 sin,其他如 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 | 自动导入,基础类型 | String, int, List, Map, print() |
dart:async | 异步编程 | Future, Stream, Timer |
dart:math | 数学函数 | sqrt, sin, Random, Point |
dart:convert | 编解码 | jsonDecode, jsonEncode, utf8 |
dart:io | 文件/网络/进程(非 Web) | File, HttpClient, Process |
dart:html | 浏览器 API(Web 专用) | document, window, WebSocket |
dart:typed_data | 高效字节数组 | Uint8List, ByteBuffer |
dart:ffi | 调用 C 语言接口 | Pointer, DynamicLibrary |
dart:isolate | 并发(Isolate) | Isolate.spawn, ReceivePort |
dart:mirrors | 反射(VM 专用,Flutter 禁用) | reflect, ClassMirror |