FlutterでVoIPキットを自作してpub.devに公開する方法
FlutterでVoIP(通話機能)アプリを開発していると、CallKitやConnectionService、FCMのハンドリングなど、ネイティブ側の複雑な設定に悩まされることが多いですよね。
今回、それらの機能を**再利用可能なライブラリ(パッケージ)**として切り出し、実際にpub.devに公開するまでの流れをまとめました。
今回は実例として、私が公開した flutter_voip_kit_osato07: ^0.1.0 を題材に解説します。
📦 作ったもの
Flutter VoIP Kit (flutter_voip_kit_osato07)
https://pub.dev/packages/flutter_voip_kit_osato07
特徴
- iOS: CallKit + PushKit (VoIP Push) の完全対応
- Android: ConnectionService + FCM (Data Message) の全画面通知対応
- Hybrid Architecture: 複雑なネイティブ設定をプラグイン内に隠蔽し、導入を簡略化
🏗 アーキテクチャと設計思想
通常、VoIPアプリを作るときは [AppDelegate.swift](file:///Users/satoshi/Develop/voip_cross_platform/ios/Runner/AppDelegate.swift) や [AndroidManifest.xml](file:///Users/satoshi/Develop/voip_cross_platform/android/app/src/main/AndroidManifest.xml) に大量の記述が必要になります。これをライブラリ化するにあたり、「アプリ側の設定を極限まで減らす」 ことを目標に設計しました。
1. ディレクトリ構成
FlutterのPluginテンプレートを使用しています。
flutter_voip_kit_osato07/
├── android/ # Androidネイティブコード & Manifest
├── ios/ # iOSネイティブコード (Swift)
├── lib/ # Dartインターフェース
└── pubspec.yaml # パッケージ定義
2. Hybridな仕組み:Androidのマニフェストマージ
これは非常に強力な機能ですが、Androidのライブラリ(AAR/Flutter Plugin)に含まれる AndroidManifest.xml は、ビルド時にアプリ本体のマニフェストと自動的にマージされます。
これを利用し、VoIPに必要な権限をライブラリ側に持たせました。
ライブラリ側の AndroidManifest.xml:
<manifest package="com.example.flutter_voip_kit">
<!-- これらが自動でアプリに追加される -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
</manifest>
これにより、ユーザーは dependencies にこのパッケージを追加するだけで、面倒な権限追加作業から解放されます。
3. iOSの設計:Delegateの隠蔽
iOSでは PKPushRegistryDelegate の実装が必須ですが、これをユーザーの AppDelegate に書かせるとコードが肥大化します。
そこで、Swift側でプラグインクラス自体にDelegateを実装し、Dart側との通信(MethodChannel)を内部で完結させました。
Swift側 FlutterVoipKitPlugin.swift:
public class FlutterVoipKitPlugin: NSObject, FlutterPlugin, PKPushRegistryDelegate {
// 初期化時にレジストリをセットアップ
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
if call.method == "initialize" {
let voipRegistry = PKPushRegistry(queue: DispatchQueue.main)
voipRegistry.delegate = self
voipRegistry.desiredPushTypes = [.voIP]
result(nil)
}
}
// トークン取得時や着信時にDartへ通知
public func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {
// MethodChannelでDartにイベント送信
channel.invokeMethod("onIncomingPush", arguments: payload.dictionaryPayload)
}
}
🚀 pub.dev への公開手順
自作したパッケージを世界中に公開するためのステップです。
1. pubspec.yaml の整備
メタデータ(説明、ホームページ、リポジトリURL)を正しく記述する必要があります。特に homepage や repository がないと、pub.devでのスコアが下がります。
name: flutter_voip_kit_osato07 # 一意な名前であること!
description: A comprehensive VoIP kit for Flutter enabling iOS CallKit/PushKit and Android ConnectionService/FCM.
version: 0.1.0
homepage: https://github.com/osato07/flutter_voip_kit_osato07
repository: https://github.com/osato07/flutter_voip_kit_osato07
issue_tracker: https://github.com/osato07/flutter_voip_kit_osato07/issues
environment:
sdk: ^3.10.7
flutter: '>=3.3.0'
flutter:
plugin:
platforms:
android:
package: com.example.flutter_voip_kit
pluginClass: FlutterVoipKitPlugin
ios:
pluginClass: FlutterVoipKitPlugin
2. 必要ファイル ライセンスと変更履歴

- LICENSE: MITライセンスなどが一般的です。ファイルがないと公開できません。
- CHANGELOG.md: バージョンごとの変更点を記述します。これもないと警告が出ます。
3. 名前の重複チェックとリネーム
今回、最初は flutter_voip_kit という名前で公開しようとしましたが、既に同名のパッケージが存在したためアップロードに失敗しました。
pub.devのパッケージ名は早い者勝ちです。
アップロード時に insufficient permissions というエラーが出たら、それは「権限がない」のではなく「既に誰かが持っている名前を使おうとしている」可能性が高いです。
今回は flutter_voip_kit_osato07 とサフィックスをつけて回避しました。
4. 公開コマンド
今回で言うと "packages/flutter_voip_kit_osato07" のようなpackageのディレクトリで実行してください。
まず検証を行います。
flutter pub publish --dry-run
問題なければ、実際に公開します。
flutter pub publish
Googleアカウントの認証用URLが表示されるので、ブラウザでログインすれば完了です!
🎉 まとめ
詳しいフォルダ構成とかはgithubを見てもらえたらと思います!
ネイティブ機能が絡む処理こそ、パッケージ化して切り出すメリットが大きいです。
特に AndroidManifest.xml のマージ機能などは、Flutterプラグイン開発ならではの強力な武器になります。
ぜひ皆さんも pub.dev デビューしてみてください!やってみれば意外と簡単なことってたくさんありますわ、
Discussion