🔥

FlutterのSubscription管理をラクにするライブラリ「subman」を作った

に公開

どうも、mikanecoです。
最近のスマブラはプリンでVIPに行きました。

殴り書きます

サブスク機能があるアプリを作ってると、「この状態、どうやって管理するのが一番スッキリするんだ……」 って何度も思ったので、自分用に作ってたコードをライブラリ化して公開しました!

https://github.com/mikaneco/subman

https://pub.dev/packages/subman


そもそもsubmanって何?

FlutterアプリでIn-App-Purchase&サブスクリプションを管理するためのユーティリティです!

  • 月額/年額/トライアルなど複数プランの状態管理
  • レシートが複数ある場合でも「一番有効なやつ」を自動で判定
  • 状態の更新をUIやロジックに反映しやすい設計(Riverpodでも組みやすい)

サブスク周りのゴチャゴチャしたif文から卒業できます。

どういうとき便利?

  • ユーザーがいろんなプランを購入・切り替えしたときに、どれが今有効か即判断したい
  • 有効期限やプラットフォーム(iOS/Android)も一緒に管理したい
  • 復元や複数レシート送信時のロジックで悩みたくない
  • サーバー検証後の「今どのプランがアクティブ?」を迷いたくない

こんな人には多分刺さります。

使い方ざっくり

  1. アプリ起動時に初期化
    main() で Subman.init() を呼んでサブスク管理を初期化します。
    ここで「扱うプロダクトID」や「購入完了時の処理」「エラー時のハンドラ」などもセットできます。
void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Subman.init(
    productIds: ['monthly_subscription', 'yearly_subscription'],
    onPurchaseCompleted: (subscription) {
      debugPrint('Purchased: ${subscription.productId}');
    },
    onRestoreCompleted: (subscriptions) {
      debugPrint('Restored: $subscriptions');
    },
    onError: (exception) {
      debugPrint('Error: ${exception.code} / ${exception.message}');
    },
    // 独自のサーバー検証クライアントを使う場合は(ま、ほとんど使うと思うんですけどね)
    serverClient: MyServerClient(),
  );

  runApp(const MyApp());
}

class MyServerClient extends SubscriptionServerClient {
  MyServerClient() : super();

  
  Future<bool> verify(Map<String, dynamic> payload) {
    // サーバーにレシートを投げて結果を返す
  }
}

  1. サブスクの状態を取得・監視

Subman.activeSubscriptionsStream を購読すれば、
サブスクの変更(購入/復元/解約など)があった時に自動でUIを更新できます。


void initState() {
  super.initState();
  Subman.activeSubscriptionsStream.listen((subs) {
    setState(() {
      _status = subs.isEmpty
          ? 'No active subscription'
          : 'Active: ${subs.map((s) => s.productId).join(', ')}';
    });
  });
}

また、手軽に現在のサブスク状態を取得したい時は、
Subman.isSubscribed や Subman.currentSubscription を使います

final isSubscribed = Subman.isSubscribed;
final current = Subman.currentSubscription;
  1. 購入・復元処理も一行でOK

UI側で「購入」「復元」ボタンを押したときは、
Subman.purchase(productId) や Subman.restore() を呼ぶかんじ

ElevatedButton(
  onPressed: () async {
    await Subman.purchase('monthly_subscription');
  },
  child: const Text('Purchase Monthly'),
),

ElevatedButton(
  onPressed: () async {
    await Subman.restore();
  },
  child: const Text('Restore'),
),

これで、サブスクの面倒な状態管理がかなりスッキリします。

ちょっとした特徴

  • SubscriptionStateで「今有効なプラン+いろんな情報」がまとまってる
  • レシートが複数あってもいい感じに自動判定
  • ハンドラで「購入後/復元後」の処理を好きに差し込める
  • 必要以上に抽象化しすぎてないので、カスタマイズもしやすい

こういう人向け

  • Flutterでサブスク実装してるけどロジックがぐちゃぐちゃになってきた
  • Riverpodなどの状態管理と組み合わせて使いたい
  • iOS/Android問わず動くアプリの「サブスク有効判定」をシンプルにしたい

注意点とか今後やりたいこと

  • もしRiverpodに最適化したProviderパッケージも要望あれば作るかも
  • Storeのプラン情報(料金/期間/説明など)をいい感じに扱う仕組みも考え中

最後に

自分のアプリでもガッツリ使ってますが、コードが本当にスッキリしたので、同じ悩みを抱えてる人にはぜひ一度触ってもらいたいです🙏

バグ報告・質問・要望・PRなんでもWelcomeなので、気軽にどうぞ!

https://github.com/mikaneco/subman

https://pub.dev/packages/subman

以上、サクッとですが紹介でした!

Discussion