现有iOS、Android项目集成Flutter

准备工作

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

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

target 'iOSDemo' do
  use_frameworks!

  target 'iOSDemoTests' do
    inherit! :search_paths
  end

  target 'iOSDemoUITests' do
  end
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 。

集成 Flutter

原生工程对 Flutter 的依赖主要分为两部分:

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

创建同级目录的Flutter module

Flutter create -t module flutter_demo

然后我们分别对 flutter_demo 进行集成

iOS 模块集成

先说一下官方集成Flutter方式 wiki,里面用的两个脚本 podhelper.rb 和xcode_backend.sh 分别在Podfile和Build Phases里面

flutter_application_path = '../flutter_demo/'
eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding)
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed

解释下这两个脚本的作用:

1.podhelper.rb

  • 通过Pod引入Flutter.framework(Flutter引擎),以及Flutter的插件注册表FlutterPluginRegistrant(创建的工程中GeneratedPluginRegistrant.{h,m}文件中)
  • 引入.flutter-plugins的依赖
  • 导入Generated.xcconfig配置,这里面放的是Flutter要用的环境变量(如:xcode_backend.sh开头就要用的$FLUTTER_ROOT)

2.xcode_backend.sh

这个脚本原本是放在Build Phases,在Debug和Release模式下分别会产出对应环境的Flutter产物(Flutter有Debug,Profile,Release三个模式)。并且里面还有一句代码是:

RunCommand cp -r -- "${app_framework}" "${derived_dir}"
  • 在Debug和Release下构建出对应的Flutter产物,App.framework,flutter_assets(App.framework就是我们在lib下面写的Dart代码,flutter_assets就是我们资源)把上面的产物拷贝到App包里

在 iOS 平台,原生工程对 Flutter 的依赖分别是:

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

iOS 平台的对 Flutter 模块依赖,实际上就是通过打包命令生成这两个产物,我们将它们封装成一个 pod 供原生工程引用,这里我们解除对Flutter工程的依赖,podhelper.rb,xcode_backend.sh脚本我们不使用。

Flutter build ios --debug 

这条命令的作用是编译 Flutter 工程生成两个产物:Flutter.framework 和 App.framework。如果需要release,把 debug 换成 release 就可以构建 release 产物.

接下来,我们让iOSDemo依赖Flutter的编译的这两个产物,这里使用cocoapods进行依赖:

我们在/flutter_demo/.ios/Flutter目录创建FlutterEngine.podspec文件(路径可以修改成想要的位置)

pod spec create FlutterEngine

编辑FlutterEngine.podspec 文件依赖 Flutter.framework 和 App.framework

Pod::Spec.new do |spec|

  spec.name         = "FlutterEngine"
  spec.version      = "0.0.1"
  spec.summary      = "A short description of FlutterEngine."
  spec.description  = <<-DESC
    A short description of FlutterEngine.
                   DESC
  spec.homepage         = 'https://github.com/xx/FlutterEngine'
  spec.license          = { :type => 'MIT', :file => 'LICENSE' }
    spec.author             = { "tema.tian" => "temagsoft@163.com" }
    spec.source       = { :git => "", :tag => "#{spec.version}" }
  spec.ios.deployment_target = '8.0'
  spec.ios.vendored_frameworks = "App.framework", "engine/Flutter.framework"

end

pod lib lint 一下 Flutter组件模块就做好了,我们再修改一下iOSDemo的 Podfile,把它集成进去

target 'iOSDemo' do
  use_frameworks!
  
  pod 'FlutterEngine', :path => '../flutter_demo/.ios/Flutter/'
  
  target 'iOSDemoTests' do
    inherit! :search_paths
  end

  target 'iOSDemoUITests' do
  end

end

pod install 一下,Flutter 模块就集成进 iOS 原生工程中了。

我们在修改一下iOSDemo AppDelegate 把window的rootViewController 设置为 FlutterViewController

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    window = UIWindow(frame: UIScreen.main.bounds)
    let flutterVC = FlutterViewController()
    flutterVC.setInitialRoute("defaultRoute")
    window?.rootViewController = flutterVC
    window?.makeKeyAndVisible()
    return true
  }

点击xcode运行,最后点击运行,官方的 Flutter Widget 也展示出来了。至此,iOS 工程的接入就完了。

ios_demo_s.png

Android 模块集成

Android 原生工程对 Flutter 的依赖主要分为两部分,对应到 Android 平台,这两部分分别是:

  • Flutter 库和引擎,也就是 icudtl.dat、libFlutter.so,还有一些 class 文件。这些文件都封装在 Flutter.jar 中。
  • Flutter 工程产物,主要包括应用程序数据段 isolate_snapshot_data、应用程序指令段 isolate_snapshot_instr、虚拟机数据段 vm_snapshot_data、虚拟机指令段 vm_snapshot_instr、资源文件 Flutter_assets。

我们对 Android 的 Flutter 依赖进行抽取,首先我们再flutter_demo根目录,执行arr打包命令

Flutter build apk --debug

如果需要release,把 debug 换成 release 就可以构建 release 产物.

打包构建的flutter-debug.arr 位于 /.android/Flutter/build/outputs/aar/目录下,我们把它拷贝到AndroidDemo的App/libs目录下,然后在build.gradle中对他添加依赖:

 implementation(name: 'flutter-debug', ext: 'aar')

sync同步一下,然后我们修改AndroidDemo MainActivity的代码,将setContentView的加载View换成FlutterView

View FlutterView = Flutter.createView(this, getLifecycle(), "defaultRoute");
setContentView(FlutterView);

最后点击运行, Flutter Widget 就展示出来了.

android_demo_s.png

IOS,Android 集成这里是手动拷贝pod, aar,多人维护很麻烦,可以本地测试用debug包,提交用release包,或者把aar/pod以版本的方式发布到私有Maven/Cocoapod,以组件名+版本的方式在工程中引用,不直接管理打包构建产物。

对于三端工程分离模式最主要的则是抽离 Flutter 工程,将不同平台的构建产物依照标准组件化的形式进行管理,即:针对 Android 平台打包构建生成 aar,通过 build.gradle 进行依赖管理;针对 iOS 平台打包构建生成 framework,将其封装成独立的 pod,并通过 podfile 进行依赖管理。

至此原生集成Flutter就分享完了,附上demo,欢迎大家留言探讨。

参考文献

flutter-wiki

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

推荐阅读更多精彩内容