FlutterSwift 旨在帮助您使用 Dart 编写 UI,并使用 Swift 编写业务逻辑。
它由三个组件构成
最终目标是允许以跨平台的方式使用 Flutter 进行 UI,并使用 Swift 进行业务逻辑。目前支持的目标平台包括 macOS、iOS、Android 和 eLinux。
以下内容假定您对 Flutter(特别是平台通道)和 Swift 语言都有相当程度的熟悉。
在移动和桌面平台(如 macOS、iOS 和 Android)上,FlutterPlatformMessenger
类包装了平台现有的 二进制消息传递器。这是因为平台二进制消息传递器是不可替换的,因为它被宿主平台插件使用。
在 Darwin 平台(即 iOS 和 macOS)上,您只需从 Xcode 将 FlutterSwift 添加为 Swift 包依赖项即可。在 Android 上,您需要将 FlutterSwift 链接到一个 Java 本地接口 (JNI) 库中,该库与您的 APK 捆绑在一起(更多内容见下文)。
FlutterDesktopMessenger
Actor 包装了 flutter_messenger.h
中的 API。此软件包将构建 Sony eLinux Flutter 分支作为子模块,使用此存储库中的工件包中包含的 Flutter 引擎。
请注意 Flutter *嵌入* 或 *嵌入器* 之间的区别,它们是 Flutter 框架与应用程序的平台特定集成,以及 *嵌入式* 用例。
示例 Xcode 项目包含在 Examples/counter 目录的标准位置。您可能需要调整 bundle identifier 以匹配您的开发者 ID。
Android 构建目前仅在 macOS 上受支持,并且需要安装以下依赖项
然后,您需要编辑 build-android.sh
脚本,并在必要时更改以下环境变量
NDK_VERS
:Android NDK 的版本SWIFT_VERS
:上面下载的 Swift SDK 和工具链的版本TARGET_JAVA_HOME
:目标机器的 JDK 路径(在 Android Studio 应用程序中)HOST_JAVA_HOME
:构建(宿主)机器的 JDK 路径(通常在 /Library/Java/JavaVirtualMachines
中)示例的 Android 特定源代码位于 Examples/counter/android/app/src/main。
这里工具的某些不便之处是已知问题,我们计划在未来 改进它。
请注意,@MainActor
在 Android 上不可用;请改用 @UIThreadActor
。
假设 Flutter SDK 安装在 /opt/flutter-elinux/flutter
中,您只需在顶层目录中运行 ./build-counter-linux.sh
,然后运行 ./run-counter-linux.sh
即可。这将构建 Flutter AOT 对象,然后构建 Swift 运行器。
环境变量 FLUTTER_SWIFT_BACKEND
可以设置为 gbm
、eglstream
或 wayland
中的一个,具体取决于情况。这应该在构建和运行时都设置。除非您真正在嵌入式系统上进行测试,否则您可能希望将其设置为 wayland
。
本节简要概述 FlutterSwift 提供的 API。
import FlutterMacOS.FlutterBinaryMessenger
import FlutterSwift
override func awakeFromNib() {
let flutterViewController = FlutterViewController() // from platform embedding
let binaryMessenger = FlutterSwift
.FlutterPlatformMessenger(wrapping: flutterViewController.engine.binaryMessenger)
...
}
Android 要求您的应用程序的 configureFlutterEngine()
方法调用您定义的本地函数来初始化您的平台通道,例如以下内容
package com.example.counter;
import io.flutter.plugin.common.BinaryMessenger;
public final class ChannelManager {
public final BinaryMessenger binaryMessenger;
public ChannelManager(BinaryMessenger binaryMessenger) {
System.loadLibrary("counter");
this.binaryMessenger = binaryMessenger;
}
public native void initChannelManager();
}
在您的 Swift 代码(此处为 initChannelManager()
)中,您可以注册您的平台通道实现
import FlutterAndroid
import JavaKit
import JavaRuntime
@JavaClass("com.example.counter.ChannelManager")
open class _ChannelManager: JavaObject {
@JavaField(isFinal: true)
public var binaryMessenger: FlutterAndroid.FlutterBinaryMessenger!
@JavaMethod
@_nonoverride
public convenience init(
_ binaryMessenger: FlutterAndroid.FlutterBinaryMessenger?,
environment: JNIEnvironment? = nil
)
}
protocol _ChannelManagerNativeMethods {
func initChannelManager()
}
@JavaImplementation("com.example.counter.ChannelManager")
extension _ChannelManager: _ChannelManagerNativeMethods {
@JavaMethod
public func initChannelManager() {
let wrappedMessenger = FlutterPlatformMessenger(wrapping: binaryMessenger!)
// initialize your channels, remembering to take a strong reference to them
}
}
在 Linux 上,使用原生 Swift 客户端包装器
@main
enum SomeApp {
static func main() {
guard CommandLine.arguments.count > 1 else {
print("usage: SomeApp [flutter_path]")
exit(1)
}
let dartProject = DartProject(path: CommandLine.arguments[1])
let viewProperties = FlutterViewController.ViewProperties(
width: 640,
height: 480,
title: "SomeApp",
appId: "com.example.SomeApp"
)
let window = FlutterWindow(properties: viewProperties, project: dartProject)
guard let window else {
debugPrint("failed to initialize window!")
exit(2)
}
let binaryMessenger = viewController.engine.binaryMessenger
...
window.run()
}
}
这展示了一个使用 JSON 消息编解码器的基本消息通道处理程序。在 eLinux 上,不要在 awakeFromNib()
中注册通道,而是从 main()
函数调用此方法(可能由管理器类间接调用)。
private func messageHandler(_ arguments: String?) async -> Int? {
debugPrint("Received message \(String(describing: arguments))")
return 0xCAFE_BABE
}
override func awakeFromNib() {
...
flutterBasicMessageChannel = FlutterBasicMessageChannel(
name: "com.example.SomeApp.basic",
binaryMessenger: binaryMessenger,
codec: FlutterJSONMessageCodec.shared
)
task = Task {
try! await flutterBasicMessageChannel.setMessageHandler(messageHandler)
...
}
}
var isRunning = true
private func methodCallHandler(
call: FlutterSwift.FlutterMethodCall<Bool>
) async throws -> Bool {
isRunning.toggle()
return isRunning
}
override func awakeFromNib() {
...
let flutterMethodChannel = FlutterMethodChannel(
name: "com.example.SomeApp.toggle",
binaryMessenger: binaryMessenger
)
task = Task {
try! await flutterMethodChannel.setMethodCallHandler(methodCallHandler)
}
}
这是一个事件通道的示例,摘自 counter 示例。
import AsyncAlgorithms
import AsyncExtensions
import FlutterSwift
...
typealias Arguments = FlutterNull
typealias Event = Int32
typealias Stream = AsyncThrowingChannel<Event?, FlutterError>
var flutterEventStream = Stream()
var task: Task<(), Error>?
var counter: Event = 0
private func onListen(_ arguments: Arguments?) throws -> FlutterEventStream<Event> {
flutterEventStream.eraseToAnyAsyncSequence()
}
private func onCancel(_ arguments: Arguments?) throws {
task?.cancel()
task = nil
}
override func awakeFromNib() {
...
let flutterEventChannel = FlutterEventChannel(
name: "com.example.SomeApp.counterEvents",
binaryMessenger: binaryMessenger
)
task = Task {
try! await flutterEventChannel.setStreamHandler(onListen: onListen, onCancel: onCancel)
repeat {
await flutterEventStream.send(counter)
count += 1
try await Task.sleep(nanoseconds: NSEC_PER_SEC)
} while !Task.isCancelled
}
}