💰

[Flutter x Firebase] カウンターアプリに広告を追加する

8 min read

やること

  • flutter creatで生成されるカウンターアプリに、Admobによる広告を追加する。

前回

  1. [Flutter x Firebase] カウンターアプリに認証機能を追加する
  2. [Flutter x Firebase] カウンターアプリに認証機能を追加する 〜自動ログイン〜
  3. [Flutter x Firebase] Crashlyticsと連携してクラッシュレポートを取得する
  4. [Flutter x Firebase] Cloud Firestoreと連携してカウントを保持する

ソースコードは以下のものを使用。

https://github.com/popy1017/firebase_counter_app

以降では、Firebaseとの連携が完了しているものとして記載する。

ちなみに、Flutter 2.0に対応しました。

手順

  1. Admob側の準備
  2. アプリの準備
    1. パッケージインストール(google_mobile_ads)
    2. 初期設定(iOS)
    3. 初期設定(Flutter)
    4. バナー広告を表示する
    5. リワード広告を表示する

Admob側の準備

基本的には表示されるガイドに従って入力やら何やらを設定する。注意点のみ記載。

後述する「お支払い情報の設定」が完了しないと広告を配信できないが、
長いと審査完了に24時間程度かかるため早めにやっておくのが吉。

Admobに登録する

https://admob.google.com/intl/ja/home/

お支払い情報を設定する

これを設定しないと広告を配信できない。これ大事
結構時間がかかる(24時間くらいで確認しますと表示される)ので早めにやっておいた方がいい。
(自分は5〜6時間かかった)

Admobにアプリを追加する

Admob上で一度作成したアプリは削除できないらしいので注意。非表示にすることはできる。

今回はお試しで作成するので「App Storeで公開していますか?」は「いいえ」を選択。
作成後、あとで使うので、アプリIDを確認しておく。(ca-app-pub-5**************2みたいなやつ)

広告ユニットを作成する

今回は、予め用意されているテスト用の広告を使用するのでお試しだけで済ませるのであればユニットの作成は不要。デモ広告ユニットIDは以下を参照。

https://developers.google.com/admob/ios/test-ads#demo\_ad\_units

実際に配信するアプリで使いたい場合は、広告のフォーマット(バナー、インタースティシャル、リワード)や広告の種類(テキスト、動画、・・・)を設定する。
https://support.google.com/admob/answer/6128738?hl=ja

テストデバイスを追加する

テスト用の広告の場合いらないのかもしれないが、念のため設定しておく。

自分で設定した広告を自分でクリックするような行為はポリシーに反するため、最悪アカウントが停止される恐れがある。そのため、特定のデバイス(自分のデバイス)ではテスト用の広告を表示するように設定する必要がある。

  1. 左側のメニューから「設定」を選択。
  2. 真ん中の上タブから「テストデバイス」を選択。
  3. 「テストデバイスを追加」ボタンを押す。

テストデバイスの追加に必要な情報は以下の通り。

  • デバイス名(自分で決める)
  • プラットフォーム(Android or iOS)
  • 広告IDまたはIDFA※

※iOSでは通常の方法でIDFAを確認する方法がない。プログラムを作成すれば確認できるが、面倒なので今回はApp Storeで「IDFA」と検索し、出てきたアプリでIDFAを取得

アプリの準備

Admob広告用のパッケージをインストールする

今回使用するのは以下のパッケージ。

https://pub.dev/packages/google_mobile_ads
pubspec.yaml
dependencies:
   google_mobile_ads: ^0.12.1+1

iOS用の初期設定

Info.plistに以下を追記する。{AdmobAppId}には、 Admobにアプリを追加するで追加したアプリのアプリIDを設定する。(例: ca-app-pub-3940256099942544~1458002511)

<key>GADApplicationIdentifier</key>
<string>{AdmobAppId}</string>
<key>SKAdNetworkItems</key>
<array>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>cstr6suwn9.skadnetwork</string>
  </dict>
</array>

初期化

Firebase同様、アプリ開始時におまじない(MobileAds.instance.initialize())を実行する。

パッケージのExampleだとawaitを使っていなかったが、初期化が終わる前に広告を表示しようとするとエラーが出たのでawaitで初期化を待つことにした。

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  final List<Future> setUpFutures = [
    DotEnv.load(fileName: ".env"), // 環境変数読み込み用(今回は説明してません)
    Firebase.initializeApp(),
    MobileAds.instance.initialize()
  ];
  await Future.wait(setUpFutures);
  
  ~
}

バナー広告を表示する

google_mobile_adsの広告の表示までの流れは以下の通り。

  1. インスタンス化
  2. ロード
  3. 表示
  4. 破棄(dispose)

インスタンス化

広告のタイプに応じたクラス(バナー広告の場合はBannerAd)でインスタンス化を行う。

  final BannerAd _bannerAd = BannerAd(
    size: AdSize.banner,
    adUnitId: TEST_BANNER_AD_ID,
    listener: AdListener(),
    request: AdRequest(),
  );

AdListenerでは、広告が閉じられたときやユーザーがアプリを離れたときなど、ライフサイクルイベントをリッスンできる。
AdRequestは詳しい説明はなかったが、コードのコメントを読むとTargeting info per the AdMob API.と書いてあるので、おそらくloadする際にリクエスト内容にマッチした広告を表示する際に設定するものと思われる。

