iOS老項目通過Cocoapods集成Flutter(iOS混編Flutter)

1待诅、Flutter環(huán)境配置 Flutter中文網(wǎng) 跟著里面一步一步來就完事了介袜。

2偎漫、iOS工程Enable Bitcode 需要關閉再登,因為Flutter混合開發(fā)目前還不支持Bitcode

3、創(chuàng)建flutter module

FlutterMixDemo/BaseFramework/  (BaseFramework 是我的 iOS 工程項目)
進入在 FlutterMixDemo 目錄下付秕,終端執(zhí)行命令:
flutter create -t module flutter_module 

flutter_module是自己起的名字兰珍,記得字母都要小寫,不然會報錯盹牧。

這里也有Flutter官方網(wǎng)站英文文檔 → iOS接入Flutter教程

4俩垃、添加以下代碼到Podfile:(沒有Podfile怎么辦?終端先cd到BaseFramework項目里汰寓,執(zhí)行pod init)

platform :ios, '9.0'

target 'BaseFramework' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  #需要添加的代碼  flutter_module是自己創(chuàng)建的名字
  flutter_application_path = '../flutter_module'
  load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
  install_all_flutter_pods(flutter_application_path)

end

執(zhí)行pod install口柳,如果有報錯,根據(jù)錯誤提示一個個解決有滑,具體哪些報錯不懂的話就谷歌百度吧跃闹。。。

注意:當你在flutter_module/pubspec.yaml中有改變了Flutter插件的依賴關系時(不管有沒修改啥望艺,只要按了command+s保存后)苛秕,一定要在BaseFramework(自己的項目)里再次運行pod install。

5找默、在iOS應用里使用 FlutterViewController

創(chuàng)建FlutterEngine

AppDelegate.h

@import UIKit;
@import Flutter;

@interface AppDelegate : FlutterAppDelegate
@property (nonatomic,strong) FlutterEngine *flutterEngine;
@end
AppDelegate.m

#import <FlutterPluginRegistrant/GeneratedPluginRegistrant.h> // Used to connect plugins.

#import "AppDelegate.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions {
  self.flutterEngine = [[FlutterEngine alloc] initWithName:@"my flutter engine"];
  // Runs the default Dart entrypoint with a default Flutter route.
  [self.flutterEngine run];
  [GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

@end

用FlutterEngine顯示一個FlutterViewController艇劫,簡單示例:在入口文件里創(chuàng)建一個button用來點擊彈出

ViewController.m

@import Flutter;
#import "AppDelegate.h"
#import "ViewController.h"

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];

    //創(chuàng)建一個button用來點擊彈出FlutterViewController
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    [button addTarget:self
               action:@selector(showFlutter)
     forControlEvents:UIControlEventTouchUpInside];
    [button setTitle:@"Show Flutter!" forState:UIControlStateNormal];
    button.backgroundColor = UIColor.blueColor;
    button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);
    [self.view addSubview:button];
}

- (void)showFlutter {
    FlutterEngine *flutterEngine =
        ((AppDelegate *)UIApplication.sharedApplication.delegate).flutterEngine;
    FlutterViewController *flutterViewController =
        [[FlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil];
    [self presentViewController:flutterViewController animated:YES completion:nil];
}
@end

當然也可以使用隱式FlutterEngine創(chuàng)建一個FlutterViewController。這里我用了個延遲0.5秒自動彈出FlutterViewController惩激,F(xiàn)lutterEngine創(chuàng)建也是需要時間店煞,如果FlutterEngine還沒創(chuàng)建好就彈出FlutterViewController會失敗。如果想一啟動app就立馬彈出Flutter风钻,就要考慮這個問題了顷蟀。

- (void)viewDidLoad {
    [super viewDidLoad];

    [self showFlutter2];
}

- (void)showFlutter2 {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        FlutterViewController *flutterViewController =
            [[FlutterViewController alloc] initWithProject:nil nibName:nil bundle:nil];
        flutterViewController.modalPresentationStyle = UIModalPresentationFullScreen;
        [self presentViewController:flutterViewController animated:NO completion:nil];
    });

}

FlutterAppDelegate可以實現(xiàn)如下功能,比如:

將應用程序回調(diào)(如openURL)轉發(fā)到插件(如local_auth)骡技。
轉發(fā)狀態(tài)欄點擊(只能在AppDelegate中檢測到)讓Flutter以實現(xiàn)滾動到頂部鸣个。

使用FlutterAppDelegate,建議使用UIApplicationDelegate子類FlutterAppDelegate布朦,但不是必需的囤萤。
如果你的AppDelegate不能直接使FlutterAppDelegate成為子類,那AppDelegate需要實現(xiàn)FlutterAppLifeCycleProvider協(xié)議是趴,以確保插件收到必要的回調(diào)阁将。否則,依賴于這些事件的插件可能會有未定義的行為右遭。代碼↓

代碼是從Flutter官網(wǎng)直接拷過來的,親測沒任何毛病缤削。

