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))之間傳遞;

調(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)建好)放到同一目錄下,如下圖所示

iOS_demo)還沒有 Podfile,請(qǐng)運(yùn)行 pod init 來創(chuàng)建一個(gè)。
- 在
Podfile中添加下面代碼:flutter_application_path = '../flutter_module' load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb') - 每個(gè)需要集成 Flutter 的 Podfile target,執(zhí)行
install_all_flutter_pods(flutter_application_path):target 'iOS_demo' do install_all_flutter_pods(flutter_application_path) end - 在
Podfile的post_install部分,調(diào)用flutter_post_install(installer)。post_install do |installer| flutter_post_install(installer) if defined?(flutter_post_install) end

- 運(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)物。

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)行效果:

