Flutter Package & Plugin
Flutter Package & Plugin
Flutter 包和插件
Package Dart 包
当你需要开发一个纯 Dart 组件(比如一个自定义的 Weidget)的时候使用的形式,内部没有 Native 平台的代码。
Plugin 插件包
插件包(Plugin packages)是当你需要暴露 Native API 给别人的时候使用的形式,内部需要使用 Platform Channels 并包含 Androiod/iOS 原生逻辑
defaultTargetPlatform 获取平台信息
- defaultTargetPlatform 获取当前应用的平台信息
- debugDefaultTargetPlatformOverride 全局变量的值来指定应用平台
1
2
debugDefaultTargetPlatformOverride=TargetPlatform.iOS;
print(defaultTargetPlatform); // 会输出TargetPlatform.iOS
上面代码即使在 Android 中运行后,Flutter APP 也会认为是当前系统是 iOS,Material 组件库中所有组件交互方式都会和 iOS 平台对齐,defaultTargetPlatform 的值也会变为 TargetPlatform.iOS。
插件开发
实现一个获取电池电量的 Flutter 插件
创建 Flutter plugin
- 命令方式
flutter create –org com.example –template=plugin pluginName
默认情况下,iOS 代码使用 swift 语言编写,Android 代码使用 Kotlin 语言编写。但是你可以通过 -i 指定 iOS 的语言;-a 指定 Android 的开发语言。
flutter create –template=plugin -i objc -a java pluginName
- AS
- 目录结构
配置 pubspec.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
flutter:
# This section identifies this Flutter project as a plugin project.
# The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)
# which should be registered in the plugin registry. This is required for
# using method channels.
# The Android 'package' specifies package in which the registered class is.
# This is required for using method channels on Android.
# The 'ffiPlugin' specifies that native code should be built and bundled.
# This is required for using `dart:ffi`.
# All these are used by the tooling to maintain consistency when
# adding or updating assets for this project.
plugin:
platforms:
android:
package: me.hacket.flutter_plugin_demo
pluginClass: FlutterPluginDemoPlugin
ios:
pluginClass: FlutterPluginDemoPlugin
添加 Flutter 层代码实现
在 lib 下新增 batteryLevel.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class BatteryLevel {
static const MethodChannel _channel = MethodChannel(
'me.hacket/battery');
static Future<String> getBatteryLevel() async {
String batteryLevel;
try {
final int result = await _channel.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level: $result%.';
} on PlatformException {
batteryLevel = 'Failed to get battery level.';
}
return batteryLevel;
}
}
添加不同平台的实现
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
class FlutterPluginDemoPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
private val CHANNEL = "me.hacket/battery"
private lateinit var channel: MethodChannel
private lateinit var context: Context
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, CHANNEL)
channel.setMethodCallHandler(this)
context = flutterPluginBinding.applicationContext
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
if (call.method == "getBatteryLevel") {
val batteryLevel = getBatteryLevel()
if (batteryLevel != -1) {
result.success(batteryLevel)
} else {
result.error("UNAVAILABLE", "Battery level not available.", null)
}
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
private fun getBatteryLevel(): Int {
val batteryLevel: Int
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val batteryManager = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
} else {
val intent = ContextWrapper(context).registerReceiver(
null,
IntentFilter(
Intent.ACTION_BATTERY_CHANGED
)
)
batteryLevel =
intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(
BatteryManager.EXTRA_SCALE,
-1
)
}
return batteryLevel
}
}
Android 工程报错:
- 用单独的 as 窗口打开 Android 目录
- 在 Android 目录下的 build.gradle 添加依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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) {
println "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")
}
iOS 平台的实现
添加文档
建议将下列文档添加到所有 package 中:
- README.md 文件用来对 package 进行介绍
- CHANGELOG.md 文件用来记录每个版本的更改
- LICENSE 文件用来阐述 package 的许可条款
- API 文档包含所有的公共 API
发布插件
发布的 pub.dev
- 自定义插件开发完成后,您可以将自定义插件发布到 pub.dev ,方便其他开发者使用。但是,在发布之前,请检查 pubspec.yaml、 README.md、 CHANGELOG.md 和 LICENSE 文件以确保内容完整和正确。
- 接下来在模式下运行 publish 命令, dry-run 看看是否都通过了分析:
$ flutter pub publish –dry-run
- 下一步是发布到 pub.dev,但请确保您已准备就绪,因为发布是无法恢复的最后一步:
$ flutter pub 发布
错误:
- The name of “lib\batteryLevel.dart”, “batteryLevel”, should match the name of the package, “flutter_plugin_demo”.
将 yaml 中的 name 改成 lib 下的 batteryLevel.dart 的 batteryLevel
本文由作者按照 CC BY 4.0 进行授权