在 Flutter 项目的开发中,我们需要根据自己的业务需求来创建各种各样的插件,这里记录下关于 Flutter 插件的创建及使用的过程。
项目结构

lib 里面是dart层实现,主要用来调用原生层的代码及对外提供插件的API,dart层采用的是代理模式:
①是PlatformInterface抽象接口,定义了平台提供的API接口。 ②是原生平台的代理类,实现了①定义的抽象接口,使用MethodChannel来调用原生方法。 ③是web平台的代理类,实现了①定义的抽象接口。 ④调用代理类对外提供插件的API。 ⑤Android原生代码实现层,实现 lib 层需要提供的原生方法。 ⑥iOS原生代码实现层,实现 lib 层需要提供的原生方法。
开发插件的一般流程如下,首先在PlatformInterface抽象类定义平台提供的API接口,如下面的getPlatformVersion方法。
abstract class FlutterPluginPlatform extends PlatformInterface {
FlutterPluginPlatform() : super(token: _token);
static final Object _token = Object();
//默认是使用MethodChannel的代理实现类
static FlutterPluginPlatform _instance = MethodChannelFlutterPlugin();
static FlutterPluginPlatform get instance => _instance;
//其他平台实现可以继承FlutterPluginPlatform实现接口方法,然后调用此方法替换代理类
static set instance(FlutterPluginPlatform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
//定义的要实现的接口方法,默认是抛出未实现异常
Future<String?> getPlatformVersion() {
throw UnimplementedError('platformVersion() has not been implemented.');
}
}
然后在原生平台的代理实现类,通过调用MethodChannel来实现接口方法。
class MethodChannelFlutterPlugin extends FlutterPluginPlatform {
//创建 MethodChannel 通道,用于与原生端通信
@visibleForTesting
final methodChannel = const MethodChannel('flutter_plugin');
@override
Future<String?> getPlatformVersion() async {
//调用原生方法
final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
return version;
}
}
然后在和插件名字相同的类中定义插件对外提供的API。
class FlutterPlugin {
Future<String?> getPlatformVersion() {
//调用代理类的方法
return FlutterPluginPlatform.instance.getPlatformVersion();
}
}
最后在原生平台提供上面需要的方法
class FlutterPlugin: FlutterPlugin, MethodCallHandler {
private lateinit var channel : MethodChannel
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding){
//创建 MethodChannel 通道,用于与Flutter端通信
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_plugin")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
又看到恶心的 if else 了,这里进阶一下。
封装一个MethodCallHandler基类
abstract class MethodHandler : MethodCallHandler {
private val methods = HashMap<String, Method>()
init {
this.javaClass.declaredMethods.forEach {
if (it.parameterTypes.size == 2 && it.isAnnotationPresent(PluginMethodInterface::class.java)) {
it.isAccessible = true
methods[it.name] = it
}
}
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
//这里记录以下曾经犯过的错误,不要把call和result放到成员,因为上一个方法可能是个异步方法,
//回调的时候下一个方法已经执行了,此时result已经变了,在用成员上保存的result返回结果时就会出错😂
val method = methods[call.method]
if (method == null) {
result.notImplemented()
} else {
method.invoke(this, call, result)
}
}
}
定义注解
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class PluginMethodInterface {
}
然后把MethodCallHandler抽取出来,通过继承MethodHandler实现,新加方法直接使用@PluginMethodInterface注解添加即可。
class PdfMethodHandler : MethodHandler() {
@PluginMethodInterface
private fun getPlatformVersion(call: MethodCall, result: MethodChannel.Result) {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
}
}
最后在Android和iOS层实现原生代码。
下面详细介绍以下 Flutter 与 Native 通信的几种方式:
MethodChannel
主要用来调用方法,Flutter 与 Native 端可以相互调用,调用后可以返回结果, Native 端调用需要在主线程中执行。
Flutter 端代码:
//创建 MethodChannel 通道,用于与原生端通信
var channel = MethodChannel('com.example.channel1');
//调用原生方法,result为原生返回的结果
var result = await channel.invokeMethod('sendData',{'name': '', 'age': 18})
//原生调用Flutter方法监听
methodChannel.setMethodCallHandler((call) async {
//flutter端方法实现
if (call.method == "sendData") {
return true;
}
});
Android 端代码
//创建 MethodChannel 通道,用于与Flutter端通信
var channel = MethodChannel(flutterPluginBinding.binaryMessenger, "com.example.channel1")
//Flutter调用原生方法监听
channel.setMethodCallHandler(PluginMethodHandler())
class PluginMethodHandler : MethodCallHandler() {
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
if (call.method == "sendData") {
result.success(true)
} else {
result.notImplemented()
}
}
}
//调用flutter方法
channel.invokeMethod("sendData",null,object :Result{
//result为flutter返回的结果
override fun success(result: Any?) {}
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {}
override fun notImplemented() {}
})
EventChannel
用于数据流(event streams)的通信, Native 端主动发送数据给 Flutter,通常用于状态的监听,比如网络变化、传感器数据等。
Flutter 端代码:
var _eventChannel = EventChannel('com.example.event1');
_eventChannel.receiveBroadcastStream().listen((event) {});
Android 端代码:
var _eventChannel = EventChannel(messenger, "com.example.event1")
_eventChannel.setStreamHandler(object :EventChannel.StreamHandler{
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
getDeviceInfo(events);
}
override fun onCancel(arguments: Any?) {
}
})
//每隔一秒向Flutter发送一次电量信息
private fun getDeviceInfo(events: EventChannel.EventSink?) {
events?.success("电量信息")
handler.postDelayed({
getDeviceInfo(events)
},1000)
}
PlatformView
PlatformView主要是用来Flutter 调用原生控件。
Flutter 端代码:
//Flutter调用原生组件的方法
class PdfController {
MethodChannel? methodChannel;
setId(int id) {
//这里一定要和原生端定义的一致
var methodChannel = MethodChannel("pdf_show_view_$id");
}
//调用原生组件的方法
scrollToPage(int page) {
if (methodChannel == null) return;
...
}
}
class PdfShowView extends StatefulWidget {
final PdfController? controller;
const PdfShowView({this.controller, Key? key}) : super(key: key);
@override
State<PdfShowView> createState() => _PdfShowViewState();
}
class _PdfShowViewState extends State<PdfShowView> {
final viewType = "com.aiosign.pdf.PdfShowView";
@override
Widget build(BuildContext context) {
return _loadNativeView();
}
Widget _loadNativeView() {
if (Platform.isAndroid) {
return AndroidView(
viewType: viewType,//这个viewType要和原生端的一一对应
onPlatformViewCreated: _onPlatformViewCreated,
);
}
return const Text("暂不支持");
}
void _onPlatformViewCreated(int id) {
//将id传给Controller
widget.controller?.setId(id);
}
}
Android 端代码:
//定义PlatformViewFactory
class FlutterPdfShowViewFactory(private val messenger: BinaryMessenger) :
PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context?, viewId: Int, args: Any?): PlatformView {
return FlutterPdfShowView(context!!, messenger, viewId, args as? Map<String, Any>?)
}
}
//定义PlatformView
class FlutterPdfShowView(
context: Context,
messenger: BinaryMessenger,
viewId: Int,
args: Map<String, Any>?
) : PlatformView {
private var disposed = false
private val pdfShowView by lazy { PdfShowView(context) }
//创建和Flutter通信的methodChannel,这里一定要和Flutter端定义的一致
private val methodChannel by lazy { MethodChannel(messenger, "pdf_show_view_$viewId") }
init {
methodChannel.setMethodCallHandler(PdfShowMethodHandler(pdfShowView))
}
//这里返回原生View
override fun getView(): View {
return pdfShowView
}
override fun dispose() {
if (disposed) {
return
}
methodChannel.setMethodCallHandler(null)
disposed = true
}
}
class PdfPlugin : FlutterPlugin {
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
val messenger = flutterPluginBinding.binaryMessenger
//把PlatformViewFactory注册到platformViewRegistry
flutterPluginBinding
.platformViewRegistry
.registerViewFactory(
"com.aiosign.pdf.PdfShowView", FlutterPdfShowViewFactory(messenger)
)
}
}
3296

被折叠的 条评论
为什么被折叠?



