Dart SDK 中鲜为人知的类和函数
最近,Flutter 社区 itsallwidgets (opens in a new window) 建了一个论坛 (opens in a new window),目前加入的人不多,大家可以注册账号,跟国外的活跃开发者们一起讨论关于 Flutter 开发的话题。
虽然人不多,但也有一些比较有意思的主题讨论,其中一个主题是: Lesser known classes and functions from the Dart core libraries (opens in a new window) ,讨论一些日常开发中比较少用到,但是却很有意思和实用的类和函数。
本文分成两部分,第一部分是对论坛评论里提到的类和函数进行总结,第二部分将额外补充一些没有提到的。
- 功能:用于从可迭代对象中找到满足特定条件的最小值或最大值的元素,相比对整个可迭代对象进行排序后取最值,效率更高。
- 示例代码:
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');}- 功能:用于从可迭代对象中过滤出非空值,使代码更简洁,避免使用
SizedBox.shrink()等占位符。 - 示例代码:
void main() { final listWithNulls = [1, null, 3, null, 5]; final nonNullList = listWithNulls.nonNulls; print(nonNullList);}- 功能:确保回调函数最多同时执行一次,适用于防止按钮多次点击重复执行异步操作等场景。
- 示例代码:
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函数只会执行一次,直到其完成。
- 功能:在可迭代对象中查找满足条件的第一个元素,如果未找到则返回
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);}- 功能:在解码 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);}- 功能:用于更方便、易读地并行等待多个
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');}- 功能:用于手动控制
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); });}-
功能描述:
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。
-
常见用途:
- 启动后台任务。
- 在不阻塞主逻辑的情况下触发不重要的异步操作,比如日志记录或缓存更新。
搜集一些网络上和自己日常开发中遇到的类和函数。
- 功能描述:
- 用于生成一个具有指定长度的列表,并且可以通过一个回调函数来初始化列表中的每个元素。这在需要创建一个有规律的列表时非常有用,比如创建一个包含从 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可以在一个对象中逐步添加字符串片段,最后通过toString方法获取拼接好的完整字符串,性能更好。- 示例代码:
- 拼接一个包含多个单词的句子:
void main() { StringBuffer buffer = StringBuffer(); buffer.write("Hello"); buffer.write(" "); buffer.write("World"); String sentence = buffer.toString(); print(sentence);}- 功能描述:从一个可迭代对象中筛选出指定类型的元素,并返回一个新的可迭代对象。这在处理包含多种类型元素的集合时,可以方便地提取出特定类型的元素。
- 示例代码:
void main() { List<Object> objects = [1, "two", 3.0, true, "four"]; // 筛选出字符串类型的元素 Iterable<String> strings = objects.whereType<String>(); print(strings.toList());}- 功能描述:
- 对可迭代对象中的元素进行累积计算,将可迭代对象中的元素通过一个二元操作符(例如加法、乘法等)合并为一个值。例如,可以用于计算一个整数列表的总和、乘积等。
- 示例代码:
- 计算整数列表的总和:
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。通常需要提供一个用于生成键的函数和一个用于生成值的函数,这样可以将可迭代对象中的元素转换为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);}- 功能描述:如果指定的键在映射中不存在,则将键值对添加到映射中;如果键已存在,则返回该键对应的值。这在需要根据条件添加或获取映射中的值时非常方便,可以避免不必要的重复添加操作。
- 示例代码:
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);}- 功能描述:用于对字符串进行 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);}-
功能描述: 允许解析或生成嵌入式 URI 数据,适用于数据 URI 的处理。
-
示例代码:
void main() { final uriData = UriData.fromString('Hello, Dart!', mimeType: 'text/plain'); print(uriData.toString()); // 输出: data:text/plain;base64,SGVsbG8sIERhcnQh}- 功能描述:用于在字符串的左侧或右侧填充指定的字符,使字符串达到指定的长度。这在格式化输出、对齐文本等场景中非常有用。
- 示例代码:
void main() { String number = "123"; // 在左侧填充 0,使字符串长度为 5 String paddedNumberLeft = number.padLeft(5, '0'); print(paddedNumberLeft); // 在右侧填充空格,使字符串长度为 6 String paddedNumberRight = number.padRight(6); print(paddedNumberRight);}-
功能描述: 允许为对象动态添加属性,而不修改其原有结构。常用于需要为不同对象添加临时数据时,避免直接更改对象定义。
-
示例代码:
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}-
功能描述: 创建一个指向对象的弱引用,允许垃圾回收器在内存不足时回收目标对象,避免内存泄漏。
-
示例代码:
import 'dart:core';
void main() { final obj = Object(); final weakRef = WeakReference(obj);
print(weakRef.target); // 输出: Instance of 'Object'
// 如果 obj 被释放,weakRef.target 会变为 null}-
功能描述: 用于测量操作的运行时间,非常适合性能分析。
-
示例代码:
void main() { final stopwatch = Stopwatch()..start();
// 模拟耗时操作 for (int i = 0; i < 1000000; i++) {}
stopwatch.stop(); print('Elapsed time: ${stopwatch.elapsedMilliseconds}ms');}-
功能描述: 提供对 Dart 字符串中 Unicode 代码点的逐个访问能力,适用于处理复杂字符集。
-
示例代码:
void main() { final input = '👋🌍'; final iterator = RuneIterator(input);
while (iterator.moveNext()) { print(iterator.currentAsString); // 输出: 👋 然后 🌍 }}