Flutter公式から広告用パッケージがでましたね
こんにちはこんばんは。すぎっと ٩( ᐛ )و です。
先日、Flutter のイベント Flutter Engage が行われました。
目玉はやはり Flutter 2 でしたね。Flutter のメジャーバージョンが 2 に上がっていろいろな機能が Stable にやってきました。Web が Stable にやってきたことが話題になりました。個人的にはまだお遊び程度でしか利用していないので Stable じゃなくても全然問題ないのですが、Stable になるのは嬉しいものです。
他にも、Windows や Linux 向け、組み込み向けなど Flutter の適用対象がどんどん拡大していくという、Flutter を勉強している身としては ワクワクが止まらない 感じでしたね。
Flutter 2 に関しては以下のアナウンスを参照してください。
さて、実は Flutter Engage は Flutter や Dart のバージョンアップだけではありませんでした。こちらをご覧ください。
右の項目にご注目。
Start monetizing your apps with the Google Mobile Ads SDK
マネタイズ、永遠の課題ですね。Flutter 公式から素敵なパッケージがリリースされました。
Google Mobile Ads SDK が出ました
すでに公式にガッツリと解説ページが用意されています。Flutter は本当に公式のドキュメントが充実しすぎていて、自分の記事の必要性に毎度首をかしげるほどです。
Google Mobile Ads SDK は以下の 2 種類の広告プラットフォームをサポートしています。
- AdMob
- Google Ad Manager
個人開発でよく使われているのは AdMob ですかね。小規模なアプリであれば AdMob で十分だと思います。企業としてビジネスで取り扱うなら Google Ad Manager が選択肢に上がるのかもしれません。説明を読んだ限りそんな感じですが、使ったことはないのでなんとも言えません。
違いについて知りたい方はこちらを見てみてください。
この記事では基本的に AdMob について書いていきます。
何が新しいの?
まず、google.dev からのリリースです。現時点でよく使われる AdMob 用パッケージとしては以下のふたつが代表的なのではないかと思います。
- firebase_admob
こちらは firebase.google.com からのリリースですね。
- admob_flutter
こちらは OSS です。
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 はこちらにあります。
Android 用はこちらです。
試しに使ってみましょう
とはいっても、この内容は公式の YouTube そのままです。日本語で書いている点を除けば公式の動画に勝る点はまったくないです。
公式 YouTube、めちゃくちゃ説明がちゃんとしています。
プロジェクトの新規作成
$ flutter create app_name
でございます。
こちらに前提条件の記載があります。
最新の flutter sdk で flutter create すれば大丈夫です。
私は iOS のみでお試しします。
Android でやってみる場合、 Gradle のバージョンでコケることがあるようなので、上記リンクにある条件にしたがってバージョンを上げるなどしてみてください。
パッケージの追加
pubspec.yaml にパッケージ情報を追加します。
dependencies:
google_mobile_ads: ^0.11.0+2
$ flutter pub get
でございます。
バージョンについては最新をチェックしてください。
AdMob 関係の情報(ID など)を管理するために Provider もついでに使用します。
dependencies:
provider: ^4.3.3
Provider についてはこちらです。最新は 5.0 なのですが、安心の 4.3.3 にしています。
初期化
細かい説明は省きますが、runApp するまでに Flutter のエンジンさんにネイティブよりのお仕事をして欲しかったりする時に行う初期化です。
void main() {
WidgetsFlutterBinding.ensureInitialized();
MobileAds.instance.initialize();
runApp(MyApp());
}
パッケージのインポートをお忘れなく。
import 'package:google_mobile_ads/google_mobile_ads.dart';
Provider 管理下に配置する
初期化した Google Mobile Ads のインスタンスは Provider で中央管理の State にしてしまいます。
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 をこんなふうに書き換えます。
void main() {
WidgetsFlutterBinding.ensureInitialized();
final initFuture = MobileAds.instance.initialize();
final adState = AdState(initFuture);
runApp(Provider.value(
value: adState,
builder: (context, child) => MyApp(),
));
}
こちらもインポートをお忘れなく。
import 'package:provider/provider.dart';
import 'ad_state.dart';
広告用 Widget の作成
広告を配置する Widget ですが、 Google Mobile Ads SDK に含まれている AdWidget を使います。こんなことが書いてあります。
/// 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 の直後にも実行されます。この辺りがミソです。
ここからはもう作業みたいなものなので、動画を見ながら写経しました。
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 は積極的に使いましょう!!
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 の配置です。簡単にコンテナーで挟んでみました。
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 のコピペに便利です。
すぎっと ٩( ᐛ )و
Discussion
そんなことありません。これらの記事に助けられている人が僕をはじめどれだけの人数いることか。
これからもFlutterの記事を楽しみにしています。
そう言っていただけると嬉しいです!最近筆が止まっていたのでまた何かネタ探します🥳
わかりやすい記事ありがとうございます!
こちら2022/2月に確認してみたら、DISCONTINUEDが立っていたので、今後の機能開発はあまり見込めないかも知れません…!
ありがとうございます😊
google_mobile_ads が Ver.1になって、安定したのでその他はほとんど止まってしまいましたね…
近いうちに説明文を追記しておきます😆とても助かります…!!