AppDelegate.h

@import Flutter;
@import UIKit;
@import FlutterPluginRegistrant;

@interface AppDelegate : UIResponder <UIApplicationDelegate, FlutterAppLifeCycleProvider> 
@property (strong, nonatomic) UIWindow *window;
@property (nonatomic,strong) FlutterEngine *flutterEngine;
@end
AppDelegate.m

@interface AppDelegate ()
@property (nonatomic, strong) FlutterPluginAppLifeCycleDelegate* lifeCycleDelegate;
@end

@implementation AppDelegate

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

- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id>*))launchOptions {
    self.flutterEngine = [[FlutterEngine alloc] initWithName:@"io.flutter" project:nil];
    [self.flutterEngine runWithEntrypoint:nil];
    [GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];
    return [_lifeCycleDelegate application:application didFinishLaunchingWithOptions:launchOptions];
}

// Returns the key window's rootViewController, if it's a FlutterViewController.
// Otherwise, returns nil.
- (FlutterViewController*)rootFlutterViewController {
    UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController;
    if ([viewController isKindOfClass:[FlutterViewController class]]) {
        return (FlutterViewController*)viewController;
    }
    return nil;
}

- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
    [super touchesBegan:touches withEvent:event];

    // Pass status bar taps to key window Flutter rootViewController.
    if (self.rootFlutterViewController != nil) {
        [self.rootFlutterViewController handleStatusBarTouches:event];
    }
}

- (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<FlutterPlugin>*)delegate {
    [_lifeCycleDelegate addDelegate:delegate];
}
@end

6窘哈、當你需要從彈出的Flutter界面返回iOS界面,需要在Flutter的文件里實現(xiàn)SystemNavigator.pop()這個方法亭敢。比如在Flutter里定義一個按鈕滚婉,實現(xiàn)其方法 ↓,注意:需要引入 services.dart 模塊才可以使用

import 'package:flutter/services.dart';
RaisedButton(
  onPressed: (){
    SystemNavigator.pop(animated: true);
  },
  child: Text('返回'),
),

注意:當你的根視圖控制器是UINavigationController時帅刀,SystemNavigator.pop()調(diào)用的是' popViewControllerAnimated: '让腹。如果是通過presentViewController:FlutterViewController,那么SystemNavigator.pop()調(diào)用的是' dismissViewControllerAnimated:completion: '扣溺。

Flutter里還有個exit(0)方法骇窍,是強制退出app,使用這個方法要先引入import 'dart:io'和上面的services.dart;

7锥余、Dart里的入口函數(shù)默認是調(diào)用 main()腹纳,入口文件:``lib/main.dart:

你也可以修改Dart的入口函數(shù)并在iOS里調(diào)用,例如:↓,就把入口函數(shù)修改為myOtherEntrypoint()嘲恍, 入口文件:lib/other_file.dart

[flutterEngine runWithEntrypoint:@"myOtherEntrypoint" libraryURI:@"other_file.dart"];

參考:

https://flutter.dev/docs/development/add-to-app/ios/project-setup

https://flutter.dev/docs/development/add-to-app/ios/add-flutter-screen

?

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末足画,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子佃牛,更是在濱河造成了極大的恐慌淹辞,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件俘侠,死亡現(xiàn)場離奇詭異象缀,居然都是意外死亡,警方通過查閱死者的電腦和手機兼贡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門攻冷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人遍希,你說我怎么就攤上這事等曼。” “怎么了凿蒜?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵禁谦,是天一觀的道長。 經(jīng)常有香客問我废封,道長州泊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任漂洋,我火速辦了婚禮遥皂,結果婚禮上,老公的妹妹穿的比我還像新娘刽漂。我一直安慰自己演训,他們只是感情好,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布贝咙。 她就那樣靜靜地躺著样悟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪庭猩。 梳的紋絲不亂的頭發(fā)上窟她,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機與錄音蔼水,去河邊找鬼震糖。 笑死,一個胖子當著我的面吹牛趴腋,可吹牛的內(nèi)容都是我干的试伙。 我是一名探鬼主播嘁信,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼疏叨!你這毒婦竟也來了潘靖?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蚤蔓,失蹤者是張志新(化名)和其女友劉穎卦溢,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秀又,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡单寂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了吐辙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宣决。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖昏苏,靈堂內(nèi)的尸體忽然破棺而出尊沸,到底是詐尸還是另有隱情,我是刑警寧澤贤惯,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布洼专,位于F島的核電站,受9級特大地震影響孵构,放射性物質(zhì)發(fā)生泄漏屁商。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一颈墅、第九天 我趴在偏房一處隱蔽的房頂上張望蜡镶。 院中可真熱鬧,春花似錦恤筛、人聲如沸帽哑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至僻族,卻和暖如春粘驰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背述么。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工蝌数, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人度秘。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓顶伞,卻偏偏與公主長得像饵撑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子唆貌,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容