Flutter 与原生进行交互
标签:
编程学习
Flutter学习
创建插件
iOS默认使用的是OC,Android默认使用的是kotlin,如果想设置语言需要加上参数,我这里就使用默认的语言了。
flutter create --org com.duicode --template=plugin dc_plugin
flutter create --org com.duicode --template=plugin -i swift -a kotlin dc_plugin
一般情况下交互涉及到的也就是方法互相调用及参数的传递,Flutter 使用的是平台通道 MethodChannel 进行交互的
平台通道使用标准消息编/解码器对消息进行编解码,它可以高效的对消息进行二进制序列化与反序列化,下面来看一下数据类型的映射关系
Dart | Android | iOS |
---|---|---|
null | null | nil (NSNull when nested) |
bool | java.lang.Boolean | NSNumber numberWithBool: |
int | java.lang.Integer | NSNumber numberWithInt: |
int, 如果不足32位 | java.lang.Long | NSNumber numberWithLong: |
int, 如果不足64位 | java.math.BigInteger | FlutterStandardBigInteger |
double | java.lang.Double | NSNumber numberWithDouble: |
String | java.lang.String | NSString |
Uint8List | byte[] | FlutterStandardTypedData typedDataWithBytes: |
Int32List | int[] | FlutterStandardTypedData typedDataWithInt32: |
Int64List | long[] | FlutterStandardTypedData typedDataWithInt64: |
Float64List | double[] | FlutterStandardTypedData typedDataWithFloat64: |
List | java.util.ArrayList | NSArray |
Map | java.util.HashMap | NSDictionary |
我这里拿我们用到的云片图片验证的插件事例来做一个演示。
插件文件的编写
插件创建的时候都会创建一个 channel 对象
static const MethodChannel _channel = const MethodChannel('dc_plugin');
///监听,这里处理的其实是原生调用 Flutter
Map<String, Function> _closures = {};
/// 构造
DcPlugin() {
/// 原生调用 flutter 时候使用
_channel.setMethodCallHandler((call) async {
var method = call.method;
var arguments = call.arguments;
print("method:$method; arguments:$arguments");
_handleCallBack(method, arguments);
});
}
///回调部分
_handleCallBack(String methodName, var arguments) {
print("---_listeners:${_closures.length}");
var function = _closures[methodName];
// 调用函数
Function.apply(function, [arguments]);
}
_listen(String methodName, CallBackClosure closure) async {
_closures[methodName] = closure;
}
/// 成功回调(这里其实就是使用的原生调用 Flutter)
successListener(CallBackClosure closure) async => await _listen('success', closure);
/// 使用 _channel.invokeMethod 调用原生方法 init
static void init(String appId) {
_channel.invokeMethod('init', appId);
}
交互的写法很简单,上面已经实现了原生与 Flutter 之间的互相调用,原生回调的时候上面使用的是监听的写法,为了使用简单还可以这样写
static void verify({
@required OnSuccess onSuccess,
@required OnFail onFail,
VoidCallback onCancel,
VoidCallback onLoaded,
ValueChanged<String> onError,
String lang = 'en',
bool isShowLoading = false,
double alpha = 0.7
}) {
Map<String, dynamic> arguments = {
'lang': langValueMap[lang],
'isShowLoading': isShowLoading,
'alpha': alpha
};
setMethodCallHandler(
onSuccess: onSuccess,
onFail: onFail,
onLoaded: onLoaded,
onError: onError,
onCancel: onCancel
);
_channel.invokeMethod('verify', arguments);
}
与iOS的交互
注册插件->保存通道->通道调用invokeMethod
@interface DcPlugin()
//参数
@property (nonatomic, strong)NSDictionary *arguments;
//保存通道对象
@property (nonatomic, strong)FlutterMethodChannel *channel;
- (id)initWithChannel:(FlutterMethodChannel *)channel;
@end
@implementation DcPlugin
// 注册插件
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"dc_plugin" binaryMessenger:[registrar messenger]];
FlutterCaptchaPlugin* instance = [[DcPlugin alloc] initWithChannel:channel];
[registrar addMethodCallDelegate:instance channel:channel];
}
/// 构造
- (id)initWithChannel:(FlutterMethodChannel *)channel {
if (self = [super init]) {
self.channel = channel;
}
return self;
}
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
// 根据方法名称字符来判断调用的方法
if ([@"init" isEqualToString:call.method]) {
[self startWithCall:call result:result];
} else if ([@"verify" isEqualToString:call.method]) {
[self verifyWithCall:call result:result];
} else {
result(FlutterMethodNotImplemented);
}
}
#pragma mark - 初始化云片插件
- (void)startWithCall: (FlutterMethodCall *)call result: (FlutterResult)result {
@try {
[YPCaptchaSDK startWithCaptchaId:call.arguments];
result(@(YES));
} @catch (NSException *exception) {
result([FlutterError errorWithCode:@"-1" message:exception.name details:exception.reason]);
}
}
// 图片验证码回调
- (void)verifyWithCall: (FlutterMethodCall *)call result: (FlutterResult)result {
BOOL showLoading = [call.arguments[@"isShowLoading"] boolValue];
NSNumber *alpha = call.arguments[@"alpha"];
YPConfigModel *model = [[YPConfigModel alloc] init];
model.alpha = [alpha floatValue];
model.showLoadingView = showLoading;
[YPCaptchaSDK verfiy:model
onLoaded:^{
//原生调用flutter 方法
[self.channel invokeMethod:@"onLoaded" arguments:NULL];
}
onSuccess:^(NSDictionary *_Nonnull info) {
NSDictionary *data = info[@"data"];
//原生调用flutter 方法
[self.channel invokeMethod:@"onSuccess" arguments: [self dictionaryToJson:data]];
}
onFail:^(NSDictionary *_Nonnull info) {
if([[NSString stringWithFormat:@"%@", info[@"code"]] isEqualToString:@"10008"]) {
//原生调用flutter 方法
[self.channel invokeMethod:@"onCancel" arguments:NULL];
} else {
//原生调用flutter 方法
[self.channel invokeMethod:@"onFail" arguments:[self dictionaryToJson:info]];
}
}];
result(@(YES));
}
#pragma mark 字典转化字符串
- (NSString *)dictionaryToJson: (NSDictionary *)dic {
NSError *parseError = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&parseError];
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
@end
与Android的交互
安卓与iOS的写法就差不多了
public class DcPlugin() : FlutterPlugin, MethodCallHandler, ActivityAware {
//保存通道
lateinit var channel: MethodChannel
lateinit var activity: Activity
// 注册插件
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
DcPlugin().apply {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "dc_plugin")
channel.setMethodCallHandler(this)
}
}
// 注册插件
companion object {
@JvmStatic
fun registerWith(registrar: Registrar) {
DcPlugin()
.apply {
channel = MethodChannel(registrar.messenger(), "dc_plugin")
channel.setMethodCallHandler(this)
activity = registrar.activity()
}
}
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
//根据方法名来判断调用的方法
if (call.method == "init") {
init(call, result);
} else if (call.method == "verify") {
verify(call, result);
} else {
result.notImplemented()
}
}
// 初始化云片插件
private fun init(call: MethodCall, result: Result) {
try {
QPCaptcha.getInstance().init(activity, call.arguments())
result.success(true);
} catch (e: Exception) {
result.error("-1", e.message, e.toString())
}
}
// 验证云片
private fun verify(call: MethodCall, result: Result) {
val showLoading = call.argument<Boolean>("isShowLoading")!!
val alpha = call.argument<Float>("alpha")!!
val builder = QPCaptchaConfig.Builder(activity)
.setAlpha(alpha) // 视图透明度
.showLoadingView(showLoading) // 是否显示加载
// 云片插件回调
builder.setCallback(object : QPCaptchaListener {
override fun onSuccess(msg: String?) {
// 原生调用 Flutter 方法
channel.invokeMethod("onSuccess", msg);
Log.d("DcPlugin:", "onSuccess$msg");
}
override fun onFail(msg: String?) {
// 原生调用 Flutter 方法
channel.invokeMethod("onFail", msg);
Log.d("DcPlugin:", "onFail$msg");
}
override fun onCancel() {
// 原生调用 Flutter 方法
channel.invokeMethod("onCancel", null);
Log.d("DcPlugin:", "onCancel");
}
override fun onLoaded() {
// 原生调用 Flutter 方法
channel.invokeMethod("onLoaded", null);
Log.d("DcPlugin:", "onLoaded");
}
override fun onError(msg: String?) {
// 原生调用 Flutter 方法
channel.invokeMethod("onSuccess", msg);
Log.d("DcPlugin:", "onError $msg");
}
}) // 设置回调接口
QPCaptcha.getInstance().verify(builder.build())
result.success(true);
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
}
override fun onDetachedFromActivity() {
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity
}
override fun onDetachedFromActivityForConfigChanges() {
}
}
注意
记得在插件的 podspec 和 build.gradle 中补充完善信息,比如依赖的插件等。
这样一个简单的插件就完成了,比起之前玩的 RN 与原生的交互简直方便太多了。