🎉

Flutter公式から広告用パッケージがでましたね

2021/03/08に公開
4

こんにちはこんばんは。すぎっと ٩( ᐛ )و です。

先日、Flutter のイベント Flutter Engage が行われました。
目玉はやはり Flutter 2 でしたね。Flutter のメジャーバージョンが 2 に上がっていろいろな機能が Stable にやってきました。Web が Stable にやってきたことが話題になりました。個人的にはまだお遊び程度でしか利用していないので Stable じゃなくても全然問題ないのですが、Stable になるのは嬉しいものです。
他にも、Windows や Linux 向け、組み込み向けなど Flutter の適用対象がどんどん拡大していくという、Flutter を勉強している身としては ワクワクが止まらない 感じでしたね。

Flutter 2 に関しては以下のアナウンスを参照してください。
https://developers.googleblog.com/2021/03/announcing-flutter-2.html

さて、実は Flutter Engage は Flutter や Dart のバージョンアップだけではありませんでした。こちらをご覧ください。
https://events.flutter.dev/

右の項目にご注目。

Start monetizing your apps with the Google Mobile Ads SDK

マネタイズ、永遠の課題ですね。Flutter 公式から素敵なパッケージがリリースされました。

Google Mobile Ads SDK が出ました

すでに公式にガッツリと解説ページが用意されています。Flutter は本当に公式のドキュメントが充実しすぎていて、自分の記事の必要性に毎度首をかしげるほどです。
https://flutter.dev/ads

Google Mobile Ads SDK は以下の 2 種類の広告プラットフォームをサポートしています。

  • AdMob
  • Google Ad Manager

個人開発でよく使われているのは AdMob ですかね。小規模なアプリであれば AdMob で十分だと思います。企業としてビジネスで取り扱うなら Google Ad Manager が選択肢に上がるのかもしれません。説明を読んだ限りそんな感じですが、使ったことはないのでなんとも言えません。

違いについて知りたい方はこちらを見てみてください。
https://support.google.com/adsense/answer/9234653?hl=ja

この記事では基本的に AdMob について書いていきます。

何が新しいの?

まず、google.dev からのリリースです。現時点でよく使われる AdMob 用パッケージとしては以下のふたつが代表的なのではないかと思います。

  • firebase_admob

こちらは firebase.google.com からのリリースですね。
https://pub.dev/packages/firebase_admob

  • admob_flutter

こちらは OSS です。
https://pub.dev/packages/admob_flutter

firebase_admob は firebase から出ているのでしっかりメンテされるだろうなぁという安心感がありますね。ただ 1 つ厄介なことがありました。

広告が Flutter のレイアウトデザインには無関係に、画面下部・上部といった固定位置に表示される という点です。
したがって、広告を表示するアプリ画面の重要なコンテンツが広告の下に隠れてしまわないように気をつけるという必要がありました。とても煩わしい作業です。

これを改善してくれているのが admob_flutter でした。こちらは 広告要素を Widget として Flutter の Widget ツリーに組み込んでくれます。 UI 要素の 1 つとして取り扱うことができるんですね。これにより、スクロールする ListView のコンテンツの 1 つとして表示するなど、柔軟な表現かでき、 ユーザーにストレスを与えない広告表示 を実現しています。

Google Mobile Ads は公式からのリリースであり、かつ広告要素を Widget として扱うことができる待望のパッケージです。

AdMob で使える広告の形式とテスト用 ID

AdMob ではいくつかの形式の広告が利用でき、広告の形式によって ID を取得する必要があります。これにより、自分のアプリから得た収益というのを AdMob 側で管理してもらえるということです。

さて、AdMob で利用できる広告の形式ですが、以下のようなものがあります。


バナーは画面の隅っこにちらっと出ている広告ですね。
インタースティシャルはガバッと全画面に出てくる広告です。
リワードはゲームアプリとかでよくある報酬系ですね。
特徴的なのがネイティブですね。これはより高度なもので、広告の表示形式をカスタマイズしてよりアプリのデザインを損なわないような形にするものです。近いうちに試してみたいなぁと思っていますが、今日はバナーを使います。

さて、AdMob はリリースするまではテスト用の ID を使います。

iOS 用のテスト ID はこちらにあります。

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

Android 用はこちらです。

https://developers.google.com/admob/android/test-ads#sample_ad_units

