【広告収益最大化の一歩】Flutter × アダプティブバナー広告のススメ【サンプルコード付き】

Flutter基礎

「アプリの広告収益を最大化したい!」

アプリ開発者なら常々思うところだと思います。

ちょっと考えてみましょう。
収益の最大化のためには、広告をより効果的な方法で表示する必要がありますよね。
効果的な方法の一つとして、より大きく広告を見せる、という方法が思いつきます。

今回紹介するアダプティブバナー広告は、
ユーザーにより大きくバナー広告を見せる方法となります。

次の画像をご覧ください。

左が通常のバナー広告、右がアダプティブバナー広告です。

どちらが効果的か、一目瞭然ですよね。

 

「でも、どうやって実装するの?」

お任せください。

今回はFlutterでこのアダプティブバナー広告を実装する方法について、
サンプルコード付きで解説します。

google_mobile_adsの公式ドキュメントや、CodeLabには通常のバナー広告の
実装しか載っていません。
調べた限りでは日本語文献ではどこにも載っていない情報です。
ぜひ読んで、実際に使用してみてください。

アダプティブバナー広告って何?

先ほど画像で紹介したアダプティブバナー広告について、もう一度紹介します。

アダプティブバナー広告とは、
デバイスに対して最適化されたサイズを提供するバナー広告です。

具体的には、指定した横幅に対して最適な高さを提供します。
デバイスの横幅を指定すれば、デバイスに対し最適なサイズのバナー広告が
提供されるわけです。

この、デバイス毎に最適化されることがアダプティブバナー広告の一番の利点だと考えます。

バナー広告にも色々サイズがありますが、デバイス毎に
「この広告は大きすぎる」
「もうちょっと大きい広告がいいかも」

などと一個一個調整し、用意するのは手間ですよね。

アダプティブバナー広告なら、デバイスの横幅を指定してあげるだけで、
最適なサイズの広告が提供されます。

ユーザーが使う様々なデバイスに対してです。
これってすごいと思いませんか?

簡単に、広告効果が高い広告を提供してくれる。
それがアダプティブバナー広告です。

デバイスによって広告の高さが変わることにより、
レイアウトの崩れやエラーを引き起こす場合があります。
アダプティブバナー広告を使用する場合は、広告の高さが変わることを考慮して
レイアウトをするよう、注意しましょう。

Flutterでどうやって実装するの?

ここからは具体的な実装の仕方について解説していきます。

今回の実装の環境です。

  • Flutter version 2.5.3
  • Dart version 2.14.4
  • Android Studio (version 2020.3)
  • Xcode 13.0
  • google_mobile_ads 0.13.5

また、広告の提供元としてGoogle AdMobを使用します。

実装準備

実装準備として、広告の準備や、パッケージの追加、広告IDの設定を行います。

広告の準備

とりあえずサンプルアプリで実装したい方はこの節は飛ばしてください。

自分の作成しているアプリで広告を実装する場合は、Google AdMob
アプリの設定、広告ユニットの設定を行ってください。

こちらのCode Labが大変参考になります。)

パッケージの追加

パッケージとしてGoogle mobile ads を追加します。

Terminalで以下のコマンドを実行してください。

1
flutter pub add google_mobile_ads

もしくは、pubspec.yamlに以下のように追加を行ってください。

1
2
3
4
5
6
7
dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
 
  #以下を追加
  google_mobile_ads: ^0.13.5

またAndroidを対象とする場合は、android/app/build.gradle のminSdkVersionを
19以上に設定してください。

広告IDの設定

公式ドキュメントのPlatform Specific Setupに沿って設定していきます。

まず、iOSからです。

iOS/Runner/Info.plist ファイルに、以下のコードを追加してください。

1
2
3
4
5
6
7
8
9
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-3940256099942544~1458002511</string>
<key>SKAdNetworkItems</key>
  <array>
    <dict>
      <key>SKAdNetworkIdentifier</key>
      <string>cstr6suwn9.skadnetwork</string>
    </dict>
  </array>

自分のアプリで実装する場合は、ca-app-pub-3940256099942544~1458002511 を
広告の準備で設定したアプリの広告IDに書き換えてください。
サンプルアプリで実装したい方は、このコードのままで構いません。

次にAndroidです。

Android/app/src/main/AndroidManifest.xmlに以下の<meta-data>のコードを追加してください。

