前言
flutter开发也不或许纯flutter开发,除非只开发一个图片文本阅读的东西,否则一旦用到原生的,那么就需求了解原生与 flutter的交互了
交互场景许多,例如:原项目以原生为主,要添加flutter作为部分功用,如订单模块;项目以flutter为主,但是运用到了原生的硬件相关,还没有合适的三方,需求通过原生处理
因此 flutter 与原生之间一同进行开发的情况不在少数,这篇文件首要介绍 flutter与ios的交互,android的也类似,只需大致交互逻辑理解即可
检验案例demo
ps:这儿一共介绍两种情况,一种原生为主、flutter为辅的(初始页面运用原生编写),一种flutter为主、原生为辅,假设功用差不多看自己选择了,实质差不多
其他实践运用进程中,完全以自己项目为基准,下面两种方案只是初期配备办法不相同,要是自己肯改动,其实原理都相同的(要是两个都看完信任很快了解)
module(原生为主、flutter为辅)
本方案首要选用module办法配备flutter,这种情况一般是原生为主、flutter为辅,即:flutter编写项目首要运用于某个功用模块,例如:订单、会员、个人信息、活动等,默许从原生初步主页等功用
注:该办法解耦后,flutter项目和原生就分开了
其他此办法假设想工作完好项目,有必要要从 xcode 或许 android studio初步工作,flutter中直接工作的只是是flutter单个模块的功用(走的是其他一个默许原生工程,后边会介绍,是不是很像检验那套)
创建flutter module
首要创建一个 flutter 的 module 模块,选择了之后,会发现跟原生编程言语不要紧,如下所示(记住项目姓名不要大写)
创建结束之后,会发现,创建的默许的app不相同,android和ios的都是.开始的躲藏文件
其他,.ios躲藏文件夹的 Generated.xcconfig后边要是报错或许会需求了解他,如下所示
然后就初步配备原生了!
配备原生
创建一个 ios 原生项目,和 flutter一个目录,如下所示(也可以不一个目录,但更新path即可),
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 Setting 中 bitcode 设置为 no
注意:到这儿就现已设置差不多了,项目也不会报错,但会有一个问题,那就是项目只能加载flutter编译好的内容,当flutter更新文件,却没有从头编译时,那么 xcode工作的会是flutter上次编译的缓存,这就很苦恼
3、脚本文件设置
1、拖拽 Generated.xcconfig文件到项目
实行脚本文件之前,我们往项目中拖拽前面提到的.ios的Flutter文件夹,如下所示,不要copy
然后右键移除多余的文件引用(选择Remove References,不要移动到废纸篓)选择只留下一个,如下所示
注:这个进程是自己检验出来的,看他人的没有提到相对途径无法辨认的问题
2、添加 run Script 脚本
幸亏的是,flutter早在tools中给我们供给了一个xcode_backend.sh的脚本文件,我们只需求设置好脚本文件,即可在项目工作时自动编译flutter项目
然后在 Build Phases 中添加一个脚本实行模块
然后,在下面方位添加脚本,且Run Script的方位,要排在第二位,要在xcode编译之前,先实行脚本编译flutter项目,否则工作会出现问题(注意:假设没有此脚本,flutter项目不会编译更新,只会用缓存)
具体脚本内容就如下所示($FLUTTER_ROOT为flutter的环境相对目录),仿制进去就行,然后修改工作成功就配备结束了(前面往项目中拖拽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,但实践工作进程中不举荐这样运用,这样运用会影响flutter对ios端运用声明周期的监听,假设想监听后续会了解到
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");
向ios和android端发送消息
//和ios端设置相同,第一个参数办法名,第二个为参数
//作用回来一个 Future,假设交互进程出现了问题,可以及时接纳反响作用,和原生端相同
_channel.invokeMethod("testInfo");
_channel.invokeMethod("testInfo", "我是参数");
监听ios和android端发来的消息
//设置回调
_channel.setMethodCallHandler((MethodCall call) {
print(call.method);
print(call.arguments);
print("flutter--回调作用");
//这个参数用于反响给原生端,假设工作出现了问题,那边能通过 callback 监听到回馈
return Future(() => null); //给一个空Future
});
这样两个端的交互问题解决了
module 单独调试
flutter项目的热更在开发中很节省时刻,上面一向用原生工作开发是不现实的,实践上需求整个项目联调的时分才需求从原生端工作
当像单独调试 flutter 的 module是,很简单,就像我们工作单独的 flutter项目相同,直接点击工作即可,他会自动以默许的备用空原生项目工作,一同加载 module内容(还记住前面的 .ios躲藏文件么,那可不是一个铺排)
就这样,将 module 当成单个 app工作了,自己可以根据调试内容,设置初步的参数,这式联调的时分去掉检验数据即可
application(flutter为主、原生为辅)
我们新创建一个demo一般就是这种,一般flutter为主、原生为辅
默许创建的是一个 flutter 项目,如下所示,不多说
然后 flutter 不需求我们配备,原生端呢,其实默许也是配备好的,其他也不需求我们配备
还记住前面讲的 Flutter模块其实就是打开了一个 FlutterViewController,其实就是一个控制器,初步的时分,体系会默许将跟控制器设置成 FlutterViewController,其实和我们的 Module 类似,且 bitcode和脚本都设置结束了,和 module根柢类似,
注:项目在xcode工作和flutter中工作作用一模相同
不信你看这个,刚出创建的项目默许加载 main.storyboard,其中跟控制器就是 FlutterViewController
是不是感觉,天哪,前面我们做了那么多,干嘛这么费力,没事,仍是有区其他,我们先看看作用,后边比照介绍
如下所示,会发现,他默许继承了 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之旅吧











