はじめに
Flutter プラグインは、Dart とネイティブ(Android/iOS)をつなぐ橋です。
しかし、単に「ネイティブコードを呼べればOK」ではなく、
設計の良し悪しがそのまま保守性・拡張性・テスト容易性に直結します。
この記事では、Flutter公式ガイドラインと実務経験をもとに、
「理想的なプラグイン設計原則と規範」を整理します。
1. プラグインとは何か?
Flutter プラグイン = Dart API + 各プラットフォーム実装 + チャネル通信層
Flutter Plugin
├── Dart層(共通API)
├── Android層(Kotlin/Java)
└── iOS層(Swift/Objective-C)
Dart 層とネイティブ層は MethodChannel
などを通して通信します。
2. 設計原則(Design Principles)
① 単一責任(Single Responsibility)
プラグインは 1つの目的 に特化する。
例:battery_info
、location_service
、file_picker
など。
複数機能を混ぜると、更新や依存解決が難しくなります。
② インターフェース分離(Interface Segregation)
Dart 層で 抽象インターフェースを定義 し、
各プラットフォームはその契約に従って実装します。
abstract class BatteryInfoPlatform {
Future<int> getBatteryLevel();
}
このように抽象化しておくと、モックテストやフェデレーション構成が可能になります。
③ 一貫性(Consistency)
- メソッド名、戻り値、例外を全プラットフォームで統一
- Dart 層から見た挙動が同じであることが重要
例:
- Android:
-1
を返す → iOS も同様に-1
- エラー →
PlatformException
を投げる
④ 可拡張性(Extensibility)
Web、Windows、macOS など新しいプラットフォームが追加されても、
既存コードを壊さずに拡張できる構造を取る。
つまり Federated Plugin構成 を採用する。
(詳細は別記事:Flutter Federated Plugin(連携プラグイン))
⑤ テスト容易性(Testability)
-
PlatformInterface
層をモック化してユニットテスト可能にする - Channel 通信を直接テストせず、抽象APIを介して検証する
class MockBatteryInfo extends BatteryInfoPlatform {
@override
Future<int> getBatteryLevel() async => 100;
}
3. プロジェクト構造(推奨)
battery_info/
├── lib/
│ ├── src/
│ │ └── method_channel_battery_info.dart
│ └── battery_info.dart # Dart APIエントリーポイント
│
├── android/
│ └── src/main/kotlin/.../BatteryInfoPlugin.kt
│
├── ios/
│ └── Classes/BatteryInfoPlugin.swift
│
├── example/
│ └── lib/main.dart
│
├── pubspec.yaml
└── README.md
4. チャネル通信の設計規範
種類 | 用途 | 説明 |
---|---|---|
MethodChannel | 一回のメソッド呼び出し | 最も一般的。Dart→NativeのRPC通信に利用。 |
EventChannel | イベントストリーム | 電池残量、センサーなど定期更新データ用。 |
BasicMessageChannel | カスタムデータ転送 | JSONや文字列を送受信。双方向通信に強い。 |
メソッドチャネルの命名規則
const MethodChannel _channel = MethodChannel('com.example.battery_info');
- パッケージ形式:
com.<organization>.<plugin_name>
- メソッド名は lowerCamelCase:
getBatteryLevel
- エラーコードは定数化して管理:
result.error("ERR_PERMISSION_DENIED", "Permission required", null);
5. iOS / Android 実装のポイント
Android側(Kotlin)
class BatteryInfoPlugin : FlutterPlugin, MethodCallHandler {
private lateinit var channel: MethodChannel
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(binding.binaryMessenger, "com.example.battery_info")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
if (call.method == "getBatteryLevel") {
val batteryLevel = getBatteryLevel(binding.applicationContext)
result.success(batteryLevel)
} else {
result.notImplemented()
}
}
}
iOS側(Swift)
public class BatteryInfoPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "com.example.battery_info", binaryMessenger: registrar.messenger())
let instance = BatteryInfoPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
if call.method == "getBatteryLevel" {
result(Int(UIDevice.current.batteryLevel * 100))
} else {
result(FlutterMethodNotImplemented)
}
}
}
6. エラー処理と例外設計
レイヤ | 対応方法 | 例 |
---|---|---|
Dart層 |
PlatformException を使用 |
throw PlatformException(code: 'ERR_XXX') |
ネイティブ層 | result.error(code, message, details) |
Swift/Kotlinで統一コード管理 |
共通ルール |
ERR_ プレフィックス + 意味的コード |
ERR_UNAVAILABLE , ERR_TIMEOUT
|
7. テスト戦略
-
flutter test
:Dartロジックの単体テスト -
integration_test
:ネイティブ呼び出し統合テスト - Mockを活用した疑似Platform層テスト
test('returns battery level', () async {
BatteryInfoPlatform.instance = MockBatteryInfo();
expect(await BatteryInfo().getBatteryLevel(), 100);
});
8. 命名・バージョン・リリース規範
項目 | ルール |
---|---|
パッケージ名 | 小文字スネークケース(例:system_battery_info ) |
クラス名 | PascalCase(例:SystemBatteryInfo ) |
バージョン管理 | Semantic Versioning 準拠 |
CHANGELOG.md | 変更履歴を必ず更新 |
LICENSE | MIT / Apache 2.0 推奨 |
README.md | 対応プラットフォーム・使用例・権限情報を明記 |
9. 図で見るプラグイン通信構造
10. まとめ
目的 | 設計の要点 |
---|---|
保守性 | 責務を分離し、各層を疎結合に設計する |
🔌 統一性 | API命名・戻り値・エラーコードを統一 |
拡張性 | Federated Plugin 構成で多プラットフォーム対応 |
テスト性 | Platform Interface でMock可能に |
品質 | ドキュメント+CHANGELOG+Semantic Versioning |
参考リンク