Dart SDK 中鲜为人知的类和函数
最近,Flutter 社区 itsallwidgets 建了一个论坛,目前加入的人不多,大家可以注册账号,跟国外的活跃开发者们一起讨论关于 Flutter 开发的话题。
虽然人不 多,但也有一些比较有意思的主题讨论,其中一个主题是: Lesser known classes and functions from the Dart core libraries ,讨论一些日常开发中比较少用到,但是却很有意思和实用的类和函数。
本文分成两部分,第一部分是对论坛评论里提到的类和函数进行总结,第二部分将额外补充一些没有提到的。
第一部分:论坛评论
1. package:collection
中的minBy()
和maxBy()
函数
- 功能:用于从可迭代对象中找到满足特定条件的最小值或最大值的元素,相比对整个可迭代对象进行排序后取最值,效率更高。
- 示例代码:
import 'package:collection/collection.dart';
void main() {
final numbers = [5, 3, 8, 1, 9];
final minNumber = numbers.minBy((num) => num);
final maxNumber = numbers.maxBy((num) => num);
print('最小值: $minNumber');
print('最大值: $maxNumber');
}
2. Iterable<T>
的nonNulls
扩展
- 功能:用于从可迭代对象中过滤出非空值,使代码更简洁,避免使用
SizedBox.shrink()
等占位符。 - 示例代码:
void main() {
final listWithNulls = [1, null, 3, null, 5];
final nonNullList = listWithNulls.nonNulls;
print(nonNullList);
}
3. package:async
的AsyncCache.ephemeral
- 功能:确保回调函数最多同时执行一次,适用于防止按钮多次点击重复执行异步操作等场景。
- 示例代码:
import 'package:async/async.dart';
Future<void> asyncOperation() async {
await Future<void>.delayed(const Duration(seconds: 2));
print('Executed');
}
void main() {
final AsyncCache _cache = AsyncCache.ephemeral();
ElevatedButton(
onPressed: () => _cache.fetch(asyncOperation),
child: const Text('Execute'),
);
}
在上述代码中,多次点击按钮时,asyncOperation
函数只会执行一次,直到其完成。
4. firstWhereOrNull
- 功能:在可迭代对象中查找满足条件的第一个元素,如果未找到则返回
null
,相比firstWhere
在未找到元素时抛出异常更安全。 - 示例代码:
class MyStuff {
final String id;
MyStuff(this.id);
}
void main() {
final someStuffs = [MyStuff('Dog'), MyStuff('Cat'), MyStuff('Bird')];
MyStuff? stuff = someStuffs.firstWhereOrNull((element) => element.id == 'Cat');
print(stuff);
}
5. 解码 HTTP 响应的优化方式
- 功能:在解码 HTTP 响应时,不在主隔离区将长字符串解析为
Map
,而是在新隔离区中使用Utf8Decoder
与JsonDecoder
融合的方式,提高性能。 - 示例代码:
import 'dart:convert';
import 'dart:isolate';
import 'package:http/http.dart' as http;
Future<Map<String, dynamic>?> isolateHttpResponseDecoder(
http.Response httpResponse,
) async =>
Isolate.run(
() => const Utf8Decoder().fuse(const JsonDecoder()).convert(httpResponse.bodyBytes) as Map<String, dynamic>?,
);
void main() async {
final response = await http.get(Uri.parse('https://example.com/api/data'));
final decodedData = await isolateHttpResponseDecoder(response);
print(decodedData);
}
6. FutureRecord2
扩展
- 功能:用于更方便、易读地并行等待多个
Future
,相比Future.wait()
或[...].wait
,它能 正确推断返回值类型。 - 示例代码:
Future<String> fetchText() async {
await Future.delayed(Duration(seconds: 1));
return 'Hello';
}
Future<String> fetchImage() async {
await Future.delayed(Duration(seconds: 2));
return 'Image data';
}
void main() async {
var (text, image) = await (fetchText(), fetchImage()).wait;
print('Text: $text');
print('Image: $image');
}
7. Completer<T>
- 功能:用于手动控制
Future
的完成状态,当需要在特定条件下完成Future
时非常有用。 - 示例代码:
import 'dart:async';
void main() {
final completer = Completer<String>();
// 模拟一个异步操作,在2秒后完成Future并传递数据
Future.delayed(Duration(seconds: 2), () {
completer.complete('Data from async operation');
});
// 等待Future完成并获取结果
completer.future.then((value) {
print(value);
});
}
8. unawaited
函数
-
功能描述:
unawaited
是dart:async
提供的一个实用函数,用于标记一个异步操作可以被安全忽略。这通常在不关心 Future 结果时使用,比如在触发后台操作但不想阻塞当前任务时。它有助于明确表明故意忽略异步任务的意图,避免潜在的代码警告。 -
示例代码:
import 'dart:async';
void main() {
// 模拟一个异步后台任务
Future<void> performBackgroundTask() async {
await Future.delayed(Duration(seconds: 1));
print('后台任务完成');
}
// 使用 unawaited 表明无需等待任务完成
unawaited(performBackgroundTask());
// 继续执行其他逻辑
print('主线程继续执行');
}
-
注意事项:
unawaited
不会停止异步任务,它只是告诉工具和开发者该任务可以被忽略。- 使用时确保任务失败不会影响应用的其他部分。
- 需要导入
dart:async
。
-
常见用途:
- 启动后台任务。
- 在不阻塞主逻辑的情况下触发不重要的异步操作,比如日志记录或缓存更新。
unawaited
跟不使用unawaited
和await
函数没有本质区别,unawaited
在语义上会更明确一些。
// 使用 unawaited 表明无需等待任务完成
unawaited(performBackgroundTask());
// 不使用 unawaited,也不用 await ,效果一样
performBackgroundTask();
第二部分:额外补充
搜集一些网络上和自己日常开发中遇到的类和函数。
List.generate
函数
- 功能描述:
- 用于生成一个具有指定长度的列表,并且可以通过一个回调函数来初始化列表中的每个元素。这在需要创建一个有规律的列表时非常有用, 比如创建一个包含从 1 到 n 的整数列表,或者一个包含 n 个相同初始值的列表等。
- 示例代码:
- 创建一个包含 10 个整数(从 0 到 9)的列表:
void main() {
List<int> numbers = List.generate(10, (index) => index);
print(numbers);
}
- 创建一个包含 5 个字符串(每个字符串都是"Hello")的列表:
void main() {
List<String> greetings = List.generate(5, (index) => "Hello");
print(greetings);
}
StringBuffer
类和相关函数
- 功能描述:
StringBuffer
用于高效地构建字符串。在需要拼接多个字符串时,直接使用+
操作符可能会导致创建多个中间字符串对象,效率较低。而StringBuffer
可以在一个对象中逐步添加字符串片段,最后通过toString
方法获取拼接好的完整字符串,性能更好。- 示例代码:
- 拼接一个包含多个单词的句子:
void main() {
StringBuffer buffer = StringBuffer();
buffer.write("Hello");
buffer.write(" ");
buffer.write("World");
String sentence = buffer.toString();
print(sentence);
}
Iterable.whereType
函数
- 功能描述:从一个可迭代对象中筛选出指定类型的元素,并返回一个新的可迭代对象。这在处理包含多种类型元素的集合时,可以方便地提取出特定类型的元素。
- 示例代码:
void main() {
List<Object> objects = [1, "two", 3.0, true, "four"];
// 筛选出字符串类型的元素
Iterable<String> strings = objects.whereType<String>();
print(strings.toList());
}
Iterable.reduce
函数
- 功能描述:
- 对可迭代对象中的元素进行累积计算,将可迭代对象中的元素通过一个二元操作符(例如加法、乘法等)合并为一个值。例如,可以用于计算一个整数列表的总和、乘积等。
- 示例代码:
- 计算整数列表的总和:
void main() {
List<int> numbers = [1, 2, 3, 4, 5];
int sum = numbers.reduce((value, element) => value + element);
print(sum);
}
- 计算整数列表的乘积:
void main() {
List<int> numbers = [2, 3, 4];
int product = numbers.reduce((value, element) => value * element);
print(product);
}
Map.fromIterable
函数
- 功能描述:
- 从一个可迭代对象创建一个
Map
。通常需要提供一个用于生成键的函数和一个用于生成值的函数,这样可以将可迭代对象中的元素转换为Map
中的键值对。 - 示例代码:
- 从一个整数列表创建一个
Map
,其中键是整数本身,值是整数的平方:
void main() {
List<int> numbers = [1, 2, 3, 4, 5];
Map<int, int> numberMap = Map.fromIterable(
numbers,
key: (number) => number,
value: (number) => number * number,
);
print(numberMap);
}
Map.putIfAbsent
函数
- 功能描述:如果指定的键在映射中不存在,则将键值对添加到映射中;如果键已存在,则返回该键对应的值。这在需要根据条件添加或获取映射中的值时非常方便,可以避免不必要的重复添加操作。
- 示例代码:
void main() {
Map<String, int> map = {};
// 如果键 "key1" 不存在,则添加键值对 "key1": 10
int value1 = map.putIfAbsent("key1", () => 10);
print(value1);
// 键 "key1" 已存在,返回其对应的值 10
int value2 = map.putIfAbsent("key1", () => 20);
print(value2);
}
Uri.encodeComponent
函数
- 功能描述:用于对字符串进行 URL 编码,将 字符串中的特殊字符转换为适合在 URL 中使用的格式。
Uri.encodeFull
会对整个字符串进行编码,而Uri.encodeComponent
则对字符串中的每个组件分别进行编码,适用于不同的 URL 构建场景。 - 示例代码:
void main() {
String url = "https://www.example.com/search?q=Dart 编程";
// 使用 Uri.encodeFull 对整个 URL 进行编码
String encodedUrlFull = Uri.encodeFull(url);
print(encodedUrlFull);
// 使用 Uri.encodeComponent 对查询参数进行编码
String query = "Dart 编程";
String encodedQuery = Uri.encodeComponent(query);
print(encodedQuery);
}
UriData
类
-
功能描述: 允许解析或生成嵌入式 URI 数据,适用于数据 URI 的处理。
-
示例代码:
void main() {
final uriData = UriData.fromString('Hello, Dart!', mimeType: 'text/plain');
print(uriData.toString()); // 输出: data:text/plain;base64,SGVsbG8sIERhcnQh
}
String.padLeft
和String.padRight
函数
- 功能描述:用于在字符串的左侧或右侧填充指定的字符,使字符串达到指定的长度。这在格式化输出、对齐文本等场景中非常有用。
- 示例代码:
void main() {
String number = "123";
// 在左侧填充 0,使字符串长度为 5
String paddedNumberLeft = number.padLeft(5, '0');
print(paddedNumberLeft);
// 在右侧填充空格,使字符串长度为 6
String paddedNumberRight = number.padRight(6);
print(paddedNumberRight);
}
Expando
-
功能描述: 允许为对象动态添加属性,而不修改其原有结构。常用于需要为不同对象添加临时数据时,避免直接更改对象定义。
-
示例代码:
void main() {
final expando = Expando<String>('labels');
final obj1 = Object();
final obj2 = Object();
expando[obj1] = 'First Object';
expando[obj2] = 'Second Object';
print(expando[obj1]); // 输出: First Object
print(expando[obj2]); // 输出: Second Object
}
WeakReference
类
-
功能描述: 创建一个指向对象的弱引用,允许垃圾回收器在内存不足时回收目标对象,避免内存泄漏。
-
示例代码:
import 'dart:core';
void main() {
final obj = Object();
final weakRef = WeakReference(obj);
print(weakRef.target); // 输出: Instance of 'Object'
// 如果 obj 被释放,weakRef.target 会变为 null
}
Stopwatch
类
-
功能描述: 用于测量操作的运行时间,非常适合性能分析。
-
示例代码:
void main() {
final stopwatch = Stopwatch()..start();
// 模拟耗时操作
for (int i = 0; i < 1000000; i++) {}
stopwatch.stop();
print('Elapsed time: ${stopwatch.elapsedMilliseconds}ms');
}
RuneIterator
类
-
功能描述: 提供对 Dart 字符串中 Unicode 代码点的逐个访问 能力,适用于处理复杂字符集。
-
示例代码:
void main() {
final input = '👋🌍';
final iterator = RuneIterator(input);
while (iterator.moveNext()) {
print(iterator.currentAsString); // 输出: 👋 然后 🌍
}
}