What's flutter插件?
flutter作为一个ui绘制工具,是不能直接调用一些原生功能的,比如相机,传感器,屏幕亮度等等,所以需要原生实现,然后再用flutter和原生通信,这就是flutter插件。
How?
那么flutter怎么和原生通信呢,flutter官网有如下的图
可以看到,flutter是通过Platform Channels(官方翻译为平台通道)和原生通信的,图中的MethodChannel就是PlatformChannel中的一种,另外还有EventChannel。
Demo
官网有一个获取设备电池状态的demo,接下来我会写一个获取设备屏幕亮度的demo,包括实际开发flutter plugin的具体流程细节和一些可能会遇到的坑。
1.新建module
选择Flutter Plugin,在location那一栏,选择到当前flutter项目下的plugins文件夹(没有就新建),就会生成一个如图的flutter项目
2.配置gradle
android端,build.gradle下添加如下
//获取local.properties配置文件
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
//获取flutter的sdk路径
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
compileOnly files("$flutterRoot/bin/cache/artifacts/engine/android-arm/flutter.jar")
compileOnly 'androidx.annotation:annotation:1.1.0'
}
在local.properties文件下增加android sdk和flutter sdk的路径(如果没有),如下
sdk.dir=D:\\as\\sdk
flutter.sdk=F:\\flutter\\flutter
然后使用androidStudio打开screen_light下的android项目,编译运行,如果这一步没有问题的话,ScreenLightPlugin这个类可以正常编译通过,不会报错。
3. flutter传值到platform
在lib文件夹下编写flutter代码
3.1 创建MethodChannel对象
final MethodChannel _channel = const MethodChannel(_channel_name);
其中的_channel_name参数,需要保证在flutter、android、ios各端都是同一个值。
3.2 调用invokeMethod方法
///调用platform方法,并返回bool
Future<bool> _invokeBooleanMethod(String method, [dynamic arguments]) async {
final bool result = await _channel.invokeMethod<bool>(
method,
arguments,
);
return result;
}
通过调用_channel的invokeMethod方法,可以把flutter的参数传到原生端
3.3 android端接受参数
在android目录下,ScreenLightPlugin类实现FlutterPlugin、ActivityAware和MethodChannel.MethodCallHandler
插件加载后,会执行onAttachedToEngine方法,可以做一些初始化操作;
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
context = flutterPluginBinding.applicationContext
channel = MethodChannel(flutterPluginBinding.binaryMessenger, _channelName)
channel.setMethodCallHandler(this)
}
插件卸载执行onDetachedFromEngine方法;
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
还有onAttachedToActivity可以获取到activity;(需要实现ActivityAware接口)
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity
}
另外一个最重要的方法是onMethodCall,当flutter通过_channel.invokeMethod调用方法后,会执行此方法,并且可以通过MethodCall参数获取到flutter传的参数
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
when (call.method) {
_getSysScreenLight -> callGetSysScreenLight(call, result)
_setAppScreenLight -> callSetAppScreenLight(call, result)
_getAppScreenLight -> callGetAppScreenLight(call, result)
_listenSysScreenLight -> callListenSysScreenLight(call, result)
_unlistenSysScreenLight -> callUnlistenSysScreenLight(call, result)
}
}
//参数获取flutter传的参数
val light: Int? = call.argument<Int>(_argLight)
3.4 platform处理后,返回值到flutter
通过onMethodCall下的Result参数,可以返回相应的值到flutter
result.success(true)
以上的流程可以总结为,在flutter端,通过MethodChannel对象,调用invokeMethod方法,传值到原生端,原生端处理后,通过Result参数返回值到flutter端
有一个问题,是flutter和android端的ScreenLightPlugin类怎么关联在一起的,其实是在插件的pubspec.yaml文件里面配置的
flutter:
plugin:
platforms:
android:
package: com.perdev.screen_light
pluginClass: ScreenLightPlugin
ios:
pluginClass: ScreenLightPlugin
4. 回调方法(原生传值到flutter)
和flutter传值到原生一样,也是先在原生端获得MethodChannel对象,然后调用invokeMethod方法,然后就会走到flutter的这个地方
_channel.setMethodCallHandler((call) => _handleMethod(call));
///回调,platform主动调用channel.invokeMethod方法后,会走到此处
Future<dynamic> _handleMethod(MethodCall call) {
switch (call.method) {
case _callbackSysScreenLightChanged:
final map = call.arguments as Map<dynamic, dynamic>;
final light = map[_argLight] as int;
final selfChange = map[_argSelfChange] as bool;
_list.forEach((v) {
v(light, selfChange);
});
break;
}
return Future<dynamic>.value(null);
}
5. 待续,ios平台实现代码
6. 发布插件
当插件开发完成后,可以把插件发布到pub.dev,供其他人使用,发布流程比较简单的,看一下这个文档就会了。