試しに使ってみましょう

とはいっても、この内容は公式の YouTube そのままです。日本語で書いている点を除けば公式の動画に勝る点はまったくないです。

公式 YouTube、めちゃくちゃ説明がちゃんとしています。

https://www.YouTube.com/watch?v=m0d_pbgeeG8&t=13s

プロジェクトの新規作成

$ flutter create app_name

でございます。

こちらに前提条件の記載があります。
最新の flutter sdk で flutter create すれば大丈夫です。
https://pub.dev/packages/google_mobile_ads#prerequisites
私は iOS のみでお試しします。
Android でやってみる場合、 Gradle のバージョンでコケることがあるようなので、上記リンクにある条件にしたがってバージョンを上げるなどしてみてください。

パッケージの追加

pubspec.yaml にパッケージ情報を追加します。

pubspec.yaml
dependencies:
  google_mobile_ads: ^0.11.0+2
$ flutter pub get

でございます。

バージョンについては最新をチェックしてください。
https://pub.dev/packages/google_mobile_ads/install

AdMob 関係の情報(ID など)を管理するために Provider もついでに使用します。

pubspec.yaml
dependencies:
  provider: ^4.3.3

Provider  についてはこちらです。最新は 5.0 なのですが、安心の 4.3.3 にしています。
https://pub.dev/packages/provider

初期化

細かい説明は省きますが、runApp するまでに Flutter のエンジンさんにネイティブよりのお仕事をして欲しかったりする時に行う初期化です。

main.dart
void main() {
  WidgetsFlutterBinding.ensureInitialized();
  MobileAds.instance.initialize();
  runApp(MyApp());
}

パッケージのインポートをお忘れなく。

main.dart
import 'package:google_mobile_ads/google_mobile_ads.dart';

Provider 管理下に配置する

初期化した Google Mobile Ads のインスタンスは Provider で中央管理の State にしてしまいます。

ad_state.dart
import 'dart:io';
import 'package:google_mobile_ads/google_mobile_ads.dart';

class AdState {
  Future<InitializationStatus> initialization;
  AdState(this.initialization);
  String get bannerAdUnitId =>
      Platform.isAndroid ? "ANDROID用ID" : "iOS用ID";
}

これを使ってさきほどの main.dart をこんなふうに書き換えます。

main.dart
void main() {
  WidgetsFlutterBinding.ensureInitialized();
  final initFuture = MobileAds.instance.initialize();
  final adState = AdState(initFuture);
  runApp(Provider.value(
    value: adState,
    builder: (context, child) => MyApp(),
  ));
}

こちらもインポートをお忘れなく。

main.dart
import 'package:provider/provider.dart';
import 'ad_state.dart';

広告用 Widget の作成

広告を配置する Widget ですが、 Google Mobile Ads SDK に含まれている AdWidget を使います。こんなことが書いてあります。

google_mobile_ads-0.11.0+2/lib/src/ad_containers.dart
/// Displays an [Ad] as a Flutter widget.
///
/// This widget takes ads inheriting from [AdWithView]
/// (e.g. [BannerAd] and [NativeAd]) and allows them to be added to the Flutter
/// widget tree.
///
/// Must call `load()` first before showing the widget. Otherwise, a
/// [PlatformException] will be thrown.
class AdWidget extends StatefulWidget {
  /// Default constructor for [AdWidget].
  ///
  /// [ad] must be loaded before this is added to the widget tree.
  const AdWidget({Key key,  this.ad})
      : assert(ad != null),
        super(key: key);

  /// Ad to be displayed as a widget.
  final AdWithView ad;

  
  _AdWidgetState createState() => _AdWidgetState();
}

// _AdWidgetState 側は割愛~~

いろいろ書いてありますが、

  • 広告表示用の AdWithView インスタンスを渡すこと
  • Ad は Widget ツリーに追加する前にロードしておくこと

というところがポイントになります。
広告表示用のインスタンスですが、広告の形式によっていくつか用意されているクラスを使います。バナー広告の場合は BannerAd です。他にも NativeAd や RewardedAd などが用意されています。これらはすべて AdWithView を継承しています。

では 2 点目の事前にロードしておくという点についてです。これは知っていれば OK という感じです。didChangeDependencies() を使います。これは、ざっくり言えば build を実行する前に実行されるメソッドです。 State の変更に合わせて呼び出されます。また、initState の直後にも実行されます。この辺りがミソです。

