开发环境
flutter doctor
[✓] Flutter (Channel stable, 3.7.7,on macOS 13.1 22C65 darwin-x64, locale
zh-Hans-CN)
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 14.2)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2022.1)
[✓] IntelliJ IDEA Ultimate Edition (version 2021.3)
[✓] VS Code (version 1.76.2)
一:新建插件工程
若已打开Android Studio:File->New->New Flutter Project->Next
Project name:工程名 注意非驼峰命名,采用下划线连接(xx_xx_xx,lower_case_with_underscores)
Project location:保存路径,文件夹名注意与Project name一致,若不改则使用Project name名称
Description:工程介绍
Project type:工程选择,这里选择plugin
Organization:包名
二:iOS功能开发
1:开发目录确定
在项目根目录 example的iOS目录下执行pod install
cd example/ios
pod install
右键iOS,最底部Flutter -> Open iOS module in Xcode或者在文件夹example/ios
中找到Runner.xcworkspace
双击打开,这个时候就能看到Runner和Pod两个target。
Runner:example 可运行的主工程,用来测试插件的,用来Flutter启动iOS的
-
Pod:将插件生成本地pod库,用来调试
点击展开Pod->Development Pods->...->Classes,一直到Classes就是我们插件代码放置的位置,可以看到已经生成的xxxPlugin.swift
2:定义方法
根目录的lib文件夹中自动生成的三个文件
xx_dart:项目调用插件的唯一途径
xx_method_channel:平台通道,将API暴露给Flutter应用程序,<u>若不需要跨端就直接在此处处理逻辑了</u>
xx_platform_interface:平台接口,定义了插件的API。
也是是我们的开发顺序是:xx_dart->xx_platform_interface->xx_method_channel
xx_dart:定义我们此插件需要为别的项目提供哪些能力,也就是插件的api。
xx_platform_interface:定义平台也就是原生端需要提供给插件的响应api,接口声明
xx_method_channel:通过通道调用原生方法
原生去响应通道的方法:
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
result("iOS " + UIDevice.current.systemVersion)
}
已自动生成的方法,需要根据call.method
判断Dart那边调用原生的方法。
学习方法使用,无非就是相互调用,参数互传。
思路:我需要插件帮我计算下a+b=?
,插件需要2秒后给我个"hellow world"
xx.dart中定义方法
class xxPlayer {
Future<String?> getPlatformVersion() {
return xxPlayerPlatform.instance.getPlatformVersion();
}
// 原生去计算A+B
Future<int?> sumAB(){
return xxPlayerPlatform.instance.sumAB(a,b);
}
// 监听原生调用
void listenNative(NativeCallBack nativeCallBack) {
return EchoPlayerPlatform.instance.listenNative(nativeCallBack);
}
}
xx_platform_interface.dart声明接口
Future<int?> sumAB(int a, int b) {
throw UnimplementedError('sumAB() has not been implemented.');
}
void listenNative(NativeCallBack nativeCallBack) {
throw UnimplementedError('startCount() has not been implemented.');
}
xx_method_channel.dart重写实现接口,通道调用原生
typedef NativeCallBack= void Function(dynamic result);
@override
Future<int?> sumAB(int a,int b) async {
final result = await methodChannel
.invokeMethod<int>('sumAB', {"a": a, "b": b});
return result;
}
// 和fullter调用原生一样,都是通过通道通信的
@override
void listenNative(NativeCallBack nativeCallBack) {
methodChannel.setMethodCallHandler((call) {
if (call.method == 'countUp') {
nativeCallBack(call.arguments);
}
return Future(() => null);
});
}
xxPlugin.swift中实现方法
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
if call.method == "sumAB" {
// 取参数 插件接口已定义必填,可强制拆包
let args = call.arguments as! [String: Int]
let a = args["a"]!
let b = args["b"]!
result(sumAB(a: a, b: b))
// 执行方法并回调
result(sumAB(a: a, b: b))
} else if call.method == "startCount" {
// 定时器模拟重复调用的场景,延迟或者其他情况直接使用channel.invokeMethod即可
countTime()
}
result("iOS " + UIDevice.current.systemVersion)
}
public func sumAB(a: Int, b: Int) -> Int {
return a + b
}
var count = 0
public func countTime() {
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(sendToFlutter), userInfo: nil, repeats: true)
}
@objc func sendToFlutter() {
count += 1
channel!.invokeMethod("countUp", arguments: ["count": count])
}
exmaple/main.dart 调用测试
TextButton(
onPressed: () {
_xxPlugin
.sumAB(3, 4)
.then((value) => {debugPrint(value.toString())});
},
child: const Text("SumAB"))
TextButton(
onPressed: () {
//开启定时计数
_echoPlayerPlugin.startCount();
//监听计数回调
_echoPlayerPlugin.listenNative((result) {
print(result);
});
},
child: const Text("countUp"))
三:Android功能开发
在工程example/android 上右键open Android module in Android studio,依赖加载有些久,打开后就可以看到我们其实要开发的就是个Android的module。
Android这边要好些的是channel已经是是变量,可以直接主动像flutter发送消息
class XXPlugin: FlutterPlugin, MethodCallHandler {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var channel : MethodChannel
//注册通道
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "echo_player")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
}
else {
result.notImplemented()
}
}
//注销通道
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
sumAB
和startCount
的实现
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 == "sumAB") {
// 获取传递的参数
val args = call.arguments as? Map<String, Int>
val a = args?.get("a")
val b = args?.get("b")
if (a != null && b != null) {
result.success(a + b)
}
} else if (call.method == "startCount") {
// 定时器模拟重复调用的场景,延迟或者其他情况直接使用channel.invokeMethod即可
countTime()
} else {
result.notImplemented()
}
}
fun countTime() {
val timer = Timer()
val delay = 0L // 0 second delay
val period = 1000L // 1 second interval
// 回去主线程 否则会崩溃
val handler = android.os.Handler(Looper.getMainLooper())
val task = object : TimerTask() {
override fun run() {
handler.post{
count++
channel.invokeMethod("countUp", mapOf("count" to count))
}
}
}
timer.scheduleAtFixedRate(task, delay, period)
}