Flutter-Packages-Plugins

Posted by 杨小狼不吃羊 on 2020-11-04
Words 5.1k and Reading Time 21 Minutes
Viewed Times

Flutter Packages & Plugins(一基础篇)

Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面,这几年随着Google的大力推广和不断完善,Flutter正在被越来越多的开发者和组织使用.

1. 为什么使用Flutter开发

  • 性能强大,界面美观、流畅
    Flutter与用于构建移动应用程序的其它大多数框架不同,既不使用webView,也不使用操作系统的原生控件,而是使用自己开发的高性能渲染引擎来绘制UI(分别实现了material design和ios风格两套风格)。
  • 热重载
    热重载可以帮助我们再无需重启应用的情况下实现毫秒级的运行最新代码,解决Android修改一点代码,编译运行半天的烦恼
  • 跨平台
    Flutter早期实现了android和ios两个移动平台跨平台开发,随着版本迭代,现已支持fuchsia、macos、windows、linux桌面应用以及Web应用的开发。可以说Flutter真正实现了write once ,run anywhere,Learn onece write anywhere,可可大大介绍跨平台应用开发的难度,让开发者快速具备开发桌面应用和移动设备应用的能力,这也是我最看重的特性。

    2. 问题

  • 但是Flutter毕竟只是一个UI框架,如果我们仅仅只是简单的应用完全可以用纯flutter实现,但是如果涉及到各平台的特性和差异,又或者我们有大量的现成的各平台代码无法切换到dart,如我们公司的统一认证、OCR识别、NFC识别等等,那么怎么办呢?
  • 在Android开发时我们经常会有一些公共的组件库发布到jcenter,或私有的maven仓库供其他项目使用,那在flutter中我们怎么去使用我们的公共组件呢?

幸运的是,flutter提供了调用原生代码的能力,这就是我们今天要讲的主题Flutter的Packages和Plugins的开发。

3. 初识Packages

3.1 认识pub

pub是用来管理dart和flutter的工具,我们可以在该网站查查找google官方和其他开发者发布的packages,在packages的主页里我们可以查看packages是否支持flutter、以及插件支持的客户端平台、使用文档、example、作者信息等
20201103002334-WX20201103-002324

3.2 Packages分类

  • 纯Dart
  • 原生插件

3.3 Packages的使用

3.3.1 添加依赖

打开应用文件夹下的 pubspec.yaml 文件,然后在 pubspec.yaml 下添加依赖,如我之前发布的两个packages,在dependencies对象下添加如下依赖

1
2
xdja_bridge: ^0.0.6
gprs_convert: ^0.0.2

3.3.2 安装&使用

可以通过以下方式安装packages

  • 在命令行运行flutter pub get
  • 在 Android Studio/IntelliJ 中点击pubspec.yaml 文件顶部操作功能区的 Packages get
  • 在 VS Code 中点击位于 pubspec.yaml 文件顶部操作功能区右侧的 Get Packages,或在编辑完pubspec.yaml文件之后使用ctrl+s(command+s),保存文件之后会自动执行flutter pub get命令

安装完成后我们就可以在代码中import相关的包并使用了。

3.3.3 冲突解决

冲突解决分为package自身冲突以及各平台本身依赖的冲突,具体可点击链接查阅相关文档
冲突解决

4. 开发Package

Package 包含以下两种类别:

  • 纯 Dart 库 (Dart packages)
    用 Dart 编写的传统 package,中一些可能包含 Flutter 的特定功能,因此依赖于 Flutter 框架,其使用范围仅限于 Flutter
  • 原生插件 (Plugin packages)
    使用 Dart 编写的,按需使用 Java 或 Kotlin、ObjC 或 Swift 分别在 Android 和/或 iOS 平台实现的 package。

4.1 创建package

想要创建package,我们可以使用带有--template=package 标志的 flutter create 命令:

1
flutter create --template=package hello

执行这个命令会在目录里生成一个名称为hello的纯dart package,当然我们可以通过添加参数来进行更多的自定义,更多的参数说明我们可以通过命令行获知:
1
flutter help create

其中与packgae创建相关的常用参数如下:

  • org 包名
  • template 创建类型,app:应用,module:创建一个module添加至android或ios项目,package:可分享的flutter包,plugin:插件

  • -a:android -i:ios

  • —platforms android

    platforms Flutter 1.20.0 版本之后可用

