首先导入依赖:
dependencies:
pigeon: ^10.0.0
定义一个文件:
/// 用于定于flutter和平台的桥接方法
/// @HostApi() 标记的,是用于 Flutter 调用原生的方法;
/// @FlutterApi() 标记的,是用于原生调用 Flutter 的方法;
/// @async 如果原生的方法,是步回调那种,你就可以使用这个标记;
/// 在项目根目录,运行以下命令生成平台代码
/// dart run pigeon --input pigeons/messages.dart
import 'package:pigeon/pigeon.dart';
@ConfigurePigeon(PigeonOptions(
dartOut: 'lib/pigeons/pigeon.dart',
dartOptions: DartOptions(),
// objcHeaderOut: '../isolarCloud/operation4ios/Flutter/SGFlutterBridge.h',
// objcSourceOut: '../isolarCloud/operation4ios/Flutter/SGFlutterBridge.m',
// objcOptions: ObjcOptions(),
kotlinOut:
'../iSolarCloud/LibBase/src/main/java/com/isolarcloud/libbase/flutter/SGFlutterBridge.kt',
kotlinOptions: KotlinOptions(),
))
class CommonParams {
String? pageName;
Map<String?, Object?>? arguments;
}
class ApiParams {
String? url;
Map<String?, Object?>? arguments;
}
/// Flutter调用原生的Api,全部放在一个抽象类中即可
@HostApi()
abstract class SGHostApi {
/// push至原生页面,参数:页面名称、参数
void pushNativePage(CommonParams params);
/// pop出当前页面,预留参数,可通过params.pageName pop到指定页面
void popPage(CommonParams? params);
/// 通过Key获取本地化文本数据(同步)
String getLocalizedText(String? key);
/// Flutter通过URL和arguments调用原生端接口,异步返回数据给Flutter端
@async
Map requestNativeApi(ApiParams apiParams);
/// 是否允许开启Native页面的原生手势返回效果
void enablePopRecognizer(bool enable);
}
运行命令行:
dart run pigeon --input pigeons/messages.dart
接着可以运行下:flutter pub get 的命令
自动会生成这个文件:
里面会实现SGHostApi的定义的方法:比如下面的方法就是自动生成:
/// push至原生页面,参数:页面名称、参数
Future<void> pushNativePage(CommonParams arg_params) async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.isolarcloud_flutter.SGHostApi.pushNativePage', codec,
binaryMessenger: _binaryMessenger);
final List<Object?>? replyList =
await channel.send(<Object?>[arg_params]) as List<Object?>?;
if (replyList == null) {
throw PlatformException(
code: 'channel-error',
message: 'Unable to establish connection on channel.',
);
} else if (replyList.length > 1) {
throw PlatformException(
code: replyList[0]! as String,
message: replyList[1] as String?,
details: replyList[2],
);
} else {
return;
}
}
注意打开这个代码注释:意思是在你Android项目的某个路径下生成SGFlutterBridge文件。
kotlinOut:
'../iSolarCloud/LibBase/src/main/java/com/isolarcloud/flutter/SGFlutterBridge.kt',
kotlinOptions: KotlinOptions(),
然后会在:上面这个路径下自动生成和原生的桥接方法,这里执行的是Android的方法,ios只需要打开:
// objcHeaderOut: '../isolarCloud/operation4ios/Flutter/SGFlutterBridge.h',
// objcSourceOut: '../isolarCloud/operation4ios/Flutter/SGFlutterBridge.m',
// objcOptions: ObjcOptions(),
我们先看看原生和flutter项目的依赖:
在Android的项目settings.gradle中配置引用:
setBinding(new Binding([gradle: this]))
evaluate(
new File(
settingsDir,
'../isolarcloud_flutter/.android/include_flutter.groovy'
)
)
以上操作就把两个项目关联在一起了。下面是自动生成的桥接方法,pigeon自动帮你实现:
/**
* Flutter调用原生的Api,全部放在一个抽象类中即可
*
* Generated interface from Pigeon that represents a handler of messages from Flutter.
*/
interface SGHostApi {
/** push至原生页面,参数:页面名称、参数 */
fun pushNativePage(params: CommonParams)
/** pop出当前页面,预留参数,可通过params.pageName pop到指定页面 */
fun popPage(params: CommonParams?)
/** Flutter通过URL和arguments调用原生端接口,异步返回数据给Flutter端 */
fun requestNativeApi(apiParams: ApiParams, callback: (Result<String?>) -> Unit)
/** 是否允许开启Native页面的原生手势返回效果 */
fun enablePopRecognizer(enable: Boolean)
/**
* 调用原生toast
* type:0 老版,1:国内,2:海外样式
*/
fun showToast(type: AppType, msg: String)
/**
* 调用原生toast
* type:0 老版,1:国内,2:海外样式
*/
fun showLoading(show: Boolean, type: AppType, msg: String?)
/** 获取用户信息 */
fun getUserInfo(): String?
companion object {
/** The codec used by SGHostApi. */
val codec: MessageCodec<Any?> by lazy {
SGHostApiCodec
}
/** Sets up an instance of `SGHostApi` to handle messages through the `binaryMessenger`. */
@Suppress("UNCHECKED_CAST")
fun setUp(binaryMessenger: BinaryMessenger, api: SGHostApi?) {
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.isolarcloud_flutter.SGHostApi.pushNativePage", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val paramsArg = args[0] as CommonParams
var wrapped: List<Any?>
try {
api.pushNativePage(paramsArg)
wrapped = listOf<Any?>(null)
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.isolarcloud_flutter.SGHostApi.popPage", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val paramsArg = args[0] as CommonParams?
var wrapped: List<Any?>
try {
api.popPage(paramsArg)
wrapped = listOf<Any?>(null)
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.isolarcloud_flutter.SGHostApi.requestNativeApi", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val apiParamsArg = args[0] as ApiParams
api.requestNativeApi(apiParamsArg) { result: Result<String?> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.isolarcloud_flutter.SGHostApi.enablePopRecognizer", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val enableArg = args[0] as Boolean
var wrapped: List<Any?>
try {
api.enablePopRecognizer(enableArg)
wrapped = listOf<Any?>(null)
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.isolarcloud_flutter.SGHostApi.showToast", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val typeArg = AppType.ofRaw(args[0] as Int)!!
val msgArg = args[1] as String
var wrapped: List<Any?>
try {
api.showToast(typeArg, msgArg)
wrapped = listOf<Any?>(null)
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.isolarcloud_flutter.SGHostApi.showLoading", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val showArg = args[0] as Boolean
val typeArg = AppType.ofRaw(args[1] as Int)!!
val msgArg = args[2] as String?
var wrapped: List<Any?>
try {
api.showLoading(showArg, typeArg, msgArg)
wrapped = listOf<Any?>(null)
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.isolarcloud_flutter.SGHostApi.getUserInfo", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
var wrapped: List<Any?>
try {
wrapped = listOf<Any?>(api.getUserInfo())
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}
同步或者异步的去调用自动生成的方法:
Future<void> makeApiCall() async {
var arguments = {"curPage": 1, "size": 10, "message_scene_code": 2};
final stopwatch = Stopwatch()
..start();
await SGHostApi()
.requestNativeApi(ApiParams(arguments: arguments))
.then((value) {
print("数据回来的时间:${DateTime
.now()
.millisecondsSinceEpoch}");
stopwatch.stop();
print("总共花费时间:${stopwatch.elapsedMilliseconds}毫秒");
toast = "android 返回的数据==$value";
// print("value===$value");
setState(() {
});
});
}
_getString(){
SGHostApi().getLocalizedText("I18N_COMMON_SERVICE_AGREEMENT_DESCRIPTION").then((value){
print("android 给过来的数据==$value");
});
setState(() {
});
}
Android的代码:
class SingleFlutterActivity : FlutterActivity(), EngineBindingsDelegate {
private val engineBindings: EngineBindings by lazy {
EngineBindings(activity = this, delegate = this, entrypoint = FlutterRouter.DEMO_ENTRY_POINTER, initialRoute = "${FlutterRouter.DEMO_ROUTER}?psId=1234")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
engineBindings.attach()
/**
* flutter 调用原生的方法
* */
SGHostApiget.setUp(getFlutterEngine()!!.getDartExecutor().getBinaryMessenger(), MyApi())
SGHostApi.setUp(getFlutterEngine()!!.getDartExecutor().getBinaryMessenger(),FlutterToNativeApi() )
}
override fun onDestroy() {
super.onDestroy()
engineBindings.detach()
}
override fun provideFlutterEngine(context: Context): FlutterEngine? {
return engineBindings.engine
}
override fun onNext() {
val flutterIntent = Intent(this, LoginActivity::class.java)
startActivity(flutterIntent)
}
/**
* flutter 调用原生的方法,原生写法
* */
override fun onNext(str: String?) {
val flutterIntent = Intent(this, FlutterToAndroidActivity::class.java)
flutterIntent.putExtra("extraString", str)
startActivity(flutterIntent)
}
/**
* flutter 调用原生的方法
* */
class MyApi : SGHostApiget {
override fun getString(): String {
println("测试===6666")
return "测试===6666,这里是Android的方法"
}
}
/**
* flutter 调用原生的方法
* */
class FlutterToNativeApi() : SGHostApi {
/**
* flutter调用原生跳原生页面
* */
override fun pushNativePage(params: CommonParams) {
//params.pageName 页面名称非类名,不可直接创建实例
/*val flutterIntent = Intent(this, params::class.java)
flutterIntent.putExtra("extraString", str)
startActivity(flutterIntent)*/
}
/**
* flutter调用原生跳原生页面
* */
override fun popPage(params: CommonParams?) {
//flutter调用Android去退出页面
}
/**
* flutter调用原生 获取国际化字符串
* */
override fun getLocalizedText(key: String?): String {
return I18nUtil.getString(key);
}
/**
* flutter调用原生 获取网络信息 异步
* */
override fun requestNativeApi(
apiParams: ApiParams,
callback: (Result<Map<Any, Any?>>) -> Unit,
) {
/*val bitmap: Bitmap = BitmapFactory.decodeResource(BaseApplication.getApplication().resources, R.drawable.test_image)
val outputStream: ByteArrayOutputStream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.PNG, 50, outputStream)
val size: Int = outputStream.size()
println("字节大小: $size")
val result: Result<Map<Any, Any?>> = Result.success(
mapOf(
"name" to outputStream
),
)
callback(result)*/
val map: MutableMap<String, Any> = HttpRequest.getCommonParamMap()
HttpUtil.postWithPath("/v1/devService/getDevModelUpgradeConfigList", /*apiParams.arguments*/map, object : HttpGlobalHandlerCallback<UpdateRequestBean?>() {
override fun onSuccess(jsonResult: JsonResults<UpdateRequestBean?>) {
// 处理成功返回的数据
val data = jsonResult.result_data // 获取数据
if (data is UpdateRequestBean) {
data as UpdateRequestBean
}
println("开始传输时间: ${System.currentTimeMillis()}")
callback(Result.success(data!!.toMap())) // 通过callback返回结果
// 打印字节大小
val byteArray = data?.toString()?.toByteArray(Charsets.UTF_8)
val size = byteArray?.size ?: 0
println("字节大小: $size")
}
fun UpdateRequestBean.toMap(): Map<Any, Any?> {
val map = mutableMapOf<Any, Any?>()
map["code"] = code
map["record"] = record
map["totalFileSize"] = totalFileSize
map["pageList"] = pageList?.map { fileInfo ->
mapOf(
"file_url" to fileInfo.file_url,
"file_signature" to fileInfo.file_signature,
"file_name" to fileInfo.file_name,
"file_id" to fileInfo.file_id,
"dev_model" to fileInfo.dev_model,
"file_size" to fileInfo.file_size,
"type" to fileInfo.type,
"dev_type_id" to fileInfo.dev_type_id,
"record_id" to fileInfo.record_id,
"sn" to fileInfo.sn,
"update_time_stamp" to fileInfo.update_time_stamp,
"block_flag" to fileInfo.block_flag
)
}
return map
}
override fun onError(type: ErrorNetType) {
super.onError(type)
// 处理错误情况
// val result: Result<Map<Any, Any?>> = Result.success(
// mapOf(
// "name" to "John",
// "age" to 30,
// "email" to "john@example.com"
// )
// )
// callback(result)
// 通过callback返回错误信息
val failureResult: Result<Map<Any, Any?>> = Result.failure(
Exception("An error occurred")
)
callback(failureResult)
}
override fun onFinish() {
super.onFinish()
}
})
}
override fun disablePopRecognizer(disable: Boolean) {
}
}
}
在flutter中统一使用这个类来获取原生获取的网络数据:
/// 成功回调
typedef SuccessCallback = void Function(
Map<String, dynamic>? resultData, String resultCode, String resultMsg);
/// 失败回调
typedef ErrorCallback = void Function();
class RequireNativeApi {
static void postWithPathOversea(String? url, Map<String?, Object?>? arguments,
SuccessCallback? successCallback, ErrorCallback? errorCallback,
{bool showToast = true}) {
postWithPath(url, arguments, successCallback, errorCallback,
showToast: showToast, appType: AppType.oversea);
}
static void postWithPathDomestic(
String? url,
Map<String?, Object?>? arguments,
SuccessCallback? successCallback,
ErrorCallback? errorCallback,
{bool showToast = true}) {
postWithPath(url, arguments, successCallback, errorCallback,
showToast: showToast, appType: AppType.domestic);
}
static void postWithPath(String? url, Map<String?, Object?>? arguments,
SuccessCallback? successCallback, ErrorCallback? errorCallback,
{bool showToast = true, AppType appType = AppType.oversea}) {
ApiParams apiParams = ApiParams();
apiParams.url = url;
apiParams.arguments = arguments;
sgApi.requestNativeApi(apiParams).then((data) {
debugPrint("");
debugPrint("======================================================");
debugPrint("api: $url");
debugPrint("业务入参: $arguments");
debugPrint("出参:$data");
debugPrint("======================================================");
if (data == null || data.isEmpty) {
/// 网络异常
if (showToast) {
sgApi.showToast(appType, SGi18n.key("I18N_COMMON_NETWORK_ERROR"));
}
if (errorCallback != null) {
errorCallback();
}
return;
}
var jsonData = json.decode(data);
if (successCallback != null) {
// 返回接口返回数据,需要调用各个实体类的方法,所以需要各个业务层将Map转为对应的实体类
successCallback(
jsonData['result_data'] ?? {},
jsonData['result_code'] ?? "",
jsonData['result_msg'] ?? "",
);
}
}).onError((error, stackTrace) {
debugPrint("RequireNativeApi error: $error");
debugPrint("RequireNativeApi error: $stackTrace");
if (showToast) {
sgApi.showToast(
appType, SGi18n.key("I18N_COMMON_NETWORK_ERROR_MESSAGE"));
}
if (errorCallback != null) {
errorCallback();
}
});
}
}
flutter中要使用的地方直接拿到成功和失败的回调即可:
RequireNativeApi.postWithPathOversea(Api.getDeleteUserMessage, argument,
getDeleteMessageSuccess, () => SGOSLoadingToast.dismissLoading());
我项目中用的是getx,大家可以看看我完整的代码:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:isolarcloud_flutter/api/api.dart';
import 'package:isolarcloud_flutter/api/require_native_api.dart';
import 'package:isolarcloud_flutter/bean/home_msg_entity.dart';
import 'package:isolarcloud_flutter/bean/update_msg_entity.dart';
import 'package:isolarcloud_flutter/modules/demo/constant/constant_state_id.dart';
import 'package:isolarcloud_flutter/utils/hud/sg_os_loading_toast.dart';
import 'package:isolarcloud_flutter/utils/json_cache_util.dart';
import 'sg_message_first_state.dart';
class SGMessageHomePageLogic extends GetxController {
final SGMessageHomePageState state = SGMessageHomePageState();
static const idMessageClearUpdate = "message_clear_update";
static const idMessageList = "message_list";
static const idLoadFail = "message_load_fail";
void getList(String labelStr, {bool isFirst = false}) async {
state.labelStr = labelStr;
isFirst ? SGOSLoadingToast.showLoading() : null;
Map<String, Object> argument = {};
var cache = await JsonCacheManageUtils.getCacheData(
JsonCacheManageUtils.HomeMessageResponse,
labelId: labelStr.toString())
.then((value) {
if (value != null) {
return HomeMsgEntity.fromJson(value as Map<String, dynamic>);
}
});
state.hasCache = false;
if (cache is HomeMsgEntity) {
state.pageList = cache;
state.hasCache = true;
update([idMessageList + labelStr.toString()]);
}
RequireNativeApi.postWithPathOversea(
Api.getHomeMessageList, argument, getHomeMessageSuccess, () {
isFirst ? SGOSLoadingToast.dismissLoading() : null;
update([idLoadFail]);
});
}
void getHomeMessageSuccess(
Map<String, dynamic>? resultData, String resultCode, String resultMsg) {
if (resultCode == "1" && resultData != null && resultData.isNotEmpty) {
JsonCacheManageUtils.saveCacheData(
JsonCacheManageUtils.HomeMessageResponse,
labelId: state.labelStr,
resultData);
state.pageList = HomeMsgEntity.fromJson(resultData);
}
SGOSLoadingToast.dismissLoading();
if (!state.hasCache) {
update(["$idMessageList${state.labelStr}"]);
}
}
/// clear 消息 传null为全部
void updateUserAllMessage(dynamic messageSceneCode) {
SGOSLoadingToast.showLoading();
Map<String, dynamic> argument = {
"is_all": 1,
"message_scene_code": messageSceneCode,
};
RequireNativeApi.postWithPathOversea(Api.getUpdateUserMessage, argument,
getClearMessageSuccess, () => SGOSLoadingToast.dismissLoading());
}
void getClearMessageSuccess(
Map<String, dynamic>? resultData, String resultCode, String resultMsg) {
if (resultCode == "1" && resultData != null && resultData.isNotEmpty) {
state.updateBean = UpdateMsgEntity.fromJson(resultData);
}
SGOSLoadingToast.dismissLoading();
update([idMessageClearUpdate]);
}
//删除单个消息大类
void deleteUserMessage(String? ids, String? messageSceneCode, int index) {
SGOSLoadingToast.showLoading();
Map<String, dynamic> argument = {
"ids": ids,
"message_scene_code": messageSceneCode,
};
state.removeIndex = index;
RequireNativeApi.postWithPathOversea(Api.getDeleteUserMessage, argument,
getDeleteMessageSuccess, () => SGOSLoadingToast.dismissLoading());
}
void getDeleteMessageSuccess(
Map<String, dynamic>? resultData, String resultCode, String resultMsg) {
debugPrint("resultData: $resultData");
if (resultCode == "1" && resultData != null && resultData.isNotEmpty) {
state.deleteBean = UpdateMsgEntity.fromJson(resultData);
}
SGOSLoadingToast.dismissLoading();
update([StateListenerId.messageDelete]);
}
///底部按钮删除全部选中的
void deleteCheckMessage(List<HomeMsgPageList> messageHomeList) {
SGOSLoadingToast.showLoading();
StringBuffer messageSceneCodes = StringBuffer();
for (int i = 0; i < messageHomeList.length; i++) {
String? sceneCode = messageHomeList[i].messageSceneCode;
messageSceneCodes.write(sceneCode);
if (i != messageHomeList.length - 1) {
messageSceneCodes.write(',');
}
}
String result = messageSceneCodes.toString();
if (result.isEmpty) {
return;
}
Map<String, dynamic> argument = {
"ids": null,
"message_scene_code": result,
};
RequireNativeApi.postWithPathOversea(Api.getDeleteUserMessage, argument,
getDeleteMessageSuccess, () => SGOSLoadingToast.dismissLoading());
}
}
按理来说这个GetxController类是用来访问网络的类,这里用来和原生交互,就这样子。
最近工作忙,加班重,疏于思考,缺乏输出,望大家理解,有时间优化文章,敬请期待!