Skip to content

插件开发与使用

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/ 是最常见、最清晰的实践。

基于 Flutter 官方文档整理