将Flutter模块集成到原生应用

使用 Flutter 从头开始写一个 App,是一件轻松惬意的事情。但,对于成熟产品来说,完全摒弃原有 App 的历史沉淀,而全面转向 Flutter 并不现实。用 Flutter 去统一 iOS/Android 技术栈,把它作为已有原生 App 的扩展能力,通过逐步试验有序推进从而提升终端开发效率,可能才是现阶段 Flutter 最具吸引力的地方。

那么,Flutter 工程与原生工程该如何组织管理?不同平台的 Flutter 工程打包构建产物该如何抽取封装?封装后的产物该如何引入原生工程?原生工程又该如何使用封装后的 Flutter 能力?

这些问题使得在已有原生 App 中接入 Flutter 看似并不是一件容易的事情。那接下来,我就和你介绍下如何在原生 App 中以最自然的方式接入 Flutter。

准备工作

既然是要在原生应用中混编 Flutter,相信你一定已经准备好原生应用工程来实施今天的改造了。如果你还没有准备好也没关系,我会以一个最小化的示例和你演示这个改造过程。

首先,我们分别用 Xcode 与 Android Studio 快速建立一个只有首页的基本工程,工程名分别为 iOSDemo 与 AndroidDemo。

对于 iOS 工程来说,由于基本工程并不支持以组件化的方式管理项目,因此我们还需要多做一步,将其改造成使用 CocoaPods 管理的工程,也就是要在 iOSDemo 根目录下创建一个只有基本信息的 Podfile 文件:

use_frameworks!
platform :ios, '8.0'
target 'iOSDemo' do
#todo
end

然后,在命令行输入 pod install 后,会自动生成一个 iOSDemo.xcworkspace 文件,这时我们就完成了 iOS 工程改造。

Flutter 混编方案介绍

如果你想要在已有的原生 App 里嵌入一些 Flutter 页面,有两个办法:

  • 将原生工程作为 Flutter 工程的子工程,由 Flutter 统一管理。这种模式,就是统一管理模式
  • 将 Flutter 工程作为原生工程共用的子模块,维持原有的原生工程管理方式不变。这种模式,就是三端分离模式
Flutter 混编工程管理方式.png

由于 Flutter 早期提供的混编方式能力及相关资料有限,国内较早使用 Flutter 混合开发的团队大多使用的是统一管理模式。但是,随着功能迭代的深入,这种方案的弊端也随之显露,不仅三端(Android、iOS、Flutter)代码耦合严重,相关工具链耗时也随之大幅增长,导致开发效率降低。

所以,后续使用 Flutter 混合开发的团队陆续按照三端代码分离的模式来进行依赖治理,实现了 Flutter 工程的轻量级接入。

除了可以轻量级接入,三端代码分离模式把 Flutter 模块作为原生工程的子模块,还可以快速实现 Flutter 功能的“热插拔”,降低原生工程的改造成本。而 Flutter 工程通过 Android Studio 进行管理,无需打开原生工程,可直接进行 Dart 代码和原生代码的开发调试。

三端工程分离模式的关键是抽离 Flutter 工程,将不同平台的构建产物依照标准组件化的形式进行管理,即 Android 使用 aar、iOS 使用 pod。换句话说,接下来介绍的混编方案会将 Flutter 模块打包成 aar 和 pod,这样原生工程就可以像引用其他第三方原生组件库那样快速接入 Flutter 了。

集成 Flutter

我曾在前面的文章中提到,Flutter 的工程结构比较特殊,包括 Flutter 工程和原生工程的目录(即 iOS 和 Android 两个目录)。在这种情况下,原生工程就会依赖于 Flutter 相关的库和资源,从而无法脱离父目录进行独立构建和运行。

  • Flutter 库和引擎,也就是 Flutter 的 Framework 库和引擎库;
  • Flutter 工程,也就是我们自己实现的 Flutter 模块功能,主要包括 Flutter 工程 lib 目录下的 Dart 代码实现的这部分功能。

