编写flutter插件
系列文章
- 使用NDK编译C/C++项目
- flutter调用C/C++
- 编写flutter插件【当前文章】
- flutter中通过插件调用C++代码
当需要与native交互的时候,就需要使用原生语言为Flutter写插件。通过Flutter插件,可以使用原生语言实现Flutter没有提供的功能。比如调用系统提供的底层功能、调用第三方库等等。
在Flutter插件中,是通过Platform Channel进行通信的。共三种交互方式:
- BasicMessageChannel :用于传递字符串和半结构化信息;
- EventChannel:用于数据流的监听与发送。
- MethodChannel :用于传递方法调用和处理回调,这是最常用的一种交互方式;
创建插件
flutter create -t plugin --platforms android flutter_plugin_t1
创建一个android平台的插件。默认使用kotlin
语言。如果想使用java语言,需要传入参数:
flutter create -t plugin --platforms android -a java flutter_plugin_t1
默认的组织名为:com.example
,我们可以通过创建时传入--org
更改,比如:
flutter create -t plugin --org top.mydata --platforms android flutter_plugin_t1
创建完成之后,我们还可以通过下面的命令为插件提供更多平台:
flutter create -t plugin --platforms ios flutter_plugin_t1
flutter create -t plugin --platforms macos flutter_plugin_t1
flutter create -t plugin --platforms linux flutter_plugin_t1
flutter create -t plugin --platforms windows flutter_plugin_t1
如果是ios,可以使用-i objc
来选择使用Object C
,默认使用的swift
。
插件的基本结构
其中:
- pubspec.yaml
Flutter项目的配置文件 - test 目录中
单元测试的代码。 - example
插件的使用范例代码。 - lib
插件flutter部分的代码。 - android、iso、linux、macos、windows...
平台相关部分的代码。
封装类
在插件模板中,实现了一个获取平台版本号的例子。
- 文件
lib/hello_plugin.dart
import 'flutter_plugin_t1_platform_interface.dart';
class FlutterPluginT1 {
Future<String?> getPlatformVersion() {
return FlutterPluginT1Platform.instance.getPlatformVersion();
}
}
在该文件中定义了一个名为`FlutterPluginT1`的类,其中有一个名为`getPlatformVersion`方法。使用者只需创建该类的对象,调用这个方法就可以了。但是,这个方法中,并没有具体的实现代码。它的具体实现代码在平台目录中。那么,又是怎么调用过去的呢。原理就不讲了,我们仅仅直白的讲解代码结构。
在文件`lib/hello_plugin_platform_interface.dart`和`lib/hello_plugin_method_channel.dart`中,我们也能找到同样名为`getPlatformVersion`的方法。
在`hello_plugin_platform_interface.dart`只是定义了接口,`hello_plugin_method_channel.dart`才是接口实现。
接口定义没啥好说的,依葫芦画瓢就可以了。关键的还是接口实现:
```dart
@override
Future<String?> getPlatformVersion() async {
final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
return version;
}
上面的代码中,通过methodChannel
执行了一个名为getPlatformVersion
的函数,返回值类型为String
。真正的函数实现在各种的平台中,至于中间的封包、解包过程,flutter已经做了很好的封装。
Android系统
在文件android/src/main/kotlin/top/mydata/flutter_plugin_t1/FlutterPluginT1Plugin.kt
的onMethodCall
方法中。
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else {
result.notImplemented()
}
}
代码很简单粗暴,判断调用的函数名,如果是getPlatformVersion
,就返回一个字符串Android ${android.os.Build.VERSION.RELEASE}
。
Linux系统
在文件linux/flutter_plugin_t1_plugin.cc
的flutter_plugin_t1_plugin_handle_method_call
函数中:
static void flutter_plugin_t1_plugin_handle_method_call(
FlutterPluginT1Plugin* self,
FlMethodCall* method_call) {
g_autoptr(FlMethodResponse) response = nullptr;
const gchar* method = fl_method_call_get_name(method_call);
if (strcmp(method, "getPlatformVersion") == 0) {
struct utsname uname_data = {};
uname(&uname_data);
g_autofree gchar *version = g_strdup_printf("Linux %s", uname_data.version);
g_autoptr(FlValue) result = fl_value_new_string(version);
response = FL_METHOD_RESPONSE(fl_method_success_response_new(result));
} else {
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
}
fl_method_call_respond(method_call, response, nullptr);
}
处理语言导致的实现区别,跟Android没有逻辑上的差异。
Windows
在文件windows/flutter_plugin_t1_plugin.cpp
的HandleMethodCall
函数中:
void FlutterPluginT1Plugin::HandleMethodCall(
const flutter::MethodCall<flutter::EncodableValue> &method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
if (method_call.method_name().compare("getPlatformVersion") == 0) {
std::ostringstream version_stream;
version_stream << "Windows ";
if (IsWindows10OrGreater()) {
version_stream << "10+";
} else if (IsWindows8OrGreater()) {
version_stream << "8";
} else if (IsWindows7OrGreater()) {
version_stream << "7";
}
result->Success(flutter::EncodableValue(version_stream.str()));
} else {
result->NotImplemented();
}
}
其他平台其实也都是大同小异。
参数传递
调用侧
@override
Future<int?> add(int a, int b) async {
final sum = await methodChannel.invokeMethod<int>('add', {"msg": msg});
return sum;
}
Android下的实现
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else if (call.method == "add") {
val a = call.argument<Integer>("a")!!
val b = call.argument<Integer>("b")!!
result.success(a + b)
} else {
result.notImplemented()
}
}
这样写,后面会不好维护。可以用when
语句,如下:
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
when(call.method) {
"getPlatformVersion" -> handleGetPlatformVersion(call, result)
"add" -> handleAdd(call, result)
else -> result.notImplemented()
}
}
func handleGetPlatformVersion(@NonNull call: MethodCall, @NonNull result: Result) {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
}
func handleAdd(@NonNull call: MethodCall, @NonNull result: Result) {
val a = call.argument<Integer>("a")!!
val b = call.argument<Integer>("b")!!
result.success(a + b)
}
原生调用Flutter
通信是双向的,在原生代码中也可以调用Flutter。方法跟Flutter类似,比如,在Android原生代码中调用Flutter提供的函数ShowMessage
,方法如下:
Android中
channel.invokeMethod("ShowMessage", {"content": "hello world"})
Flutter中
默认模板中没有这部分代码。需要在MethodChannelXXXXX
中加入以下代码:
MethodChannelXXXXX() {
methodChannel.setMethodCallHandler(_handleMethodCall);
}
// _handleMethodCall
Future<void> _handleMethodCall(MethodCall call) async {
switch (call.method) {
case 'ShowMessage':
_handleShowMessage(call);
break;
default:
throw ('Not implemented: ${call.method}');
}
}
void _handleShowMessage(MethodCall call) async {
final content = call.arguments["content"];
print(content);
}
``
代码跟Flutter调用原生非常类似,不过多解释了。
下一篇,我们聊一下,通过`插件`+`NDK`在flutter中调用C++代码。
系列文章
- 使用NDK编译C/C++项目
- flutter调用C/C++
- 编写flutter插件【当前文章】
- flutter中通过插件调用C++代码