🕊️ はじめに:なぜPigeonが必要なのか?
「手書きの
MethodChannel時代に終止符を打つ」
PigeonはFlutter通信を“宣言的”に再定義した、公式のIDL(Interface Definition Language)ツールだ。
Flutterでネイティブ機能を呼び出すには、通常 MethodChannel を使用します。
しかし、プロジェクトが大きくなるにつれ次のような問題が浮き彫りになります:
| 問題点 | 説明 |
|---|---|
| メソッド名が文字列管理 | タイポしてもコンパイルで検出できない |
| 型が不一致 | Dart ↔ Kotlin ↔ Swift 間で型ミスしても実行時エラー |
| 3言語で同じ定義を手動維持 | コード量と保守コストが爆増 |
| テストが困難 | 仕様の「契約」が明示されない |
この課題を解決するために、Flutterチームが設計したのが Pigeon(ピジョン) です。
Dartで一度インターフェースを書けば、Kotlin・Swiftのコードを自動生成できます。
Pigeonとは?
Pigeonは、Flutterアプリとネイティブコード間の通信を型安全に自動生成するツールです。
- IDL(Interface Definition Language) 形式で通信契約を記述
- Dart / Kotlin / Swift のコードを自動生成
- ランタイム依存なし(すべてコンパイル時解決)
- MethodChannelを内部で安全にラップ
1. セットアップ
pubspec.yaml の dev_dependencies に追加します。
dev_dependencies:
pigeon: ^26.0.1
2. 通信定義を作る(IDL)
pigeons/battery.dart:
import 'package:pigeon/pigeon.dart';
class BatteryInfo {
int? level;
}
@HostApi() // Dart → ネイティブ
abstract class BatteryApi {
BatteryInfo getBatteryLevel();
}
Dartで定義するだけ。これがすべてのプラットフォームの“契約書”になる。
3. コード生成
dart run pigeon \
--input pigeons/battery.dart \
--dart_out lib/pigeon/battery.g.dart \
--kotlin_out android/src/main/kotlin/com/example/system_battery_info/Battery.kt \
--kotlin_package com.example.system_battery_info \
--swift_out ios/Classes/Battery.swift
4. 各プラットフォーム実装
Android(Kotlin)
class BatteryApiImpl(private val context: Context): BatteryApi {
override fun getBatteryLevel(): BatteryInfo {
val iFilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
val batteryStatus = context.registerReceiver(null, iFilter)
val level = batteryStatus?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1
val scale = batteryStatus?.getIntExtra(BatteryManager.EXTRA_SCALE, -1) ?: -1
return BatteryInfo().apply { this.level = (level * 100) / scale }
}
}
class SystemBatteryInfoPlugin : FlutterPlugin {
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
BatteryApi.setUp(binding.binaryMessenger, BatteryApiImpl(binding.applicationContext))
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
BatteryApi.setUp(binding.binaryMessenger, null)
}
}
iOS(Swift)
public class BatteryApiImpl: BatteryApi {
public func getBatteryLevel() throws -> BatteryInfo {
UIDevice.current.isBatteryMonitoringEnabled = true
let info = BatteryInfo()
info.level = NSNumber(value: Int(UIDevice.current.batteryLevel * 100))
return info
}
}
public class SystemBatteryInfoPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
BatteryApiSetup.setUp(binaryMessenger: registrar.messenger(), api: BatteryApiImpl())
}
}
Dart側で呼び出す
import 'pigeon/battery.g.dart';
final api = BatteryApi();
Future<void> main() async {
final info = await api.getBatteryLevel();
print('Battery level: ${info.level}%');
}
もう
invokeMethod()もtry/catchも不要。
型安全なAPIとして補完される。
双方向通信(@FlutterApi)
Flutter側にイベントを返すことも可能です。
@FlutterApi()
abstract class BatteryListener {
void onBatteryChanged(BatteryInfo info);
}
ネイティブから:
val listener = BatteryListener(binaryMessenger)
listener.onBatteryChanged(BatteryInfo().apply { level = 90 })
Pigeonのメリットまとめ
| 特徴 | 内容 |
|---|---|
| 型安全 | Dart/Kotlin/Swift 間の型一致を保証 |
| 保守性 | 定義を1箇所に集約 |
| パフォーマンス | MethodChannelより高速(バイナリCodec使用) |
| 双方向通信 |
@HostApi + @FlutterApi で可能 |
| 自動生成 | すべての橋渡しコードをツールが生成 |
| Null-safety完全対応 | Dart 3.0以降のNull安全仕様と整合 |
Pigeonの歴史と進化
| 年 | バージョン | 進化 |
|---|---|---|
| 2018 | Flutter初期 | 手書きMethodChannel時代 |
| 2020 | v0.1 | Pigeon登場(Dart→Native) |
| 2021 | v1.x |
@FlutterApi登場、双方向通信対応 |
| 2022 | v3.x | Null-safety / Enum / Nestedクラス対応 |
| 2023 | v7.x | Swift5/Kotlin1.8サポート |
| 2024 | v10+ | Federated構成・@ConfigurePigeon対応 |
| 2025 | 現在 | Desktop/Linux対応、CI統合容易化 |
名前の由来は “信鸽(pigeon)”。
「Flutter ↔ Native のメッセージを安全に届ける使者」という意味。
Pigeonの設計哲学
“コードを手で書くより、契約を定義するべき。”
Flutter開発が拡大するにつれ、手作業の通信コードはバグの温床になりました。
Pigeonは「宣言」だけで通信を完結させ、人間の手から橋梁ロジックを解放しました。
設計の三原則
- IDLで定義、コードは自動生成
- コンパイル時型チェック
- 実行時に文字列を使わない(ゼロ反射)
現在の活用例
- Flutter公式プラグイン(
camera,shared_preferences,connectivity_plus) - Firebase、Agora、MapboxなどのSDK
- 企業社内の業務アプリ(契約定義書として利用)
未来展望
Flutterチームが提案している次世代Pigeonは:
| 方向 | 説明 |
|---|---|
| gRPC風IDL化 | 将来的にはprotobufのようにschemaベース通信へ |
| FFI統合 | C/Rustバインディングとの統一生成 |
| Web/Desktop対応 | Pigeonで完全クロスプラットフォーム通信を実現 |
| build_runner連携 | コマンド不要の自動生成パイプライン化 |
通信構造図(Mermaid)
テストTips
class MockBatteryApi implements BatteryApi {
@override
Future<BatteryInfo> getBatteryLevel() async {
return BatteryInfo()..level = 99;
}
}
test('mocked battery api', () async {
final mock = MockBatteryApi();
final info = await mock.getBatteryLevel();
expect(info.level, 99);
});
まとめ
Flutter開発は「文字列通信」から「契約通信」へ。
Pigeonはその変化の象徴だ。
たった1つのDart定義で、3言語が同じ約束を守る。
その時あなたのFlutterアプリは、真の多言語協調に到達する。