添加完参数之后,我们常用的命令如下:

1
2
3
4
5
6
7
8
9
10
11
//创建纯dart库的package
flutter create --template=package bridge
//创建包含了example但不包含任何平台实现的package
flutter create --template=plugin bridge
//创建包含了example,并指定包名但不包含任何平台实现的package
flutter create --org com.xdja.flutter.bridge --template=plugin xdja_bridge
//创建android&ios插件项目
flutter create --org com.xdja.flutter.bridge --template=plugin --platforms android -a kotlin -i swift xdja_bridge

//在已有项目创建新平台的实现
flutter create -t plugin --platforms <platforms> .

前两个命令可以创建全新的工程,后一个命令可以在已有的工程添加对新平台的支持。

一个创建好的项目结构如下:
20201103003012-WX20201103-003006

4.2 指定插件支持的平台

插件可以通过向 pubspec.yaml 中的 platforms map 添加 keys 来指定其支持的平台。例如,以下是 xdja_bridge 插件的 flutter: map:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
flutter:
plugin:
platforms:
android:
package: com.xdja.flutter.bridge
pluginClass: BridgePlugin
# macos和web未实现
ios:
pluginClass: BridgePlugin
macos:
pluginClass: BridgePlugin
web:
pluginClass: HelloPlugin
fileName: bridge_web.dart

environment:
sdk: ">=2.1.0 <3.0.0"
# Flutter versions prior to 1.12 did not support the
# flutter.plugin.platforms map.
flutter: ">=1.12.0 <2.0.0"

4.3 实现Package

由于原生插件类型的 package 包含了使用多种编程语言编写的多个平台代码,因此需要一些特定步骤来保证体验的流畅性。

4.3.1 定义 package API(.dart)

原生插件类型 package 的 API 在 Dart 代码中要首先定义好,我们可以使用VS CodeAndroid Studio编辑package目录下lib/xxxx.dart,例如我上面创建的package就可以通过编辑lib/xdja_bridge.dart定义API。
20201102221612-WX20201102-221601

4.3.2 添加Android平台代码

20201102222904-WX20201102-222857

  1. 启动Android Studio;
  2. 在 Android Studio 的欢迎菜单 (Welcome to Android Studio) 对话框中选择打开现有的 Android Studio 项目 (Open an existing Android Studio Project),或在菜单中选择 File > Open,然后选择 xdja_bridge/example/android/build.gradle 文件;
  3. 构建完成后编辑BridgePlugin.kt添加对应API的实现,并运行查看结果

5. 添加文档

20201103003728-WX20201103-003713
为了发布package并方便其他人能够快速了解并使用,我们需要添加以下文档

  1. README.md 文件用来对 package 进行介绍
  2. CHANGELOG.md 文件用来记录每个版本的更改
  3. LICENSE 文件用来阐述 package 的许可条款
  4. API 文档包含所有的公共 API

5.1 生成文档

当我们提交package时,会自动生成API文档并将其提交到 pub.flutter-io.cn/documentation,如果希望在本地开发环境生成API文档,可以按以下步骤

5.1.1 切换至package目录

1
cd ~/dev/mypackage

5.1.2 告知文档工具Flutter SDK所在位置

1
2
3
4
5
# on macOS or Linux (适用于 macOS 或 Linux 操作系统)
export FLUTTER_ROOT=~/dev/flutter

# on Windows (适用于 Windows 操作系统)
set FLUTTER_ROOT="D:\Develop Tools\flutter-sdk\"

windows环境需要注意路径

5.1.3 运行 dartdoc 工具

1
2
3
4
# on macOS or Linux (适用于 macOS 或 Linux 操作系统)
$FLUTTER_ROOT/bin/cache/dart-sdk/bin/dartdoc
# on Windows (适用于 Windows 操作系统)
FLUTTER_ROOT%\bin\cache\dart-sdk\bin\dartdoc

5.2 添加许可证

Flutter package需要添加一个许可证文件,否则发布时可能报错。
每个 LICENSE 文件中的各个许可证应由 80 个短线字符组成的线段进行分割。
如果 LICENSE 文件中包含多个组件许可证,那么每个组件许可证必须以其所在 package 的名称开始,每个 package 名称单独一行显示,并且 package 名称列表与实际许可证内容由空行隔开。(package 名称则需与 pub package 相匹配。比如,一个 package 可能包含多个第三方代码,并且可能需要为每个 package 添加许可证。)

