V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
winlee28
V2EX  ›  iOS

Flutter 混合开发(二): iOS 项目集成 Flutter 模块详细指南

  •  
  •   winlee28 · 2019-12-13 16:08:37 +08:00 · 3178 次点击
    这是一个创建于 1842 天前的主题,其中的信息可能已经有所发展或是发生改变。

    为防止文章中的图片链接失效或者显示失败,这里贴下原文地址

    Flutter 混合开发(二):iOS 项目集成 Flutter 模块详细指南

    本文首发于微信公众号「 Android 开发之旅」,欢迎关注 ,获取更多技术干货

    前言

    前一篇文章讲解了 Android 原生工程如何集成 Flutter 项目的具体过程,Flutter 混合开发(一):Android 项目集成 Flutter 模块详细指南 ,本篇将带着大家来一起学习原生 iOS 项目如何集成 Flutter。

    因为每个版本的集成逻辑都是有差别的,所以这里交代下本篇文章的集成版本:

    Flutter ( channel dev,v1.10.16 )
    dart ( v2.7.0 )
    Xcode ( v11.2 )
    cocoapds ( 1.8.4 )
    

    创建 Flutter module

    假如 iOS 项目的路径是这样的:flutter/flutter_hybrid/iOS Project,那么我们需要在 iOS Project 上一层目录 flutter_hybrid 中创建 Flutter module。

    cd flutter/flutter_hybrid/
    
    flutter create -t module flutter_module
    

    输入后控制台打印如下:

    $ flutter create -t module flutter_module
    Creating project flutter_module...
      flutter_module/test/widget_test.dart (created)
      flutter_module/flutter_module.iml (created)
      flutter_module/.gitignore (created)
      flutter_module/.metadata (created)
      flutter_module/pubspec.yaml (created)
      flutter_module/README.md (created)
      flutter_module/lib/main.dart (created)
      flutter_module/flutter_module_android.iml (created)
      flutter_module/.idea/libraries/Flutter_for_Android.xml (created)
      flutter_module/.idea/libraries/Dart_SDK.xml (created)
      flutter_module/.idea/modules.xml (created)
      flutter_module/.idea/workspace.xml (created)
    Running "flutter pub get" in flutter_module...                      1.2s
    Wrote 12 files.
    
    All done!
    Your module code is in flutter_module/lib/main.dart.
    

    看到 All done 就表示我们项目创建好了。整个 module 目录和原生 Flutter 基本一样,主要就是 Android、iOS 的宿主工程和 lib 目录以及 pubspec.yaml 文件。

    #添加 Flutter module 依赖

    为 iOS 项目添加依赖需要使用 CocoaPods,如果你还没有用到 CocoaPods,可以参考https://cocoapods.org/上面的说明来安装 CocoaPods。

    如果你的项目之前没有使用过 cocoapods,那么需要进行初始化生成 podfile 文件,进入 iOS 项目的根目录执行:

    pod init
    

    然后打开 podfile 文件,进行配置:

    # 配置
    flutter_application_path = '../flutter_module/'
    load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
    
    target 'iOSFlutterHybrid' do
      # Comment the next line if you don't want to use dynamic frameworks
      use_frameworks!
    
      # Pods for iOSFlutterHybrid
      # 配置
      install_all_flutter_pods(flutter_application_path)
    
      target 'iOSFlutterHybridTests' do
        inherit! :search_paths
        # Pods for testing
      end
    
      target 'iOSFlutterHybridUITests' do
        # Pods for testing
      end
    
    

    配置添加好后在项目根目录运行以下命令进行安装:

    pod install
    

    控制台输出:

    Analyzing dependencies
    Downloading dependencies
    Installing Flutter (1.0.0)
    Installing FlutterPluginRegistrant (0.0.1)
    Installing flutter_module (0.0.1)
    Generating Pods project
    Integrating client project
    
    [!] Please close any current Xcode sessions and use `iOSFlutterHybrid.xcworkspace` for this project from now on.
    Pod installation complete! There are 3 dependencies from the Podfile and 3 total pods installed.
    
    [!] Automatically assigning platform `iOS` with version `13.2` on target `iOSFlutterHybrid` because no platform was specified. Please specify a platform for this target in your Podfile. See `https://guides.cocoapods.org/syntax/podfile.html#platform`.
    
    

    这里我们看到有三个依赖安装完成了。并提醒我们关闭当前项目,在根目录下面使用 iOSFlutterHybrid.xcworkspace 来打开运行项目。这里可能很多人在执行命令的时候会发现提示 0 个依赖完成。这里有可能是你的 Xcode 版本的问题。因为 Flutter 要求最低版本是 10.2 及以上。

    当在 flutter_module/pubspec.yaml 中添加一个 Flutter 插件时,需要在 flutter_module 目录下运行:

    flutter packages get
    

    来刷新 podhelper.rb 脚本中的插件列表,然后在 iOS 目录下运行:

    pod install
    

    这样 podhelper.rb 脚本才能确保添加的插件和 Flutter.framework 能够添加到 iOS 项目中。

    目前 Flutter 还不支持 Bitcode,所以集成了 Flutter 的 iOS 项目需要禁用 Bitcode。

    在以下路径下找到 Bitcode 并禁用:

    Build Settings->Build Options->Enable Bitcode 
    

    flutter 以前的版本是需要添加 build phase 以构建 Dart 代码,但是最新的版本已经不需要添加了,可以自动构建。

    调用 Flutter module

    Flutter 为我们提供了两种调用方式:FlutterViewController 和 FlutterEngine,FlutterEngine 在使用的时候会有一些问题,将在下文进行说明。

    FlutterViewController 方式:

    我们打开 ViewController.m 文件,在里面添加一个加载 flutter 页面的方法并且添加一个按钮看来调用:

    
    #import "ViewController.h"
    #import <Flutter/Flutter.h>
    #import "AppDelegate.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
         
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        [button addTarget:self action:@selector(handleButtonAction) forControlEvents:UIControlEventTouchUpInside];
        
        [button setTitle:@"加载 Flutter" forState:UIControlStateNormal];
        [button setBackgroundColor:[UIColor blueColor]];
        button.frame = CGRectMake(100, 100, 160, 60);
        [self.view addSubview:button];
    }
    
    - (void)handleButtonAction{
    	
        FlutterViewController *flutterViewController =[FlutterViewController new];
        //设置路由参数
        [flutterViewController setInitialRoute:@"route2"];
        [self presentViewController:flutterViewController animated:false completion:nil];
         
    }
    
    @end
    

    当我们运行项目点击加载 Flutetr 按钮时,将会调用 Flutter 页面。和 Android 项目集成一样,这里的 setInitialRoute 可以设置一个 json 数组来传递需要交互的参数。并在 Flutter 中使用 window.defaultRouteName 来获取传递的参数。

    FlutterEngine 方式:

    我们需要在 AppDelegate 中对 FlutterEngine 进行初始化。打开 AppDelegate.h 文件:

    #import <UIKit/UIKit.h>
    #import <Flutter/Flutter.h>
    
    @interface AppDelegate : FlutterAppDelegate
    @property (nonatomic,strong) FlutterEngine *flutterEngine;
    @end
    

    在打开 AppDelegate.m 文件:

    // 如果你需要用到 Flutter 插件时
    #import <FlutterPluginRegistrant/GeneratedPluginRegistrant.h> 
    #include "AppDelegate.h"
    
    @implementation AppDelegate
    
    
    - (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
      self.flutterEngine = [[FlutterEngine alloc] initWithName:@"io.flutter" project:nil];
      [self.flutterEngine runWithEntrypoint:nil];
      [GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine]; //如果你需要用到 Flutter 插件时
      return [super application:application didFinishLaunchingWithOptions:launchOptions];
    }
    @end
    

    然后在 ViewController.m 文件定义的 handleButtonAction 中调用:

    - (void)handleButtonAction{
       
        FlutterEngine *flutterEngine = [(AppDelegate *)[[UIApplication sharedApplication] delegate] flutterEngine];
        FlutterViewController *flutterViewController = [[FlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil];
        [flutterViewController setInitialRoute:@"route2"];
        [self presentViewController:flutterViewController animated:false completion:nil];
         
    }
    

    当我们运行项目点击加载 Flutter 按钮时,将会调用 Flutter 页面。前文讲到使用 FlutterEngine 会有问题,就是我们 setInitialRoute 传递的参数在 flutter 中永远获取到的都是 “/” ,这个是 Fltter SDK 的一个 Bug,所以如果必须依赖 setInitialRoute,还是使用 FlutterViewController 的形式来加载 Flutter 模块。

    热重启 /重新加载

    大家在写纯 Flutter 应用的时候,知道是有热重启 /重新加载功能的,但是在做混合开发的过程中,你会发现热重启 /重新加载功能失效了。那么如何在混合开发中开启热重启 /重新加载功能呢?

    • 首先接入我们的设备或者模拟器
    • 将我们的 App 关闭,退出后台,在 terminal 中运行 flutter attach 命令
    $ flutter attach
    Waiting for a connection from Flutter on Android SDK built for x86...
    

    复制代码此时就在等待设备的连接。这里要注意的是,如果电脑连接了多台设备需要使用 -d 命令来指定一台设备,参数为设备的 id。

    flutter attach -d '你的设备 id'
    

    然后启动我们的应用会看到控制台输出:

    Done.
    Syncing files to device Android SDK built for x86...             1,393ms
    
    🔥  To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".
    An Observatory debugger and profiler on Android SDK built for x86 is available at: http://127.0.0.1:59354/zRsDBfpesrk=/
    For a more detailed help message, press "h". To detach, press "d"; to quit, press "q".
    

    这样就表示我们连接成功了。在输出的日志中也告诉了我们如何使用热重启 /重新加载功能。

    在 Terminal 中输入以下命令​:​

    r : 热加载;
    R : 热重启;
    h : 获取帮助;
    d : 断开连接;
    q : 退出;
    

    这里的的 d 和 q 的命令都有退出调试,区别在于 d 命令只是单纯的断开而 q 命令会将应用退到后台。

    调试 Dart 代码

    同样在混合开发过程中我们如何调试 dart 代码呢?

    • 关闭我们的应用
    • 点击 Android Studio 工具栏上的 Flutter Attach 按钮(需要安装 Flutter 与 Dart 插件)

    • 启动我们的应用

    接下来就可以像调试普通 Flutter 项目一样来调试混合开发模式下的 Dart 代码了。

    总结

    本文主要是讲解了 iOS 集成 Flutter 项目的步骤,其中也遇到了一些问题,由于我的 Xcode 版本较低,在集成的过程中 iOS 项目的依赖一直失败。最后才发现是 Xcode 的版本问题。这里花费了很多时间去排查问题,所以大家在集成的过程中有问题可以关注公众号加我微信,给我留言,我会帮助大家解决问题。

    全部 Demo 源码已经上传到后台,关注公众号回复「混合开发」即可获得下载链接。

    如果你觉得文章还不错,请大家点赞分享下,你的肯定是对我最大的鼓励和支持。

    扫描下方二维码关注公众号,获取更多技术干货。

    1 条回复    2019-12-13 16:27:18 +08:00
    so898
        1
    so898  
       2019-12-13 16:27:18 +08:00
    iOS 上的 Flutter 混合开发不叫 iOS 项目集成 Flutter 模块,是 Flutter 项目添加 iOS 项目代码,集成 iOS 项目
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2599 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 06:46 · PVG 14:46 · LAX 22:46 · JFK 01:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.