插件开发与使用
Flutter 插件(Plugin)是连接 Dart 代码与平台原生能力的桥梁。当你需要调用 Android、iOS 或鸿蒙等平台特有的 API(如摄像头、蓝牙、传感器)时,就需要通过插件来实现。
插件与包的区别
| 概念 | Dart 代码 | 原生代码 | 适用场景 |
|---|---|---|---|
| 包(Package) | ✅ | ❌ | 纯 Dart 逻辑:工具类、UI 组件 |
| 插件(Plugin) | ✅ | ✅ | 需要调用平台原生 API |
简单理解:纯 Dart 写的叫"包",需要写原生代码的叫"插件"。
开发插件
创建插件项目
bash
# 创建一个支持 Android/iOS 的插件
flutter create --template=plugin --platforms=android,ios my_plugin
# 创建一个支持鸿蒙的插件(需 flutter-ohos 环境)
flutter create --template=plugin --platforms=android,ios,harmony my_plugin创建后,插件的目录结构如下:
my_plugin/
├── lib/ # 📁 Dart 代码
│ └── my_plugin.dart # 插件的 Dart 接口
├── android/ # 📁 Android 原生实现
│ └── src/main/kotlin/... # Kotlin 代码
├── ios/ # 📁 iOS 原生实现
│ └── Classes/ # Swift 代码
├── harmony/ # 📁 鸿蒙原生实现
│ └── entry/src/main/ets/ # ArkTS 代码
├── example/ # 📁 示例应用(用于调试插件)
├── pubspec.yaml # 📄 插件配置
└── README.md插件的核心结构
一个插件由三部分组成:
1. Dart 接口(lib/my_plugin.dart) — 定义给调用方使用的 API:
dart
import 'package:flutter/services.dart';
class MyPlugin {
// 创建 MethodChannel,名称需与原生端一致
static const MethodChannel _channel = MethodChannel('my_plugin');
/// 调用原生方法
static Future<String> getPlatformVersion() async {
final String version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
/// 监听原生事件
static EventChannel get eventChannel => const EventChannel('my_plugin/events');
}2. 原生端实现(以 Android 为例) — 处理来自 Dart 的调用:
kotlin
// android/src/main/kotlin/com/example/my_plugin/MyPlugin.kt
package com.example.my_plugin
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
class MyPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
private lateinit var channel: MethodChannel
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
// 注册 Channel,名称需与 Dart 端一致
channel = MethodChannel(binding.binaryMessenger, "my_plugin")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"getPlatformVersion" -> {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
}
else -> result.notImplemented()
}
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}3. 鸿蒙端实现 — 使用 ArkTS 处理调用:
typescript
// harmony/entry/src/main/ets/MyPlugin.ts
import { MethodChannel, FlutterPlugin, FlutterPluginBinding } from '@ohos/flutter_ohos';
export class MyPlugin implements FlutterPlugin {
private channel?: MethodChannel;
getUniqueClassName(): string {
return 'MyPlugin';
}
onAttachedToEngine(binding: FlutterPluginBinding): void {
this.channel = new MethodChannel(binding.getBinaryMessenger(), 'my_plugin');
this.channel.setMethodCallHandler({
onMethodCall: (call, result) => {
if (call.method === 'getPlatformVersion') {
result.success('HarmonyOS NEXT');
} else {
result.notImplemented();
}
}
});
}
onDetachedFromEngine(binding: FlutterPluginBinding): void {
this.channel?.setMethodCallHandler(null);
}
}插件通信机制
Dart 与原生端通过 MethodChannel 通信,核心流程:
Dart 调用方 原生实现
│ │
│ invokeMethod('方法名', 参数) │
│ ─────────────────────────> │
│ │ 处理调用,返回结果
│ <───────────────────────── │
│ Future<结果> │| Channel 类型 | 用途 | 通信方向 |
|---|---|---|
MethodChannel | 调用方法、获取返回值 | Dart ↔ 原生 |
EventChannel | 原生向 Dart 持续推送事件 | 原生 → Dart |
BasicMessageChannel | 双向消息传递 | Dart ↔ 原生 |
使用插件
方式一:从 pub.dev 安装
公开发布的插件,直接添加依赖:
bash
flutter pub add shared_preferences方式二:引用本地插件(私有插件)
在 pubspec.yaml 中使用 path 引用本地目录下的插件:
yaml
dependencies:
my_plugin:
path: plugins/my_plugin # 相对于 pubspec.yaml 的路径方式三:引用 Git 仓库中的插件
yaml
dependencies:
my_plugin:
git:
url: https://github.com/xxx/my_plugin.git
ref: main # 分支名或 commit hash在代码中调用
dart
import 'package:my_plugin/my_plugin.dart';
// 调用插件方法
final version = await MyPlugin.getPlatformVersion();
print(version); // Android 14 / HarmonyOS NEXT私有插件的目录管理
如果插件只在自己的项目中使用,不需要发布到 pub.dev,推荐的做法是将插件放在项目根目录的 plugins/ 目录下。
推荐目录结构
my_app/ # 你的 Flutter 项目
├── lib/ # 应用源码
├── plugins/ # 📁 私有插件目录
│ ├── my_plugin_a/ # 插件 A
│ │ ├── lib/
│ │ ├── android/
│ │ ├── harmony/
│ │ ├── pubspec.yaml
│ │ └── ...
│ └── my_plugin_b/ # 插件 B
│ ├── lib/
│ ├── pubspec.yaml
│ └── ...
├── pubspec.yaml # 应用配置
└── ...配置引用
在项目根目录的 pubspec.yaml 中通过 path 引用:
yaml
dependencies:
my_plugin_a:
path: plugins/my_plugin_a
my_plugin_b:
path: plugins/my_plugin_b为什么放在 plugins/ 目录?
- 代码内聚:插件和项目放在一起,方便同步修改和调试
- 版本一致:不依赖外部仓库,避免版本不同步问题
- 团队协作:插件跟随项目一起进行 Git 管理,所有人拿到代码即可编译
- 约定俗成:这是 Flutter 社区常见的私有插件组织方式
你也可以放在其他目录(如项目根目录下直接建 my_plugin/),但 plugins/ 是最常见、最清晰的实践。
