前言

flutter开发也不或许纯flutter开发,除非只开发一个图片文本阅读的东西,否则一旦用到原生的,那么就需求了解原生flutter的交互了

交互场景许多,例如:原项目以原生为主,要添加flutter作为部分功用,如订单模块;项目以flutter为主,但是运用到了原生的硬件相关,还没有合适的三方,需求通过原生处理

因此 flutter原生之间一同进行开发的情况不在少数,这篇文件首要介绍 flutterios的交互,android的也类似,只需大致交互逻辑理解即可

检验案例demo

ps:这儿一共介绍两种情况,一种原生为主、flutter为辅的(初始页面运用原生编写),一种flutter为主、原生为辅,假设功用差不多看自己选择了,实质差不多

其他实践运用进程中,完全以自己项目为基准,下面两种方案只是初期配备办法不相同,要是自己肯改动,其实原理都相同的(要是两个都看完信任很快了解)

module(原生为主、flutter为辅)

本方案首要选用module办法配备flutter,这种情况一般是原生为主、flutter为辅,即:flutter编写项目首要运用于某个功用模块,例如:订单、会员、个人信息、活动等,默许从原生初步主页等功用

:该办法解耦后,flutter项目和原生就分开了

其他此办法假设想工作完好项目,有必要要从 xcode 或许 android studio初步工作,flutter中直接工作的只是是flutter单个模块的功用(走的是其他一个默许原生工程,后边会介绍,是不是很像检验那套)

创建flutter module

首要创建一个 fluttermodule 模块,选择了之后,会发现跟原生编程言语不要紧,如下所示(记住项目姓名不要大写)

flutter-与ios原生交互

创建结束之后,会发现,创建的默许的app不相同,androidios的都是.开始的躲藏文件

flutter-与ios原生交互

其他,.ios躲藏文件夹的 Generated.xcconfig后边要是报错或许会需求了解他,如下所示

flutter-与ios原生交互

然后就初步配备原生了!

配备原生

创建一个 ios 原生项目,和 flutter一个目录,如下所示(也可以不一个目录,但更新path即可),

flutter-与ios原生交互

1、配备Podfile

然后编写 Podfile文件,假设没有 pod init初始化,参加内容如下所示

platform :ios, '9.0'
target 'FlutterObjcDemo' do
  use_frameworks!
  # 导入flutter相关环境
  # 途径,../表明前一个目录,./表明当时目录
  flutter_application_path = '../flutter_module/'
  # 加载环境
  load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
  install_all_flutter_pods(flutter_application_path)
  #  下面这个也可以加载环境
  # eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding)
end

修改结束后,然后 pod install 即可

2、设置 bitcode

然后 Build Settingbitcode 设置为 no

flutter-与ios原生交互

注意:到这儿就现已设置差不多了,项目也不会报错,但会有一个问题,那就是项目只能加载flutter编译好的内容,当flutter更新文件,却没有从头编译时,那么 xcode工作的会是flutter上次编译的缓存,这就很苦恼

3、脚本文件设置

1、拖拽 Generated.xcconfig文件到项目

实行脚本文件之前,我们往项目中拖拽前面提到的.iosFlutter文件夹,如下所示,不要copy

flutter-与ios原生交互

然后右键移除多余的文件引用(选择Remove References,不要移动到废纸篓)选择只留下一个,如下所示

flutter-与ios原生交互

:这个进程是自己检验出来的,看他人的没有提到相对途径无法辨认的问题

2、添加 run Script 脚本

幸亏的是,flutter早在tools中给我们供给了一个xcode_backend.sh的脚本文件,我们只需求设置好脚本文件,即可在项目工作时自动编译flutter项目

然后在 Build Phases 中添加一个脚本实行模块

flutter-与ios原生交互

然后,在下面方位添加脚本,且Run Script的方位,要排在第二位,要在xcode编译之前,先实行脚本编译flutter项目,否则工作会出现问题(注意:假设没有此脚本,flutter项目不会编译更新,只会用缓存)

flutter-与ios原生交互

具体脚本内容就如下所示($FLUTTER_ROOTflutter的环境相对目录),仿制进去就行,然后修改工作成功就配备结束了(前面往项目中拖拽Generated.xcconfig就是为了这儿,有些机器环境就是不辨认),工作失败看下面一步

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

