メインコンテンツへスキップ

概要

リワード広告は、ユーザーが広告を最後まで視聴すると報酬を受け取れるフルスクリーン広告です。ゲーム内アイテムや追加コンテンツなどの報酬を提供する際に使用します。

主な特徴

  • 画面全体を覆うフルスクリーン広告
  • 動画広告対応
  • 報酬イベントコールバック提供
  • 報酬タイプと数量情報の伝達
  • ロードと表示を分離した柔軟なタイミング制御
開発環境ではテストユニットIDを使用してください:PUBLIC_TEST_UNIT_ID_REWARDED

AdropRewardedAd

コンストラクタ

AdropRewardedAd({
  required String unitId,
  AdropRewardedListener? listener,
})
パラメータ
パラメータタイプ必須説明
unitIdStringYAd Controlコンソールで作成したユニットID
listenerAdropRewardedListenerN広告イベントリスナー

プロパティ

プロパティタイプ説明
isLoadedbool広告のロード完了状態
unitIdString広告ユニットID
creativeIdStringクリエイティブID
txIdStringトランザクションID
campaignIdStringキャンペーンID
destinationURLString遷移先URL

メソッド

メソッド戻り値説明
load()Future<void>広告をロードします
show()Future<void>広告を画面に表示します
dispose()Future<void>リソースを解放します

基本的な使い方

import 'package:flutter/material.dart';
import 'package:adrop_ads_flutter/adrop_ads_flutter.dart';

class RewardedExample extends StatefulWidget {
  const RewardedExample({super.key});

  @override
  State<RewardedExample> createState() => _RewardedExampleState();
}

class _RewardedExampleState extends State<RewardedExample> {
  bool isLoaded = false;
  AdropRewardedAd? rewardedAd;

  @override
  void initState() {
    super.initState();
    _createRewardedAd();
  }

  void _createRewardedAd() {
    rewardedAd = AdropRewardedAd(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropRewardedListener(
        onAdReceived: (ad) {
          debugPrint('リワード広告ロード成功: ${ad.creativeId}');
          setState(() {
            isLoaded = true;
          });
        },
        onAdFailedToReceive: (ad, errorCode) {
          debugPrint('リワード広告ロード失敗: $errorCode');
        },
        onAdClicked: (ad) {
          debugPrint('リワード広告クリック');
        },
        onAdDidPresentFullScreen: (ad) {
          debugPrint('リワード広告表示');
        },
        onAdDidDismissFullScreen: (ad) {
          debugPrint('リワード広告閉じる');
        },
        onAdFailedToShowFullScreen: (ad, errorCode) {
          debugPrint('リワード広告表示失敗: $errorCode');
        },
        onAdEarnRewardHandler: (ad, type, amount) {
          debugPrint('報酬獲得: タイプ=$type, 数量=$amount');
          _giveReward(type, amount);
        },
      ),
    );
  }

  void _giveReward(int type, int amount) {
    // 報酬付与ロジックを実装
    // 例:コイン、アイテム、追加ライフなど
  }

  void loadAd() {
    rewardedAd?.load();
  }

  void showAd() {
    if (isLoaded) {
      rewardedAd?.show();
    }
  }

  @override
  void dispose() {
    rewardedAd?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('リワード広告サンプル')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: loadAd,
              child: const Text('広告をロード'),
            ),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: isLoaded ? showAd : null,
              child: const Text('広告を見て報酬を受け取る'),
            ),
          ],
        ),
      ),
    );
  }
}

AdropRewardedListener

リワード広告イベントを処理するリスナーです。

コールバック関数

AdropRewardedListener(
  onAdReceived: (AdropAd ad) {
    // 広告ロード成功
  },
  onAdClicked: (AdropAd ad) {
    // 広告クリック
  },
  onAdImpression: (AdropAd ad) {
    // 広告表示
  },
  onAdWillPresentFullScreen: (AdropAd ad) {
    // 広告表示直前(iOS専用)
  },
  onAdDidPresentFullScreen: (AdropAd ad) {
    // 広告表示完了
  },
  onAdWillDismissFullScreen: (AdropAd ad) {
    // 広告閉じる直前(iOS専用)
  },
  onAdDidDismissFullScreen: (AdropAd ad) {
    // 広告閉じる完了
  },
  onAdFailedToReceive: (AdropAd ad, AdropErrorCode errorCode) {
    // 広告ロード失敗
  },
  onAdFailedToShowFullScreen: (AdropAd ad, AdropErrorCode errorCode) {
    // 広告表示失敗
  },
  onAdEarnRewardHandler: (AdropAd ad, int type, int amount) {
    // 報酬獲得
  },
)

