终端创建 flutter项目:flutter create plugin(项目名称)
终端上 cd 到此目录下
执行:
flutter build ios --no-codesign
pod install
在 ios 中创建需要的 iOS 原生视图(创建 flutter 项目后,项目中会有iOS 项目文件夹与 Android 项目文件夹), 打开iOS 项目。
① 创建原生视图,可提供外界调用的方法 / 属性等。
import Lottie //在 pod 中引入的庫
import SnapKit
class LottieView: UIView {
// MARK: - UI
private lazy var animationView: AnimationView = {
let aniView = AnimationView()
aniView.loopMode = .loop
aniView.backgroundColor = .clear
return aniView
}()
// MARK: - Property
// MARK: - init
init(frame: CGRect, jsonName: String) {
super.init(frame: frame)
animationView.animation = Animation.named(jsonName)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension LottieView {
func setupUI() {
addSubview(animationView)
animationView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
}
extension LottieView {
func ae_play(competionBlock: LottieCompletionBlock? = nil) {
animationView.play(completion: competionBlock)
}
func ae_pause() {
animationView.pause()
}
func ae_stop() {
animationView.stop()
}
}
② 创建遵守 FlutterPlatformViewFactory协议的 NSObject class 类
//代码:
class LottieFactory: NSObject, FlutterPlatformViewFactory {
let messenger: FlutterBinaryMessenger
let lottieType:LottieType
//自定义初始化(在 AppDelegate 中注册创建)
init(messenger: FlutterBinaryMessenger, lottieType: LottieType) {
self.messenger = messenger
self.lottieType = lottieType
super.init()
}
func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView {
// 返回遵守 FlutterPlatformView 协议的自定义初始化类
return LottiePlatformView(frame: frame, viewId: viewId, arguments: args, messenger: self.messenger, lottieType: self.lottieType)
}
// Only needs to be implemented if `createWithFrame` needs an arguments parameter.
func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
return FlutterStandardMessageCodec.sharedInstance()
}
}
③ 创建遵守 FlutterPlatformView 协议的 NSObject class 类
代码:
class LottiePlatformView: NSObject, FlutterPlatformView {
// MARK: - UI
private let lottieView: LottieView //自定义原生视图
// MARK: - Property
let CHANNEL_NAME: String = "Kit.LottieView.viewId_" // 多个视图时 利用 viewId
// MARK: - init
init(frame: CGRect = .zero, viewId: Int64, arguments: Any?, messenger: FlutterBinaryMessenger, lottieType: LottieType) {
var jsonName: String = "file_pick_wave"
if lottieType == .sleep {
jsonName = "file_asleep"
} else if lottieType == .surprise {
jsonName = "file_pick_surprise"
}
self.lottieView = LottieView(frame: frame, jsonName: jsonName)
super.init()
// 交互事件
let channel = FlutterMethodChannel(name: CHANNEL_NAME + "\(viewId)", binaryMessenger: messenger)
//与flutter 交互的方法通道(FlutterMethodChannel:由于FlutterMethodChannel(flutter 与 原生来回一次)只能一个来回传递. 用于方法调用(调用一次,反应一次)
// 使用 FlutterEventChannel(原生会不中断(非一次来回)发送数据到 flutter,只要原生有响应,flutter 就会有响应)用于数据流(event streams)的通信,(监听,发送))
channel.setMethodCallHandler { [weak self] call, result in
if call.method == "play" {
self?.lottieView.ae_play() //原生视图提供的函数
result(true)
} else if call.method == "pause" {
self?.lottieView.ae_pause()
result(true)
} else if call.method == "stop" {
self?.lottieView.ae_stop()
result(true)
} else {
result(FlutterMethodNotImplemented)
}
}
}
func view() -> UIView {
return self.lottieView
}
}
④ 在 Appdelegate 中注册
if let register = self.registrar(forPlugin: "lottie_view_plugin") {
register.register(LottieFactory(messenger: register.messenger(), lottieType: .wave), withId: "wave_lottie_view")
register.register(LottieFactory(messenger: register.messenger(), lottieType: .surprise), withId: "surprise_lottie_view")
register.register(LottieFactory(messenger: register.messenger(), lottieType: .sleep), withId: "sleep_lottie_view")
}
Flutter
创建个通信 dart 文件
import 'package:flutter/services.dart';
class WaveLottieManager {
static MethodChannel? _wave_channel;
static void createChannel(int viewId) {
_wave_channel ??= MethodChannel("Kit.LottieView.viewId_$viewId");
}
static Future<bool> play() async {
//交互回调方法(注意 play 必须与原生的中 call.method == ''play'' 一致)
bool result = await WaveLottieManage._wave_channel?.invokeMethod("play");
return result;
}
static Future<bool> pause() async {
bool result = await WaveLottieManage._wave_channel?.invokeMethod("pause");
return result;
}
static Future<bool> stop() async {
bool result = await WaveLottieManage._wave_channel?.invokeMethod("stop");
return result;
}
}
dart 文件中使用:
class WaveLottieView extends StatelessWidget {
const WaveLottieView({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(
width: kScreenW,
height: kScreenH,
child: UiKitView(
viewType: "wave_lottie_view", //注意:此处必须与AppDelegate 中注册此视图的标识一致
creationParamsCodec: const StandardMessageCodec(),
onPlatformViewCreated: (viewId) {
WaveLottieManager.createChannel(viewId); //创建交互通道(注意通道的命名必须与原生中通道命名得保持一致)
},
),
);
}
}
为了保证用户界面在交互过程中的流畅性,无论是从Flutter向Native端发送消息, 还是Native向Flutter发送消息都是以异步的形式进行传递的。
######在交互中,原生有三类 channel:
① MethodChannel:用于传递方法调用(method invocation):MethodChannel使用场景:无论是Flutter端还是Native端都可以通过MethodChannel向对方平台发送两端提前定义好的方法名来调用对方平台相对应的消息处理逻辑并且带回返回值给被调用方。
② EventChannel:用于事件流的发送(event streams):更侧重于Native平台主动向Flutter平台,单向给Flutter平台发送消息,Flutter无法返回任何数据给Native端,EventChannel描述是单通的。可以类比Android里面的广播 ,iOS 中得通知。
EventChannel 通信特点:
1、用于 iOS 端向 Flutter 端传递事件.
2、单向通信, 只能是 iOS 发送事件, Flutter 监听.
3、可持续性通信.
③ BasicMessageChannel:用于传递字符串和半结构化的消息:比如flutter想拍照,拍完照后的图片路径需要传给flutter,照片的路径发送可以使用BasicMessageChannel.Reply回复,也可以使用sendMessage主动再发一次消息。个人认为接收消息并回复消息属于一次通信,所以倾向于使用BasicMessageChannel.Reply。
与flutter 交互的方法通道(FlutterMethodChannel:由于FlutterMethodChannel(flutter 与 原生来回一次)只能一个来回传递. 用于方法调用(调用一次,反应一次)。
使用 FlutterEventChannel(原生会不中断(非一次来回)发送数据到 flutter,只要原生有响应,flutter 就会有响应)用于数据流(event streams)的通信,(监听,发送))。
关于 FlutterEventChannel 使用
public class SwiftSoundPlugin: NSObject, FlutterPlugin {
static let OIFI_CHANNEL_Method = "method_channel"
private let OIFI_CHANNEL_EVENT = "event_channel"
var eventSink: FlutterEventSink?
init(with registrar: FlutterPluginRegistrar) {
super.init()
let channel_event = FlutterEventChannel(name: OIFI_CHANNEL_EVENT, binaryMessenger: registrar.messenger())
channel_event.setStreamHandler(self)
}
public static func register(with registrar: FlutterPluginRegistrar) {
let channel_method = FlutterMethodChannel(name: OIFI_CHANNEL_Method, binaryMessenger: registrar.messenger())
registrar.addMethodCallDelegate(SwiftSoundPlugin(with: registrar), channel: channel_method)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
if call.method == "initPartnerID" {
guard let dict = call.arguments as? Dictionary<String, Any> else {
return
}
let partnerId = dict["partnerId"] as! String
let privateKey = dict["privateKey"] as! String
OIFIManager.shared.initPartnerID(partnerId: partnerId, privateKey: privateKey) { value in
result(value >= 0);
}
} else if call.method == "startSound" {
result(OIFIManager.shared.startListener());
} else if call.method == "stopSound" {
result(OIFIManager.shared.stopListener());
} else {
result(FlutterMethodNotImplemented);
}
}
}
extension SwiftSoundPlugin: FlutterStreamHandler {
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
self.eventSink = events;
//OIFIManager.shared.receiveListenerSound 原生方法(这里就不展示了)
OIFIManager.shared.receiveListenerSound { message, soundData in
guard soundData != nil else {
events(nil)
return
}
events(message);
}
return nil
}
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
self.eventSink = nil
return nil
}
}