How to develop and publish Flutter native plugins? Plugin lifecycle management?
考察点:原生插件开发和发布。
答案:
Flutter插件开发是扩展Flutter生态系统的重要方式,涉及跨平台API设计、原生代码集成、测试验证和发布流程。插件生命周期管理确保资源的正确分配和释放,避免内存泄漏和性能问题。
插件项目创建和结构:
-
创建插件项目:
flutter create --template=plugin --platforms=android,ios my_awesome_plugin
my_awesome_plugin/
├── lib/
│ └── my_awesome_plugin.dart
├── android/
│ └── src/main/kotlin/
├── ios/
│ └── Classes/
├── example/
├── test/
└── pubspec.yaml
-
插件配置文件:
name: my_awesome_plugin
description: A powerful Flutter plugin for awesome features
version: 1.0.0
homepage: https://github.com/username/my_awesome_plugin
environment:
sdk: '>=2.17.0 <4.0.0'
flutter: ">=2.5.0"
flutter:
plugin:
platforms:
android:
package: com.example.my_awesome_plugin
pluginClass: MyAwesomePlugin
ios:
pluginClass: MyAwesomePlugin
dependencies:
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
Dart API层设计:
-
插件主接口:
library my_awesome_plugin;
import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
export 'src/models.dart';
export 'src/exceptions.dart';
class MyAwesomePlugin {
static const MethodChannel _methodChannel =
MethodChannel('my_awesome_plugin/methods');
static const EventChannel _eventChannel =
EventChannel('my_awesome_plugin/events');
static final MyAwesomePlugin _instance = MyAwesomePlugin._internal();
factory MyAwesomePlugin() => _instance;
MyAwesomePlugin._internal();
static Future<void> initialize({required String apiKey}) async {
try {
await _methodChannel.invokeMethod('initialize', {
'apiKey': apiKey,
'platform': Platform.operatingSystem,
});
} on PlatformException catch (e) {
throw PluginException.fromPlatformException(e);
}
}
static Future<PluginResult> performAction({
required String action,
Map<String, dynamic>? parameters,
}) async {
try {
final result = await _methodChannel.invokeMethod('performAction', {
'action': action,
'parameters': parameters ?? {},
});
return PluginResult.fromMap(result);
} on PlatformException catch (e) {
throw PluginException.fromPlatformException(e);
}
}
static Stream<PluginEvent> get eventStream {
return _eventChannel
.receiveBroadcastStream()
.map((data) => PluginEvent.fromMap(data));
}
static Future<void> dispose() async {
await _methodChannel.invokeMethod('dispose');
}
}
-
数据模型定义:
class PluginResult {
final bool success;
final String? message;
final Map<String, dynamic>? data;
PluginResult({
required this.success,
this.message,
this.data,
});
factory PluginResult.fromMap(Map<String, dynamic> map) {
return PluginResult(
success: map['success'] ?? false,
message: map['message'],
data: map['data'],
);
}
}
class PluginEvent {
final String type;
final Map<String, dynamic> payload;
PluginEvent({required this.type, required this.payload});
factory PluginEvent.fromMap(Map<String, dynamic> map) {
return PluginEvent(
type: map['type'] ?? '',
payload: Map<String, dynamic>.from(map['payload'] ?? {}),
);
}
}
Android平台实现:
class MyAwesomePlugin: FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler,
ActivityAware, PluginRegistry.ActivityResultListener {
private lateinit var methodChannel: MethodChannel
private lateinit var eventChannel: EventChannel
private var eventSink: EventChannel.EventSink? = null
private var activity: Activity? = null
private var context: Context? = null
private var isInitialized = false
private val backgroundExecutor = Executors.newCachedThreadPool()
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
context = binding.applicationContext
setupChannels(binding.binaryMessenger)
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
cleanup()
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity
binding.addActivityResultListener(this)
}
override fun onDetachedFromActivity() {
activity = null
}
private fun setupChannels(messenger: BinaryMessenger) {
methodChannel = MethodChannel(messenger, "my_awesome_plugin/methods")
methodChannel.setMethodCallHandler(this)
eventChannel = EventChannel(messenger, "my_awesome_plugin/events")
eventChannel.setStreamHandler(this)
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"initialize" -> {
val apiKey = call.argument<String>("apiKey")
initializePlugin(apiKey, result)
}
"performAction" -> {
if (!isInitialized) {
result.error("NOT_INITIALIZED", "Plugin not initialized", null)
return
}
val action = call.argument<String>("action")!!
val parameters = call.argument<Map<String, Any>>("parameters") ?: emptyMap()
performActionAsync(action, parameters, result)
}
"dispose" -> {
cleanup()
result.success(null)
}
else -> result.notImplemented()
}
}
private fun initializePlugin(apiKey: String?, result: MethodChannel.Result) {
if (apiKey.isNullOrEmpty()) {
result.error("INVALID_API_KEY", "API key is required", null)
return
}
backgroundExecutor.execute {
try {
ThirdPartySDK.initialize(context!!, apiKey)
isInitialized = true
Handler(Looper.getMainLooper()).post {
result.success(mapOf("initialized" to true))
}
} catch (e: Exception) {
Handler(Looper.getMainLooper()).post {
result.error("INITIALIZATION_FAILED", e.message, null)
}
}
}
}
private fun cleanup() {
isInitialized = false
eventSink = null
backgroundExecutor.shutdown()
ThirdPartySDK.cleanup()
}
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
eventSink = events
startListening()
}
override fun onCancel(arguments: Any?) {
eventSink = null
}
}
插件发布流程:
-
预发布检查:
dart format .
dart analyze
flutter test
cd example
flutter test
flutter build apk --debug
flutter build ios --debug
-
版本管理和发布:
dart pub publish --dry-run
dart pub publish
插件生命周期管理最佳实践:
- 在onAttachedToEngine中初始化资源
- 在onDetachedFromEngine中清理资源
- 正确处理Activity生命周期变化
- 使用后台线程处理耗时操作
- 实现完善的错误处理和日志记录
- 提供清晰的API文档和示例代码