互操作性增强、平台特定的网络组件、优化类型推断,以及空安全语言里程碑的近期更新
文/ Michael Thomsen, Google Flutter & Dart 产品经理
Dart 2.18 稳定版也随着 Flutter 3.3 稳定版一起发布,本次更新带来了 Dart 与 Objective-C & Swift 互操作特性的预览版,以及根据这个特性构建的 iOS/macOS 网络组件的 package。新的 Dart 还包括泛型方法的类型推断优化、异步代码的性能提升、pub.dev 新的功能,以及对我们工具和核心库的一些调整。
文章最后我们也给出了最新的空安全迁移状态情况数据,以及最终完全实现 Dart 空安全特性路线图的一个重要更新,请务必读到最后。

早在 2020 年的时候,我们发布了外部功能接口 (FFI) 用于调用原生 C 语言接口的预览,并在 2021 年的 Dart 2.12 中正式发布。自那时起,大量的 package 借助于 FFI 的优势与现有的原生 C 语言接口 API 集成,举一些例子,比如 file_picker、printing、win32、objectbox、realm、isar、tflite_flutter 以及 dbus 这些 package。
Dart 团队希望主流编程语言之间的互操作能够在所有 Dart 可以运行的平台上都支持,2.18 正式版达到了这个目标的下一个里程碑,现在,Dart 代码可以直接调用 Objective-C 和 Swift 代码了,主要用于在 macOS 和 iOS 平台调用 API。Dart 支持“全端调用”——从后端的命令行代码,再到前端的 Flutter 界面,你可以在任何应用中使用这种互操作机制。
这种全新机制源自于 Objective-C 和 Swift 代码可以通过 API 绑定机制用 C 语言代码来调用。Dart 的 ffigen 工具可以通过 API 头文件来创建这些绑定,接下来看一个例子。
macOS 上有一个查询时区信息的 API,可以通过 NSTimeZone 类来调用,开发者们可以通过这个 API 来查询用户为设备设定的时区和 UTC 时区偏移。
下面的示例 Objective-C 应用就调用了这个时区 API 来获得系统时区设定和 GMT 偏移。
#import
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSTimeZone *timezone = [NSTimeZone systemTimeZone]; // Get current time zone.
NSLog(@"Timezone name: %@", timezone.name);
NSLog(@"Timezone offset GMT: %ld hours", timezone.secondsFromGMT/60/60);
}
return 0;
}
这个示例应用首先导入了 Foundation.h 头文件,它包含了 Apple 的基础库的 API 头文件。在接下来的方法体中,它调用了 NSTimeZone 的 systemTimeZone 方法,这个方法会返回一个实例化之后的 NSTimeZone 并包含了设备所设定的时区信息。
最后,这个应用会向控制台输出两行内容,包含时区名称和 UTC 的小时偏移量:
Timezone name: Europe/Copenhagen
Timezone offset GMT: 2 hours
让我们用 Dart 和 Objective-C 的互操作来重复一遍刚刚的实现。
首先通过 Dart 命令行创建一个应用:
$ dart create timezones
接着,在你的 pubspec 文件里加入 ffigen 的配置参数,这些配置会在 headers 里设定头文件路径,并且罗列出要生成的包装类 (wrapper) 的 Objective-C 接口:
ffigen:
name: TimeZoneLibrary
language: objc
output: "foundation_bindings.dart"
exclude-all-by-default: true
objc-interfaces:
include:
- "NSTimeZone"
headers:
entry-points:
- "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/
Headers/NSTimeZone.h"
这会为 NSTimeZone.h 这个头文件配置 Objective-C 绑定,并且仅包含 NSTimeZone 接口中的 API,然后运行下面代码生成包装类:
$ dart run ffigen
这个命令会创建一个包含了各种 API 绑定的新 dart 文件 foundation_bindings.dart,调用这个文件之后,我们就可以来写 Dart 主方法 (main) 了,这个方法「镜像」了 Objective-C 的代码,如下:
void main(List