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

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

2、iOS工程Enable Bitcode 需要關(guān)閉,因?yàn)镕lutter混合開發(fā)目前還不支持Bitcode

3、創(chuàng)建flutter module

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

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

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

4、添加以下代碼到Podfile:(沒有Podfile怎么辦?終端先cd到BaseFramework項(xiàng)目里,執(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ù)錯誤提示一個個解決,具體哪些報錯不懂的話就谷歌百度吧。。。

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

5、在iOS應(yīng)用里使用 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用來點(diǎn)擊彈出

ViewController.m

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

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

    //創(chuàng)建一個button用來點(diǎn)擊彈出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

當(dāng)然也可以使用隱式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可以實(shí)現(xiàn)如下功能,比如:

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

使用FlutterAppDelegate,建議使用UIApplicationDelegate子類FlutterAppDelegate,但不是必需的。
如果你的AppDelegate不能直接使FlutterAppDelegate成為子類,那AppDelegate需要實(shí)現(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、當(dāng)你需要從彈出的Flutter界面返回iOS界面,需要在Flutter的文件里實(shí)現(xiàn)SystemNavigator.pop()這個方法。比如在Flutter里定義一個按鈕,實(shí)現(xiàn)其方法 ↓,注意:需要引入 services.dart 模塊才可以使用

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

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

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

7、Dart里的入口函數(shù)默認(rèn)是調(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

?

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

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

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