コールバック説明

コールバック説明
onAdReceived広告ロード成功時に呼び出し
onAdClicked広告クリック時に呼び出し
onAdImpression広告表示時に呼び出し
onAdWillPresentFullScreen広告表示直前に呼び出し(iOS専用)
onAdDidPresentFullScreen広告表示後に呼び出し
onAdWillDismissFullScreen広告閉じる直前に呼び出し(iOS専用)
onAdDidDismissFullScreen広告閉じた後に呼び出し
onAdFailedToReceive広告ロード失敗時に呼び出し
onAdFailedToShowFullScreen広告表示失敗時に呼び出し
onAdEarnRewardHandler報酬獲得時に呼び出し

報酬処理

報酬コールバック

onAdEarnRewardHandlerコールバックで報酬情報を受け取り処理します。
onAdEarnRewardHandler: (ad, type, amount) {
  debugPrint('報酬獲得: タイプ=$type, 数量=$amount');

  // 報酬タイプに応じた処理
  switch (type) {
    case 1:
      // コイン報酬
      _addCoins(amount);
      break;
    case 2:
      // アイテム報酬
      _addItem(amount);
      break;
    default:
      // デフォルト報酬
      _giveDefaultReward(amount);
  }
}

報酬付与タイミング

報酬は必ずonAdEarnRewardHandlerコールバックで付与してください。広告が完全に視聴される前に閉じられた場合、このコールバックは呼び出されません。
class RewardManager {
  bool _rewardEarned = false;

  void setupRewardedAd() {
    rewardedAd = AdropRewardedAd(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropRewardedListener(
        onAdEarnRewardHandler: (ad, type, amount) {
          // 報酬フラグ設定
          _rewardEarned = true;
        },
        onAdDidDismissFullScreen: (ad) {
          // 広告閉じた後に報酬付与確認
          if (_rewardEarned) {
            _giveReward();
            _rewardEarned = false;
          }
        },
      ),
    );
  }
}

広告の再作成

リワード広告は一回限りです。一度表示された広告は再表示できないため、新しい広告をロードするにはインスタンスを再作成する必要があります。
class _RewardedState extends State<RewardedWidget> {
  bool isLoaded = false;
  bool isShown = false;
  AdropRewardedAd? rewardedAd;

  @override
  void initState() {
    super.initState();
    _createRewardedAd();
  }

  void _createRewardedAd() {
    rewardedAd?.dispose();
    rewardedAd = AdropRewardedAd(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropRewardedListener(
        onAdReceived: (ad) {
          setState(() {
            isLoaded = true;
          });
        },
        onAdDidPresentFullScreen: (ad) {
          setState(() {
            isShown = true;
          });
        },
        onAdEarnRewardHandler: (ad, type, amount) {
          _giveReward(type, amount);
        },
        onAdDidDismissFullScreen: (ad) {
          // 広告閉じた後に新しい広告を準備
          _createRewardedAd();
          rewardedAd?.load();
        },
      ),
    );

    setState(() {
      isLoaded = false;
      isShown = false;
    });
  }

  @override
  void dispose() {
    rewardedAd?.dispose();
    super.dispose();
  }
}

エラー処理

class _RewardedState extends State<RewardedWidget> {
  bool isLoaded = false;
  AdropErrorCode? errorCode;
  AdropRewardedAd? rewardedAd;

  void _createRewardedAd() {
    rewardedAd = AdropRewardedAd(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropRewardedListener(
        onAdReceived: (ad) {
          setState(() {
            isLoaded = true;
            errorCode = null;
          });
        },
        onAdFailedToReceive: (ad, error) {
          setState(() {
            isLoaded = false;
            errorCode = error;
          });
          _handleError(error);
        },
        onAdFailedToShowFullScreen: (ad, error) {
          setState(() {
            errorCode = error;
          });
          _handleError(error);
        },
      ),
    );
  }

