将 Flutter 模块集成到 iOS 项目中
Flutter UI 组件可以作为嵌入式框架逐步添加到现有 iOS 应用程序中。有几种方法可以在现有应用程序中嵌入 Flutter。
-
使用 CocoaPods 依赖项管理器和已安装的 Flutter SDK。在这种情况下,每次构建应用程序时都会从源代码编译
flutter_module
。(推荐) -
为 Flutter 引擎、已编译的 Dart 代码和所有 Flutter 插件创建框架。在此,您手动嵌入框架,并在 Xcode 中更新现有应用程序的构建设置。这对于不想要求每个开发人员都在本地安装 Flutter SDK 和 Cocoapods 的团队很有用。
-
为已编译的 Dart 代码和所有 Flutter 插件创建框架。在 CocoaPods 中使用 Flutter 引擎。使用此选项,在 Xcode 中嵌入应用程序和插件的框架,但将 Flutter 引擎分发为 CocoaPods podspec。这类似于第二个选项,但它提供了一种替代方案来分发大型 Flutter.xcframework。
有关使用采用 UIKit 构建的应用的示例,请参阅 add_to_app 代码示例 中的 iOS 目录。有关使用 SwiftUI 的示例,请参阅 新闻提要应用 中的 iOS 目录。
系统要求
你的开发环境必须满足 Flutter 的 macOS 系统要求,且 已安装 Xcode。Flutter 支持 iOS 12 及更高版本。此外,你还需要 CocoaPods 1.10 或更高版本。
创建 Flutter 模块
若要使用上述任何方法将 Flutter 嵌入到现有应用中,请先创建一个 Flutter 模块。
在命令行中运行
cd some/path/
flutter create --template module my_flutter
Flutter 模块项目在 some/path/my_flutter/
中创建。如果你使用上面提到的第一个方法,则应在与现有 iOS 应用相同的父目录中创建模块。
在 Flutter 模块目录中,你可以运行与在任何其他 Flutter 项目中相同的 flutter
命令,例如 flutter run --debug
或 flutter build ios
。你还可以使用 Flutter 和 Dart 插件在 Android Studio/IntelliJ 或 VS Code 中运行模块。此项目包含模块的单视图示例版本,在将其嵌入到现有应用之前,该版本可用于逐步测试代码中仅限 Flutter 的部分。
模块组织
my_flutter
模块目录结构类似于普通 Flutter 应用
my_flutter/
├── .ios/
│ ├── Runner.xcworkspace
│ └── Flutter/podhelper.rb
├── lib/
│ └── main.dart
├── test/
└── pubspec.yaml
将 Dart 代码添加到 lib/
目录中。
将 Flutter 依赖项添加到 my_flutter/pubspec.yaml
中,包括 Flutter 包和插件。
.ios/
隐藏子文件夹包含一个 Xcode 工作区,你可以在其中运行模块的独立版本。这是一个用于引导 Flutter 代码的包装器项目,其中包含帮助脚本,以便于使用 CocoaPods 构建框架或将模块嵌入到现有应用中。
将 Flutter 模块嵌入到现有的应用程序中
开发完 Flutter 模块后,可以使用页面顶部描述的方法将其嵌入。
使用 Flutter 会增加应用程序的大小。
选项 A - 使用 CocoaPods 和 Flutter SDK 嵌入
此方法要求项目中的每个开发者都本地安装了 Flutter SDK 的版本。每次构建应用程序时,都会从源代码编译 Flutter 模块。只需在 Xcode 中构建应用程序即可自动运行脚本来嵌入 Dart 和插件代码。这允许使用 Flutter 模块的最新版本快速迭代,而无需在 Xcode 外部运行其他命令。
以下示例假定现有的应用程序和 Flutter 模块位于同级目录中。如果目录结构不同,则可能需要调整相对路径。
some/path/
├── my_flutter/
│ └── .ios/
│ └── Flutter/
│ └── podhelper.rb
└── MyApp/
└── Podfile
如果现有的应用程序 (MyApp
) 还没有 Podfile,请在
MyApp
目录中运行 pod init
以创建一个 Podfile。可以在 CocoaPods 入门指南 中找到有关使用 CocoaPods 的更多详细信息。
-
将以下行添加到
Podfile
MyApp/Podfileflutter_application_path = '../my_flutter' load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
-
对于需要嵌入 Flutter 的每个 Podfile 目标,请调用
install_all_flutter_pods(flutter_application_path)
。MyApp/Podfiletarget 'MyApp' do install_all_flutter_pods(flutter_application_path) end
-
在
Podfile
的post_install
块中,调用flutter_post_install(installer)
。MyApp/Podfilepost_install do |installer| flutter_post_install(installer) if defined?(flutter_post_install) end
-
运行
pod install
。
podhelper.rb
脚本将您的插件、Flutter.framework
和 App.framework
嵌入到您的项目中。
您的应用的 Debug 和 Release 构建配置分别嵌入 Debug 或 Release Flutter 的构建模式。向您的应用添加一个 Profile 构建配置以在 profile 模式下测试。
在 Xcode 中打开 MyApp.xcworkspace
。您现在可以使用 ⌘B
构建项目。
选项 B - 在 Xcode 中嵌入框架
或者,您可以通过手动编辑现有的 Xcode 项目来生成必要的框架并将它们嵌入到您的应用程序中。如果您团队的成员无法在本地安装 Flutter SDK 和 CocoaPods,或者您不想在现有应用程序中使用 CocoaPods 作为依赖项管理器,则可以这样做。您必须在每次在 Flutter 模块中进行代码更改时运行 flutter build ios-framework
。
以下示例假定您希望将框架生成到 some/path/MyApp/Flutter/
。
flutter build ios-framework --output=some/path/MyApp/Flutter/
some/path/MyApp/
└── Flutter/
├── Debug/
│ ├── Flutter.xcframework
│ ├── App.xcframework
│ ├── FlutterPluginRegistrant.xcframework (only if you have plugins with iOS platform code)
│ └── example_plugin.xcframework (each plugin is a separate framework)
├── Profile/
│ ├── Flutter.xcframework
│ ├── App.xcframework
│ ├── FlutterPluginRegistrant.xcframework
│ └── example_plugin.xcframework
└── Release/
├── Flutter.xcframework
├── App.xcframework
├── FlutterPluginRegistrant.xcframework
└── example_plugin.xcframework
在 Xcode 中将生成的框架链接并嵌入到您的现有应用程序中。有多种方法可以做到这一点——使用最适合您项目的那个方法。
框架链接
例如,你可以将框架从 Finder 中的 some/path/MyApp/Flutter/Release/
拖到目标的构建设置 > 构建阶段 > 使用库链接二进制文件中。
在目标的构建设置中,将 $(PROJECT_DIR)/Flutter/Release/
添加到框架搜索路径 (FRAMEWORK_SEARCH_PATHS
) 中。
嵌入框架
生成的动态框架必须嵌入到你的应用中才能在运行时加载。
链接框架后,你应该在目标的常规设置的框架、库和嵌入式内容部分中看到它们。要嵌入动态框架,请选择嵌入和签名。
然后它们将出现在构建阶段中的嵌入框架下,如下所示
现在,您应该可以使用 ⌘B
在 Xcode 中构建项目。
选项 C - 使用 CocoaPods 在 Xcode 和 Flutter 框架中嵌入应用程序和插件框架
或者,您可以通过添加标志 --cocoapods
来生成 Flutter 作为 CocoaPods podspec,而不是将大型 Flutter.xcframework 分发给其他开发者、机器或持续集成系统。这将生成 Flutter.podspec
而不是引擎 Flutter.xcframework。App.xcframework 和插件框架的生成方式如选项 B 中所述。
要生成 Flutter.podspec
和框架,请在 Flutter 模块的根目录中从命令行运行以下命令
flutter build ios-framework --cocoapods --output=some/path/MyApp/Flutter/
some/path/MyApp/
└── Flutter/
├── Debug/
│ ├── Flutter.podspec
│ ├── App.xcframework
│ ├── FlutterPluginRegistrant.xcframework
│ └── example_plugin.xcframework (each plugin with iOS platform code is a separate framework)
├── Profile/
│ ├── Flutter.podspec
│ ├── App.xcframework
│ ├── FlutterPluginRegistrant.xcframework
│ └── example_plugin.xcframework
└── Release/
├── Flutter.podspec
├── App.xcframework
├── FlutterPluginRegistrant.xcframework
└── example_plugin.xcframework
使用 CocoaPods 的宿主应用程序可以将其 Podfile 添加到 Flutter
pod 'Flutter', :podspec => 'some/path/MyApp/Flutter/[build mode]/Flutter.podspec'
按照选项 B 中的说明,将生成的 App.xcframework、FlutterPluginRegistrant.xcframework 和任何插件框架链接并嵌入到您的现有应用程序中。
本地网络隐私权限
在 iOS 14 及更高版本中,在应用程序的调试版本中启用 Dart 多播 DNS 服务,以通过 flutter attach
添加 调试功能,如热重载和 DevTools。
执行此操作的一种方法是为每个构建配置维护应用的 Info.plist 的单独副本。以下说明假定默认的调试和发布。根据应用的构建配置按需调整名称。
-
将应用的Info.plist重命名为Info-Debug.plist。创建其副本并将其命名为Info-Release.plist,并将其添加到 Xcode 项目中。
-
仅在Info-Debug.plist中添加键
NSBonjourServices
,并将值设置为包含字符串_dartVmService._tcp
的数组。请注意,Xcode 将此显示为“Bonjour 服务”。(可选)添加键
NSLocalNetworkUsageDescription
,并将其设置为所需的自定义权限对话框文本。 -
在目标的构建设置中,将Info.plist 文件 (
INFOPLIST_FILE
) 设置路径从path/to/Info.plist
更改为path/to/Info-$(CONFIGURATION).plist
。这将解析为调试中的路径Info-Debug.plist和发布中的Info-Release.plist。
或者,你可以明确将调试路径设置为Info-Debug.plist,将发布路径设置为Info-Release.plist。
-
如果Info-Release.plist副本位于目标的构建设置 > 构建阶段 > 复制包资源构建阶段中,请将其移除。
现在,由调试应用加载的第一个 Flutter 屏幕将提示获取本地网络权限。还可以通过启用设置 > 隐私 > 本地网络 > 你的应用来允许权限。
arm64
Mac)
Apple Silicon (在 Apple Silicon (M1) Mac 上,主机应用为 arm64
模拟器构建。虽然 Flutter 支持 arm64
模拟器,但某些插件可能不支持。如果你使用其中一个插件,你可能会看到编译错误,例如架构 arm64 的未定义符号,你必须从主机应用中的模拟器架构中排除 arm64
。
在主机应用目标中,找到排除的架构 (EXCLUDED_ARCHS
) 构建设置。单击右箭头展开指示符图标以展开可用的构建配置。将鼠标悬停在调试上,然后单击加号图标。将任何 SDK更改为任何 iOS 模拟器 SDK。将 arm64
添加到构建设置值。
正确完成后,Xcode 将向你的project.pbxproj文件添加 "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
。
对任何 iOS 单元测试目标重复。
开发
您现在可以将Flutter 屏幕添加到现有应用程序。