https://developers.google.com/admob/ios/targeting?hl=ja

リスナーの設定

今回のコードでは、バナー広告には何も設定していないが、サンプルでは以下のようなListenerが掲載されている。
以下のように、広告のロードが完了した際や失敗した際、広告が開かれた際などに何らかの処理を行うことができる。(リワード広告の場合は、リワードが与えられた場合(onRewardedAdUserEarnedReward)の処理を書く必要がある。

final AdListener listener = AdListener(
 // Called when an ad is successfully received.
 onAdLoaded: (Ad ad) => print('Ad loaded.'),
 // Called when an ad request failed.
 onAdFailedToLoad: (Ad ad, LoadAdError error) {
   ad.dispose();
   print('Ad failed to load: $error');
 },
 // Called when an ad opens an overlay that covers the screen.
 onAdOpened: (Ad ad) => print('Ad opened.'),
 // Called when an ad removes an overlay that covers the screen.
 onAdClosed: (Ad ad) => print('Ad closed.'),
 // Called when an ad is in the process of leaving the application.
 onApplicationExit: (Ad ad) => print('Left application.'),
);

ロードする

おそらくすべてのタイプの広告で、広告を表示する前にロードを実行する必要がある。バナー広告の場合は、基本的には最初に読み込んだものを表示し続けることになるので、initStateで実行すればOK。(バナー広告は一定時間経過後、自動的に更新される)

  
  void initState() {
    super.initState();
    _bannerAd.load();
    ~~
  }

表示する

BannerAdをWidgetとして表示するには、load()を呼び出した後、AdWidgetをインスタンス化する必要がある。 load()を呼び出す前にWidgetを作成することはできるが、WidgetTreeに追加する前にload()を呼び出す必要がある。
また、iOSではAdWidgetの幅と高さを指定して配置しないと広告が表示されないことがあるため、Containerなどでサイズを指定して配置する必要がある。広告のサイズは、_bannerAd.sizeで取ってこれる。


Widget build(BuildContext context) {
  final AdWidget adWidget = AdWidget(ad: _bannerAd);
   ~~
  Container(
    alignment: Alignment.center,
    child: adWidget,
    width: _bannerAd.size.width.toDouble(),
    height: _bannerAd.size.height.toDouble(),
  ),
}

リワード広告を表示する

今回はカウンターアプリということで、カウントが10の倍数になる時にリワード広告を表示するようにしたい。
そのため、+ボタンを押した際にカウントが10の倍数-1の数だった場合にリワード広告を表示するようにする、

インスタンス化

バナー広告とは違い訳あり(後述)のためinitState内でListenerの設定・インスタンス化を行う。

  
  void initState() {
    super.initState();
    _bannerAd.load();

    listener = AdListener(
      // 広告のロードが完了したら、広告の表示を行い、EasyLoadingを閉じる
      onAdLoaded: (Ad ad) {
        print('Ad loaded.');
        _rewardedAd!.show();
        EasyLoading.dismiss();
      },
      // Called when an ad request failed.
      onAdFailedToLoad: (Ad ad, LoadAdError error) {
        ad.dispose();
        print('Ad failed to load: $error');
      },
      // Called when an ad opens an overlay that covers the screen.
      onAdOpened: (Ad ad) => print('Ad opened.'),
      // Called when an ad removes an overlay that covers the screen.
      onAdClosed: (Ad ad) {
        ad.dispose();
        print('Ad closed.');
      },
      // Called when an ad is in the process of leaving the application.
      onApplicationExit: (Ad ad) => print('Left application.'),
      // 広告動画の再生が終わったら、カウントを1進める
      onRewardedAdUserEarnedReward: (RewardedAd ad, RewardItem reward) {
        print('Reward earned: $reward');
        _incrementCounter();
        //ad.dispose();
      },
    );

    _rewardedAd = RewardedAd(
      adUnitId: TEST_REWARD_AD_ID,
      request: AdRequest(),
      listener: listener!,
    );
  }

ロード・表示する

注意点
google_mobile_adsload()は、Future<void>関数であるが、awaitで待っても意味がないので注意。ロードの完了を待って処理したい場合は、listenerのonAdLoadedにロード後に実行したい処理を行う必要がある。
例えば、以下のコードではロードが完了してないのに広告を表示しようとしたとしてエラーが出てしまう。

        onPressed: () async {
          if (_currentCount % 10 == 9) {
            EasyLoading.show(status: '動画広告を読み込んでいます。。。');
            await _rewardedAd!.load();
            print('loaded');
            await _rewardedAd!.show();
            print('finish');
          } else {
            _incrementCounter();
          }
        },
// 出力
flutter: loaded
RewardedAd failed to show because the ad was not ready.
flutter: finish
flutter: Ad loaded.

上記のような理由から、広告読み込み→広告表示→リワード授与を同期的に行いたい場合は、

  1. load()実行
  2. onAdLoadedで広告表示
  3. onRewardedAdUserEarnedRewardでリワード授与

とする必要がある。と思う。

思ってたのと違うので、間違っていたら教えてください。。。

      floatingActionButton: FloatingActionButton(
        onPressed: () {
          if (_currentCount % 10 == 9) {
            EasyLoading.show(status: '動画広告を読み込んでいます。。。');
            _rewardedAd!.load();
          } else {
            _incrementCounter();
          }
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),

以上!前回からだいぶ時間が空いてしまった。。。