1. 程式人生 > >iOS 如何在現有的工程引入Flutter?

iOS 如何在現有的工程引入Flutter?

前言

Flutter 是一個很有潛力的框架,但是目前使用Flutter的APP並不算很多,相關資料並不豐富,介紹現有工程引入Flutter的相關文章也比較少。專案從零開始,引入Flutter操作比較簡單,但是現有工程引入Flutter 需要費很多精力和時間,這裡是我在完成現有iOS工程引入Flutter後寫的一次總結文章。

Flutter 環境搭建

現有iOS工程引入Flutter

一、建立Flutter module

首先建立flutter module,主要是用於獲取改flutter app中的Generated.xcconfig和framework

cd some/path/
$ flutter create -t module my_flutter

也可以用

flutter create app

建立flutter app,flutter app中也有Generated.xcconfig和framework

二、新建配置檔案

根據官網,需要在工程中建立三個配置檔案:

Flutter.xcconfig 、Debug.xcconfig、Release.xcconfig

在XCode工程對應目錄,右擊,選擇新建檔案(New File),選中建立xcconfig檔案,如圖:

1541496429152623.jpg

在Flutter.xcconfig中填寫:

//這裡填寫前面建立的flutter module 的Generated.xcconfig的路徑
#include "../../my_flutter/.ios/Flutter/Generated.xcconfig"
ENABLE_BITCODE=NO

在 Debug.xcconfig中填寫:

#include "../Flutter/Flutter.xcconfig"

在 Release.xcconfig中填寫:

#include "../Flutter/Flutter.xcconfig"
FLUTTER_BUILD_MODE=release

如果工程中用cocoapods管理,需要在 Debug.xcconfig和Release.xcconfig新增pod的路徑:

例如 Release.xcconfig中

#include "Flutter.xcconfig"
#include "工程路徑/Pods/Target Support Files/******.release.xcconfig"//pod路徑
FLUTTER_BUILD_MODE=release

在準備好這些xcconfig檔案後,需要到XCode工程PROJECT(注意是PROJECT,不是Target)中的Configuration選項裡,將對應的target選擇成前面的xcconfig檔案,Debug用Debug.xcconfig, Release用 Release.xcconfig

注意:進行Archive打包的時候,無論是Debug包還是Release包,需要切換到Release.xcconfig,不然會報錯。

1541496485753262.jpg

三、為編譯Dart引入相關build phase

在工程的Build Phase中新建一個Run Script,用於編譯時執行指令碼,
建立方法如圖:
在這裡插入圖片描述

建立Run Script後,需要移動其對應的位置,需要在Target dependencies之後,如果用cocoapods管理工程需要在,Check Pods Manifest.lock之後:

1541496511319987.jpg

在指令碼框中,填入以下程式碼,用於引進Flutter中的xcode_backend指令碼:

"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build

如圖:
1541496529585927.jpg

四、生成和新增Framework

完成前面的配置後,便可以在XCode對工程進行編譯build (Command+B),在提示“ Build Success ” 後,在iOS工程資料夾中會生成一個Flutter資料夾,將其加入工程目錄中,建議和剛才xcconfig所在目錄並列,

右鍵專案目錄 ,選擇 Add Files to ‘xxx’ ,Options選Create groups,新增編譯生成的Flutter資料夾。需要注意但是:Flutter目錄下有個flutter_assets檔案,不能使用Create groups的方式新增,只能用Creat folder references的Options, 否則Flutter頁面會空白渲染不出來。可以刪了flutter_assets在用Creat folder references重新新增。

在新增完Flutter 資料夾之後,去Embeded Binaries中新增App.framework和Flutter.framework
在這裡插入圖片描述

五、AppDelegate改造

Flutter需要和APP進行互動,需要對AppDelegate 進行改造:

AppDelegate.h檔案中:

#import 