6. 发布Package

6.1 检查package内容是否都通过了分析

通过如下命令,我们可以发现package中存在的问题,如果有问题,会给出相应的提示,例如不存在LICENSE文件、代码错误等,我们需要进行修改,直到不存在任何问题后再进行下一步操作。
20201103003346-WX20201103-003333

1
2
3
flutter pub publish --dry-run

flutter packages pub publish --dry-run --server=https://pub.dartlang.org

6.2 发布

发布是永久性的,在发布前我们需要拥有一个google账户,并确保可以正常登录。必要时我们还需要进行Publisher认证,绑定自己的域名,经过验证的发布者发布的包可以提高可信度,并且方便使用者联系到你。

通过以下命令执行发布:

1
flutter pub publish

因众所周知的原因,中国的开发者在开发时是用了Flutter的镜像,目前所存在的镜像都不能(也不应该)进行 package 的上传。如果你设置了镜像,执行上述发布代码可能会造成发布失败。故我们需要在网络条件具备之后,无需取消中文镜像,执行下述代码可直接上传:

1
flutter packages pub publish --server=https://pub.dartlang.org

Flutter Packages & Plugins(二原理篇)

在上一篇中我们中我们熟悉了pub、package的种类,以及从创建到发布的整个流程,本篇将总结下flutter调用其他平台原始代码的原理,尤其是Android平台的实现原理。

本篇主要内容来源于flutter官方文档和博客码字坞

1. 架构概述:平台通道

Flutter 内置的平台特定 API 支持不依赖于任何生成代码,而是灵活的依赖于传递消息格式。或者,你也可以使用 Pigeon 这个 package,通过生成代码来 发送结构化类型安全消息。

  • 应用程序中的 Flutter 部分通过平台通道向其宿主(应用程序中的 iOS 或 Android 部分)发送消息。
  • 宿主监听平台通道并接收消息。然后,它使用原生编程语言来调用任意数量的相关平台 API,并将响应发送回客户端(即应用程序中的 Flutter 部分)。

消息使用平台通道在客户端(UI)和宿主(平台)之间传递,如下图所示:

20201102221250-ZTYvcD

2. Android Platform Channel

flutter的android平台的通讯Channel大致有三种。

2.1 Channel种类

Name Function
BasicMessageChannel 用于简单的数据传递
MethodChannel 传递一个方法名和参数来完成通讯
EventChannel 类似于长连接的通讯

2.2 Channel结构

20201102225814-DsdKp4

每个Channel都会配置一个Codec,用于通讯数据的编解码。Flutter的平台通讯方式采用了二进制协议,图中的 MethodCodec和MessageCodec的实现者都是采用了字节写入写出完成数据的编解码。

MessageCodec完成基础的编解码,MethodCodec在MessageCodec的基础上进一步完成方法名字的编解码。 MessageCodec目前的实现类有四个,BinaryCodec基本不做操作,直接将获得的字节数据返回;StringCodec完成 字节和字符串的转化;JSONMessageCodec完成Json结构的字符串与字节之间转化;而StandardMessageCodec采用了 更加细分的规则。

总的来说参数传递时都是只支持基本数据类型和List与Map类型的数据,只不过StandardMessageCodec比 其他的Codec更加精准,StandardMessageCodec能根据数字编号直接确定当前读到的字节数据是何种类型。

2.3 数据结构

20201102230100-FI0C8r
如图中所示,一串数据,开头字节表示为类型type,然后才是数据本体,比如当前type表示为Int型数据,那么在取数据时 直接以int形式的方式将data取出。

而MethodCodec先将方法名取出,然后将参数取出,实际还是由MessageCodec来处理。
20201102230324-J23OKR
在Flutter android中真正完成数据收发的是DartMessenger,DartMessenger包含了两个回调集合,一个关于BinaryMessageHandler 的回调集合用于接收来自Flutter的主动调用,一个BinaryReply集合用于接收Flutter对于平台调用的响应,当然BinaryMessageHandler 是需要事先完成注册的,这样Flutter调用原生时才不会扑空。

3. 通讯流程

