Flutter

【Flutter】AdMobのリワード広告をRiverpodを使用して実装するサンプルコード

記事内に商品プロモーションを含む場合があります

Flutterでマネタイズをする際、AdMobを使用した広告がよく使用されます。

広告の中でも、広告を視聴して貰うお礼に報酬を付与するリワード広告は、収益性が高いためアプリオーナーに人気かつユーザーはアプリを便利にするための報酬がもらえるため、人気が高い広告となっています。

AdMobを使用することで、Flutterで作成するアプリにも比較的簡単にリワード広告を実装することが可能です。

本記事では状態管理用のパッケージであるRiverpodを使用して、AdMobのリワード広告を実装するサンプルコードを備忘録として残しておきます。

※あくまでもサンプルなので、ご利用の際は自身のアプリに合わせて適宜変更してください。

AdMobでバナー広告を実装するサンプルコードをまとめた記事も合わせてどうぞ。

【Flutter】AdMobのバナー広告を表示するサンプルコードFlutterでマネタイズをする際、AdMobを使用した広告がよく使用されます。 私も自分のアプリにAdMobを使用して広告を表示...

AdMob リワード広告で使用するパッケージ

AdMobのリワード広告を実装する際は、以下のパッケージを使用します。

google_mobile_ads

Google本家から出ているパッケージで、これを使用してAdMob関連の実装をすることができます。

リワード広告以外にも、バナー広告やインタースティシャル広告を実装する場合にも、このパッケージを使用します。

pubspec.yamlでパッケージの使用を宣言しておきましょう。

FlutterでAdMobのリワード広告をRiverpodを使用して実装する

今回のサンプルで作成するファイルは以下の3点です。

  • RewardAdState
  • RewardAdController
  • RewardAdScreen

RewardAdStateの実装

まずはリワード広告の状態を保持する「RewardAdState」を作成していきます。

作成にはfreezedパッケージを使用します。

※freezedパッケージの使い方については以下の記事をご覧ください。

【簡単】Flutter freezedパッケージを最小限の機能だけで使用する手順Flutterの状態管理手法は現在時点(2021年2月時点)で、以下のような組み合わせが流行っています。 Riverpod + S...

RewardAdStateにはリワード広告広告の読み込みが完了したかどうかのフラグをもたせることにします。

@freezed
class RewardAdState with _$RewardAdState {
  const factory RewardAdState({
    RewardedAd? rewardedAd,
    @Default(false) bool isLoaded,
  }) = _RewardAdState;
}

RewardAdControllerの実装

次にRewardAdControllerを実装していきます。

サンプルコードのあとでポイントごとに解説を入れています。

final rewardAdProvider =
    StateNotifierProvider.autoDispose<RewardAdController, RewardAdState>(
        (ref) => RewardAdController());

class RewardAdController extends StateNotifier<RewardAdState> {
  RewardAdController() : super(const RewardAdState());

  void loadRewardAd() {
    if (!state.isLoaded) {
      RewardedAd.load(
        adUnitId: _rewardAdUnitId,
        request: const AdRequest(),
        rewardedAdLoadCallback: RewardedAdLoadCallback(
          onAdLoaded: (RewardedAd ad) {
            print('$ad loaded.');
            state = state.copyWith(
              rewardedAd: ad,
              isLoaded: true,
            );
            state.rewardedAd?.fullScreenContentCallback =
                FullScreenContentCallback(
              onAdShowedFullScreenContent: (RewardedAd ad) =>
                  print('$ad onAdShowedFullScreenContent.'),
              onAdDismissedFullScreenContent: (RewardedAd ad) {
                print('$ad onAdDismissedFullScreenContent.');
                ad.dispose();
                state.rewardedAd?.dispose();
              },
              onAdFailedToShowFullScreenContent:
                  (RewardedAd ad, AdError error) {
                print('$ad onAdFailedToShowFullScreenContent: $error');
                ad.dispose();
                state.rewardedAd?.dispose();
              },
              onAdImpression: (RewardedAd ad) =>
                  print('$ad impression occurred.'),
            );
          },
          onAdFailedToLoad: (LoadAdError error) {
            print('RewardedAd failed to load: $error');
            state = state.copyWith(isLoaded: false);
          },
        ),
      );
    }
  }

  /// 引数は報酬を付与する処理
  Future<void> showRewardAd(VoidCallback callback) async {
    await state.rewardedAd?.show(
      onUserEarnedReward: (AdWithoutView ad, RewardItem reward) {
        callback(); // リワードの視聴を完了した場合の報酬付与処理を呼び出す
      },
    );
  }

  String get _rewardAdUnitId {
    if (kDebugMode) {
      // return RewardedAd.testAdUnitId;  // Deprecatedになった
      // 公式ドキュメントで公開されているデモ広告ユニットIDを指定する
      // https://developers.google.com/admob/android/test-ads?hl=ja
      // https://developers.google.com/admob/ios/test-ads?hl=ja
      return Platform.isAndroid ? 'ca-app-pub-3940256099942544/5224354917' 
                                : 'ca-app-pub-3940256099942544/1712485313';
    }

    if (Platform.isAndroid) {
      return ''; // Androidのリワード広告IDを指定
    } else if (Platform.isIOS) {
      return ''; // iOSのリワード広告IDを指定
    } else {
      // return BannerAd.testAdUnitId;  // Deprecatedになった
      // https://developers.google.com/admob/android/test-ads?hl=ja
      // AndroidのテストIDを暫定で返却しておく
      return 'ca-app-pub-3940256099942544/5224354917';
    }
  }

