美文网首页
Flutter混合开发:在已有iOS项目中引入Flutter

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

作者: 李华光 | 来源:发表于2024-07-31 17:55 被阅读0次

    前言:

    这里不讲怎么搭建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

    参考文章

    相关文章

      网友评论

          本文标题:Flutter混合开发:在已有iOS项目中引入Flutter

          本文链接:https://www.haomeiwen.com/subject/jcpnhjtx.html