对通道有了一个初步的认识之后,我们继续看Flutter是如何实现平台通讯的。

3.1 注册

Flutter想要调用android平台上一个功能,android就必须实现注册一个接收器,这个接收器就是BinaryMessageHandler。android平台事先会将自己定义的Channel进行注册,这里我们选择MethodChannel。并且实现了一个FlutterPlugin 插件,继承于MethodCallHandler。 比如我注册名字就是 bridge

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BridgePlugin : FlutterPlugin, MethodCallHandler {

private lateinit var channel: MethodChannel
private lateinit var context: Context

override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "bridge")
channel.setMethodCallHandler(this)
context = flutterPluginBinding.applicationContext
}

override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {

}

在我们的项目中,使用到的每一个原生插件都会在对应平台生成如下的代码进行注册:
20201102233001-WX20201102-225041
调用了setMethodCallHandler之后会封装一个BinaryMessageHandler。

1
2
3
4
5
6
7
8
9
10
11
public final class MethodChannel {
...
public void setMethodCallHandler(@Nullable MethodChannel.MethodCallHandler handler) {
this.messenger.setMessageHandler(this.name, handler == null ? null : new MethodChannel.IncomingMethodCallHandler(handler));
}
...
private final class IncomingMethodCallHandler implements BinaryMessageHandler {
...
}
...
}

最终会调用到DartMessenger的setMessageHandler,根据注册名字来完成注册。

1
2
3
4
5
6
7
8
9
10
11
12
13
class DartMessenger implements BinaryMessenger, PlatformMessageHandler {
...
private final Map<String, BinaryMessageHandler> messageHandlers;
...
public void setMessageHandler(@NonNull String channel, @Nullable BinaryMessageHandler handler) {
if (handler == null) {
this.messageHandlers.remove(channel);
} else {
this.messageHandlers.put(channel, handler);
}
}
...
}

3.2 调用

20201102233546-EhRG4N

完成注册之后,从Flutter发起调用。由Dart完成调用逻辑,Dart也实现了跟Java层一样的对接接口, 调用了MethodChannel来完成分发。在MethodChannel中调用codec将参数封装成二进制数据,然后调用 binaryMessenger发送。这里属于异步调用,可以直接将任务的Future返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Future<T> invokeMethod<T>(String method, [ dynamic arguments ]) async {
assert(method != null);
//发送
final ByteData result = await binaryMessenger.send(
name,
//封装数据
codec.encodeMethodCall(MethodCall(method, arguments)),
);
if (result == null) {
throw MissingPluginException('No implementation found for method $method on channel $name');
}
final T typedResult = codec.decodeEnvelope(result);
return typedResult;
}

在Dart层代码中binaryMessenger的实现者目前只有_DefaultBinaryMessenger,_DefaultBinaryMessenger 关于发送的逻辑的实现如下,并且设置了相应的回调。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class _DefaultBinaryMessenger extends BinaryMessenger {
...
Future<ByteData> _sendPlatformMessage(String channel, ByteData message) {
final Completer<ByteData> completer = Completer<ByteData>();
//最终调用了window的发送渠道
ui.window.sendPlatformMessage(channel, message, (ByteData reply) {
...
completer.complete(reply);
...
});
return completer.future;
}
...
@override
Future<ByteData> send(String channel, ByteData message) {
...
return _sendPlatformMessage(channel, message);
}
...
}

在window的实现中最终调用了native层的代码
1
2
3
4
//window.dart
String _sendPlatformMessage(String name,
PlatformMessageResponseCallback callback,
ByteData data) native 'Window_sendPlatformMessage';

Window_sendPlatformMessage 这个标示是在Engine层事先注册过的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//window.cc

Dart_Handle SendPlatformMessage(Dart_Handle window,
const std::string& name,
Dart_Handle callback,
const tonic::DartByteData& data) {
...
dart_state->window()->client()->HandlePlatformMessage(
fml::MakeRefCounted<PlatformMessage>(
name, std::vector<uint8_t>(buffer, buffer + data.length_in_bytes()),
response));
...
}

void _SendPlatformMessage(Dart_NativeArguments args) {
tonic::DartCallStatic(&SendPlatformMessage, args);
}
void _SendPlatformMessage(Dart_NativeArguments args) {
tonic::DartCallStatic(&SendPlatformMessage, args);
}

