為現有 iOS項目集成 Flutter
08-06
為現有 iOS項目集成 Flutter
// AppDelegate實現@interface DemoFlutterBaseAppDelegate ()@property(nonatomic, strong) DemoFlutterBaseViewController *rootController;@property(readonly, nonatomic) NSMutableArray* pluginDelegates;@property(readonly, nonatomic) NSMutableDictionary* pluginPublications;@end@implementation DemoFlutterBaseAppDelegate/* * ... 這裡寫轉發各種聲明周期事件給 plugin
推薦閱讀:
Flutter 在經過一番洗禮後終於迎來了 Release Preview 1版本,也吸引了更多人的關注。使用 Flutter從頭開始寫一個 App非常輕鬆,但越來越多的人發現 Flutter貌似並不很友好地支持現有的 App接入。所以本文帶大家了解一下如何讓現有 App支持 Flutter。
環境
Flutter:0.5.1
Xcode 9.4.1Flutter工程:flutter/examples/hello_worldDebug: Flutter Hot Restart
我們都知道在開發環境下,Flutter的 hot restart 對 UI快速成型是非常有幫助的,要讓現有的 App支持 Flutter並且開啟 Hot restart也不難。
Flutter(engine) 基礎庫首先,在你的項目裡面拖入Flutter.framework
,這個庫是 Flutter Engine,承載了 Dart運行時和繪圖引擎。Flutter.framework
和命令行工具版本是一一對應的,如果你不知道從哪裡找這個文件,可以直接在 Flutter源碼項目裡面進行一次 flutter run
,然後你就能在 /<project>/ios/Flutter/
目錄下面找到了,直接拖進項目即可。Flutter ViewController
接下來需要把 Flutter的基礎代碼引入現有工程,有了基礎的 Flutter ViewController才可以顯示 Flutter視圖。這一步很簡單,只需要在你現有的 ViewController中 Push過去就可以了:- (void)jumpToFlutter { FlutterViewController *viewController = [FlutterViewController new]; [self.navigationController pushViewController:viewController animated:YES];}但是還需要注意需要把 AppDelegate裡面的生命周期事件傳遞給 FlutterRelease Preview 1後文檔中提到有 FlutterAppLifeCycleProvider
這個協議,但是 0.5.1還沒有,所以這裡先野路子來了
- 直接讓現有的 AppDelegate繼承
FlutterAppDelegate
即可,但這帶來的負面影響是 root ViewController被設置為 Flutter ViewController - 自行改造 AppDelegate。很多中大型 App喜歡在開發中將業務模塊化,幸好 Flutter APPDelegate並不是很難改造過來,也能支持模塊 Delegate。首先讓你的 Delegate遵循
FlutterPluginRegistry
協議
// AppDelegate 或者模塊的 Delegate
@interface DemoFlutterBaseAppDelegate : NSObject <ModularApplicationDelegate, FlutterPluginRegistry>/** FlutterBinaryMessenger this determines which view controller is the flutter view controller nomally, flutter view controller provides the binary messages @return root Flutter ViewController */- (NSObject<FlutterBinaryMessenger> *)binaryMessenger;/**
FlutterTextureRegistry this determines which view controller is the flutter view controller nomally, flutter view controller provides the custom textures @return root Flutter ViewController */- (NSObject<FlutterTextureRegistry> *)textures;@end然後在實現中支持 Flutter messenger 和 texture
// Registrar 聲明和實現
@interface DemoFlutterAppDelegateRegistrar : NSObject<FlutterPluginRegistrar>@property(nonatomic, copy) NSString *pluginKey;@property(nonatomic, strong) DemoFlutterBaseAppDelegate *appDelegate;- (instancetype)initWithPlugin:(NSString*)pluginKey appDelegate:(DemoFlutterBaseAppDelegate*)delegate;@end@implementation DemoFlutterAppDelegateRegistrar- (instancetype)initWithPlugin:(NSString*)pluginKey appDelegate:(DemoFlutterBaseAppDelegate*)appDelegate { self = [super init]; NSAssert(self, @"Super init cannot be nil");_pluginKey = [pluginKey copy];
_appDelegate = appDelegate; return self;}- (NSObject<FlutterBinaryMessenger>*)messenger { return [_appDelegate binaryMessenger];}- (NSObject<FlutterTextureRegistry>*)textures { return [_appDelegate textures];}
- (void)publish:(NSObject*)value { _appDelegate.pluginPublications[_pluginKey] = value;}- (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate channel:(FlutterMethodChannel*)channel { [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { [delegate handleMethodCall:call result:result]; }];}- (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate {
[_appDelegate.pluginDelegates addObject:delegate];}- (NSString*)lookupKeyForAsset:(NSString*)asset { return [FlutterDartProject lookupKeyForAsset:asset];}- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package];}@end*/
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { for (id<FlutterPlugin> plugin in _pluginDelegates) { if ([plugin respondsToSelector:_cmd]) { [plugin application:application didFinishLaunchingWithOptions:launchOptions]; } } return YES;}#pragma mark - getters for flutter// 返回 FlutterViewController實例- (FlutterViewController *)rootController { // ...}- (NSObject<FlutterBinaryMessenger> *)binaryMessenger{ if ([self.rootController conformsToProtocol:@protocol(FlutterBinaryMessenger)]) { return (NSObject<FlutterBinaryMessenger> *)self.rootController; } return nil;}- (NSObject<FlutterTextureRegistry> *)textures{ if ([self.rootController conformsToProtocol:@protocol(FlutterTextureRegistry)]) { return (NSObject<FlutterTextureRegistry> *)self.rootController; } return nil;}- (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey { NSAssert(self.pluginPublications[pluginKey] == nil, @"Duplicate plugin key: %@", pluginKey); self.pluginPublications[pluginKey] = [NSNull null]; return [[DemoFlutterAppDelegateRegistrar alloc] initWithPlugin:pluginKey appDelegate:self];}- (BOOL)hasPlugin:(NSString*)pluginKey { return _pluginPublications[pluginKey] != nil;}- (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey { return _pluginPublications[pluginKey];}@end這樣 Flutter的運行環境其實就準備好了,無論是 Hot Restart還是 AOT都可以支持。接下來我們實現 Debug Hot Restart首先在你的 Flutter代碼目錄下執行一遍Flutter build bundle
,這可以幫助我們打包出一個 Flutter Asset,然後把這個 flutter_assets
目錄拖入項目。對你的項目進行一次 build,確保能夠得到一個 .app 文件。然後新建一個文件夾叫做 Payload
,把 .app文件放入 Payload文件夾,然後壓縮成 zip文件。這個文件便可以被 Flutter命令行工具使用了。flutter run --use-application-binary /path/to/Payload.zip然後效果如圖:其實這裡並沒有實現 Hot Reload,主要是目前 flutter工具鏈支持度還不好,不過 Hot Restart也夠用了。Release: Flutter AOTRelease模式和 Debug下不一樣,我們需要做幾件事情:
- 把
Flutter.framework
替換成flutter/bin/cache/artifacts/engine/ios-release/Flutter.framework
,因為上一步我們用的庫其實是 JIT Runtime - 在 Flutter代碼項目下面執行
flutter build aot --release --target-platform ios --ios-arch armv7,arm64
然後我們可以在 build目錄下拿到一個打包好的App.framework
,不過別忘記在裡面放一個 Info.plist。並且把這個庫拖到工程裡面 - 刪除工程裡面的
flutter_assets
文件夾下的isolate_snapshot_data
、kernel_blob.bin
、platform.dill
、vm_snapshot_data
這幾個文件 - 編譯打包給真機運行,效果如下:
The End
其實 Flutter官方有支持現有 App 集成的計劃,並且現在文檔也有一部分介紹,但其實整體工具鏈還沒支持上來,目前所支持的程度和上文的方法也大同小異。如果有需要的話,現有項目完全可以採用上面的方法集成,為了減少工作流程,還需要做一些工作,比如:- 項目中提供 App.framework 、 Flutter.framework的空殼,方便在 debug 和 release下隨時用腳本替換
- Debug時可以先打出包給 Flutter開發者用,也可以直接添加一個 build post action,直接調用 flutter 命令行,把 Xcode和 flutter整合起來,不要像上文一樣全都手動,容易漏掉必要流程
- Release AOT的自動化肯定要做,並且要和現有 CI整合
>>>>閱讀全文
推薦閱讀:
※Android O最給力改進:App啟動速度快兩倍!
※凱立德Android版報詳細路名設置方法
※內核空間鏡像攻擊|利用ARM MMU硬體特性開啟安卓8終端的上帝模式
※小米8跟三星s9+的差距大嗎?