🎉

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

10 min read 2

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

先日、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

すぎっと٩( ᐛ )و