void Window::RegisterNatives(tonic::DartLibraryNatives* natives) {
natives->Register({
{"Window_defaultRouteName", DefaultRouteName, 1, true},
{"Window_scheduleFrame", ScheduleFrame, 1, true},
{"Window_sendPlatformMessage", _SendPlatformMessage, 4, true},
{"Window_respondToPlatformMessage", _RespondToPlatformMessage, 3, true},
{"Window_render", Render, 2, true},
{"Window_updateSemantics", UpdateSemantics, 2, true},
{"Window_setIsolateDebugName", SetIsolateDebugName, 2, true},
{"Window_reportUnhandledException", ReportUnhandledException, 2, true},
});
}

这里有个client的转发,具体的关联如下图。

20201102234035-ERAgoj

最终会分发到PlatformViewAndroid的HandlePlatformMessage上,之后就会使用JNI技术传给Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void PlatformViewAndroid::HandlePlatformMessage(
fml::RefPtr<blink::PlatformMessage> message) {
...
FlutterViewHandlePlatformMessage(env, view.obj(), java_channel.obj(),
nullptr, response_id);
...
}
}

void FlutterViewHandlePlatformMessage(JNIEnv* env,
jobject obj,
jstring channel,
jobject message,
jint responseId) {
env->CallVoidMethod(obj, g_handle_platform_message_method, channel, message,
responseId);
FML_CHECK(CheckException(env));
}
...
g_flutter_jni_class = new fml::jni::ScopedJavaGlobalRef<jclass>(
env, env->FindClass("io/flutter/embedding/engine/FlutterJNI"));
...
g_handle_platform_message_method =
env->GetMethodID(g_flutter_jni_class->obj(), "handlePlatformMessage",
"(Ljava/lang/String;[BI)V");
...

上边代码显示,最终java层的FlutterJNI类的handlePlatformMessage方法会被调用。
1
2
3
4
5
6
7
8
9
10
public class FlutterJNI {
...
private void handlePlatformMessage(@NonNull String channel, byte[] message, int replyId) {
if (this.platformMessageHandler != null) {
this.platformMessageHandler.handleMessageFromDart(channel, message, replyId);
}

}
...
}

继续分发,会给到platformMessageHandler调用。platformMessageHandler其实就是DartMessenger
1
2
3
4
5
6
7
8
9
10
class DartMessenger implements BinaryMessenger, PlatformMessageHandler {
public void handleMessageFromDart(@NonNull String channel, @Nullable byte[] message, int replyId) {
BinaryMessageHandler handler = (BinaryMessageHandler)this.messageHandlers.get(channel);
...
ByteBuffer buffer = message == null ? null : ByteBuffer.wrap(message);
handler.onMessage(buffer, new DartMessenger.Reply(this.flutterJNI, replyId));
...
}

}

这里会取出之前注册时 ,相应的名字对应的BinaryMessageHandler,这个BinaryMessageHandler应该时MethodChannel中实现的 IncomingMethodCallHandler,而MethodCallHandler就是插件需要实现的逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public final class MethodChannel {
private final class IncomingMethodCallHandler implements BinaryMessageHandler {
private final MethodChannel.MethodCallHandler handler;

IncomingMethodCallHandler(MethodChannel.MethodCallHandler handler) {
this.handler = handler;
}

@UiThread
public void onMessage(ByteBuffer message, final BinaryReply reply) {
MethodCall call = MethodChannel.this.codec.decodeMethodCall(message);
...
this.handler.onMethodCall(call, new MethodChannel.Result() {
public void success(Object result) {
reply.reply(MethodChannel.this.codec.encodeSuccessEnvelope(result));
}

public void error(String errorCode, String errorMessage, Object errorDetails) {
reply.reply(MethodChannel.this.codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails));
}

public void notImplemented() {
reply.reply((ByteBuffer)null);
}
});
...
}
}
}

3.3 回传

上面就完成了调用过程,下面就是回传过程:

20201102234454-2wxAQ0

Flutter Packages & Plugins(三实战篇)

在前两篇中我们分别学习了package的从创建到发布流程和Android平台的实现原理,这一篇我将结合实际工作实践插件开发。