备用:由于环境配备或许其他不知道问题,有些电脑或许仍是无法辨认 $FLUTTER_ROOT指令,因此会犯错,之前提到过Generated.xcconfig,这个文件里边有这个绝对途径信息,我们将他们仿制替换过来就行,如下所示(横竖我是相同的配备办法,一台电脑可以,而另一台不可以)

"/Users/lishuai/flutter/packages/flutter_tools/bin/xcode_backend.sh" build
"/Users/lishuai/flutter/packages/flutter_tools/bin/xcode_backend.sh" embed

两头交互

这儿先阐明一下,打开 flutter 模块,实践上就是创建了一个 FlutterViewController控制器,然后工作,假设想发动工作 Flutter模块(和默许创建的Flutter项目相同),那么直接设置 rootViewController即可,否则就像一个正常的控制器相同,进行push或许present

:虽然下面的案例也是设置rootViewController,但实践工作进程中不举荐这样运用,这样运用会影响flutterios端运用声明周期的监听,假设想监听后续会了解到

ios端交互设置

Object-c 为例,在 ios 我们需求引进头文件 Flutter.h,如下所示

#import <Flutter/Flutter.h>

然后创建 FlutterViewController,通过 FlutterMethodChannel 进行交互

//创建flutter模块寄予的 ViewController
FlutterViewController *vc = [[FlutterViewController alloc] init];
//可以直接设置成根控制器,也可以进行push或许dismiss,这儿设置成根控制器
self.window.rootViewController = vc;
//设置 methodChannel 用于和 flutter 通讯
//假设原生端组件化开发,那就每个组件创建各自的methodChannel即可,分别对应特定功用
//可以创建多个methodChannel变量,即:起不同的姓名,可以分别和flutter不同的功用进行通讯
//例如:一个订单页面通讯,一个和个人页面通讯
self.methodChannel = 
    [FlutterMethodChannel methodChannelWithName:@"mine/mine_page" binaryMessenger:vc];
//向flutter端以method发送一个消息
//第一个参数为办法名,第二个为参数,参数多的话主张json字符串,两边分别解析
[self.methodChannel invokeMethod:@"one" arguments:nil];
//最终一个参数是发送回调,假设fluter端出现了问题,可以通过回调得到回馈,否则就是成功,一般不会犯错
[self.methodChannel invokeMethod:@"one" arguments:nil result:^(id _Nullable result) {}];
//监听flutter发送过来的消息
[self.methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call,
    FlutterResult  _Nonnull result) {
    //接纳到flutter传递过来的消息
    //可以看到 call 有 method 和 arguments两个参数,分别为办法名,参数
    NSLog(@"ios回调 %@ -- %@", call.method, call.arguments);
    //假设交互进程出现了问题,可以调用 result 回调,将内容回调给 flutter端
}];

假设想在工作flutter时当即传递一个参数,则通过下面办法传递创建 FlutterViewController即可

FlutterViewController *vc = [[FlutterViewController alloc]
    initWithProject:nil initialRoute:@"one" nibName:nil bundle:nil];

flutter端交互设置

如下所示,flutter 中直接声明MethodChannel目标和ios端进行通讯(android也是一套逻辑),这儿没有先引用头文件,信任开发过的会马上理解,报错提示直接自动纠正就引进了 flutter/services 文件

//头文件
import 'package:flutter/services.dart';
//声明 MethodChannel
里边名称需求仅有化:可以功用模块姓名/功用姓名,也可以像官方提示相同com... 保证不重复即可
final MethodChannel _channel = const MethodChannel("mine/mine_page");

iosandroid端发送消息

//和ios端设置相同,第一个参数办法名,第二个为参数
//作用回来一个 Future,假设交互进程出现了问题,可以及时接纳反响作用,和原生端相同
_channel.invokeMethod("testInfo");
_channel.invokeMethod("testInfo", "我是参数");

监听iosandroid端发来的消息

//设置回调
_channel.setMethodCallHandler((MethodCall call) {
  print(call.method);
  print(call.arguments);
  print("flutter--回调作用");
  //这个参数用于反响给原生端,假设工作出现了问题,那边能通过 callback 监听到回馈
  return Future(() => null); //给一个空Future
});

这样两个端的交互问题解决了

module 单独调试

flutter项目的热更在开发中很节省时刻,上面一向用原生工作开发是不现实的,实践上需求整个项目联调的时分才需求从原生端工作

