Flutter插件开发流程

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

项目结构

FlutterPlugin项目结构图

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)
            )
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值