@interface AppDelegate : FlutterAppDelegate <UIApplicationDelegate, FlutterAppLifeCycleProvider>

@end

AppDelegate.m 檔案中:

#import "AppDelegate.h"

@interface AppDelegate ()

@end

@implementation AppDelegate
{
  FlutterPluginAppLifeCycleDelegate *_lifeCycleDelegate;
}

- (instancetype)init {
    if (self = [super init]) {
        _lifeCycleDelegate = [[FlutterPluginAppLifeCycleDelegate alloc] init];
    }
    return self;
}

- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
    return [_lifeCycleDelegate application:application didFinishLaunchingWithOptions:launchOptions];
}

- (void)applicationDidEnterBackground:(UIApplication*)application {
    [_lifeCycleDelegate applicationDidEnterBackground:application];
}

- (void)applicationWillEnterForeground:(UIApplication*)application {
    [_lifeCycleDelegate applicationWillEnterForeground:application];
}

- (void)applicationWillResignActive:(UIApplication*)application {
    [_lifeCycleDelegate applicationWillResignActive:application];
}

- (void)applicationDidBecomeActive:(UIApplication*)application {
    [_lifeCycleDelegate applicationDidBecomeActive:application];
}

- (void)applicationWillTerminate:(UIApplication*)application {
    [_lifeCycleDelegate applicationWillTerminate:application];
}

- (void)application:(UIApplication*)application
didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings {
    [_lifeCycleDelegate application:application
didRegisterUserNotificationSettings:notificationSettings];
}

- (void)application:(UIApplication*)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
    [_lifeCycleDelegate application:application
didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}

- (void)application:(UIApplication*)application
didReceiveRemoteNotification:(NSDictionary*)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
    [_lifeCycleDelegate application:application
       didReceiveRemoteNotification:userInfo
             fetchCompletionHandler:completionHandler];
}

- (BOOL)application:(UIApplication*)application
            openURL:(NSURL*)url
            options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options {
    return [_lifeCycleDelegate application:application openURL:url options:options];
}

- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url {
    return [_lifeCycleDelegate application:application handleOpenURL:url];
}

- (BOOL)application:(UIApplication*)application
            openURL:(NSURL*)url
  sourceApplication:(NSString*)sourceApplication
         annotation:(id)annotation {
    return [_lifeCycleDelegate application:application
                                   openURL:url
                         sourceApplication:sourceApplication
                                annotation:annotation];
}

- (void)application:(UIApplication*)application
performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
  completionHandler:(void (^)(BOOL succeeded))completionHandler NS_AVAILABLE_IOS(9_0) {
    [_lifeCycleDelegate application:application
       performActionForShortcutItem:shortcutItem
                  completionHandler:completionHandler];
}

- (void)application:(UIApplication*)application
handleEventsForBackgroundURLSession:(nonnull NSString*)identifier
  completionHandler:(nonnull void (^)(void))completionHandler {
    [_lifeCycleDelegate application:application
handleEventsForBackgroundURLSession:identifier
                  completionHandler:completionHandler];
}

- (void)application:(UIApplication*)application
performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
    [_lifeCycleDelegate application:application performFetchWithCompletionHandler:completionHandler];
}

- (void)addApplicationLifeCycleDelegate:(NSObject*)delegate {
    [_lifeCycleDelegate addDelegate:delegate];
}

六、新建FlutterViewController

主要配置基本上已經完成,只要在main.dart實現Flutter的業務程式碼即可

1541496590316427.jpg

在原有工程中 ,建立FlutterViewController來承載main.dart實現的Flutter頁面,如:

  self.flutterViewController = [[FlutterViewController alloc] initWithProject:nil nibName:nil bundle:nil];
    [self.navigationController pushViewController:self.flutterViewController animated:YES];

後語

到這裡現有iOS工程引入Flutter的工作就完成了,一些細節上的修改需要根據場景進行修改,例如Flutter和Native的資料通訊等。