1
2
3
4
5
6
7
<manifest>
    <application>
        <meta-data
            android:name="com.google.android.gms.ads.APPLICATION_ID"
            android:value="ca-app-pub-3940256099942544~3347511713"/>
    </application>
</manifest>

自分のアプリで実装する場合は、<meta-data>の
ca-app-pub-3940256099942544~3347511713 を
広告の準備で設定したアプリの広告IDに書き換えてください。
サンプルアプリで実装したい方は、このコードのままで構いません。

以上で実装準備は終わりとなります。

実装

ここからサンプルアプリでの実装をしていきます。
自分のアプリに実装したい方は、適宜必要な部分をキャッチアップしてください。

準備として、main.dartを以下のように書き換えてください。

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import 'package:flutter/material.dart';
//TODO:パッケージをimportする
 
void main() {
  //TODO:Mobile Ads SDK の初期化を行う
  runApp(const MyAdMobSample());
}
 
//TODO:広告ユニットIDのヘルパークラスを用意する
 
class MyAdMobSample extends StatefulWidget {
  const MyAdMobSample({Key? key}) : super(key: key);
 
  @override
  State<MyAdMobSample> createState() => _MyAdMobSampleState();
}
 
class _MyAdMobSampleState extends State<MyAdMobSample> {
 
  //TODO:広告を管理する状態を用意する
 
  //TODO:バナー広告のサイズを取得するメソッドを用意する
 
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'AdMob Sample',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: const Text("AdMob Sample"),
        ),
        body: SafeArea(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Expanded(
                child: Container(
                  decoration: const BoxDecoration(color: Colors.green),
                  child: const Center(
                    child: Text("contents"),
                  ),
                ),
              ),
              //TODO:アダプティブバナーを設定する
            ],
          ),
        ),
      ),
    );
  }
 
  //TODO:disposeをオーバーライドする
}

パッケージをインポートする

//TODO:パッケージをimportする を以下のインポート文に置き換えてください

1
2
import 'dart:io';
import 'package:google_mobile_ads/google_mobile_ads.dart';

広告ユニットIDのヘルパークラスを作成する

Android,iOSで切り替えがスムーズにできるように、
ユニットIDのヘルパークラスを作成します。

main関数のすぐ下の、//TODO:広告ユニットIDのヘルパークラスを用意する を
以下のコードに置き換えてください。

01
02
03
04
05
06
07
08
09
10
11
12
//1
class AdHelper {
  static String get bannerAdUnitId {
    if (Platform.isAndroid) {
      return "ca-app-pub-3940256099942544/6300978111";
    } else if (Platform.isIOS) {
      return "ca-app-pub-3940256099942544/2934735716";
    } else {
      throw UnsupportedError("Unsupported platform");
    }
  }
}
  1. 広告ユニットIDをプラットフォームで分岐して取得しています。

自分のアプリで実装する場合は、ca-app-pub-~ を
広告の準備で設定したアプリの広告ユニットIDに書き換えてください。
サンプルアプリで実装したい方は、このコードのままで構いません。
尚、テスト広告用のIDが古い場合があります。
このブログの最後に最新のID記載先を載せておきますので参照ください。

広告の初期化を行う

Mobile Ads SDK の初期化を行います。
main関数内、//TODO:Mobile Ads SDK の初期化を行う を以下のコードに置き換えてください。

1
2
3
//2
WidgetsFlutterBinding.ensureInitialized();
MobileAds.instance.initialize();

2. 2行目のコードでMobile Ads SDKの初期化を行っています。

広告を管理する状態を用意する

バナー広告を管理する状態を3つ用意します。

_MyAdMobSampleStateの下の、//TODO:広告を管理する状態を用意するを以下のコードに置き換えてください。

1
2
3
4
5
6
7
8
//3
late BannerAd _ad;
 
//4
AdSize? _adSize;
 
//5
bool _isAdLoaded = false;

3. バナー広告を管理する変数です。

4. バナー広告のサイズを管理する変数です。
 アダプティブバナー広告のサイズを取得する関数の返り値がFutureとなっています。
 Futureで値を取得するまでの状態を使用するため、null許容としています。

5. バナー広告がロードされたかどうかを判別するフラグを管理する変数です。

バナー広告のサイズを取得するメソッドを用意する

バナー広告のサイズを取得するメソッドを用意します。

