0
点赞
收藏
分享

微信扫一扫

Flutter 组件集录 | MenuAnchor 与多级菜单

心如止水_c736 1天前 阅读 2
flutterios

一、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`
  1. flutter层,每次将UiKitView重新加入widget树时,都会触发create方法
  2. UiKitViewcreationParams属性会传递到createargs
  3. create方法内的framex=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: {},
)
  1. creationParamecreationParamsCodec需要同时使用
  2. 此处的viewId 是由flutter进行分配的,每次从widget树上移除再添加都会触发重分配,也会调用原生层工厂类的create方法

二、场景分析

1. 接入某摄像头设备

假设插件为:camera_plugin

摄像头设备提供的sdk对于直播和历史视频播放提供了封装的view (假设直播realView、历史视频historyView),即相关的画面播放、声音播放、实时对讲对需要通过sdk提供的view来完成。

生成三种methodChannel

  1. camera_plugin :处理通用的方法,如sdk初始化、下载录像、生成另外两种methodChannel等不涉及view的功能
  2. camera_plugin_\(camId):处理realView相关的方法
  3. 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()
  }
  
}
  • 其他
  1. NativeHistoryViewNativeView就可以正常使用methodChanneleventChannel
  2. 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(),
  },
)
  1. 一级页面有UiKitView时,二级页面也存在UiKitView时且共用同一个methodChannel,此时从二级页面回到一级页面时需要将UiKitViewwidget树上重新添加,以便将methodChannel重新绑定到UiKitView
举报

相关推荐

0 条评论