iOS工程集成Flutter Module及混合開發(fā)

Flutter混編方案

  • Flutter 工程作為原生工程共用的子模塊,維持原有的原生工程管理方式不變。這種模式,就是三端分離模式。
  • 除了實(shí)現(xiàn)輕量級(jí)接入,還可以快速實(shí)現(xiàn) Flutter 功能的“熱插拔”,降低原生工程的改造成本。而 Flutter 工程獨(dú)自管理,無需打開原生工程,可直接進(jìn)行 Dart 代碼和原生代碼的開發(fā)調(diào)試。
  • 三端工程分離模式的關(guān)鍵是抽離 Flutter 工程,將不同平臺(tái)的構(gòu)建產(chǎn)物依照標(biāo)準(zhǔn)組件化的形式進(jìn)行管理,即 Android 使用 aar、iOS 使用 pod。換句話說,將 Flutter 模塊打包成 aar 和 pod,這樣原生工程就可以像引用其他第三方原生組件庫那樣快速接入 Flutter 了。
    Flutter混編工程管理方式

平臺(tái)通道架構(gòu)設(shè)計(jì)

消息使用platform channels(平臺(tái)通道)在客戶端(UI)和宿主(平臺(tái))之間傳遞;

消息和響應(yīng)以異步的形式進(jìn)行傳遞,以確保用戶界面能夠保持響應(yīng);
平臺(tái)通道架構(gòu)圖

調(diào)用過程大致如下:

1.客戶端(Flutter端)發(fā)送與方法調(diào)用相對(duì)應(yīng)的消息;
2.平臺(tái)端(iOS、Android端)接收方法,并返回結(jié)果;

  • iOS端通過FlutterMethodChannel做出響應(yīng);
  • Android端通過MethodChannel做出響應(yīng);

集成Flutter

現(xiàn)有的Flutter是一個(gè)Application項(xiàng)目,現(xiàn)在想把它嵌入原生應(yīng)用中。首先需要將現(xiàn)有Flutter工程轉(zhuǎn)成Module工程,在flutter 工程中的pubspec.yaml 文件中flutter節(jié)點(diǎn)下添加如下配置,然后pug get跑一下

module:
    androidX: true
    androidPackage: com.ganyuan.flutter_module
    iosBundleIdentifier: com.ganyuan.flutterModule

pub get 一下就會(huì)生成:.android.ios 文件夾,這兩個(gè)文件是flutter applicaiton沒有的,在 mac上是兩個(gè) 隱藏文件夾,window 是可見的

iOS中集成Flutter module

這里采用官方推薦的方法,使用 CocoaPods 依賴管理器安裝 Flutter SDK 使用,每次構(gòu)建應(yīng)用的時(shí)候都會(huì)從源代碼中編譯 flutter_module。
首先在本地安裝Flutter的SDK及環(huán)境配置,FlutterSDK安裝教程
安裝完成后,將我們的iOS工程和Flutter工程(默認(rèn)已創(chuàng)建好)放到同一目錄下,如下圖所示

如果你的應(yīng)用下(iOS_demo)還沒有 Podfile,請(qǐng)運(yùn)行 pod init 來創(chuàng)建一個(gè)。

  1. Podfile 中添加下面代碼:
    flutter_application_path = '../flutter_module'
    load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
    
  2. 每個(gè)需要集成 Flutter 的 Podfile target,執(zhí)行 install_all_flutter_pods(flutter_application_path)
    target 'iOS_demo' do
      install_all_flutter_pods(flutter_application_path)
    end
    
  3. Podfilepost_install 部分,調(diào)用 flutter_post_install(installer)。
    post_install do |installer|
      flutter_post_install(installer) if defined?(flutter_post_install)
    end
    
  1. 運(yùn)行 pod install。
    提示
    當(dāng)你在 flutter_module/pubspec.yaml 改變了 Flutter plugin 依賴,需要在 Flutter module 目錄運(yùn)行 flutter pub get,來更新會(huì)被podhelper.rb 腳本用到的 plugin 列表,然后再次在你的應(yīng)用目錄 some/path/MyApp 運(yùn)行 pod install.

podhelper.rb 腳本會(huì)把你的 plugins, Flutter.framework,和 App.framework 集成到你的項(xiàng)目中。
你應(yīng)用的 Debug 和 Release 編譯配置,將會(huì)集成相對(duì)應(yīng)的 Debug 或 Release 的 編譯產(chǎn)物??梢栽黾右粋€(gè) Profile 編譯配置用于在 profile 模式下測(cè)試應(yīng)用。
小提示
Flutter.framework 是 Flutter engine 的框架, App.framework 是你的 Dart 代碼的編譯產(chǎn)物。