  @override
  void dispose() {
    if (state.rewardedAd != null) {
      state.rewardedAd?.dispose();
      state = state.copyWith(rewardedAd: null);
    }
    super.dispose();
  }
}

loadRewardAd

まずはじめにloadRewardAdで、リワード広告のロード処理を行います。

リワード広告は他の広告と同じように、表示するためにAdMobへのリクエストをして広告をロードしておく必要があります。

google_mobile_adsのRewardedAd.loadで広告をロードすることができます。

onAdLoadedにはリクエスト/広告のロードが完了した場合の処理を記述します。

サンプルコードでは広告の読み込みが完了したかどうかのフラグをtrueにし、広告を閲覧された時の処理を設定しています。

このとき大切なことが、広告を閲覧された時の処理内でonAdDismissedFullScreenContentとonAdFailedToShowFullScreenContentの2つで、広告を破棄しておくことです。

広告が不要になった場合にリワード広告を破棄する必要があるので、破棄しておきましょう。このproviderではAutoDisposeするようにしていますが、上記の2パターンの場合はは明示的に破棄しておいたほうが無難です。

showRewardAd

リワード広告を実際に呼び出す処理を実装します。

呼び出し方は非常に簡単で、ロードした広告のshowメソッドを呼び出すだけです。

その際のポイントがshowメソッドのonUserEarnedRewardにて、報酬を付与する処理を呼び出すようにすることです。

ここで呼び出すようにした処理は、ユーザーが広告を見終わったタイミングで呼び出されます。

サンプルコードでは共通処理としてリワード広告を使用したいので、showRewardAdの引数に報酬付与処理を渡すようにしています。

※2022/2/14追記

最新のgoogle_mobile_adsを使用している場合、onUserEarnedRewardに与えるOnUserEarnedRewardCallbackの引数が「RewardedAdからAdWithoutView」に変わっています。

このサンプルにおいては処理に変更はありませんが、ドキュメントは確認しておくと良いかもしれません。

RewardedAd.testAdUnitIdについて

※2022/2/14追記

最新のgoogle_mobile_adsを使用している場合、「RewardedAd.testAdUnitId」がDeprecated(非推奨)となっています。

公式ドキュメントのデモ広告IDを使用しなさいということなので、それを使用するといいでしょう。

(今の所、RewardedAd.testAdUnitIdとドキュメントのデモ広告IDは同じですが、いつの間にか変わっているかもしれないので、非推奨の方は使用しないほうが良いと思います。)

RewardAdScreenの実装

最後にリワード広告を実際に表示する画面の実装です。

画面の処理は非常にシンプルで、広告の呼び出し完了前はロード中の表示をしておき、ロードが完了したらRewardAdControllerで用意したshowRewardAdを呼び出すだけです。呼び出されるとリワード広告が呼び出されます!

showRewadAdにはユーザーへの報酬付与処理を与えることを忘れずに。

お疲れ様です!これで無事にリワード広告を呼び出すことができました!

class RewardAdScreen extends HookConsumerWidget {
  const RewardAdScreen({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final isLoaded =
        ref.watch(rewardAdProvider.select((value) => value.isLoaded));
    final rewardNotifier = ref.watch(rewardAdProvider.notifier)..loadRewardAd();
    return Scaffold(
      body: Column(
        children: [
          if (!isLoaded) ...[
            SizedBox(
              width: 80,
              child: ElevatedButton(
                onPressed: () {},
                child: const SizedBox(
                  height: 20,
                  width: 20,
                  child: CircularProgressIndicator(
                    color: kWhiteColor,
                  ),
                ),
              ),
            ),
          ] else ...[
            SizedBox(
              width: 80,
              child: ElevatedButton(
                onPressed: () async {
                  await rewardNotifier.showRewardAd(() async {
                    print('報酬を付与する処理1です!'); // 報酬を付与する処理を呼び出す
                    print('報酬を付与する処理2です!'); // 複数でもOK
                  });
                },
                child: const Text('リワード!'),
              ),
            ),
          ]
        ],
      ),
    );
  }
}

事前に広告をロードしておいたほうが良い

ちなみにですが、そこそこ通信環境がいい場所でもロード処理は2,3秒程かかります。

広告のロード時間を減らすために、リワード広告を使用する画面に到着する前のタイミングで、事前に広告のロードを開始しておくことは、ユーザビリティを向上させるために推奨される手段です。

このサンプルコードで実装する場合は、事前の画面でロード処理を呼び出しておくなどの工夫が必要です。

まとめ

FlutterのAdMobでリワード広告を実装するサンプルコードでした。

Riverpodを使用することで、各ファイルの役割が明確になり非常に見通しの良い実装にすることができました。

別途、AdMobを使用したバナー広告の実装サンプルも記事にまとめているのでよろしければ御覧ください。

【Flutter】AdMobのバナー広告を表示するサンプルコードFlutterでマネタイズをする際、AdMobを使用した広告がよく使用されます。 私も自分のアプリにAdMobを使用して広告を表示...

 

○Flutter中級者以上にオススメの参考書

○Flutter初学者にはやはりこの参考書がオススメ