上で追加した状態のすぐ下にある//TODO:バナー広告のサイズを取得するメソッドを用意する
を以下のコードに置き換えてください。

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
//6
Future<AdSize?> _getAdSize(BuildContext context) async {
 
  //7
  if (_adSize != null) {
    return _adSize;
  }
 
  //8
  _adSize = await AdSize.getAnchoredAdaptiveBannerAdSize(
      MediaQuery.of(context).orientation == Orientation.portrait
          ? Orientation.portrait
          : Orientation.landscape,
      MediaQuery.of(context).size.width.toInt()) as AdSize;
  return _adSize;
}

6.バナー広告のサイズの取得をするメソッドです。
 メソッド内でawaitを使用するため、Future関数としています。

7._adSizeに値が入っているなら、そのまま返すようにします。

8. アダプティブバナー広告のサイズを取得します。
 AdSize.getAnchoredAdaptiveBannerAdSizeの第一引数は画面の向きとなっています。
 MediaQueryで取得した値を引数に渡します。
 第二引数はアダプティブバナー広告の横幅です。
 MediaQueryで画面の横幅を取得し、引数に渡します。

ここでポイントなのが、AdSize.getAnchoredAdaptiveBannerAdSizeの返り値が
Futureであることです。

これが原因で通常のバナー広告と同様にinitState内でバナー広告の初期化処理を
行うことができません。
(initStateを非同期処理にすることができないため)

そこで用いるのがFutureBuilderです。

アダプティブバナーを設定する

アダプティブバナーを設定します。
buildメソッド内の//TODO:アダプティブバナーを設定するを以下のコードに置き換えてください。

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//9
Builder(builder: (context) {
  return Container(
    alignment: Alignment.center,
    //10
    height: _adSize != null ? _adSize!.height.toDouble() : 0,
    width: _adSize != null ? _adSize!.width.toDouble() : 0,
     //11
    child: FutureBuilder(
        future: _getAdSize(context),
        builder: (context, snapshot) {
          if (snapshot.connectionState ==
              ConnectionState.done) {
           //futureのエラー処理
            if (snapshot.hasError) {
              return Center(
                child: Text(snapshot.error.toString(),
                    textAlign: TextAlign.center,
                    textScaleFactor: 1.3),
              );
          }
 
            //12
            if (!_isAdLoaded) {
              _ad = BannerAd(
                adUnitId: AdHelper.bannerAdUnitId,
                size: _adSize!,
                request: AdRequest(),
                listener: BannerAdListener(
                  onAdLoaded: (_) {
                   //13
                    setState(() {
                     _isAdLoaded = true;
                    });
                },
                  onAdFailedToLoad: (ad, error) {
                   //Load失敗時の処理
                    ad.dispose();
                    print(
                        'Ad load failed (code=${error.code} message=${error.message})');
                  },
                ),
              );
              _ad.load();
              return Container();
            }
            //14
            return AdWidget(ad: _ad);
          }
          //非同期処理未完了時は何も表示させない
          else {
            return Container();
           }
        }),
  );
})

9.アダプティブバナー広告の表示部分です。
 _getAdSizeでcontextを使用するため、Builderで囲っています。

10.サイズ指定をここで行っています。
 アダプティブバナー広告のサイズを取得前は0,
 サイズ取得後はアダプティブバナー広告の大きさに設定されます。

11.FutureBuilderを使用します。futureに_getAdSizeを指定します。

12.広告がLoad済かどうかで条件分岐します。
 Loadされていない時だけ、_adの初期化を行います。

13.Loadが完了したらリビルドするよう、ここでsetStateを行います。

14.広告がロードされた場合に広告を表示します。

FutureBuilderを使うことで、広告のサイズを取得後に
初期化、Load処理を行うことができます。
また、onAdLoadedでsetStateを行うことで、
ロード後にリビルドし、広告のサイズのContainerを用意できます。

disposeをオーバーライドする

最後に広告のdispose処理を記載します。
一番下の //TODO:disposeをオーバーライドする を以下のコードに置き換えてください。

1
2
3
4
5
6
7
8
//15
@override
void dispose() {
  _ad.dispose();
  _adSize = null;
  _isAdLoaded = false;
  super.dispose();
}

15.disposeが呼ばれるタイミングで、BannerAdをdispose&状態を初期化します。

ここまでできたらアプリを実行してみてください。
下の画像のように画面横幅いっぱいに広告が表示されたら成功です。

以上でアダプティブバナー広告の構築は完了となります。
お疲れ様でした!

