Flutter是移动开发的跨平台框架,在使用该框架开发过程中,很多原生如Android的系统版本、toast、定位、电量等功能,Flutter是无法实现的这些特定平台的功。这个场景下我们就只有使用插件来达到使用平台特定功能的效果。
那么插件是什么呢
Flutter中的插件是这样定义的:
一种专用的Dart包,其中包含用Dart代码编写的API,以及针对Android(使用Java或Kotlin)和/或针对iOS(使用ObjC或Swift)平台的特定实现。
从这个定义来看,一个插件应该包括平台代码、dart代码。但是这两部分代码功能怎么关联?
官网写到:
“如果你想开发一个调用特定平台API的包,你需要开发一个插件包,插件包是Dart包的专用版本。 插件包包含针对Android(Java或Kotlin代码)或iOS(Objective-C或Swift代码)编写的特定于平台的实现(可以同时包含Android和Ios原生的代码)。 API使用platform channels连接到特定平台(Android或IOS)。”
这里的“platform channels”即平台通道:https://flutterchina.club/platform-channels/
所以,写一个插件,我们要先了解平台通道是什么?
平台通道是一个桥梁,应用的Flutter部分通过平台通道(platform channel)将消息发送到其应用程序的所在的宿主(iOS或Android)。
宿主监听的平台通道,并接收该消息。然后它会调用特定于该平台的API(使用原生编程语言) - 并将响应发送回客户端,即应用程序的Flutter部分。
官网图如下:
值得注意的是:
消息和响应是异步传递的,以确保用户界面保持响应(不会挂起)。
调用方式:
支持数据类型:
官网说明支持的类型如下: (另外,这些值在消息中的序列化和反序列化会自动进行,使用者不必关心。PS:是否支持自定义类型的转换?还不清楚。估计是不支持)
第一次接触Flutter,可能很疑惑,AS自带创建项目类型有好几个,有啥区别?
使用As自动创建插件项目flutter_plugin_test后,会自动生成平台代码,目录结构如下:
1.android目录:存放android代码
2.example目录:测试plugin的目录,可写代码测试plugin
3.ios目录:存放ios的平台代码
4.lib目录:存放dart代码
创建该插件项目时候,平台已经自动生成了平台版本号获取的代码。
其中:
MethodChannel类
构造函数如下:
const MethodChannel(this.name, [this.codec = const StandardMethodCodec()]);
name:The logical channel on which communication happens, not null
( channel的名称,同平台代码中注册名字)
codec:The message codec used by this channel, not null.(消息编码,暂时不关心)。
Flutter与iOS和Android的交互就是通过这个MethodChannel。MethodChannel就是我们的信使,负责dart和原生代码通信。
如上面测试代码,channe name为flutter_plugin_test,此时对应到平台代码(android平台为例,在目录android下面):
package com.demo.flutterplugintest;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;
/** FlutterPluginTestPlugin */
public class FlutterPluginTestPlugin implements MethodCallHandler {
/** Plugin registration. */
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_plugin_test");
channel.setMethodCallHandler(new FlutterPluginTestPlugin());
}
@Override
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals("getPlatformVersion")) {
result.success("Android " + android.os.Build.VERSION.RELEASE);
} else {
result.notImplemented();
}
}
}
如上所述,平台代码中,需要注册这个channel。flutter通过一个这个channel name 才能才够在对应平台上找到对应的MethodChannel,从而实现flutter与平台的交互。
注册方式:
1.建立channel(对应到void registerWith(Registrar registrar)方法中进行)
final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_plugin_test");
channel.setMethodCallHandler(new FlutterPluginTestPlugin());
2.实现MethodCallHandler接口
因为channel注册时候setMethodCallHandler为FlutterPluginTestPlugin;故FlutterPluginTestPlugin需要实现该接口:
public interface MethodCallHandler {
void onMethodCall(MethodCall var1, MethodChannel.Result var2);
}
onMethodCall说明:
该接口onMethodCall再dart调用平台方法时,被执行。
MethodCall中保存了dart调用的方法名和参数。如下:
MethodCall :
public final class MethodCall {
public final String method; //调用方法名
public final Object arguments; //方法中的参数
....
}
Result中表示执行结果,最终会返回到dart层:
public interface Result {
void success(@Nullable Object var1);
void error(String var1, @Nullable String var2, @Nullable Object var3);
void notImplemented();
}
其中,执行成功,此时通过success将结果Object返回到dart层。
channel方法调用
在lib目录的dart文件中,获取到flutter_plugin_test的MethodChannel,封装到FlutterPluginTest类中,通过invokeMethod去调用本地方法。因为是异步执行,故需要加上async,获取平台版本号。文件lib/flutter_plugin_test.dart
import 'dart:async';
import 'package:flutter/services.dart';
class FlutterPluginTest {
static const MethodChannel _channel =
const MethodChannel('flutter_plugin_test');
static Future get platformVersion async {
final String version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
}
这样Flutter就可以访问,本地的方法了。
通常应用于访问本地资源,例如访问相机,本地存储,图片的选择等。
由于插件flutter_plugin_test已经注册,并在dart中(lib/flutter_plugin_test.dart)提供了属性:platformVersion,所以使用插件很简单。
新建dart文件,import该插件后,调用方法属性就行了。
import ‘package:flutter_plugin_test/flutter_plugin_test.dart’;
try {
String platformVersion = await FlutterPluginTest.platformVersion;
} on PlatformException {
platformVersion = ‘Failed to get platform version.’;
}
如上,Flutter创建插件就是这样一个过程。要理解MethodChannel进行本地和flutter代码的互相调用达到插件的效果。其实,Flutter还有其他的channel,如MessageChannel,EventChannel。后续再详细说明各个channel。