2021-12-27更新原生主动调用flutter
业务中的原生交互
由于目前我的测试需求只以 flutter为项目架构,所以本节只讲述flutter 主动调用原生,传递参数,接受原生参数返回.
直接上逻辑分析.原生主动调用 flutter 等需要各位自己去寻找其他文章.
Flutter提供三中原生和dart端的交互方式
- BasicMessageChannel
- MethodChannel
- EventChannel
BasicMessageChannel和MethodChannel底层实际都是在调用BinaryMessenger
去发送消息,不过BasicMessageChannel发送的message 对象,MethodChannel发送的是MethodCall(method, arguments)
对象,所以BasicMessageChannel发送的只是你的数据对象,MethodChannel发送的对象自带了 method和 argument,本质是没啥区别的
至于EventChannel,则是使用了一个 streamcontroller 去接受BinaryMessenger
的数据,这个看需求而采用.
所以这里我只MethodChannel做数据展示,BasicMessageChannel大同小异,其实用BasicMessageChannel也行,规则全部由你们自己确定,一级 command,二级 command 等等,只要做好 Map 中各类公共参数的区分即可.
flutter端
前端页面
class NativeTestPage extends StatefulWidget {
const NativeTestPage({Key? key}) : super(key: key);
@override
_NativeTestPageState createState() => _NativeTestPageState();
}
class _NativeTestPageState extends State<NativeTestPage> {
int _num = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("测试原生交互"),
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
child: Text("点击传递原生"),
onPressed: () async {
var res = await NativeChannel.to.getNum(_num);
setState(() {
_num = res["num"] ?? 0;
});
},
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("回传回来的文字 ${_num}"),
SizedBox(
width: 10,
),
// Text(
// controller.
// ),
],
),
],
),
),
);
}
}
逻辑十分简单,点击按钮,调用原生获取 num,回显到底下的 label 上
NativeChannel
NativeChannel
是我利用 GetService
封装的一个单例,其内部逻辑也十分简单,封装了全局唯一通信通道com.taoqu.www.MethodChannel
,所以的原生交互都通过该通道进行交互.
class NativeChannel extends GetxService {
static NativeChannel get to => Get.find();
final channel = MethodChannel('com.taoqu.www.MethodChannel');
Future<Map<dynamic, dynamic>> getNum(int num) async {
var result = await channel.invokeMethod('getNum', {'num': num});
return result;
}
}
invokeMethod
invokeMethod
是通道与原生交互的方法,传入方法名,参数,这里建议与原生约定一个协议,所有参数都通过 Map 传递.
剩下的
invokeListMethod
和invokeMapMethod
我觉得都没太必要使用的,我认为在实际的业务开发过程中,原生与 flutter 通信只要约定一个统一的协议,全都是用 map 传输即可.
iOS 端(Swift)
封装一个类MethodChannelDemo
,参数的方法名,接受参数在下列代码中展示
public class MethodChannelDemo {
init(messenger: FlutterBinaryMessenger) {
let channel = FlutterMethodChannel(name: "com.taoqu.www.MethodChannel", binaryMessenger: messenger)
channel.setMethodCallHandler { (call:FlutterMethodCall, result:@escaping FlutterResult) in
if (call.method == "getNum") {
if let dict = call.arguments as? Dictionary<String, Any> {
let num:Int = dict["num"] as? Int ?? 0
DispatchQueue.main.async {
//通过 result 回传数据
result(["num": num + 20])
}
}
}
}
}
}
调用逻辑在 application
-
iOS 端接受到 flutter 的参数详情
- flutter 端接受到 iOS 端数据回传
注意,回传的参数为Map<dynamic, dynamic>
,而不是Map<String, dynamic>
这里贴出出错的 exception
[VERBOSE-2:ui_dart_state.cc(209)] Unhandled Exception: type '_InternalLinkedHashMap<Object?, Object?>' is not a subtype of type 'Map<String, dynamic>' in type cast
#0 NativeChannel.getNum
package:flutter_dd/…/native_test/native_channel.dart:11
正确接受回传的参数为
Android 端(Kotlin)
毕竟我只是个做 iOS 的,Android 端我不太熟,稍微学了点空安全,做了下测试代码(Android 后面有需要再去看看),我们只需要知道 MethodCall类中 arguments 为接收参数,method 为方法命即可,
通过result.success
来返回我们需要的数据
- Android 注册flutter 方法响应类
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
// 在 MainActivity中调用
MethodChannelDemo(flutterEngine.dartExecutor.binaryMessenger)
}
}
- Android 接受到 flutter 参数代码
- flutter 接收到 Android 回传的代码同 iOS.
顺便说一句,我好像在哪里看到网上的资料说是 flutter 不允许在非主线程调用 channel,我没有去验证这个结论,不过我验证了另外一个点,iOS 端是可以在非主线程调用数据回传的.
最后,展示下刚刚所写的双平台正常的通信.
2021-12-27更新原生调用 dart
iOS
我重新封装了下 iOS 端的消息处理类,主动发送消息也在里面dart
flutter 中接收为
其实flutter 端和 platform 端的消息接收,发送代码调用方式都是一致的,这里可以点赞一下 flutter 的代码设计.
原生插件包开发
上面讲述的是对业务中各个平台的通信交互,而这里顺便讲一下对原生平台逻辑进行 plugin封装,以达到多项目能够使用同一插件的目的.
我们可以通过快捷创建项目的方式,创建出插件模板项目.
flutter create --org com.taoqu --template=plugin -a java -i swift --platforms=ios,android flutter_do_native
- --org 为组织名称,体现在 ```Bundle Identifier
- --template有4个参数
-t, --template=<type> Specify the type of project to create.
[app] (default) Generate a Flutter application.
[module] Generate a project to add a Flutter module to an
existing Android or iOS application.
[package] Generate a shareable Flutter project containing
modular Dart code.
[plugin] Generate a shareable Flutter project containing an
API in Dart code with a platform-specific
implementation for Android, for iOS code, or for
both.
[skeleton] Generate a List View / Detail View Flutter
application that follows community best practices.
app为flutter 项目,module 为开发供原生调用的 framework,package 为纯 dart 三方,plugin 为各平台的三方.
- --platforms=ios,android写法为左侧,可填入平台
[ios (default), android (default), windows (default), linux (default), macos (default), web (default)]
- -a java -i swift a 为安卓平台语言,i 为 iOS 平台语言
这里我主要只提一点,打开项目后,需要在 example 例子中开发.iOS 平台的注册组件如下图位置.
如果你在Development Pods
中没有看到以你项目名称的文件夹,你得先用 flutter run 一下,如果有未指定平台的错误,说明你在创建项目中未填写--platforms=ios,android
具体细节我就不说了,因为是在做通信中查询到的资料,这里就测试了一下创建逻辑,测试了下简单通信,为了以后的插件开发做准备.
更新不易,如有帮助到各位看官的,点个喜欢, 谢谢啦~
网友评论