当像单独调试 fluttermodule是,很简单,就像我们工作单独的 flutter项目相同,直接点击工作即可,他会自动以默许的备用空原生项目工作,一同加载 module内容(还记住前面的 .ios躲藏文件么,那可不是一个铺排)

就这样,将 module 当成单个 app工作了,自己可以根据调试内容,设置初步的参数,这式联调的时分去掉检验数据即可

application(flutter为主、原生为辅)

我们新创建一个demo一般就是这种,一般flutter为主、原生为辅

默许创建的是一个 flutter 项目,如下所示,不多说

flutter-与ios原生交互

然后 flutter 不需求我们配备,原生端呢,其实默许也是配备好的,其他也不需求我们配备

还记住前面讲的 Flutter模块其实就是打开了一个 FlutterViewController,其实就是一个控制器,初步的时分,体系会默许将跟控制器设置成 FlutterViewController,其实和我们的 Module 类似,且 bitcode脚本都设置结束了,和 module根柢类似,

:项目在xcode工作和flutter中工作作用一模相同

不信你看这个,刚出创建的项目默许加载 main.storyboard,其中跟控制器就是 FlutterViewController

flutter-与ios原生交互

是不是感觉,天哪,前面我们做了那么多,干嘛这么费力,没事,仍是有区其他,我们先看看作用,后边比照介绍

如下所示,会发现,他默许继承了 FlutterAppDelegate,这个往常我们也用不到,modules可以不用继承,默许的项目继承我们也不用管,改回来也没事

//FlutterAppDelegate的介绍阐明,一般用于检验阶段,例如:modules根柢不需求,可以根据自己需求重写回调
/**
 * `UIApplicationDelegate` subclass for simple apps that want default behavior.
 *
 * This class implements the following behaviors:
 *   * Status bar touches are forwarded to the key window's root view
 *     `FlutterViewController`, in order to trigger scroll to top.
 *   * Keeps the Flutter connection open in debug mode when the phone screen
 *     locks.
 *
 * App delegates for Flutter applications are *not* required to inherit from
 * this class. Developers of custom app delegate classes should copy and paste
 * code as necessary from FlutterAppDelegate.mm.
 */
@interface AppDelegate : FlutterAppDelegate
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
      [GeneratedPluginRegistrant registerWithRegistry:self];
      [self setupFlutter];
      // Override point for customization after application launch.
      return [super application:application 
          didFinishLaunchingWithOptions:launchOptions];
}

flutter和ios交互

交互不多说了,和ios相同,可以抽离出来,就是由于以 flutter为主,导致现在只能写到 AppDelegate中了,可以一致放到一同整理起来

- (void)setupFlutter {
    UIViewController *vc = [UIApplication sharedApplication].delegate.window.rootViewController;
    _methodChannel =  [FlutterMethodChannel methodChannelWithName:@"one_page" binaryMessenger:(FlutterViewController *)vc];
    [_methodChannel invokeMethod:@"one" arguments:nil];
    //监听退出
    [_methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
        NSLog(@"ios回调 %@ -- %@", call.method, call.arguments);
    }];
}

两种办法差异,以及举荐场景

差异

通过 module创建的项目:

flutter的模块可以单独工作,不受原生端代码的影响,相比照较独立,当然要通过 flutter端工作(一般在 android studio),需求整体工作的时分,通过原生端发动即可;

特征flutter端调试便当,减少了原生项目的编译时刻,仍然能热更,减少了原生项目的影响,但检验简单遗失与原生端的沟通问题

通过 application创建的项目:

flutter模块不可以单独工作,项目工作的时分会自动先编译flutter在编译原生工作,因此每次都要编译两个端,每次都是工作整个项目;

特征:调试进程中总是针对整体代码,但flutter仍然可以热更,检验进程中不会遗失功用,就是每次从头发动都要从头编译整个项目,简单受到原生项目的过错影响

举荐场景

module比较适用于老到项目,原生端和 flutter 分别明显,希望他们之间最小影响,更适用于现已用原生开发的项目,后续直接接入module,开发进程对原生影响也不大

application比较合适小公司项目或许首要运用 flutter编写的项目,他们之间耦合略严峻,但也不是不能分别

可以根据当时场景抉择,当然这只是一部分考量,实践中心偏移也没问题,根据自己项目来选择更快的办法

最终

本篇文章可以作为一个参考,初步你的flutter之旅吧