5.本地隱私權(quán)限,如果你的Flutter應(yīng)用需要調(diào)用諸如藍(lán)牙、網(wǎng)絡(luò)等能力,需要在iOS原生項(xiàng)目中配置用戶隱私使用權(quán)限,如下圖:
添加藍(lán)牙使用權(quán)限

6.在原生項(xiàng)目中引用Flutter,確保項(xiàng)目能編譯成功:
為了在既有的iOS應(yīng)用中展示Flutter頁面,需要啟動(dòng) Flutter Engine和 FlutterViewController。
通常建議為我們的應(yīng)用預(yù)熱一個(gè)長(zhǎng)時(shí)間存活的FlutterEngine,我們將在應(yīng)用啟動(dòng)的 app delegate 中創(chuàng)建一個(gè) FlutterEngine,并作為屬性暴露給外界。
AppDelegate.h代碼:

@import UIKit;
@import Flutter;

@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (nonatomic,strong) FlutterEngine *flutterEngine;

@end

AppDelegate.m代碼:

#import "AppDelegate.h"
#import <FlutterPluginRegistrant/GeneratedPluginRegistrant.h>

@interface AppDelegate ()

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions {
    self.flutterEngine = [[FlutterEngine alloc] initWithName:@"my flutter engine"];
    [self.flutterEngine run];
    // Connects plugins with iOS platform code to this app.
    [GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];

    return YES;
}
@end

創(chuàng)建一個(gè)FlutterViewController用于展示Flutter的內(nèi)容

調(diào)用FlutterViewController

    FlutterEngine *flutterEngine = ((AppDelegate *)UIApplication.sharedApplication.delegate).flutterEngine;
    myFlutterViewController *flutterViewController = [[myFlutterViewController alloc]initWithEngine:flutterEngine nibName:nil bundle:nil];

SceneDelegate.m代碼:

#import "SceneDelegate.h"
#import "myFlutterViewController.h"
#import "AppDelegate.h"

@interface SceneDelegate ()

@end

@implementation SceneDelegate


- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
    UIWindowScene *windowScene = (UIWindowScene *)scene;
      self.window = [[UIWindow alloc] initWithWindowScene:windowScene];
      self.window.frame = windowScene.coordinateSpace.bounds;
      
      UITabBarController *tabBarController = [[UITabBarController alloc] init];
      
    FlutterEngine *flutterEngine = ((AppDelegate *)UIApplication.sharedApplication.delegate).flutterEngine;
    myFlutterViewController *flutterViewController = [[myFlutterViewController alloc]initWithEngine:flutterEngine nibName:nil bundle:nil];
    flutterViewController.view.backgroundColor = [UIColor whiteColor];
    flutterViewController.tabBarItem.title = @"Tab1";
    flutterViewController.tabBarItem.image = [UIImage imageNamed:@"icon.bundle/home"];
    flutterViewController.tabBarItem.selectedImage = [UIImage imageNamed:@"icon.bundle/home_selected"];
      
      UIViewController *controller2 = [[UIViewController alloc] init];
      controller2.view.backgroundColor = [UIColor yellowColor];
      controller2.tabBarItem.title = @"Tab2";
      controller2.tabBarItem.image = [UIImage imageNamed:@"icon.bundle/video"];
      controller2.tabBarItem.selectedImage = [UIImage imageNamed:@"icon.bundle/video_selected"];
      
      UIViewController *controller3 = [[UIViewController alloc] init];
      controller3.view.backgroundColor = [UIColor blueColor];
      controller3.tabBarItem.title = @"Tab3";
      controller3.tabBarItem.image = [UIImage imageNamed:@"icon.bundle/like"];
      controller3.tabBarItem.selectedImage = [UIImage imageNamed:@"icon.bundle/like_selected"];
      
      UIViewController *controller4 = [[UIViewController alloc] init];
      controller4.view.backgroundColor = [UIColor greenColor];
      controller4.tabBarItem.title = @"Tab4";
      controller4.tabBarItem.image = [UIImage imageNamed:@"icon.bundle/page"];
      controller4.tabBarItem.selectedImage = [UIImage imageNamed:@"icon.bundle/page_selected"];
      
      [tabBarController setViewControllers: @[flutterViewController, controller2, controller3, controller4]];
      
      tabBarController.tabBar.backgroundColor = [UIColor whiteColor];
          
      self.window.rootViewController = tabBarController;
      [self.window makeKeyAndVisible];
}

@end

在 Xcode 中打開 iOS_demo.xcworkspace ,你現(xiàn)在可以使用 ?B 編譯項(xiàng)目了。
demo運(yùn)行效果:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容