サンプルコード

最後に今回構築したコードの全体を記載します。
もし動かない等あったら、こちらを参照してください。

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:google_mobile_ads/google_mobile_ads.dart';
 
void main() {
  WidgetsFlutterBinding.ensureInitialized();
  MobileAds.instance.initialize();
 
  runApp(const MyAdMobSample());
}
 
class AdHelper {
  static String get bannerAdUnitId {
    if (Platform.isAndroid) {
      return "ca-app-pub-3940256099942544/6300978111";
    } else if (Platform.isIOS) {
      return "ca-app-pub-3940256099942544/2934735716";
    } else {
      throw UnsupportedError("Unsupported platform");
    }
  }
}
 
class MyAdMobSample extends StatefulWidget {
  const MyAdMobSample({Key? key}) : super(key: key);
 
  @override
  State<MyAdMobSample> createState() => _MyAdMobSampleState();
}
 
class _MyAdMobSampleState extends State<MyAdMobSample> {
 
  late BannerAd _ad;
  AdSize? _adSize;
  bool _isAdLoaded = false;
 
  Future<AdSize?> _getAdSize(BuildContext context) async {
 
    if (_adSize != null) {
      return _adSize;
    }
    _adSize = await AdSize.getAnchoredAdaptiveBannerAdSize(
        MediaQuery.of(context).orientation == Orientation.portrait
            ? Orientation.portrait
            : Orientation.landscape,
        MediaQuery.of(context).size.width.toInt()) as AdSize;
    return _adSize;
  }
 
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'AdMob Sample',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: const Text("AdMob Sample"),
        ),
        body: SafeArea(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Expanded(
                child: Container(
                  decoration: const BoxDecoration(color: Colors.green),
                  child: const Center(
                    child: Text("contents"),
                  ),
                ),
              ),
 
              Builder(builder: (context) {
                return Container(
                  alignment: Alignment.center,
                  height: _adSize != null ? _adSize!.height.toDouble() : 0,
                  width: _adSize != null ? _adSize!.width.toDouble() : 0,
                  child: FutureBuilder(
                      future: _getAdSize(context),
                      builder: (context, snapshot) {
                        if (snapshot.connectionState ==
                            ConnectionState.done) {
                          if (snapshot.hasError) {
                            return Center(
                              child: Text(snapshot.error.toString(),
                                  textAlign: TextAlign.center,
                                  textScaleFactor: 1.3),
                            );
                          }
 
                          if (!_isAdLoaded) {
                            _ad = BannerAd(
                              adUnitId: AdHelper.bannerAdUnitId,
                              size: _adSize!,
                              request: AdRequest(),
                              listener: BannerAdListener(
                                onAdLoaded: (_) {
                                  setState(() {
                                    _isAdLoaded = true;
                                  });
                                },
                                onAdFailedToLoad: (ad, error) {
                                  //Load失敗時の処理
                                  ad.dispose();
                                  print(
                                      'Ad load failed (code=${error.code} message=${error.message})');
                                },
                              ),
                            );
                            _ad.load();
                            return Container();
                          }
                          return AdWidget(ad: _ad);
                        }
                        else {
                          return Container();
                        }
                      }),
                );
              })
            ],
          ),
        ),
      ),
    );
  }
 
  @override
  void dispose() {
    _ad.dispose();
    _adSize = null;
    _isAdLoaded = false;
 
    super.dispose();
  }
}

まとめ

アダプティブバナー広告の紹介と、実装方法について記載しました。

いかがでしたでしょうか?
ちょっとテクニカルなことをしていますが、
本記事の通りに実装すれば、簡単に実装することができます。

ぜひ収益の最大化に向けて採用してみてください。

参考にしたサイト

バナー広告  |  Android  |  Google for Developers
How to add adaptive banner ads in your flutter app using google mobile ads?
Step 1: Add google_mobile_ads and provider to your pubspec.yaml.
Flutter Apprentice
Build for both iOS and Android with Flutter! Flutter is a new and exciting software development toolkit that lets you target multiple platforms at once, so you ...

テスト広告の広告ユニットID最新はこちらを参照ください。

https://developers.google.com/admob/android/quick-start?hl=ja
https://developers.google.com/admob/android/test-ads?hl=ja
https://developers.google.com/admob/ios/quick-start?hl=ja
https://developers.google.com/admob/ios/test-ads?hl=ja

コメント

タイトルとURLをコピーしました