将Flutter模块集成到本机iOS的最简单选择是使用CocoaPods。

在已经有原生工程的情况下,我们需要在同级目录创建 Flutter 模块,构建 iOS 和 Android 各自的 Flutter 依赖库。这也很好实现,Flutter 就为我们提供了这样的命令。我们只需要在原生项目的同级目录下,执行 Flutter 命令创建名为 my_flutter 的模块即可:

cd  ~/Desktop/FlutterDemo
flutter create --template module my_flutter
创建my_flutter.png

my_flutter文件夹下的很多文件默认是隐藏状态的,我们可以使用快捷键 ⌘⇧.(Command + Shift + .) 来快速(在 Finder 中)显示和隐藏隐藏文件。

将Flutter模块集成到本机iOS的最简单选择是使用CocoaPods。如果您已经在iOS项目中创建了Podfile,则可以跳过这一步。

  1. 在iOS项目目录运行以下命令:
pod init
  1. 创建Podfile之后,整个项目结构应如下所示:
some/path/
├── my_flutter/
│   └── .iOS/
│       └── Flutter/
│         └── podhelper.rb
└── MyApp/
    └── Podfile
  1. 打开Podfile ,将以下几行添加到您的Podfile:
flutter_application_path = '../my_flutter'

load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')

4.对于每个需要嵌入Flutter的,请调用install_all_flutter_pods(flutter_application_path)

target 'MyApp' do
  install_all_flutter_pods(flutter_application_path)
end

5.保存并关闭Podfile 运行以下命令来安装Pods

pod install

然后使用带有.xcworkspace扩展名的文件重新打开项目。

podhelper.rb脚本嵌入你的插件, Flutter.framework以及App.framework到项目中。

您应用的Debug和Release构建配置分别嵌入了Flutter的Debug或Release构建模式。将配置文件构建配置添加到您的应用,以在配置文件模式下进行测试。

iOS 模块集成

iOS 工程接入的情况要稍微复杂一些。在 iOS 平台,原生工程对 Flutter 的依赖分别是:

  • Flutter 库和引擎,即 Flutter.framework;
  • Flutter 工程的产物,即 App.framework。

iOS 平台的 Flutter 模块抽取,实际上就是通过打包命令生成这两个产物,并将它们封装成一个 pod 供原生工程引用。

类似地,首先我们在 my_flutter 的根目录下,执行 iOS 打包构建命令:

flutter build ios --debug

这条命令的作用是编译 Flutter 工程生成两个产物:Flutter.framework 和 App.framework。同样,把 debug 换成 release 就可以构建 release 产物(当然,你还需要处理一下签名问题)

其次,在 MyApp 的根目录下创建一个名为 FlutterEngine 的目录,并把这两个 framework 文件拷贝进去。我们需要把这两个产物手动封装成 pod。因此,我们还需要在该目录下创建 FlutterEngine.podspec,即 Flutter 模块的组件定义:


Pod::Spec.new do |s|
  s.name             = 'FlutterEngine'
  s.version          = '0.1.0'
  s.summary          = 'XXXXXXX'
  s.description      = <<-DESC
TODO: Add long description of the pod here.
                       DESC
  s.homepage         = 'https://github.com/xx/FlutterEngine'
  s.license          = { :type => 'MIT', :file => 'LICENSE' }
  s.author           = { 'chenhang' => 'hangisnice@gmail.com' }
  s.source       = { :git => "", :tag => "#{s.version}" }
  s.ios.deployment_target = '8.0'
  s.ios.vendored_frameworks = 'App.framework', 'Flutter.framework'
end
target 'iOSDemo' do
    pod 'MyApp', :path => '../'
end

pod install 一下,Flutter 模块就集成进 iOS 原生工程中了。
或者我们直接把FlutterEngine模块组件作为一个私有库组件,my_flutter项目负责flutter相关项目的开发,然后FlutterEngine模块直接用脚本自动打包成相应的Flutter 库。

相关配置参考将Flutter模块集成到您的iOS项目中

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容