在pub平台上,目前已经有大量可供Flutterk开发使用的库,例如可供网络请求的dio,用于json解析的json_annotation,json_serializable,xml解析的xml库,以及toastpath_provider等,能够满足我们的开发需求,但对于实现我们公司自己的相关业务,仍旧需要我们自己封装,gprs_convertxdja_bridge就是为了解决该问题而开发的。

1. gprs_convert

做过警务通的同学应该都知道jwt gprs server协议,在之前的项目中我们封装了java层对该协议的支持,如果使用Flutter开发警务通项目,就不可避免的需要使用到该协议,这时候如果继续使用java层的代码就不合适了,因此我做了dart层的实现,可以完成从dart对象到xml字符串的转换和从xml字符串到dart对象的解析。
下面是最常用的查询协议的实现
20201103000232-WX20201103-000211
具体使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Future<QueryResponse> queryTest() async {
QueryResponse queryResponse;
try {
QueryRequest queryRequest = QueryRequest();
queryRequest.appKey = '123';
queryRequest.reqDigest = '交通管理';
queryRequest.sessionID = Globals.loginResponse?.sessionID;
queryRequest.beginNo = '1';
// queryRequest.reqType = 'zhcx_czrk';
// queryRequest.addCondition('c_xm', '李刚',
// relation: RelationCharExpression.rightLike);
queryRequest.reqType = 'dxcx_czrk';
queryRequest.addCondition('c_sfzh', '410185198701281039');
// var request = queryRequest.buildXml();
var request = GprsConvert.convertToXml(queryRequest);
print('request:$request');
var response =
await Bridge.gprsServerRequest("192.168.20.152", "2003", request);
// var response = await rootBundle.loadString('data/query_response.xml');
print('response:$response');
// var queryRespon = QueryResponse.parseXml(response);
queryResponse = GprsConvert.parseXml(response, QueryResponse());
print(queryResponse.type);
} on PlatformException catch (e, s) {
print('Stack trace:\n ${e.message}');
}
return queryResponse;
}

2. xdja_bridge

xdja_bride是针对统一认证获取票据、获取安全卡id、执行socket请求的封装。

2.1 dart层

20201103000649-WX20201102-221601

2.2 Android层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
private fun getSafeCardId(@NonNull result: Result) {
var cardId = VpnApi50Impl(context).cardID
if (!TextUtils.isEmpty(cardId)) {
result.success(cardId)
} else {
result.error("", "获取安全卡id失败", null)
}
}

private fun getUaacToken(@NonNull result: Result) {
UaacApi.getToken(context, object : TokenCallback {
override fun onSuccess(token: String?, isSafeClientConnected: Boolean) {
result.success(token)
}

override fun onError(errorMsg: String?) {
result.error("", errorMsg, null)
}
})
}

private fun uaacLogout(@NonNull result: Result) {
var flag = UaacApi.logoutUaac(context)
if (flag != 0) {
result.success(true)
} else {
result.error("", "统一认证失败,无删除token权限", null)
}
}

private fun getPlatformVersion(@NonNull result: Result) {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
}

private fun gprsServerRequest(@NonNull call: MethodCall, @NonNull result: Result) {
var ip = call.argument<String>("ip")
var port = call.argument<String>("port")
var request = call.argument<String>("request")
var socketManager = SocketManager(ip, port)
AppExecutors.getInstance().networkIO.execute {
var response = socketManager.execute(request)
// 解析返回结果
CErrorInfo.paserErrorMsg(response)
AppExecutors.getInstance().mainThread.execute {
if (!CErrorInfo.isError) {
result.success(response)
} else {
result.error(CErrorInfo.code, CErrorInfo.message, null)
}
}
}

}

3. 问题和思考

在Android平台经过常年的积累,我们已经有大量的公共组件,一时想用flutter来进行实际项目的开发,仍会面临很多的问题,例如我demo中使用socket进行网络请求,而警务通也有更多的项目对接到服务总线,对警务通常用的UI组件仍旧缺失,仍需大量的工作。
从长远来看,flutter对跨平台的支持越来越完善,如果我们能充分利用现有的开发人员,完善相关平台插件的开发,例如统一认证桌面端和android端,安全卡操作相关的业务,未来我们完全可以实现一套代码各个平台运行,缩短项目的使用周期,实现更高的工作效率。


This is copyright.

...

...

00:00
00:00