背景
用 Flutter 开发 Android、iOS 双端 APP 并集成飞书移动应用登录能力,实现拉起飞书 APP 并授权登录后回跳。飞书开放平台文档提供了 Android 和 iOS 双端文档。Flutter 中使用 Pigeon 调用原生平台代码完成飞书登录。
因不熟悉 Kotlin/Swift,在网上寻找 Flutter 集成飞书移动应用登录的示例无果,借助 AI 实现后做此简要记录,代码可以实现需求但可能存在不合理的部分。
代码中your、your/app/bundleID为示例名称和包名。本文只关注 Flutter、Android、iOS 三端的代码实现,针对 Pigeon、飞书开放平台、飞书 SDK 的配置和详细用法请参考各自的文档和 Demo,样板代码部分不做解释。
Pigeon 配置
编辑 messages.dart 文件后,执行dart run pigeon --input pigeons/messages.dart后,根据文件中@ConfigurePigeon(PigeonOptions(...))部分的配置项内容,会分别生成 Flutter 端的 Dart 代码,位于lib/pigeons/pigeon.dart,Android 平台的 Kotlin 代码,位于android/app/src/main/kotlin/com/your/app/bundleID/Messages.g.kt,和 iOS 平台的 Swift 代码,位于ios/Runner/Messages.g.swift
messages.dart
import 'package:pigeon/pigeon.dart';
/// 用于定义flutter和平台的桥接方法
/// @HostApi() 标记的,是用于 Flutter 调用原生的方法;
/// @FlutterApi() 标记的,是用于原生调用 Flutter 的方法;
/// @async 如果原生的方法,是异步回调,你就可以使用这个标记;
/// 在项目根目录,运行以下命令生成平台代码
/// dart run pigeon --input pigeons/messages.dart
@ConfigurePigeon(PigeonOptions(
dartOut: 'lib/pigeons/pigeon.dart',
dartOptions: DartOptions(),
kotlinOut:
'android/app/src/main/kotlin/com/your/app/bundleID/Messages.g.kt',
kotlinOptions: KotlinOptions(),
swiftOut: 'ios/Runner/Messages.g.swift',
swiftOptions: SwiftOptions(),
))
class MessageData {
MessageData({required this.code, required this.deviceId});
String code;
String deviceId;
}
/// Flutter 调用原生的 Api,全部放在一个抽象类中即可
@HostApi()
abstract class LarkAuthHostApi {
String getHostLanguage();
@async
MessageData larkAuth();
}
/// 原生主动调用 Flutter 的 Api,全部放在一个抽象类中即可
@FlutterApi()
abstract class LarkAuthFlutterApi {
bool onLarkAuthResult(MessageData messageData);
}
Flutter 登录业务实现
- 登录页提供一个【使用飞书账号登录】按钮,点击后调用 LarkAuthHostApi 的 larkAuth 方法。
- Kotlin 的实现会拉起飞书申请授权后,返回结果,Flutter 端收到结果执行后续业务。
- Swift 的实现会拉起飞书申请授权同时返回一个无效结果,Flutter 端收到无效结果不执行任何动作,而真正的授权结果会由 Swift 平台通过 LarkAuthFlutterApi 的 onLarkAuthResult 方法主动传递给 Flutter 端,Flutter 端收到授权结果后执行后续业务。和 Kotlin 实现不一致是因为看到飞书 SDK 在两端提供的 API 用法不完全相同,针对这个情况我不会用 Swift 写出和 Kotlin 相同的执行逻辑。
使用 Riverpod + flutter_hooks 实现交互逻辑,loginNotifier.login() 方法中封装了拿到飞书授权 code 后具体的登录逻辑,本文只关心拉起飞书 APP 授权后携带 code 回跳应用过程的实现。
LoginScreen.dart
import 'package:your_app_bundleID/pigeons/pigeon.dart';
import 'package:your_app_bundleID/providers/lark_open_api_provider.dart';
import 'package:your_app_bundleID/utils/toast_util.dart';
import 'package:your_app_bundleID/views/auth/widgets/login_button.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../../../providers/app_info.dart';
import '../widgets/login_title.dart';
class LoginScreen extends HookConsumerWidget implements LarkAuthFlutterApi {
LoginScreen({super.key});
final LarkAuthHostApi _api = LarkAuthHostApi();
/// ios 拉起飞书登录授权回调 start
static BuildContext? _context;
static WidgetRef? _ref;
@override
bool onLarkAuthResult(MessageData msgData) {
if (_context != null && _ref != null) {
final loginNotifier = _ref?.read(larkOpenApiProvider.notifier);
if (msgData.code.length > 2) {
logger.d('授权成功 code: ${msgData.code}');
loginNotifier?.login(msgData.code).then((res) {
if (res) {
_context?.go('/home');
} else {
ToastUtils.toast('登录失败');
}
});
}
return true;
} else {
return false;
}
}
/// ios 拉起飞书登录授权回调 end
@override
Widget build(BuildContext context, WidgetRef ref) {
_context = context;
_ref = ref;
final loginNotifier = ref.read(larkOpenApiProvider.notifier);
/// 拉起飞书登录授权
void startLarkAuth() async {
try {
MessageData msgData = await _api.larkAuth();
if (msgData.code.length > 2) {
// Kotlin 会返回授权 code,并执行下方逻辑,Swift 会立刻返回无实际意义的"0",跳过下方逻辑
logger.d('授权成功 code: ${msgData.code}');
loginNotifier.login(msgData.code).then((res) {
if (res) {
context.go('/home');
} else {
ToastUtils.toast('登录失败');
}
});
}
} catch (e) {
ToastUtils.toast('飞书授权失败');
}
}
return Scaffold(
backgroundColor: const Color(0XFFF3F9FF),
body: Container(
width: 1.sw,
height: 1.sh,
decoration: const BoxDecoration(color: Color(0xFFFCFDFF)),
child: Stack(
children: [
Positioned(
top: 400.w,
left: 0,
right: 0,
child: Column(
children: [
LoginButton(
onPressed: startLarkAuth,
text: '使用飞书登录',
iconName: 'images/icons/lark_icon')
],
),
)
],
),
),
);
}
}
Android 平台实现(Kotlin)
Kotlin 的实现比较简单,将 Pigeon 示例代码和 飞书 SDK 示例代码简单结合即可。
根据 Pigeon 的示例,需要定义一个类 PigeonApiImplementation 实现生成的 Messages.g.kt 文件中 LarkAuthHostApi 的成员方法,在成员方法 larkAuth 中,按照飞书 SDK 的示例,使用 LarkSSO 的能力拉起飞书 APP 授权,并在成功或失败回调中将结果回调给 Flutter。
MainActivity.kt
package com.your.app.bundleID
import androidx.annotation.NonNull
import androidx.annotation.Nullable
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import android.content.Intent
import android.util.Log
import android.app.Activity
import com.ss.android.larksso.CallBackData
import com.ss.android.larksso.IGetDataCallback
import com.ss.android.larksso.LarkSSO
import LarkAuthHostApi
import LarkAuthFlutterApi
import FlutterError
import MessageData
class MainActivity : FlutterActivity() {
private val TAG = "com.your.app.bundeldID.MainActivity"
lateinit var nativeApi: LarkAuthFlutterApi
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
nativeApi = LarkAuthFlutterApi(flutterEngine.dartExecutor.binaryMessenger)
val api = PigeonApiImplementation(this, nativeApi)
LarkAuthHostApi.setUp(flutterEngine.dartExecutor.binaryMessenger, api)
}
override protected fun onResume() {
super.onResume()
LarkSSO.inst().parseIntent(this, getIntent())
}
override protected fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
LarkSSO.inst().parseIntent(this, intent)
}
override protected fun onActivityResult(
requestCode: Int,
resultCode: Int,
@Nullable data: Intent?
) {
super.onActivityResult(requestCode, resultCode, data)
LarkSSO.inst().parseIntent(this, data)
}
}
private class PigeonApiImplementation(
private val activity: Activity,
private val nativeApi: LarkAuthFlutterApi
) : LarkAuthHostApi {
private val TAG = "com.your.app.bundleID.PigeonApiImplementation"
override fun getHostLanguage(): String {
return "Kotlin"
}
override fun larkAuth(callback: (Result<MessageData>) -> Unit) {
val scopeList =
arrayListOf("contact:user.employee_id:readonly", "contact:user.id:readonly")
val builder = LarkSSO.Builder()
.setAppId("cli_xxxxxxxxxxxxxxxx")
.setServer("Feishu")
.setLanguage("zh")
.setChallengeMode(true)
.setScopeList(scopeList)
.setContext(activity)
val deviceId = builder.getDeviceId()
Log.i(TAG, "deviceId is $deviceId")
LarkSSO.inst().startSSOVerify(builder, object : IGetDataCallback {
override fun onSuccess(callBackData: CallBackData) {
var message = MessageData(callBackData.code, deviceId)
callback(Result.success(message))
}
override fun onError(callBackData: CallBackData) {
var message = MessageData(callBackData.code, deviceId)
callback(Result.failure(FlutterError("code", "message", "details")))
}
})
}
}
Pigeon 自动生成的文件 Messages.g.kt
// Autogenerated from Pigeon (v22.3.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
import android.util.Log
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MessageCodec
import io.flutter.plugin.common.StandardMessageCodec
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
private fun wrapResult(result: Any?): List<Any?> {
return listOf(result)
}
private fun wrapError(exception: Throwable): List<Any?> {
return if (exception is FlutterError) {
listOf(
exception.code,
exception.message,
exception.details
)
} else {
listOf(
exception.javaClass.simpleName,
exception.toString(),
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)
)
}
}
private fun createConnectionError(channelName: String): FlutterError {
return FlutterError("channel-error", "Unable to establish connection on channel: '$channelName'.", "")}
/**
* Error class for passing custom error details to Flutter via a thrown PlatformException.
* @property code The error code.
* @property message The error message.
* @property details The error details. Must be a datatype supported by the api codec.
*/
class FlutterError (
val code: String,
override val message: String? = null,
val details: Any? = null
) : Throwable()
/**
* 用于定义flutter和平台的桥接方法
* @HostApi() 标记的,是用于 Flutter 调用原生的方法;
* @FlutterApi() 标记的,是用于原生调用 Flutter 的方法;
* @async 如果原生的方法,是异步回调,你就可以使用这个标记;
* 在项目根目录,运行以下命令生成平台代码
* dart run pigeon --input pigeons/messages.dart
*
* Generated class from Pigeon that represents data sent in messages.
*/
data class MessageData (
val code: String,
val deviceId: String
)
{
companion object {
fun fromList(pigeonVar_list: List<Any?>): MessageData {
val code = pigeonVar_list[0] as String
val deviceId = pigeonVar_list[1] as String
return MessageData(code, deviceId)
}
}
fun toList(): List<Any?> {
return listOf(
code,
deviceId,
)
}
}
private open class MessagesPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) {
129.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MessageData.fromList(it)
}
}
else -> super.readValueOfType(type, buffer)
}
}
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
when (value) {
is MessageData -> {
stream.write(129)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
}
/**
* Flutter调用原生的Api,全部放在一个抽象类中即可
*
* Generated interface from Pigeon that represents a handler of messages from Flutter.
*/
interface LarkAuthHostApi {
fun getHostLanguage(): String
fun larkAuth(callback: (Result<MessageData>) -> Unit)
companion object {
/** The codec used by LarkAuthHostApi. */
val codec: MessageCodec<Any?> by lazy {
MessagesPigeonCodec()
}
/** Sets up an instance of `LarkAuthHostApi` to handle messages through the `binaryMessenger`. */
@JvmOverloads
fun setUp(binaryMessenger: BinaryMessenger, api: LarkAuthHostApi?, messageChannelSuffix: String = "") {
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.your_app_bundleID.LarkAuthHostApi.getHostLanguage$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
val wrapped: List<Any?> = try {
listOf(api.getHostLanguage())
} catch (exception: Throwable) {
wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.your_app_bundleID.LarkAuthHostApi.larkAuth$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
api.larkAuth{ result: Result<MessageData> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}
/**
* 原生主动调用Flutter的Api,全部放在一个抽象类中即可
*
* Generated class from Pigeon that represents Flutter messages that can be called from Kotlin.
*/
class LarkAuthFlutterApi(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") {
companion object {
/** The codec used by LarkAuthFlutterApi. */
val codec: MessageCodec<Any?> by lazy {
MessagesPigeonCodec()
}
}
fun onLarkAuthResult(messageDataArg: MessageData, callback: (Result<Boolean>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.your_app_bundleID.LarkAuthFlutterApi.onLarkAuthResult$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(messageDataArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else if (it[0] == null) {
callback(Result.failure(FlutterError("null-error", "Flutter api returned null value for non-null return value.", "")))
} else {
val output = it[0] as Boolean
callback(Result.success(output))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
}
}
}
}
iOS 平台实现(Swift)
首先在 XCode 中 Build Phases 栏,将生成的 Messages.g.swift 文件添加到 Compile Sources 中
Swift 中同样需要定义一个 PigeonApiImplementation 实现 LarkAuthHostApi 的成员方法,在 larkAuth 方法中,使用 LarkSSO 的能力拉起飞书 APP,LarkSSO.send() 方法需要传入 viewController 和 delegate 实例,viewController 的实例在 AppDelegate 的样板代码中,实例化 PigeonApiImplementation 时直接传入即可。
并且根据飞书提供的 Demo,获取飞书 APP 的授权返回结果需要定义一个类,实现 LarkSSODelegate 的 lkSSODidReceive 方法(这里就是和 Kotlin 不同的地方,不是在拉起飞书 APP 的方法回调中直接获取返回结果,而是在另一个单独的方法中获取返回结果),这样复用 PigeonApiImplementation 类就可以直接获取到自身的 delegate 实例。
在 lkSSODidReceive 方法中,获取到授权返回结果后,需要通过主动调用 Flutter 的方法将授权 code 作为参数传递给 Flutter 端,这里根据 Pigeon 的 Demo,单独封装一个类 PigeonFlutterApi 书写样板代码。该类中实例化 LarkAuthFlutterApi 时需要 binaryMessenger 参数,通过在实例化 PigeonFlutterApi 时传参即可。
AppDelegate.swift
import Flutter
import UIKit
import LarkSSOSDK
// #docregion swift-class
private class PigeonApiImplementation: LarkAuthHostApi, LarkSSODelegate {
weak var controller: FlutterViewController?
init(controller: FlutterViewController) {
self.controller = controller
}
func getHostLanguage() throws -> String {
return "Swift"
}
func larkAuth(completion: @escaping (Result<MessageData, Error>) -> Void) -> Void {
guard let controller = controller else {
print("controller is not set")
return
}
let request: SSORequest = .feishu
request.scope = ["contact:user.employee_id:readonly", "contact:user.id:readonly"]
request.useChallengeCode = true
do {
try LarkSSO.send(request: request, viewController: controller, delegate: self)
let msgData: MessageData = MessageData(code: "0", deviceId: "ios")
completion(.success(msgData))
} catch {
// 记录错误日志
print("LarkSSO.send failed with error: \(error)")
completion(.failure(error)) // 使用 completion 处理错误
}
}
// 实现 LarkSSODelegate 方法
func lkSSODidReceive(response: SSOResponse) {
// 处理响应
func errorHandle(error: SSOError) {
switch error.type {
case .cancelled:
print("User cancelled")
default:
print("Failure: \(error)")
}
}
guard let controller = controller else {
print("controller is not set")
return
}
response.safeHandleResultWithCodeVerifier(success: { (code, codeVerifier) in
let messageData = MessageData(code: code, deviceId: "ios")
// 调用 Flutter 方法
let flutterAPI = PigeonFlutterApi(binaryMessenger: controller.binaryMessenger)
flutterAPI.callFlutterMethod(messageData: messageData)
print("LarkSSO.send success with code: \(code) verifier: \(codeVerifier)")
}, failure: { (error) in
errorHandle(error: error)
})
}
}
// #enddocregion swift-class
// #docregion swift-class-flutter
private class PigeonFlutterApi {
var flutterAPI: LarkAuthFlutterApi
init(binaryMessenger: FlutterBinaryMessenger) {
flutterAPI = LarkAuthFlutterApi(binaryMessenger: binaryMessenger)
}
func callFlutterMethod(
messageData messageDataArg: MessageData) {
flutterAPI.onLarkAuthResult(messageData: messageDataArg){ result in
print("result")
switch result {
case .success(let success):
print("LarkAuthFlutterApi.onLarkAuthResult success: \(success)")
case .failure(let error):
print("LarkAuthFlutterApi.onLarkAuthResult failure: \(error)")
}
}
}
}
// #enddocregion swift-class-flutter
func setUpSDK() {
// register app
LarkSSO.register(apps: [App(server: .feishu, appId: "cli_xxxxxxxxxxxxxxxx", scheme: "clixxxxxxxxxxxxxxxx")])
// setup log
let logDid = LarkSSO.setupLog()
print("LarkSSO logDid: \(logDid)")
// setup language
LarkSSO.setupLang("zh")
}
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
setUpSDK()
let controller = window?.rootViewController as! FlutterViewController
let api = PigeonApiImplementation(controller: controller)
LarkAuthHostApiSetup.setUp(binaryMessenger: controller.binaryMessenger, api: api)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
return LarkSSO.handleURL(url)
}
}
extension UIApplication {
func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let navigationController = controller as? UINavigationController {
return topViewController(controller: navigationController.visibleViewController)
}
if let tabController = controller as? UITabBarController {
if let selected = tabController.selectedViewController {
return topViewController(controller: selected)
}
}
if let presented = controller?.presentedViewController {
return topViewController(controller: presented)
}
return controller
}
}
Pigeon 自动生成的 Messages.g.swift
// Autogenerated from Pigeon (v22.3.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
import Foundation
#if os(iOS)
import Flutter
#elseif os(macOS)
import FlutterMacOS
#else
#error("Unsupported platform.")
#endif
/// Error class for passing custom error details to Dart side.
final class PigeonError: Error {
let code: String
let message: String?
let details: Any?
init(code: String, message: String?, details: Any?) {
self.code = code
self.message = message
self.details = details
}
var localizedDescription: String {
return
"PigeonError(code: \(code), message: \(message ?? "<nil>"), details: \(details ?? "<nil>")"
}
}
private func wrapResult(_ result: Any?) -> [Any?] {
return [result]
}
private func wrapError(_ error: Any) -> [Any?] {
if let pigeonError = error as? PigeonError {
return [
pigeonError.code,
pigeonError.message,
pigeonError.details,
]
}
if let flutterError = error as? FlutterError {
return [
flutterError.code,
flutterError.message,
flutterError.details,
]
}
return [
"\(error)",
"\(type(of: error))",
"Stacktrace: \(Thread.callStackSymbols)",
]
}
private func createConnectionError(withChannelName channelName: String) -> PigeonError {
return PigeonError(code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "")
}
private func isNullish(_ value: Any?) -> Bool {
return value is NSNull || value == nil
}
private func nilOrValue<T>(_ value: Any?) -> T? {
if value is NSNull { return nil }
return value as! T?
}
/// 用于定义flutter和平台的桥接方法
/// @HostApi() 标记的,是用于 Flutter 调用原生的方法;
/// @FlutterApi() 标记的,是用于原生调用 Flutter 的方法;
/// @async 如果原生的方法,是异步回调,你就可以使用这个标记;
/// 在项目根目录,运行以下命令生成平台代码
/// dart run pigeon --input pigeons/messages.dart
///
/// Generated class from Pigeon that represents data sent in messages.
struct MessageData {
var code: String
var deviceId: String
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> MessageData? {
let code = pigeonVar_list[0] as! String
let deviceId = pigeonVar_list[1] as! String
return MessageData(
code: code,
deviceId: deviceId
)
}
func toList() -> [Any?] {
return [
code,
deviceId,
]
}
}
private class MessagesPigeonCodecReader: FlutterStandardReader {
override func readValue(ofType type: UInt8) -> Any? {
switch type {
case 129:
return MessageData.fromList(self.readValue() as! [Any?])
default:
return super.readValue(ofType: type)
}
}
}
private class MessagesPigeonCodecWriter: FlutterStandardWriter {
override func writeValue(_ value: Any) {
if let value = value as? MessageData {
super.writeByte(129)
super.writeValue(value.toList())
} else {
super.writeValue(value)
}
}
}
private class MessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter {
override func reader(with data: Data) -> FlutterStandardReader {
return MessagesPigeonCodecReader(data: data)
}
override func writer(with data: NSMutableData) -> FlutterStandardWriter {
return MessagesPigeonCodecWriter(data: data)
}
}
class MessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable {
static let shared = MessagesPigeonCodec(readerWriter: MessagesPigeonCodecReaderWriter())
}
/// Flutter调用原生的Api,全部放在一个抽象类中即可
///
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
protocol LarkAuthHostApi {
func getHostLanguage() throws -> String
func larkAuth(completion: @escaping (Result<MessageData, Error>) -> Void)
}
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
class LarkAuthHostApiSetup {
static var codec: FlutterStandardMessageCodec { MessagesPigeonCodec.shared }
/// Sets up an instance of `LarkAuthHostApi` to handle messages through the `binaryMessenger`.
static func setUp(binaryMessenger: FlutterBinaryMessenger, api: LarkAuthHostApi?, messageChannelSuffix: String = "") {
let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""
let getHostLanguageChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.your_app_bundleID.LarkAuthHostApi.getHostLanguage\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
getHostLanguageChannel.setMessageHandler { _, reply in
do {
let result = try api.getHostLanguage()
reply(wrapResult(result))
} catch {
reply(wrapError(error))
}
}
} else {
getHostLanguageChannel.setMessageHandler(nil)
}
let larkAuthChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.your_app_bundleID.LarkAuthHostApi.larkAuth\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
larkAuthChannel.setMessageHandler { _, reply in
api.larkAuth { result in
switch result {
case .success(let res):
reply(wrapResult(res))
case .failure(let error):
reply(wrapError(error))
}
}
}
} else {
larkAuthChannel.setMessageHandler(nil)
}
}
}
/// 原生主动调用Flutter的Api,全部放在一个抽象类中即可
///
/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift.
protocol LarkAuthFlutterApiProtocol {
func onLarkAuthResult(messageData messageDataArg: MessageData, completion: @escaping (Result<Bool, PigeonError>) -> Void)
}
class LarkAuthFlutterApi: LarkAuthFlutterApiProtocol {
private let binaryMessenger: FlutterBinaryMessenger
private let messageChannelSuffix: String
init(binaryMessenger: FlutterBinaryMessenger, messageChannelSuffix: String = "") {
self.binaryMessenger = binaryMessenger
self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""
}
var codec: MessagesPigeonCodec {
return MessagesPigeonCodec.shared
}
func onLarkAuthResult(messageData messageDataArg: MessageData, completion: @escaping (Result<Bool, PigeonError>) -> Void) {
let channelName: String = "dev.flutter.pigeon.your_app_bundleID.LarkAuthFlutterApi.onLarkAuthResult\(messageChannelSuffix)"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([messageDataArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName: channelName)))
return
}
if listResponse.count > 1 {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(PigeonError(code: code, message: message, details: details)))
} else if listResponse[0] == nil {
completion(.failure(PigeonError(code: "null-error", message: "Flutter api returned null value for non-null return value.", details: "")))
} else {
let result = listResponse[0] as! Bool
completion(.success(result))
}
}
}
}