ここからはもう作業みたいなものなので、動画を見ながら写経しました。

main.dart
class _MyHomePageState extends State<MyHomePage> {
  BannerAd banner;
  
  void didChangeDependencies() {
    super.didChangeDependencies();
    final adState = Provider.of<AdState>(context);
    adState.initialization.then((status) {
      setState(() {
        banner = BannerAd(
          adUnitId: adState.bannerAdUnitId,
          size: AdSize.banner,
          request: AdRequest(),
          listener: adState.adListener,
        )..load();
      });
    });
  }

  
  Widget build(BuildContext context) {
  //... 以下略

didChangeDependencies()をオーバーライドして呼び出されるようにし、元のメソッドをコールした上で追加実装をしています。追加実装では、State のセットアップをしています。また、広告の形式としてバナーを採用しています。

あとは AdWidget をレイアウトに組み込むだけですなのですが、Listener の実装を忘れていました。ここで追加します。動画の中でも説明されていますが、まぁおまじないです。詳しく知りたい方は AdListener の実装を見てみてください。 GO TO DEFINITIOIN は積極的に使いましょう!!

ad_state.dart
class AdState {
  Future<InitializationStatus> initialization;

  AdState(this.initialization);

  String get bannerAdUnitId =>
      Platform.isAndroid ? "ca-app-pub-3940256099942544/6300978111" : "ca-app-pub-3940256099942544/2934735716";

  AdListener get adListener => _adListener;
  AdListener _adListener = AdListener(
    onAdLoaded: (ad) => print('Ad loaded: ${ad.adUnitId}.'),
    onAdClosed: (ad) => print('Ad closed: ${ad.adUnitId}.'),
    onAdFailedToLoad: (ad, error) => print('Ad failed to load: ${ad.adUnitId}, $error.'),
    onAdOpened: (ad) => print('Ad opened: ${ad.adUnitId}.'),
    onAppEvent: (ad, name, data) => print('App event: ${ad.adUnitId}, $name, $data'),
    onApplicationExit: (ad) => print('App Exit: ${ad.adUnitId}.'),
    onNativeAdClicked: (nativeAd) => print('Native ad clicked: ${nativeAd.adUnitId}'),
    onNativeAdImpression: (nativeAd) => print('Native ad impression: ${nativeAd.adUnitId}'),
    onRewardedAdUserEarnedReward: (ad, reward) =>
        print('User rewarded: ${ad.adUnitId}, ${reward.amount} ${reward.type}.'),
  );
}

では、Widget の配置です。簡単にコンテナーで挟んでみました。

main.dart
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Container(height: 200, color: Colors.cyan, child: Center(child: Text('AdMob Text'))),
            if (banner == null)
              SizedBox(height: 50) // Ads
            else
              Container(
                height: 50,
                child: AdWidget(ad: banner),
              ),
            Container(height: 200, color: Colors.lightBlue, child: Center(child: Text('AdMob Text'))),
          ],
        ),
      ),
    );
  }

こんなふうに、通常の UI 構築と同じ感覚で広告を配置できます。

説明は以上です。NativeAd にもチャレンジしてみたいですね。

今回のサンプルはこちらにあります。Listener のコピペに便利です。
https://github.com/sugitlab/flutter_googleads_sample

すぎっと ٩( ᐛ )و

GitHubで編集を提案

Discussion

みるみみるみ

自分の記事の必要性に毎度首をかしげるほどです。

そんなことありません。これらの記事に助けられている人が僕をはじめどれだけの人数いることか。
これからもFlutterの記事を楽しみにしています。

sugitsugit

そう言っていただけると嬉しいです!最近筆が止まっていたのでまた何かネタ探します🥳

sueko14sueko14

わかりやすい記事ありがとうございます!

firebase_admob は firebase から出ているのでしっかりメンテされるだろうなぁという安心感がありますね。

こちら2022/2月に確認してみたら、DISCONTINUEDが立っていたので、今後の機能開発はあまり見込めないかも知れません…!

sugitsugit

ありがとうございます😊
google_mobile_ads が Ver.1になって、安定したのでその他はほとんど止まってしまいましたね…
近いうちに説明文を追記しておきます😆とても助かります…!!