flutter原生插件开发--IOS端
简介
做Flutter开发肯定会需要自定义插件,下面我把自己开发的插件的Demo分享出来,希望可以对新手小白有点帮助。
准备工作
需要一台Mac设备,安装Android Stodio、Xcode,及搭建Flutter、CocoPods环境
切入主题
IOS 插件 -- OC篇
首先打开Android Stodio,创建Flutter Project项目 选择Flutter Plugin
一路Next,这里选中OCk开发
这里需要打开你的项目目录.以我的目录结构为例
新创建的iOS代码是不包含 pod文件夹的,需要确保插件代码通过example工程被构建过一次(这很重要),可以在终端执行以下命令进行构建安装需要的依赖包
//首先执行 (这里为以上目录路径)cd flutter_plugin_demo/example/ios //然后执行(这里默认已经安装好Xcode和CocoPods)pod install
Dart区代码
这是项目中初始代码
现在我们对他进行一下简化修改
/// NOTE:通过字符串test_plugin_demo约定MethodChannel
/// NOTE:这里主要负责和原生进行交互,调用原生的方法和监听原生方法,
class TestPluginDemo {MethodChannel _channel;TestPluginDemo.init(int id){_channel = new MethodChannel('test_plugin_demo');}
}
下面我们来创建一个Dart类显示加载原生的视图
///我是使用的 StatefulWidget 使用StatelessWidget 也是一样
class TestView extends StatefulWidget {///根据自己的需求创建初始化参数final TestViewCreatedCallback onCreated; ///是上面创建的回调final String titleStr;TestView({Key key,this.onCreated,this.titleStr,});@override_TestViewState createState() => _TestViewState();
}class _TestViewState extends State<TestView> {@overrideWidget build(BuildContext context) {return Container(child: _loadNativeView(),);}///加载原生视图Widget _loadNativeView(){///根据不同的平台显示相应的视图if(Platform.isAndroid){ ///加载安卓原生视图return AndroidView(viewType: 'testView',///视图标识符 要和原生 保持一致 要不然加载不到视图onPlatformViewCreated:onPlatformViewCreated,///原生视图创建成功的回调creationParams: <String, dynamic>{ ///给原生传递初始化参数 就是上面定义的初始化参数'titleStr':widget.titleStr,},/// 用来编码 creationParams 的形式,可选 [StandardMessageCodec], [JSONMessageCodec], [StringCodec], or [BinaryCodec]/// 如果存在 creationParams,则该值不能为nullcreationParamsCodec: const StandardMessageCodec(),);}else if(Platform.isIOS){///加载iOS原生视图return UiKitView(viewType: 'testView',///视图标识符 要和原生 保持一致 要不然加载不到视图onPlatformViewCreated:onPlatformViewCreated,///原生视图创建成功的回调creationParams: <String, dynamic>{ ///给原生传递初始化参数 就是上面定义的初始化参数'titleStr':widget.titleStr,},/// 用来编码 creationParams 的形式,可选 [StandardMessageCodec], [JSONMessageCodec], [StringCodec], or [BinaryCodec]/// 如果存在 creationParams,则该值不能为nullcreationParamsCodec: const StandardMessageCodec(),);}else{return Text('这个平台老子不支持');}}///我也是刚学Flutter 所以我理解的:这个基本上是固定写法Future<void> onPlatformViewCreated(id) async {if (widget.onCreated == null) {return;}widget.onCreated(new TestFlutterPluginDemo.init(id));}
}
然后在main.dart里面加载新增的页面
class MyApp extends StatefulWidget {@override_MyAppState createState() => _MyAppState();
}class _MyAppState extends State<MyApp> {///定义一个测试类的属性 用来调用原生方法 和原生交互var testPluginDemo;@overridevoid initState() {super.initState();}@overrideWidget build(BuildContext context) {///初始化 测试视图的类TestView testView = new TestView(onCreated: onTestViewCreated,);return MaterialApp(home: Scaffold(appBar: AppBar(title: const Text('flutter原生调用'),),body: Column(children: <Widget>[Container(height: 400,width: 400,child: testView,///使用原生视图)],)),);}void onTestViewCreated(TestPluginDemo){this.testPluginDemo = TestPluginDemo;}
}
到这里Dart相关代码就写完了。接下来到原生部分OC
打开Flutter项目根目录下的ios文件夹,双击Runner.xcworkspace打开原生项目,打开原生代码查看图1是否存在,没有就去如图2目录把Podfile.lock删除,如果不存在Podfile.lock
去终端执行下列操作
//首先执行 (这里为以上目录路径)cd flutter_plugin_demo/example/ios //然后执行(这里默认已经安装好Xcode和CocoPods)pod install
这里发现有两个文件 GeneratedPluginRegistrant.h 和 GeneratedPluginRegistrant.m,
在pod /Development Pods 里面找到Classes文件夹里的这两个文件 开发, 新建类也要在这个目录里面,
在原生端需要做的事情
原生端只需要做以下两件事:
- 创建视图工厂对象,并在工厂方法中返回需要的原生视图对象。
- 在AppDelegate对象的App启动方法中,使用viewType属性值注册视图工厂对象。
打开Flutter项目根目录下的ios文件夹,双击Runner.xcworkspace打开原生项目。创建一个新的视图工厂类TestPluginViewFactory.h,其需要继承自NSObject类,并遵从FlutterPlatformViewFactory协议,代码如下:
TestPluginViewFactory.h
#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN@interface TestPluginViewFactory : NSObject<FlutterPlatformViewFactory>
/// 重写一个构造方法 来接收 Flutter 相关蚕食
/// @param messenger Flutter类 包含回调方法等信息
-(instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;
@endNS_ASSUME_NONNULL_END
TestPluginViewFactory.m
#import "TestPluginViewFactory.h"
#import "TestPluginView.h"
@interface TestPluginViewFactory ()@property(nonatomic)NSObject<FlutterBinaryMessenger>* messenger;@end@implementation TestPluginViewFactory- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {self = [super init];if (self) {self.messenger = messenger;}return self;
}#pragma mark -- 实现FlutterPlatformViewFactory 的代理方法
- (NSObject<FlutterMessageCodec>*)createArgsCodec {return [FlutterStandardMessageCodec sharedInstance];
}/// FlutterPlatformViewFactory 代理方法 返回过去一个类来布局 原生视图
/// @param frame frame
/// @param viewId view的id
/// @param args 初始化的参数
- (NSObject<FlutterPlatformView> *)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id)args{TestPluginView *testPluginView = [[TestPluginView alloc] initWithFrame:frame viewId:viewId args:args messager:self.messenger];return testPluginView;}@end
FlutterPlatformViewFactory协议中的工厂方法createWithFrame:viewIdentifier:arguments:是一个必须实现的方法,该方法生成一个遵从FlutterPlatformView协议的对象并返回。FlutterPlatformView协议用于将原生视图嵌入到Flutter的widget树中,代码如下:
TestPluginView.h
#import <Foundation/Foundation.h>
#include <Flutter/Flutter.h>@interface TestPluginView : NSObject<FlutterPlatformView>
- (id)initWithFrame:(CGRect)frameviewId:(int64_t)viewIdargs:(id)argsmessager:(NSObject<FlutterBinaryMessenger>*)messenger;
@end
TestPluginView.m
#import "TestPluginView.h"@interface TestPluginView ()
/** channel*/
@property (nonatomic, strong) FlutterMethodChannel *channel;
@property (nonatomic, strong) UIButton *button;
@end@implementation TestPluginView
{CGRect _frame;int64_t _viewId;id _args;}- (id)initWithFrame:(CGRect)frameviewId:(int64_t)viewIdargs:(id)args
messager:(NSObject<FlutterBinaryMessenger>*)messenger
{if (self = [super init]){_frame = frame;_viewId = viewId;_args = args;///建立通信通道 用来 监听Flutter 的调用和 调用Fluttter 方法 这里的名称要和Flutter 端保持一致_channel = [FlutterMethodChannel methodChannelWithName:@"test_plugin_demo" binaryMessenger:messenger];__weak __typeof__(self) weakSelf = self;[_channel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {[weakSelf onMethodCall:call result:result];}];}return self;
}- (UIView *)view{UIView *nativeView = [[UIView alloc] initWithFrame:_frame];nativeView.backgroundColor = [UIColor purpleColor];self.button = [UIButton buttonWithType:UIButtonTypeSystem];[self.button setTitle:@"我是按钮" forState:UIControlStateNormal];[self.button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];[self.button setBackgroundColor:[UIColor redColor]];self.button.frame = CGRectMake(100, 100, 200, 100);[nativeView addSubview:self.button];[self.button addTarget:self action:@selector(flutterMethod) forControlEvents:UIControlEventTouchUpInside];return nativeView;}
#pragma mark -- Flutter 交互监听
-(void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result{}
//按钮点击事件
- (void)flutterMethod{}@end
TestPluginView协议中有一个必须实现的方法view,该方法用于真正生成原生视图树并返回根视图,这里创建了一个UIButton及对应点击处理。
工厂类和视图类都创建完了,现在可以到 刚开始的模板里创建的 TestFlutterPluginDemoPlugin 这个类里去做关联了 是/Development Pods 里面找到Classes文件夹里的这两个文件
实现原理
App启动后,调用registerWithRegistrar:方法注册插件,要求插件名字唯一。然后调用registerViewFactory:withId:方法注册视图工厂,传入对应的工厂对象和Flutter端对应的viewType值即可。
TestPluginDemoPlugin.m
#import "TestPluginDemoPlugin.h"
#import "TestPluginViewFactory.h"
@implementation TestPluginDemoPlugin+(void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {TestPluginViewFactory *testViewFactory = [[TestPluginViewFactory alloc] initWithMessenger:registrar.messenger];
//这里填写的id 一定要和dart里面的viewType 这个参数传的id一致[registrar registerViewFactory:testViewFactory withId:@"testView"];}@end
修改项目配置
这里运行Flutter端,发现App无法显示原生视图内容,同时控制台报错:
Trying to embed a platform view but the PrerollContext does not support embedding.
因为在Flutter中嵌入原生视图的开销很大,默认情况下是不开放此功能的,因此需要手动修改info.plist文件。在Xcode中右键单击info.plist -> Open As -> Source Code,这样会以源代码方式打开:
<key>io.flutter.embedded_views_preview</key> <true/>
现在让我们运行一下,效果如下
视图显示基本已经大功告成
Flutter 方法互相调用和传值
Dart代码区,在之前创建的 TestPluginDemo 这个类里面添加 和原生交互代码
/// NOTE:定义一个controller创建成功回调
typedef void TestViewCreatedCallback(TestPluginDemo controller);/// NOTE:通过字符串test_plugin_demo约定MethodChannel
/// NOTE:这里主要负责和原生进行交互,调用原生的方法和监听原生方法,
class TestPluginDemo {MethodChannel _channel;TestPluginDemo.init(int id){_channel = new MethodChannel('test_plugin_demo');_channel.setMethodCallHandler(platformCallHandler);///设置原生参数监听}///Flutter 调用原生///这里我传了一个 字符串 当然也可以传MapFuture<void> changeNativeTitle(String str) async{return _channel.invokeListMethod('changeNativeTitle',str);}///实现监听原生方法回调Future<dynamic> platformCallHandler(MethodCall call) async {switch (call.method) {case "clickAciton":print('收到原生回调 ---- $call.arguments');return ;break;}}
}
最后给原生视图添加按钮的点击回调
#import "TestPluginView.h"@interface TestPluginView ()
/** channel*/
@property (nonatomic, strong) FlutterMethodChannel *channel;
@property (nonatomic, strong) UIButton *button;
@end@implementation TestPluginView
{CGRect _frame;int64_t _viewId;id _args;}- (id)initWithFrame:(CGRect)frameviewId:(int64_t)viewIdargs:(id)args
messager:(NSObject<FlutterBinaryMessenger>*)messenger
{if (self = [super init]){_frame = frame;_viewId = viewId;_args = args;///建立通信通道 用来 监听Flutter 的调用和 调用Fluttter 方法 这里的名称要和Flutter 端保持一致_channel = [FlutterMethodChannel methodChannelWithName:@"test_plugin_demo" binaryMessenger:messenger];__weak __typeof__(self) weakSelf = self;[_channel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {[weakSelf onMethodCall:call result:result];}];}return self;
}- (UIView *)view{UIView *nativeView = [[UIView alloc] initWithFrame:_frame];nativeView.backgroundColor = [UIColor purpleColor];self.button = [UIButton buttonWithType:UIButtonTypeSystem];[self.button setTitle:@"我是按钮" forState:UIControlStateNormal];[self.button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];[self.button setBackgroundColor:[UIColor redColor]];self.button.frame = CGRectMake(100, 100, 200, 100);[nativeView addSubview:self.button];[self.button addTarget:self action:@selector(flutterMethod) forControlEvents:UIControlEventTouchUpInside];return nativeView;}
#pragma mark -- Flutter 交互监听
-(void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result{//监听Fluterif ([[call method] isEqualToString:@"changeNativeTitle"]) {[self.button setTitle:call.arguments forState:UIControlStateNormal];}}
//调用Flutter
- (void)flutterMethod{[self.channel invokeMethod:@"clickAciton" arguments:@"传递参数"];
}@end
关于flutter IOS插件以上的demo已经实现了。
内容转载自https://www.jianshu.com/p/43a45500e2f6
GitHub:https://github.com/xuan6zm/flutter_ios_plugin
安卓案例https://blog.csdn.net/DearLC/article/details/100974303
flutter原生插件开发--IOS端相关推荐
- Flutter UiKitView 嵌入iOS原生View
目前在西瓜视频上免费刊登 Flutter 系列教程,每日更新,欢迎关注接收提醒 [x1]点击查看提示 [x2]各种系列的教程 [x3]更多文章请查看 flutter从入门 到精通 本篇文章 中写到的是 ...
- UNI APP---Android端原生插件开发实战(二)
1.前言 最近一个项目要求我们的产品必须走网络隧道,并且提供了对应的SDK,很明显只能通过原生开发的方式才能实现这个流程,之前已经写过一篇通过代理的方式进行数据请求,而这次Android端的方式是采用 ...
- uniapp ios原生插件开发之插件包格式(package.json)
文章目录 一.前言 二.插件包格式介绍 2.1 package.json 2.2 iOS 插件包配置 plugins integrateType 依赖资源文件 resources embedSwift ...
- WebRTC 实现P2P音视频通话——原生IOS端使用WebRTC实现一对一音视频通话
IOS端使用WebRTC实现一对一音视频通话 前言 环境 一.环境配置 搭建项目,配置权限,通过CocoaPods安装第三方库 二.音视频通话的实现 音视频通话实现主要分为两部分,信令客户端以及web ...
- flutter permission动态权限申请以及IOS端权限问题审核被拒处理
前言 Google在 Android 6.0 开始引入了权限申请机制,将所有权限分成了正常权限和危险权限.应用的相关功能每次在使用危险权限时需要动态的申请并得到用户的授权才能使用,否则将会导致应用程序 ...
- Flutter - Mac m1和Windows10配置flutter环境 ,iOS原生项目接入flutter
Flutter - Mac和Windows10配置flutter环境 ,iOS原生项目接入flutter 一.Mac m1配置flutter环境 1.下载flutter SDK 2.安装和配置 Flu ...
- 62、Flutter插件通信iOS,Android实现过程<二>
前言 61.Flutter插件通信原理<一>_风雨「83」的博客-CSDN博客Flutter与原生通讯 - Flutter Plugin - 知乎前言Flutter优势主要体现在UI上-- ...
- 一套代码两端运行不靠谱?是时候放弃 C++ 跨 Android、iOS 端开发!
「Write once,run anywhere!」想必是很多开发者以及企业梦寐以求的愿望,但是在分析跨平台中的种种成本之后,我们不禁发问,这种策略真的靠谱吗? 近日,云存储公司 Dropbox 就此 ...
- uni原生插件开发--友盟一键登录
原生插件开发--友盟一键登录(UMVerify) 友盟SDK准备 下载UMVerify SDK 将SDK 导入项目 遇到的问题 代码调用 iOS端代码 iOS 登录成功返回数据 uniapp端代码 本 ...
最新文章
- 【送】VMware 虚拟化知识思维导图
- linux deploy 版本,Linux Deploy
- 《学做程序经理》完整版
- centos中使用rpm包或yum命令在线安装的软件默认是安装在那个目录下
- Linux C面试题(内存管理)
- 神经损伤怎么康复好 成都顾连康复医院专科专治
- 如何在一台电脑上使用两个git@osc的账号进行操作
- i.MXRT1050 从外部QSPI Nor Flash的启动
- ERP管理软件哪家好?比较好的ERP管理系统软件推荐
- 数据结构导论 笔记整理
- Resolver error Error Downloading VS Code Server failed - please install either curl or wget on the
- 微信小程序点击商品跳转商品详情页面的方法
- ipad怎么压缩文件?教你一招快捷压缩图片
- 2 树莓派设置连接WiFi,开启VNC等等
- 《正本清源区块链》课程分享裂变运营复盘
- 智能家居实训第一天 嵌入式介绍 需求分析 开发环境 Linux基础知识
- Java jta 原理_Java的分布式事务(JTA和XA)
- paddlepaddle学习—波士顿房价预测
- RSocket——Http协议的替代者
- Android 自定义斑马波纹进度条