持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第24天,点击查看活动详情
- 本文主要介绍Flutter学习之原生客户端交互,主要是在flutter使用iOS中的view。
1. 创建iOS工程View在Flutter中展示
我们想要在flutter使用iOS中的view,应该怎么交互呢。我们使用Xcode打开我们的Runner.xcworkspace
我们在 iOS工程中创建一个测试
view
import UIKit
import Flutter
class TestView:NSObject, FlutterPlatformView{
lazy var nameLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 15)
label.textColor = .white
// label.backgroundColor = .red
label.textAlignment = .center
return label
}()
init(_ frame: CGRect,viewID: Int64,args :Any?,messenger :FlutterBinaryMessenger) {
super.init()
nameLabel.text = "我是 iOS Test View"
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func view() -> UIView {
return nameLabel
}
}
同时创建TestViewFactory
import UIKit
import Flutter
class TestViewFactory:NSObject, FlutterPlatformViewFactory {
var messenger: FlutterBinaryMessenger
init(messenger:FlutterBinaryMessenger) {
self.messenger = messenger
super.init()
}
func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView {
return TestView(frame,viewID: viewId,args: args,messenger: messenger)
}
func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
return FlutterStandardMessageCodec.sharedInstance()
}
}
实现FlutterPlatformViewFactory
的协议方法,返回我们的TestView
对象,通过实现协议方法进行交互。
import UIKit
import Flutter
class TestViewFactory:NSObject, FlutterPlatformViewFactory {
var messenger: FlutterBinaryMessenger
init(messenger:FlutterBinaryMessenger) {
self.messenger = messenger
super.init()
}
func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView {
return TestView(frame,viewID: viewId,args: args,messenger: messenger)
}
func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
return FlutterStandardMessageCodec.sharedInstance()
}
}
我们在delegate
中进行注册,进行通信。
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
let registrar:FlutterPluginRegistrar = self.registrar(forPlugin: "plugins.flutter.io/custom_platform_view_plugin")!
let factory = TestViewFactory(messenger: registrar.messenger())
registrar.register(factory, withId: "plugins.flutter.io/custom_platform_view")
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
我们在flutter项目中创建一个展示的页面
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
class IosViewPage extends StatelessWidget {
const IosViewPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
String? title = Get.arguments['title'];
return Scaffold(
appBar: AppBar(title: Text(title ?? ''),),
body: Center(
child:iosView(),
),
);
}
Widget iosView() {
if(defaultTargetPlatform == TargetPlatform.iOS){
return const UiKitView(
viewType: 'plugins.flutter.io/custom_platform_view',
creationParams: {'text': 'Flutter传给IOSTextView的参数'},
creationParamsCodec: StandardMessageCodec(),
);
}else {
return Container();
}
}
}
显示效果如下:
2. Flutter向ios传值
我们在flutter页面中传一些参数
return const UiKitView(
viewType: 'plugins.flutter.io/custom_platform_view',
creationParams: {'text': 'Flutter传给IOSLabel的参数'},
creationParamsCodec: StandardMessageCodec(),
);
这里viewType
就是我们注册的时候使用的标识符
。creationParams
为创建iOS view时带的参数
。creationParamsCodec
:将 creationParams
编码后再发送给平台侧,它应该与传递给构造函数的编解码器匹配
。
我们在iOS项目中判断是否传递了参数
init(_ frame: CGRect,viewID: Int64,args :Any?,messenger :FlutterBinaryMessenger) {
super.init()
if args is NSDictionary {
let dict = args as! NSDictionary
nameLabel.text = dict.value(forKey: "text") as? String
}else{
nameLabel.text = "这是一个 iOS view"
}
}
运行结果
我们在运行中通过按钮点击改变iOS View的内容
static const platform = MethodChannel('com.flutter.test.TestView');
我们在flutter页面定义一个方法通道
RaisedButton(
child: const Text('传递参数给原生View'),
onPressed: () {
platform.invokeMethod('userInfo', {'name': 'Jack', 'city': "New York"});
},
点击的时候传参给iOS页面
let method = FlutterMethodChannel(name: "com.flutter.test.TestView", binaryMessenger: messenger)
method.setMethodCallHandler { (call, reslut) in
if (call.method == "userInfo") {
let dict: Dictionary? = call.arguments as? Dictionary<String, Any>
self.nameLabel.text = "my Name is:\(dict?["name"] ?? "")\n from:\(dict?["city"] ?? "")"
}
}
在页面中设置回调处理
,判断方法获取参数,进行展示
设置回调的时候还有一个result
没有使用,这个是用于我们原生页面向flutter
传参使用的。
let method = FlutterMethodChannel(name: "com.flutter.test.TestView", binaryMessenger: messenger)
method.setMethodCallHandler { (call, reslut) in
if (call.method == "userInfo") {
let dict: Dictionary? = call.arguments as? Dictionary<String, Any>
self.nameLabel.text = "my Name is:\(dict?["name"] ?? "")\n from:\(dict?["city"] ?? "")"
}else if (call.method == "callBack") {
reslut(["title": self.nameLabel.text])
}
}
这里我们使用reslut进行回传
,在flutter
中页面进行调用
RaisedButton(
child: Text(callbackData),
onPressed: () async {
var result = await IosViewPage.platform.invokeMethod('callBack');
setState((){
callbackData = '${result['title']}';
});
},
),
我们进行回调显示我们iOS中view的label文字到按钮上
3. 多个原生View通信冲突问题
当我们的页面有多个原生view,他们的通信
怎么区分
。
当我们点击的时候发现只有最好一个view发生改变,我们如何改变。可以从2个方面,一个让这个方法变成唯一
的。将一个唯一 id
通过初始化参数传递给原生 View,原生 View使用这个id
构建不同名称的 MethodChannel
。或者是通过viewID,原生 View 生成时,系统会为其生成唯一id:viewId
,使用 viewId 构建不同名称的 MethodChannel
。
let method = FlutterMethodChannel(name: "com.flutter.test.TestView\(viewID)", binaryMessenger: messenger)
我们在原生iOS的view中FlutterMethodChannel的name拼接viewID
var iosViews = [];
return UiKitView(
viewType: 'plugins.flutter.io/custom_platform_view',
creationParams: const {'text': 'Flutter传给IOSLabel的参数'},//初始值
creationParamsCodec: const StandardMessageCodec(),
onPlatformViewCreated: (viewID) => iosViews.add(MethodChannel('com.flutter.test.TestView$viewID')),
);
使用的时候取出对应的方法即可
最终结果
4. 小结
Flutter中嵌入原生iOS视图,主要通过flutter中的UiKitView
进行获取原生工程APPDelegate
中注册的FlutterPlatformView
类型从而获取加载到flutter页面。flutter和原生页面交互通过FlutterMethodChannel
,根据一些key, method
方法等进行交互回调函数的相互回调实现通信
。