Flutterアプリの収益化戦略:AdMob 3種類と内部課金の実装メモ
自動生成パイプラインで毎日アプリを量産するにあたって、収益化をどうするかを整理した。結論としては当面は広告(AdMob)のみ、内部課金は後から追加する方針にした。その理由と、将来の内部課金実装に必要なことをここにまとめておく。
収益化の選択肢と今回の判断
Flutterアプリの収益化は3パターンありますが、審査リスク最小・実装最速のAdMob広告が量産フェーズの個人開発者には最適です。
| 方式 | 実装難度 | 審査リスク | 特徴 |
|---|---|---|---|
| AdMob広告 | 低 | 低 | ダウンロード数に比例。実装がシンプル |
| 有料アプリ | なし | 低 | App Store側で価格設定のみ。DL数は激減 |
| 内部課金(IAP) | 高 | 高 | 収益単価高。Apple審査が厳しい |
自動生成パイプラインでは毎日アプリを量産する。審査に落ちるリスクを最小化しつつ、とにかく本数を出してロングテールで収益を積み上げる戦略をとる。そのためAdMobを中心に据えた。
AdMob 3種類の広告:テストID一覧
すべてのアプリに以下3種類の広告を実装する。
AdMob テスト用ID(開発・テスト時のみ使用)
| 種類 | テストID |
|---|---|
| App ID | ca-app-pub-3940256099942544~1458002511 |
| バナー | ca-app-pub-3940256099942544/2934735716 |
| インタースティシャル | ca-app-pub-3940256099942544/4411468910 |
| リワード | ca-app-pub-3940256099942544/1712485313 |
本番リリース前にAdMobコンソールで実際のユニットIDに差し替えること。
バナー広告
画面下部に常時表示。実装が最もシンプルで、どんなアプリにも馴染む。収益単価は低いが、起動中ずっと表示されるので積み上がりやすい。
// バナー広告の実装例
BannerAd(
adUnitId: 'ca-app-pub-3940256099942544/2934735716',
size: AdSize.banner,
request: const AdRequest(),
listener: BannerAdListener(
onAdLoaded: (ad) => setState(() => _bannerAd = ad as BannerAd),
onAdFailedToLoad: (ad, error) => ad.dispose(),
),
)..load();
// ウィジェットとして表示
SizedBox(
height: 50,
child: AdWidget(ad: _bannerAd!),
)
インタースティシャル広告
全画面広告。ユーザーがタスクを完了したとき・画面遷移時・クイズの答え合わせ後などに表示する。表示頻度を上げすぎるとUXが下がるので、1セッションに1〜2回が目安。
// インタースティシャル広告のロード
InterstitialAd.load(
adUnitId: 'ca-app-pub-3940256099942544/4411468910',
request: const AdRequest(),
adLoadCallback: InterstitialAdLoadCallback(
onAdLoaded: (ad) => _interstitialAd = ad,
onAdFailedToLoad: (error) => print('load failed: $error'),
),
);
// 表示
_interstitialAd?.show();
_interstitialAd = null; // 表示後は破棄
リワード広告
ユーザーが動画を最後まで見ると特典を受け取れる広告。「広告を見る」ボタンを設置して、追加コンテンツや機能のアンロックと引き換えにする。単価が最も高く、ユーザーの能動的な行動なのでUXへの影響も少ない。
// リワード広告のロード
RewardedAd.load(
adUnitId: 'ca-app-pub-3940256099942544/1712485313',
request: const AdRequest(),
rewardedAdLoadCallback: RewardedAdLoadCallback(
onAdLoaded: (ad) => _rewardedAd = ad,
onAdFailedToLoad: (error) => print('load failed: $error'),
),
);
// 表示(報酬コールバックつき)
_rewardedAd?.show(
onUserEarnedReward: (ad, reward) {
// ここでコンテンツをアンロック
setState(() => _isPremiumContent = true);
},
);
必須:Info.plist への GADApplicationIdentifier 追加
これを忘れると起動直後にクラッシュする。ios/Runner/Info.plist の </dict> 直前に追加:
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-3940256099942544~1458002511</string>
pubspec.yaml の設定
dependencies:
flutter:
sdk: flutter
google_mobile_ads: ^5.1.0
shared_preferences: ^2.3.2
intl: ^0.19.0
in_app_purchase は今回は入れない。広告のみで運用する。
将来:内部課金(IAP)を追加するときに必要なこと
今はやらないが、ユーザーが付いてきたら追加する。忘れないうちにやることを記録しておく。
① App Store Connect で商品登録
サブスクリプショングループを作成
App Store Connect → アプリ → サブスクリプション → 新規グループ。グループ名は「プレミアム」など。
商品IDを決める
命名規則の例:com.seadice.kateisaien1.premium_monthly。このIDはコードでも使うので一度決めたら変更不可。
価格・説明文を設定
月額180円など。App Store審査用の説明文(英語必須)も入力する。
② pubspec.yaml に追加
dependencies:
in_app_purchase: ^3.2.0
③ Flutter側の実装フロー
- アプリ起動時に
InAppPurchase.instance.queryProductDetails()で商品情報を取得 - 購入ボタンを押したら
InAppPurchase.instance.buyNonConsumable()でAppleの購入フローを呼び出す purchaseStreamをリッスンして購入完了を検知- 購入完了したら
SharedPreferencesにisPremium = trueを保存 - アプリ起動時に
isPremiumを読み込んで広告非表示・機能解放
④ Apple審査で必須の実装
- 購入の復元ボタン:「購入を復元」ボタンを必ず設置(Apple規約で必須)
- 利用規約リンク:購入画面に利用規約・プライバシーポリシーへのリンク
- サブスクリプションの説明:「毎月自動更新・いつでも解約可能」の明記
- サンドボックステスト:App Store Connect でサンドボックスアカウントを作成してテスト
⑤ レシート検証(任意だが推奨)
クライアント側だけで検証するとリスクがあるため、本格運用するならサーバー側でAppleのレシート検証APIを叩く実装が必要になる。ただし個人開発の初期フェーズなら省略して構わない。
今の判断:広告のみで進める理由
- 内部課金はApple審査が厳しく、落とされるリスクが高い
- サンドボックステストの準備が面倒(テストアカウント作成、テストフロー確認など)
- 購入の復元ボタン・利用規約表示など実装必須事項が多い
- 自動生成で量産する今のフェーズでは、シンプルな広告で本数を出すほうが優先度が高い
- ユーザーが付いたアプリに後から追加する方が ROI が高い
リワード広告は単価が高く、ユーザーが自分で「広告を見る」を選ぶので強制感がない。内部課金と広告の中間のような位置づけで、まずここを育てる。