一、iOS
端(Swift
实现)
1. 新建原生view
(NativeView.swift
)
🏷️ 需要继承`FlutterPlatformView` ,实现`view()`方法
import Foundation
import Flutter
class NativeView: NSObject, FlutterPlatformView {
private var _view: UIView
init(
frame: CGRect,
viewIdentifier viewId: Int64,
arguments args: Any?,
binaryMessenger messenger: FlutterBinaryMessenger
) {
_view = UIView()
super.init()
}
func view() -> UIView {
self._view
}
}
2. 新建工厂方法(NativeFactory.swift
)
🏷️ 需要继承`FlutterPlatformViewFactory`,实现`create`和`createArgsCodec`
flutter
层,每次将UiKitView
重新加入widget
树时,都会触发create
方法UiKitView
的creationParams
属性会传递到create
的args
create
方法内的frame
为x=0, y=0, width=0, height=0
,所以在flutter
层获取的widget
宽高传递到args
import Foundation
import Flutter
class NativeFactory: NSObject, FlutterPlatformViewFactory {
private var messenger: FlutterBinaryMessenger
init(messenger:FlutterBinaryMessenger) {
self.messenger = messenger
super.init()
}
func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView {
return NativeView(frame: frame,viewIdentifier: viewId,arguments: args,binaryMessenger: messenger)
}
func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
return FlutterStandardMessageCodec.sharedInstance()
}
}
3. 注册原生view(AppDelegate.swift
)
此处举例为AppDelegate
处添加,也可在插件内添加
@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: "com.example.app.common_channel")!
let factory = NativeFactory(messenger: registrar.messenger())
registrar.register(instance.factory!, withId: "common_channel_native_view")
}
}
此处的common_channel_native_view
需要牢记,会在flutter
层使用
4. 嵌入flutter
UiKitView(
viewType: "common_channel_native_view",
creationParamsCodec: const StandardMessageCodec(),
onPlatformViewCreated: (int viewId) {},
creationParams: {},
)
creationParame
和creationParamsCodec
需要同时使用- 此处的
viewId
是由flutter
进行分配的,每次从widget
树上移除再添加都会触发重分配,也会调用原生层工厂类的create
方法
二、场景分析
1. 接入某摄像头设备
假设插件为:camera_plugin
摄像头设备提供的sdk
对于直播和历史视频播放提供了封装的view
(假设直播realView
、历史视频historyView
),即相关的画面播放、声音播放、实时对讲对需要通过sdk
提供的view
来完成。
生成三种methodChannel
camera_plugin
:处理通用的方法,如sdk初始化、下载录像、生成另外两种methodChannel
等不涉及view
的功能camera_plugin_\(camId)
:处理realView
相关的方法camera_plugin_history_\(camId)
:处理historyView
相关的方法
共用一个eventChannel
CameraPlugin.swift
public class CameraPlugin: NSObject, FlutterPlugin {
public static let instance: LeCameraPlugin = LeCameraPlugin()
private var factory: NativeFactory?
private var eventSink: FlutterEventSink?;
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "camera_plugin", binaryMessenger: registrar.messenger())
let eventChannel = FlutterEventChannel(name: "camera_plugin_event", binaryMessenger: registrar.messenger())
registrar.addMethodCallDelegate(instance, channel: channel)
eventChannel.setStreamHandler(SwiftStreamHandler())
instance.factory = NativeFactory(messenger: registrar.messenger())
registrar.register(instance.factory!, withId: "camera_plugin_control")
}
class SwiftStreamHandler: NSObject, FlutterStreamHandler {
func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
instance.eventSink = events
// 将events传递给工厂类
instance.factory?.updateEventSink(eventSink: events)
return nil
}
func onCancel(withArguments arguments: Any?) -> FlutterError? {
instance.eventSink = nil
instance.factory?.updateEventSink(eventSink: nil)
return nil
}
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "prepare":
self.prepare(argument: call.arguments, result: result)
...
default:
result(FlutterMethodNotImplemented)
}
}
// 根据设备ID,为不同的设备的realView和historyView生成methodChannel
public func prepare(argument: Any?, result: @escaping FlutterResult) {
if argument == nil {
result(false)
return
}
let arg = argument as! Dictionary<String, String>
let camId = arg["camId"]
if camId != nil {
// 主要为此处
factory?.createChannel(whithCamId: camId!)
result(true)
} else {
result(false)
}
}
}
NativeFactory.swift
class NativeFactory: NSObject, FlutterPlatformViewFactory {
private var messenger: FlutterBinaryMessenger
private var eventSink:FlutterEventSink?
private var camerasMethodChannel: [String: FlutterMethodChannel] = [:]
private var camerasHistoryMethodChannel: [String: FlutterMethodChannel] = [:]
init(messenger:FlutterBinaryMessenger) {
self.messenger = messenger
super.init()
}
func createChannel(whithCamId camId: String) {
if camerasMethodChannel[camId] == nil {
let methodChannel = FlutterMethodChannel(name: "camera_plugin_\(camId)", binaryMessenger: messenger)
camerasMethodChannel[camId] = methodChannel
}
if camerasHistoryMethodChannel[camId] == nil {
let methodChannel = FlutterMethodChannel(name: "camera_plugin_history_\(camId)", binaryMessenger: messenger)
camerasHistoryMethodChannel[camId] = methodChannel
}
}
func updateEventSink(eventSink: FlutterEventSink?) {
self.eventSink = eventSink
}
func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView {
let data = args as! Dictionary<String, Any>
let camId = data["camId"] as! String
// 判断是否为history,缺省为否
let type = data["history"] as? Bool
let methodChannel: FlutterMethodChannel?
if type == true {
if camerasHistoryMethodChannel[camId] == nil {
createChannel(whithCamId: camId)
}
methodChannel = camerasHistoryMethodChannel[camId]
return NativeHistoryView(frame: frame,viewIdentifier: viewId,arguments: args,binaryMessenger: messenger, methodChannel: methodChannel!) { key, payload in
// 结构体自定义
self.eventSink?([
"key": key,
"payload": payload
])
}
} else {
if camerasMethodChannel[camId] == nil {
createChannel(whithCamId: camId)
}
methodChannel = camerasMethodChannel[camId]
return NativeView(frame: frame,viewIdentifier: viewId,arguments: args,binaryMessenger: messenger, methodChannel: methodChannel!) { key, payload in
// 结构体自定义
self.eventSink?([
"key": key,
"payload": payload
])
}
}
}
func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
return FlutterStandardMessageCodec.sharedInstance()
}
}
- 其他
NativeHistoryView
和NativeView
就可以正常使用methodChannel
和eventChannel
flutter
直接使用UiKitView
即可
UiKitView(
viewType: "camera_plugin_control",
creationParamsCodec: const StandardMessageCodec(),
onPlatformViewCreated: (int viewId) {},
creationParams: {
'camId': widget.mo.deviceId,
'width': widget.width.ceil(),
'height': widget.height.ceil(),
},
)
- 一级页面有
UiKitView
时,二级页面也存在UiKitView
时且共用同一个methodChannel
,此时从二级页面回到一级页面时需要将UiKitView
从widget
树上重新添加,以便将methodChannel
重新绑定到UiKitView