  void _handleError(AdropErrorCode error) {
    switch (error) {
      case AdropErrorCode.network:
        debugPrint('ネットワークエラーが発生しました。');
        break;
      case AdropErrorCode.adNoFill:
        debugPrint('表示する広告がありません。');
        break;
      case AdropErrorCode.adShown:
        debugPrint('すでに表示された広告です。');
        break;
      default:
        debugPrint('エラー発生: ${error.code}');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ElevatedButton(
          onPressed: rewardedAd?.load,
          child: const Text('広告をロード'),
        ),
        ElevatedButton(
          onPressed: isLoaded ? rewardedAd?.show : null,
          child: const Text('広告を見て報酬を受け取る'),
        ),
        if (errorCode != null)
          Text(
            'エラー: ${errorCode?.code}',
            style: const TextStyle(color: Colors.red),
          ),
      ],
    );
  }
}

ベストプラクティス

1. 事前ロード

ユーザーが報酬を要求した時にすぐに広告を表示できるよう事前にロードします。
class GameScreen extends StatefulWidget {
  @override
  State<GameScreen> createState() => _GameScreenState();
}

class _GameScreenState extends State<GameScreen> {
  AdropRewardedAd? rewardedAd;
  bool isAdReady = false;

  @override
  void initState() {
    super.initState();
    _preloadAd();
  }

  void _preloadAd() {
    rewardedAd = AdropRewardedAd(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropRewardedListener(
        onAdReceived: (ad) {
          setState(() {
            isAdReady = true;
          });
        },
        onAdEarnRewardHandler: (ad, type, amount) {
          _giveReward(type, amount);
        },
        onAdDidDismissFullScreen: (ad) {
          // 次の広告を事前ロード
          _preloadAd();
        },
      ),
    );
    rewardedAd?.load();
  }

  void showRewardedAd() {
    if (isAdReady) {
      rewardedAd?.show();
      setState(() {
        isAdReady = false;
      });
    }
  }

  @override
  void dispose() {
    rewardedAd?.dispose();
    super.dispose();
  }
}

2. ユーザーへの報酬案内

広告視聴前に受け取れる報酬を明確に案内します。
void showRewardDialog() {
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: const Text('報酬を受け取る'),
      content: const Text('広告を視聴すると100コインを受け取れます!'),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: const Text('キャンセル'),
        ),
        ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
            showRewardedAd();
          },
          child: const Text('広告を見る'),
        ),
      ],
    ),
  );
}

3. 広告の準備状態表示

広告が準備できた時のみ報酬ボタンを有効にします。
Widget buildRewardButton() {
  return ElevatedButton.icon(
    onPressed: isAdReady ? showRewardedAd : null,
    icon: const Icon(Icons.play_circle_outline),
    label: Text(isAdReady ? '広告を見て報酬を受け取る' : '広告ロード中...'),
    style: ElevatedButton.styleFrom(
      backgroundColor: isAdReady ? Colors.green : Colors.grey,
    ),
  );
}

4. 報酬付与の信頼性

報酬付与はサーバーで検証することをお勧めします。
onAdEarnRewardHandler: (ad, type, amount) async {
  try {
    // サーバーに報酬付与をリクエスト
    await _api.grantReward(
      userId: currentUserId,
      transactionId: ad.txId,
      rewardType: type,
      amount: amount,
    );

    // ローカル状態を更新
    _updateLocalBalance(amount);
  } catch (e) {
    debugPrint('報酬付与失敗: $e');
    // リトライまたはエラー処理
  }
}

5. リソース管理

使用しない広告インスタンスは必ず解放してください。
@override
void dispose() {
  rewardedAd?.dispose();
  super.dispose();
}

完全なサンプル

import 'package:flutter/material.dart';
import 'package:adrop_ads_flutter/adrop_ads_flutter.dart';

class RewardedAdPage extends StatefulWidget {
  const RewardedAdPage({super.key});

  @override
  State<RewardedAdPage> createState() => _RewardedAdPageState();
}

class _RewardedAdPageState extends State<RewardedAdPage> {
  bool isLoaded = false;
  bool isLoading = false;
  bool isShown = false;
  int coins = 0;
  AdropErrorCode? errorCode;
  AdropRewardedAd? rewardedAd;

  @override
  void initState() {
    super.initState();
    _createRewardedAd();
  }

