有时候,用 Flutter 一次性重写整个已有的应用是不切实际的。对于这些情况,Flutter 可以作为一个库或模块,集成进现有的应用当中。模块引入到您的 Android 或 iOS 应用(当前支持的平台)中,以使用 Flutter 来渲染一部分的 UI,或者仅运行多平台共享的 Dart 代码逻辑。
仅需几步,你就可以将高效而富有表现力的 Flutter 引入您的应用。
在 Flutter v1.12 中,添加到现有应用的基本场景已被支持,每个应用在同一时间可以集成一个全屏幕的 Flutter 实例。目前仍有以下限制:
- 目前尚未支持将多个 Flutter 库打包到同一个应用中;
- 在 Android 平台,使用了添加到现有应用 (add-to-app) 的插件需要迁移到基于
FlutterPlugin的 Android 插件 API。 - 一些不支持
FlutterPlugin的插件可能会有不可预知的行为,比如产生错误的预判,认为 FlutterActivity一直处于活跃状态。 - 从 1.17 开始,Flutter 模块仅支持 Android 平台中的 AndroidX 应用。
自 Flutter 1.26 版本开始,add-to-app 开始实验性的支持将多个 Flutter 引擎 (engine)、页面 (screen) 或视图 (view) 添加到你的应用中。适用于混合栈应用在导航到原生页面和 Flutter 页面的情况,也适用于一个页面有原生视图和 Flutter 视图的情况等混合栈应用。多个 Flutter 实例会帮助每个实例保持独立的应用和 UI 状态,同时使用最少的内存资源。请多详细内容,请参考文档: 多个 Flutter 实例。
集成到 iOS 应用
- 在 Xcode 的 Build Phase 以及 CocoaPods 中,添加一个自动构建并引入 Flutter 模块的 Flutter SDK 钩子。
- 将 Flutter 模块构建为通用的 iOS Framework 以便集成到您自己的构建系统中;
FlutterEngineAPI 用于启动并持续地为挂载FlutterViewController以提供独立的 Flutter 环境;- 支持了 Objective-C 和 Swift 为宿主的应用程序;
- Flutter 模块可以通过使用 Flutter plugins 与平台进行交互;
- 支持通过从 IDE 或命令行中使用
flutter attach来实现 Flutter 调试与有状态的热重载。
查看 add-to-app GitHub 示例仓库 中在 iOS 和 Android 平台上引入 Flutter module 的示例项目。
Flutter 可以以 framework 框架的形式添加到你的既有 iOS 应用中。
请参阅 add_to_app 代码示例 的 iOS 目录。
创建 Flutter module
为了将 Flutter 集成到你的既有应用里,第一步要创建一个 Flutter module。
在命令行中执行:
cd some/path/
flutter create --template module my_flutter
Flutter module 会创建在 some/path/my_flutter/ 目录。在这个目录中,你可以像在其它 Flutter 项目中一样,执行 flutter 命令。比如 flutter run --debug 或者 flutter build ios。
模块组织
在 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 packages 和 plugins。
.ios/ 隐藏文件夹包含了一个 Xcode workspace,用于单独运行你的 Flutter module。它是一个独立启动 Flutter 代码的壳工程,并且包含了一个帮助脚本,用于编译 framewroks 或者使用 CocoaPods 将 Flutter module 集成到你的既有应用。
提示
iOS 代码要添加到你的既有应用或者 Flutter plugin中,而不是 Flutter module 的 .ios/ 目录下。 .ios/ 下的改变不会集成到你的既有应用,并且这有可能被 Flutter 重写。
由于 .ios/ 目录是自动生成的,因此请勿对其进行版本控制。在新机器上构建 module 时,请在使用 Flutter module 构建 iOS 项目之前,先于 my_flutter 目录运行 flutter pub get 以生成 .ios/ 目录。
在你的既有应用中集成 Flutter module
这里有两种方式可以将 Flutter 集成到你的既有应用中。
- 使用 CocoaPods 依赖管理和已安装的 Flutter SDK 。(推荐)
- 把 Flutter engine 、你的 dart 代码和所有 Flutter plugin 编译成 framework 。用 Xcode 手动集成到你的应用中,并更新编译设置。
提示
你的应用将不能在模拟器上运行 Release 模式,因为 Flutter 还不支持将 Dart 代码编译成 x86/x86_64 ahead-of-time (AOT) 模式的二进制文件。你可以在模拟机和真机上运行 Debug 模式,在真机上运行 Release 模式。
使用 Flutter 会 增加应用体积 。
选项 A - 使用 CocoaPods 和 Flutter SDK 集成
这个方法需要你的项目的所有开发者,都在本地安装 Flutter SDK。只需要在 Xcode 中编译应用,就可以自动运行脚本来集成 dart 代码和 plugin。这个方法允许你使用 Flutter module 中的最新代码快速迭代开发,而无需在 Xcode 以外执行额外的命令。
下面的示例假设你的既有应用和 Flutter module 在相邻目录。如果你有不同的目录结构,需要适配到对应的路径。
some/path/
├── my_flutter/
│ └── .ios/
│ └── Flutter/
│ └── podhelper.rb
└── MyApp/
└── Podfile
如果你的应用(MyApp)还没有 Podfile,根据 CocoaPods getting started guide 来在项目中添加 Podfile。
-
在
Podfile中添加下面代码:flutter_application_path = '../my_flutter' load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb') -
每个需要集成 Flutter 的 Podfile target,执行
install_all_flutter_pods(flutter_application_path):target 'MyApp' do install_all_flutter_pods(flutter_application_path) end -
运行
pod install。提示
当你在
my_flutter/pubspec.yaml改变了 Flutter plugin 依赖,需要在 Flutter module 目录运行flutter pub get,来更新会被podhelper.rb脚本用到的 plugin 列表,然后再次在你的应用目录some/path/MyApp运行pod install.
podhelper.rb 脚本会把你的 plugins, Flutter.framework,和 App.framework 集成到你的项目中。
你应用的 Debug 和 Release 编译配置,将会集成相对应的 Debug 或 Release 的 编译产物。可以增加一个 Profile 编译配置用于在 profile 模式下测试应用。
小提示
Flutter.framework 是 Flutter engine 的框架, App.framework 是你的 Dart 代码的编译产物。
在 Xcode 中打开 MyApp.xcworkspace ,你现在可以使用 ⌘B 编译项目了。
选项 B - 在 Xcode 中集成 frameworks
除了上面的方法,你也可以创建必备的 frameworks,手动修改既有 Xcode 项目,将他们集成进去。当你组内其它成员们不能在本地安装 Flutter SDK 和 CocoaPods,或者你不想使用 CocoaPods 作为既有应用的依赖管理时,这种方法会比较合适。但是每当你在 Flutter module 中改变了代码,都必须运行 flutter build ios-framework。
如果你使用前面的 使用 CocoaPods 和 Flutter SDK 集成,你可以跳过本步骤。
下面的示例假设你想在 some/path/MyApp/Flutter/ 目录下创建 frameworks:
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
请注意
始终使用相同目录下的 Flutter.framework 和 App.framework。混合使用不同目录(例如 Profile/Flutter.framework 以及 Debug/App.framework)将会导致运行失败。
小提示
在 Xcode 11 中,你可以添加 --xcframework --no-universal 参数来生成 XCFrameworks,而不是使用通用的 framework。
在 Xcode 中将生成的 frameworks 集成到你的既有应用中。例如,你可以在 some/path/MyApp/Flutter/Release/ 目录拖拽 frameworks 到你的应用 target 编译设置的 General > Frameworks, Libraries, and Embedded Content 下,然后在 Embed 下拉列表中选择 “Embed & Sign”。
链接到框架
例如,你可以将框架从 Finder 的 some/path/MyApp/Flutter/Release/ 拖到你的目标项目中,然后点击以下步骤 build settings > Build Phases > Link Binary With Libraries。
在 target 的编译设置中的 Framework Search Paths (FRAMEWORK_SEARCH_PATHS) 增加 $(PROJECT_DIR)/Flutter/Release/。
Embed the frameworks
内嵌框架
生成的动态框架必须嵌入你的应用并且在运行时被加载。
重点提醒
插件会帮助你生成 静态或动态框架。静态框架应该直接链接而不是嵌入。如果你在应用中嵌入了静态框架,你的应用将不能发布到 App Store 并且会得到一个 Found an unexpected Mach-O header code 的 archive error。
例如,你可以从应用框架组中拖拽框架(除了 FlutterPluginRegistrant 以及其他的静态框架)到你的目标 ‘ build settings > Build Phases > Embed Frameworks。然后从下拉菜单中选择 “Embed & Sign”。
你现在可以在 Xcode中使用 ⌘B 编译项目。
小提示
如果你想在 Debug 编译配置下使用 Debug 版本的 Flutter frameworks,在 Release 编译配置下使用 Release 版本的 Flutter frameworks,在 MyApp.xcodeproj/project.pbxproj 文件中,尝试在所有 Flutter 相关 frameworks 上使用 path = "Flutter/$(CONFIGURATION)/example.framework"; 替换 path = Flutter/Release/example.framework; (注意添加引号 ")。
你也必须在 Framework Search Paths (FRAMEWORK_SEARCH_PATHS) 编译设置中使用 $(PROJECT_DIR)/Flutter/$(CONFIGURATION)。
选项 C - 使用 CocoaPods 在 Xcode 和 Flutter 框架中内嵌应用和插件框架
除了将一个很大的 Flutter.framework 分发给其他开发者、机器或者持续集成 (CI) 系统之外,你可以加入一个参数 --cocoapods 将 Flutter 框架作为一个 CocoaPods 的 podspec 文件分发。这将会生成一个 Flutter.podspec 文件而不再生成 Flutter.framework 引擎文件。如选项 B 中所说的那样,它将会生成 App.framework 和插件框架。
flutter build ios-framework --cocoapods --output=some/path/MyApp/Flutter/
content_copy
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
Host apps using CocoaPods can add Flutter to their Podfile:
pod 'Flutter', :podspec => 'some/path/MyApp/Flutter/[build mode]/Flutter.podspec'