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

2024-07-31  本文已影响0人  李华光

前言:

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

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

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

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

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

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

首先我们在现有工程目录创建一个flutter module的项目,可以用命令创建
flutter create -t module flutter_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 的依赖分别是:

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

参考文章

上一篇下一篇

猜你喜欢

热点阅读