On this page14 sections
  1. Prerequisites
  2. Connection With Previous Lectures
  3. Learning Objectives
  4. Lesson
  5. 1. The Helper
  6. 2. The Screen With a Dialog
  7. 3. Rewarded vs Rewarded Interstitial
  8. Code Examples
  9. Common Beginner Mistakes
  10. Best Practices
  11. Mini Challenge
  12. Practice Questions
  13. Practice Answers
  14. Key Takeaways

Lecture 009 — Rewarded Interstitial Ads

Category3 — Native & Rewarded Ads
Lecture009 of 010
Estimated video length18 min
TargetFlutter 3.44 · Dart 3.12 · google_mobile_ads 9
LevelBeginner
Source codeGitHub — urraan_admob_final

A rewarded interstitial looks like an interstitial but also gives a reward. Google asks you to warn the user first, so we add a small dialog before the ad.

Contents

Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways


Prerequisites

Lectures 005 and 008.


Connection With Previous Lectures

It combines the previous two: a full-screen ad that starts at a natural pause, with a reward.


Learning Objectives

By the end of this lecture you will be able to:

  • Load and show a RewardedInterstitialAd
  • Show an intro dialog before the ad
  • Grant the reward and keep AdState in sync

Lesson

1. The Helper

dart
// lib/admob/rewarded_interstitial_ad_helper.dart
import 'package:flutter/foundation.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';

import 'admob_manager.dart';

class RewardedInterstitialAdHelper {
  bool isAdLoaded = false;
  RewardedInterstitialAd? _ad;

  void loadRewardedInterstitialAd({
    required VoidCallback onAdShowFullScreen,
    required VoidCallback onAdDismissed,
  }) {
    RewardedInterstitialAd.load(
      adUnitId: AdmobManager.rewardedInterstitialId,
      request: const AdRequest(),
      rewardedInterstitialAdLoadCallback: RewardedInterstitialAdLoadCallback(
        onAdLoaded: (ad) {
          isAdLoaded = true;
          _ad = ad;

          ad.fullScreenContentCallback = FullScreenContentCallback(
            onAdShowedFullScreenContent: (_) => onAdShowFullScreen(),
            onAdDismissedFullScreenContent: (ad) {
              ad.dispose();
              isAdLoaded = false;
              _ad = null;
              onAdDismissed();
              loadRewardedInterstitialAd(
                onAdShowFullScreen: onAdShowFullScreen,
                onAdDismissed: onAdDismissed,
              );
            },
            onAdFailedToShowFullScreenContent: (ad, error) {
              ad.dispose();
              isAdLoaded = false;
              _ad = null;
            },
          );
        },
        onAdFailedToLoad: (error) {
          isAdLoaded = false;
          debugPrint('Rewarded interstitial failed: ${error.code} ${error.message}');
        },
      ),
    );
  }

  bool showAdIfAvailable(void Function(RewardItem reward) onEarnedReward) {
    if (_ad == null || !isAdLoaded) return false;
    _ad!.show(onUserEarnedReward: (_, reward) => onEarnedReward(reward));
    return true;
  }

  void dispose() => _ad?.dispose();
}

It mirrors the rewarded helper: load, callbacks, showAdIfAvailable, reload.

2. The Screen With a Dialog

dart
// lib/screen_rewarded_interstitial_ads.dart
import 'package:flutter/material.dart';

import 'admob/ad_state.dart';
import 'admob/rewarded_interstitial_ad_helper.dart';

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

  @override
  State<ScreenRewardedInterstitialAds> createState() =>
      _ScreenRewardedInterstitialAdsState();
}

class _ScreenRewardedInterstitialAdsState
    extends State<ScreenRewardedInterstitialAds> {
  final adHelper = RewardedInterstitialAdHelper();
  int _coins = 0;

  @override
  void initState() {
    super.initState();
    adHelper.loadRewardedInterstitialAd(
      onAdShowFullScreen: () => AdState().disableAppOpenAds(),
      onAdDismissed: () => AdState().enableAppOpenAdsAfterCooldown(),
    );
  }

  @override
  void dispose() {
    adHelper.dispose();
    super.dispose();
  }

  void _showDialogThenAd() {
    showDialog(
      context: context,
      builder: (dialogContext) => AlertDialog(
        title: const Text('Information'),
        content: const Text(
          'To access this feature you need to watch an ad first.',
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.of(dialogContext).pop(),
            child: const Text('Cancel'),
          ),
          TextButton(
            onPressed: () {
              Navigator.of(dialogContext).pop();
              _watchAd();
            },
            child: const Text('Watch ad'),
          ),
        ],
      ),
    );
  }

  void _watchAd() {
    final shown = adHelper.showAdIfAvailable((reward) {
      setState(() => _coins += reward.amount.toInt());
    });
    if (!shown) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Ad is not ready yet, try again')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Rewarded Interstitial Ads')),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text('Coins: $_coins', style: const TextStyle(fontSize: 24)),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: _showDialogThenAd,
              child: const Text('Download'),
            ),
          ],
        ),
      ),
    );
  }
}
  • The dialog tells the user an ad is coming and why. Cancel closes it; Watch ad closes it then shows the ad.
  • We close the dialog with its own dialogContext, then call _watchAd — never use the same context for both.
  • AdState disables app-open ads while the ad shows.

3. Rewarded vs Rewarded Interstitial

RewardedRewarded interstitial
Starts whenUser taps a buttonAt a natural pause (you trigger it)
WarningButton textRequired intro screen

Code Examples

Example 1 — Dialog Wording

"Watch a short ad to unlock this download?" with Cancel and Watch ad is clear. A dialog that says only "Continue" is not.


Common Beginner Mistakes

1. No intro — Google requires the user to be told first.

2. Using the dialog's context after it closes — use the screen's context for the snackbar.

3. Rewarding on dismiss.


Best Practices

  • Always show an intro before a rewarded interstitial
  • Offer a clear Cancel
  • Keep reward logic in the callback

Mini Challenge

Skip the dialog if the user has already watched one ad today (store a DateTime).


Practice Questions

  1. Why a dialog first?
  2. Which callback grants the reward?
  3. Which format needs a button tap to start?
  4. Why two contexts?

Practice Answers

  1. Users must know an ad will play and why.
  2. onUserEarnedReward.
  3. Rewarded.
  4. The dialog's context is invalid after it closes.

Key Takeaways

  • Intro dialog is required
  • Reward in the callback
  • Same helper pattern as rewarded