Flutter混合开发:在已有iOS项目中引入Flutter

前言:

这里不讲怎么搭建Flutter环境,请自行Google,这里只讲在已有iOS项目中引入Flutter。
目前混合开发属于主流,因为多数都在原来的项目上集成Flutter模块,除非新的项目用纯Flutter。
想要在已有的原生 App 里嵌入一些 Flutter 页面,有两个办法:

  • 将原生工程作为 Flutter 工程的子工程,由 Flutter 统一管理。这种模式,就是统一管理模式。

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

    image

三端代码分离的模式来进行依赖治理,实现了 Flutter 工程的轻量级接入,三端代码分离模式把 Flutter 模块作为原生工程的子模块,还可以快速实现 Flutter 功能的“热插拔”,降低原生工程的改造成本。而 Flutter 工程通过 Android Studio 进行管理,无需打开原生工程,可直接进行 Dart 代码和原生代码的开发调试;
三端工程分离模式的关键是抽离 Flutter 工程,将不同平台的构建产物依照标准组件化的形式进行管理,即 Android 使用 aar、iOS 使用 pod。换句话说,接下来介绍的混编方案会将 Flutter 模块打包成 aar 和 pod,这样原生工程就可以像引用其他第三方原生组件库那样快速接入 Flutter 了。

集成(以iOS为例),使用Pods方式

官方给出了三种接入方案,这三种方案各有优缺点,我们先简单看看这三种方案:

  • 使用 CocoaPods 和 Flutter SDK 集成:ios项目中用CocoaPods直接接入管理flutter module。这种方案需要所有开发人员都配置flutter环境,且安装CocoaPods;优点是通过CocoaPods自动集成,配置简单。
  • 在 Xcode 中集成 frameworks:将flutter module先build成FrameWork文件,然后在ios项目中引入文件。这种方案的优点是ios开发人员不需要flutter环境,且项目不需要安装CocoaPods;缺点是每次修改都需要重新build,重新导入。
  • 通过CocoaPods打包Framework:与2类似,只不过在build时加入--cocoapods参数:flutter build ios-framework --cocoapods --xcframework --no-universal --output=some/path/MyApp/Flutter/。打包出来的是Flutter.podspec 文件,ios项目中通过CocoaPods管理集成。这个方案的与2方案差不多,缺点也是每次改动需要重新build,优点是ios开发人员不需要flutter环境。

所以要根据自身的情况来选择符合自己的方案。官方推荐第一种方案,我也先尝试了第一个方案。

方案一(CocoaPods直接接入管理flutter module)

首先我们在现有工程目录创建一个flutter module的项目,可以用命令创建
flutter create -t module flutter_module

  • 注:flutter create flutter_moduleflutter create -t module flutter_module区别在于带module参数,创建出来的工程会把辅助用的android,ios工程给隐藏掉,要涉及打包以及修改配置,可以不带module参数创建。

这里的 Flutter 模块,也是 Flutter 工程,我们用 Android Studio或vs code 打开它:


image.png

在现有工程中找到Podfile,添加如下配置:

此处省略...

# my_flutter 是创建Flutter的模块名称
flutter_application_path = './flutter_module'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')

platform :ios, '13.0'

target 'FlutterDemo' do
  # use_frameworks!
  
  # 这边引入flutter
  install_all_flutter_pods(flutter_application_path)

  此处省略...
  
end

post_install do |installer|   
    installer.pods_project.targets.each do |target|
      flutter_additional_ios_build_settings(target)
      target.build_configurations.each do |config|
        xcconfig_path = config.base_configuration_reference.real_path
        xcconfig = File.read(xcconfig_path)
        xcconfig_mod = xcconfig.gsub(/DT_TOOLCHAIN_DIR/, "TOOLCHAIN_DIR")
        File.open(xcconfig_path, "w") { |file| file << xcconfig_mod }
      end
    end

    # 防止flutter sdk使用的是最新3.x版本 pod install报错 Missing `flutter_post_install(installer)`
    flutter_post_install(installer) if defined?(flutter_post_install)
end

由于xcode15之后会报错error: DT_TOOLCHAIN_DIR cannot be used to evaluate LIBRARY_SEARCH_PATHS, use TOOLCHAIN_DIR instead (in target 'MyFlutter' from project 'Pods')
所有需要在末尾加上post_install相关配置。

然后在原生项目下 执行 pod install 如果以上不报错,混合开发模式到这里就集成完了。

集成后编译不通过,报错:framework not found FlutterPluginRegistrant。但是我们并没有使用任何flutter plugin,所以不存在这个文件,但是CocoaPods不知道为什么一定要这个文件,所以导致一直编译失败,加上这个方案入侵有点大,所以放弃了这种集成方案。

方案二(在 Xcode 中集成 frameworks)

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

  • Flutter 库和引擎,即 Flutter.framework;
  • Flutter 工程的产物,即 App.framework;
  • Flutter 资源文件,即Assets.car;

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

接下来,我们要做的事情就是把这段代码编译打包,构建出对应的 Android 和 iOS 依赖库,实现原生工程的接入,命令如下:
flutter build ios
这里就会出现一个问题:签名问题。执行上面命令后会报错,这里可以在build的时候选择不签名,命令如下:
flutter build ios --no-codesign
这样就可以build成功,默认编译的是release包,加上--debug可以编译debug包。
也可以使用下面的命令:
flutter build ios-framework --cocoapods --xcframework --no-universal --output=build/archive/ios

然后在ios项目中直接将Flutter.xcframework和App.xcframework等文件引入工程。
然后修改原生代码,启动 FlutterEngineFlutterViewController,如下:

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
     self.flutterEngine = [[FlutterEngine alloc] initWithName:@"my flutter engine"];
     [self.flutterEngine run];
     [GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];
     return YES;
}
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    
    FlutterViewController *flutterViewController =
             [[FlutterViewController alloc] initWithEngine:appDelegate.flutterEngine nibName:nil bundle:nil];
    flutterViewController.modalPresentationStyle = UIModalPresentationFullScreen;
    [self presentViewController:flutterViewController animated:YES completion:nil];
}

注:确保引擎启动完毕后,再调用FlutterViewController,否则flutter页面展示失败。

然后编译工程报错如下:
iOS Xcode 15 Sandbox: rsync(xxxx) deny(1) file-write-create
解决方案:设置里面搜索user 把User Script Sanboxing 改为NO。

最后点击运行,Flutter Widget 页面展示出来了。

方案三(通过CocoaPods打包Framework)

修改工程下的Podfile:
pod "Flutter", :path => "./LocalPods/Flutter"

然后和方案二一样修改原生代码,启动 FlutterEngineFlutterViewController

参考文章

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

推荐阅读更多精彩内容