• Kotlin/Native 用KMM写Flutter插件


    一、用KMM写Flutter插件

    Google官方有一个写Flutter例子How to write a Flutter plugin,这里把Google plugin_codelab 例子改成用KMM写Flutter插件。

    二、如何运行

    Github项目地址:kmm-flutter-plugin

    Android: run shared/plugin_codelab/example/android

    iOS:

    1、build shared.framework

    use ./gradlew releaseIOSFramework
    or use new version Android Studio sync
    

    2、run shared/plugin_codelab/example/ios

    Tips: before run,shared/build/cocoapods/framework/shared.framework should be generated. The shared.h header file shared/build/cocoapods/framework/shared.framework/Headers/shared.h is generated.

    三、设计思路

    Android/iOS插件PluginCodelabPlugin只需要实现KMM Module的接口,不写任何逻辑,把逻辑通过接口放在KMM Module中。

    1、定义接口中间层用于转发数据

    如参考Flutter插件的MethodCall、MethodChannel,定义CommonMethodCall数据类、CommonMethodChannel.Result接口。

    data class CommonMethodCall(
        val method: String,
        val arguments: Any?,
    )
    
    class CommonMethodChannel {
        interface Result {
            fun success(result: Any?)
    
            fun error(errorCode: String?, errorMessage: String?, errorDetails: Any?)
    
            fun notImplemented()
        }
    }
    

    2、在KMM中的commonMain实现CommonCodelabPlugin插件的公共逻辑

    CommonCodelabPlugin需要初始化并启动synth?.start(),处理getPlatformVersion、onKeyDown、onKeyUp逻辑。

    class CommonCodelabPlugin {
    
        private val synth = Synth()
    
        init {
            synth?.start()
        }
    
        fun onMethodCall(call: CommonMethodCall, result: CommonMethodChannel.Result) {
            when (call.method) {
                "getPlatformVersion" -> {
                    result.success(Platform().platform)
                }
                "onKeyDown" -> {
                    try {
                        val arguments = call.arguments as List<*>
                        val numKeysDown = synth?.keyDown((arguments[0] as Int))
                        result.success(numKeysDown)
                    } catch (ex: Exception) {
                        result.error("1", ex.message, ex.cause)
                    }
                }
                "onKeyUp" -> {
                    try {
                        val arguments = call.arguments as List<*>
                        val numKeysDown = synth?.keyUp((arguments[0] as Int))
                        result.success(numKeysDown)
                    } catch (ex: Exception) {
                        result.error("1", ex.message, ex.cause)
                    }
                }
                else -> {
                    result.notImplemented()
                }
            }
        }
    }
    

    还有包括插件名称也属于公共逻辑

    // 插件Channel名称
    const val PLUGIN_CODE_LAB_CHANNEL = "plugin_codelab"
    

    3、实现平台差异特性

    这里只列出expect接口,具体实现平台差异特性类请查看源码

    expect class Synth() {
        fun start()
    
        fun keyDown(key: Int): Int
    
        fun keyUp(key: Int): Int
    }
    
    expect class Platform() {
        val platform: String
    }
    

    4、Android Flutter实现插件KMM接口

    Android Flutter实现插件KMM接口,注意这里只实现接口用于中转Flutter与Android/iOS 数据,不能有任何业务逻辑

    class PluginCodelabPlugin : FlutterPlugin, MethodCallHandler {
        private var channel: MethodChannel? = null
        private var commonCodelabPlugin: CommonCodelabPlugin? = null
    
        override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
            setup(this, flutterPluginBinding.binaryMessenger)
        }
    
        override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
            commonCodelabPlugin?.onMethodCall(
                call = CommonMethodCall(call.method, call.arguments),
                result = object : CommonMethodChannel.Result {
                    override fun success(successResult: Any?) {
                        result.success(successResult)
                    }
    
                    override fun error(errorCode: String?, errorMessage: String?, errorDetails: Any?) {
                        result.error(errorCode, errorMessage, errorDetails)
                    }
    
                    override fun notImplemented() {
                        result.notImplemented()
                    }
                })
        }
    
        override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
            channel?.setMethodCallHandler(null)
        }
    
        companion object {
            private fun setup(plugin: PluginCodelabPlugin, binaryMessenger: BinaryMessenger) {
                plugin.channel = MethodChannel(binaryMessenger, PLUGIN_CODE_LAB_CHANNEL)
                plugin.channel?.setMethodCallHandler(plugin)
                plugin.commonCodelabPlugin = CommonCodelabPlugin()
            }
        }
    }
    

    5、iOS Flutter实现插件KMM接口

    Android Flutter实现插件KMM接口,注意这里只实现接口用于中转Flutter与Android/iOS 数据,不能有任何业务逻辑

    #import "PluginCodelabPlugin.h"
    
    @implementation PluginCodelabPlugin{
      int _numKeysDown;
      FlutterResult _flutterResult;
      SharedCommonCodelabPlugin* _codelabPlugin;
    }
    
    - (instancetype)init {
      self = [super init];
      if (self) {
        // create music
        _codelabPlugin = [[SharedCommonCodelabPlugin alloc] init];
      }
      return self;
    }
    
    - (void)dealloc {
        // destroy music
    }
    
    + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
      FlutterMethodChannel* channel = [FlutterMethodChannel
          methodChannelWithName: SharedPluginCodeLabKt.PLUGIN_CODE_LAB_CHANNEL
                binaryMessenger:[registrar messenger]];
      PluginCodelabPlugin* instance = [[PluginCodelabPlugin alloc] init];
      [registrar addMethodCallDelegate:instance channel:channel];
    }
    
    - (void)handleMethodCall:(FlutterMethodCall *)call
                      result:(FlutterResult)result {
        SharedCommonMethodCall *methodCall = [[SharedCommonMethodCall alloc] initWithMethod:call.method arguments:call.arguments];
        _flutterResult = result;
        [_codelabPlugin onMethodCallCall:methodCall result:self ];
    }
    
    - (void)errorErrorCode:(NSString * _Nullable)errorCode errorMessage:(NSString * _Nullable)errorMessage errorDetails:(id _Nullable)errorDetails {
        NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain code:errorCode.intValue userInfo:@{@"errorMessage":errorMessage, @"errorDetails":errorDetails}];
        if (_flutterResult) {
            _flutterResult(error);
        }
    }
    
    - (void)notImplemented {
        if (_flutterResult) {
            _flutterResult(FlutterMethodNotImplemented);
        }
    }
    
    - (void)successResult:(id _Nullable)result {
        if (_flutterResult) {
            _flutterResult(result);
        }
    }
    
    @end
    

    到这里,已经完成了使用KMM开发一个Flutter插件。使用KMM开发插件的好处是公共逻辑都使用kotlin写,一般公共逻辑比较简单适合使用kotlin写,便于维护。而且,实现了KMM写插件,Flutter写UI。

    四、参考链接

    本文地址:https://www.cnblogs.com/liqw/p/15477079.html
    Github项目地址:kmm-flutter-plugin

  • 相关阅读:
    App Submission Issues
    番茄钟App(Pomodoro Tracker)
    WatchKit App Submission Issues
    Watch​Kit Learning Resources
    Scrum Planning Card
    How to get cocoapods work on Yosemite
    如何在DigitalOcean安装Ghost
    Swift学习资源
    Xcode 6 模拟器路径
    【Android-4】Scheme跳转协议
  • 原文地址:https://www.cnblogs.com/liqw/p/15477079.html
Copyright © 2020-2023  润新知