美文网首页
iOS老项目通过Cocoapods集成Flutter(iOS混编

iOS老项目通过Cocoapods集成Flutter(iOS混编

作者: 汣远 | 来源:发表于2020-03-03 15:07 被阅读0次

    1、Flutter环境配置 Flutter中文网 跟着里面一步一步来就完事了。

    2、iOS工程Enable Bitcode 需要关闭,因为Flutter混合开发目前还不支持Bitcode

    3、创建flutter module

    FlutterMixDemo/BaseFramework/  (BaseFramework 是我的 iOS 工程项目)
    进入在 FlutterMixDemo 目录下,终端执行命令:
    flutter create -t module flutter_module 
    

    flutter_module是自己起的名字,记得字母都要小写,不然会报错。

    这里也有Flutter官方网站英文文档 → iOS接入Flutter教程

    4、添加以下代码到Podfile:(没有Podfile怎么办?终端先cd到BaseFramework项目里,执行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是自己创建的名字
      flutter_application_path = '../flutter_module'
      load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
      install_all_flutter_pods(flutter_application_path)
    
    end
    

    执行pod install,如果有报错,根据错误提示一个个解决,具体哪些报错不懂的话就谷歌百度吧。。。

    注意:当你在flutter_module/pubspec.yaml中有改变了Flutter插件的依赖关系时(不管有没修改啥,只要按了command+s保存后),一定要在BaseFramework(自己的项目)里再次运行pod install。

    5、在iOS应用里使用 FlutterViewController

    创建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,简单示例:在入口文件里创建一个button用来点击弹出

    ViewController.m
    
    @import Flutter;
    #import "AppDelegate.h"
    #import "ViewController.h"
    
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        //创建一个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创建一个FlutterViewController。这里我用了个延迟0.5秒自动弹出FlutterViewController,FlutterEngine创建也是需要时间,如果FlutterEngine还没创建好就弹出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可以实现如下功能,比如:

    将应用程序回调(如openURL)转发到插件(如local_auth)。
    转发状态栏点击(只能在AppDelegate中检测到)让Flutter以实现滚动到顶部。

    使用FlutterAppDelegate,建议使用UIApplicationDelegate子类FlutterAppDelegate,但不是必需的。
    如果你的AppDelegate不能直接使FlutterAppDelegate成为子类,那AppDelegate需要实现FlutterAppLifeCycleProvider协议,以确保插件收到必要的回调。否则,依赖于这些事件的插件可能会有未定义的行为。代码↓

    代码是从Flutter官网直接拷过来的,亲测没任何毛病。

    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的文件里实现SystemNavigator.pop()这个方法。比如在Flutter里定义一个按钮,实现其方法 ↓,注意:需要引入 services.dart 模块才可以使用

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

    注意:当你的根视图控制器是UINavigationController时,SystemNavigator.pop()调用的是' popViewControllerAnimated: '。如果是通过presentViewController:FlutterViewController,那么SystemNavigator.pop()调用的是' dismissViewControllerAnimated:completion: '。

    Flutter里还有个exit(0)方法,是强制退出app,使用这个方法要先引入import 'dart:io'和上面的services.dart;

    7、Dart里的入口函数默认是调用 main(),入口文件:``lib/main.dart:

    你也可以修改Dart的入口函数并在iOS里调用,例如:↓,就把入口函数修改为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

    相关文章

      网友评论

          本文标题:iOS老项目通过Cocoapods集成Flutter(iOS混编

          本文链接:https://www.haomeiwen.com/subject/epqhlhtx.html