  void _createRewardedAd() {
    rewardedAd?.dispose();
    rewardedAd = AdropRewardedAd(
      unitId: 'PUBLIC_TEST_UNIT_ID_REWARDED', // テストユニットID
      listener: AdropRewardedListener(
        onAdReceived: (ad) {
          debugPrint('広告ロード成功');
          debugPrint('クリエイティブID: ${ad.creativeId}');
          debugPrint('キャンペーンID: ${ad.campaignId}');
          setState(() {
            isLoaded = true;
            isLoading = false;
            errorCode = null;
          });
        },
        onAdClicked: (ad) {
          debugPrint('広告クリック: ${ad.destinationURL}');
        },
        onAdImpression: (ad) {
          debugPrint('広告表示: ${ad.creativeId}');
        },
        onAdWillPresentFullScreen: (ad) {
          debugPrint('広告表示予定');
        },
        onAdDidPresentFullScreen: (ad) {
          debugPrint('広告表示完了');
          setState(() {
            isShown = true;
          });
        },
        onAdWillDismissFullScreen: (ad) {
          debugPrint('広告閉じる予定');
        },
        onAdDidDismissFullScreen: (ad) {
          debugPrint('広告閉じる完了');
          // 新しい広告を準備
          _createRewardedAd();
        },
        onAdFailedToReceive: (ad, error) {
          debugPrint('広告ロード失敗: $error');
          setState(() {
            isLoaded = false;
            isLoading = false;
            errorCode = error;
          });
        },
        onAdFailedToShowFullScreen: (ad, error) {
          debugPrint('広告表示失敗: $error');
          setState(() {
            errorCode = error;
          });
        },
        onAdEarnRewardHandler: (ad, type, amount) {
          debugPrint('報酬獲得: タイプ=$type, 数量=$amount');
          setState(() {
            coins += amount;
          });
          _showRewardNotification(amount);
        },
      ),
    );

    setState(() {
      isLoaded = false;
      isShown = false;
      errorCode = null;
    });
  }

  void loadAd() {
    setState(() {
      isLoading = true;
      errorCode = null;
    });
    rewardedAd?.load();
  }

  void showAd() {
    if (isLoaded) {
      rewardedAd?.show();
    }
  }

  void _showRewardNotification(int amount) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('$amountコインを獲得しました!'),
        backgroundColor: Colors.green,
        duration: const Duration(seconds: 2),
      ),
    );
  }

  @override
  void dispose() {
    rewardedAd?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('リワード広告サンプル'),
      ),
      body: SafeArea(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              // コイン表示
              Container(
                padding: const EdgeInsets.all(16),
                decoration: BoxDecoration(
                  color: Colors.amber.shade100,
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Row(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    const Icon(Icons.monetization_on, color: Colors.amber),
                    const SizedBox(width: 8),
                    Text(
                      '$coinsコイン',
                      style: const TextStyle(
                        fontSize: 24,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                  ],
                ),
              ),
              const SizedBox(height: 32),

              // ロードボタン
              ElevatedButton(
                onPressed: isLoading ? null : loadAd,
                child: Text(isLoading ? 'ロード中...' : '広告をロード'),
              ),
              const SizedBox(height: 16),

              // 表示ボタン
              ElevatedButton.icon(
                onPressed: isLoaded ? showAd : null,
                icon: const Icon(Icons.play_circle_outline),
                label: const Text('広告を見て報酬を受け取る'),
                style: ElevatedButton.styleFrom(
                  backgroundColor: isLoaded ? Colors.green : Colors.grey,
                ),
              ),
              const SizedBox(height: 16),

              // リセットボタン
              TextButton(
                onPressed: (isShown || errorCode != null)
                    ? () => _createRewardedAd()
                    : null,
                child: const Text('リセット'),
              ),
              const SizedBox(height: 24),

              // 状態表示
              Text('ロード済み: ${isLoaded ? "はい" : "いいえ"}'),
              Text('表示済み: ${isShown ? "はい" : "いいえ"}'),

              // エラー表示
              if (errorCode != null) ...[
                const SizedBox(height: 16),
                Text(
                  'エラー: ${errorCode?.code}',
                  style: const TextStyle(color: Colors.red),
                ),
              ],
            ],
          ),
        ),
      ),
    );
  }
}

次のステップ