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

Lecture 005 — Interstitial Ads

Category2 — Banner & Full-Screen Ads
Lecture005 of 010
Estimated video length20 min
TargetFlutter 3.44 · Dart 3.12 · google_mobile_ads 9
LevelBeginner
Source codeGitHub — urraan_admob_final

An interstitial covers the whole screen, so timing is everything. We show it at a natural pause — when the user leaves a screen — and carry on with the navigation when it is closed.

Contents

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


Prerequisites

Lectures 001–004; PopScope, callbacks.


Connection With Previous Lectures

The banner sat quietly. The interstitial interrupts, so we need callbacks: what to do when it opens, when it closes, and when it fails.


Learning Objectives

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

  • Load and show an interstitial
  • Use FullScreenContentCallback for shown, dismissed and failed
  • Preload the next ad after one is shown
  • Continue the user's action after the ad closes

Lesson

1. The Helper

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

import 'admob_manager.dart';

class InterstitialAdHelper {
  bool isAdLoaded = false;
  InterstitialAd? _interstitialAd;

  void loadInterstitialAd({
    required VoidCallback onAdDismissed,
    required VoidCallback onAdShowFullScreen,
  }) {
    InterstitialAd.load(
      adUnitId: AdmobManager.interstitialId,
      request: const AdRequest(),
      adLoadCallback: InterstitialAdLoadCallback(
        onAdLoaded: (ad) {
          isAdLoaded = true;
          _interstitialAd = ad;

          ad.fullScreenContentCallback = FullScreenContentCallback(
            onAdShowedFullScreenContent: (_) => onAdShowFullScreen(),
            onAdDismissedFullScreenContent: (ad) {
              ad.dispose();
              isAdLoaded = false;
              _interstitialAd = null;
              onAdDismissed();
              // Load the next one so it is ready next time.
              loadInterstitialAd(
                onAdDismissed: onAdDismissed,
                onAdShowFullScreen: onAdShowFullScreen,
              );
            },
            onAdFailedToShowFullScreenContent: (ad, error) {
              ad.dispose();
              isAdLoaded = false;
              _interstitialAd = null;
            },
          );
        },
        onAdFailedToLoad: (error) {
          isAdLoaded = false;
          debugPrint('Interstitial failed: ${error.code} ${error.message}');
        },
      ),
    );
  }

  void showAdIfAvailable() {
    if (_interstitialAd != null && isAdLoaded) {
      _interstitialAd!.show();
      isAdLoaded = false;
    }
  }

  void dispose() => _interstitialAd?.dispose();
}
  • Load early, show later. load is asynchronous; we call it in initState so the ad is ready when needed.
  • An interstitial can be shown once. After it closes we dispose it and load the next.
  • onAdShowFullScreen and onAdDismissed are callbacks the screen provides — the helper does not know about navigation.
  • onAdFailedToShowFullScreenContent cleans up so a broken ad is not reused.

2. The Shared Flag

An interstitial and an app-open ad must not collide. This tiny class records "a full-screen ad was just shown":

dart
// lib/admob/ad_state.dart
/// Shared flag: while a full-screen ad is showing (and for 30 seconds after),
/// the app-open ad must stay quiet.
class AdState {
  static final AdState _instance = AdState._internal();
  factory AdState() => _instance;
  AdState._internal();

  bool isOtherAdsDisabled = false;

  void disableAppOpenAds() => isOtherAdsDisabled = true;

  /// Re-enable app-open ads after a cooldown.
  void enableAppOpenAdsAfterCooldown({
    Duration cooldown = const Duration(seconds: 30),
  }) {
    Future.delayed(cooldown, () => isOtherAdsDisabled = false);
  }
}

Lecture 006 reads it; here we only set it.

3. The Screen

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

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

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

  @override
  State<ScreenInterstitials> createState() => _ScreenInterstitialsState();
}

class _ScreenInterstitialsState extends State<ScreenInterstitials> {
  final interstitialAdHelper = InterstitialAdHelper();

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

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

  void _doNextFunctionality() {
    if (mounted) Navigator.pop(context);
  }

  void _onBack() {
    if (interstitialAdHelper.isAdLoaded) {
      interstitialAdHelper.showAdIfAvailable();
    } else {
      _doNextFunctionality();
    }
  }

  @override
  Widget build(BuildContext context) {
    return PopScope(
      canPop: false,
      onPopInvokedWithResult: (didPop, _) {
        if (!didPop) _onBack();
      },
      child: Scaffold(
        appBar: AppBar(
          leading: IconButton(
            onPressed: _onBack,
            icon: const Icon(Icons.arrow_back_rounded),
          ),
          title: const Text('Second Screen'),
        ),
        body: const Center(child: Text('Press back to see the interstitial')),
      ),
    );
  }
}
  • Back press and the arrow both call _onBack: show the ad if ready, otherwise leave at once. The user is never blocked by an ad that has not loaded.
  • When the ad closes, onAdDismissed runs _doNextFunctionality (here, Navigator.pop).
  • PopScope(canPop: false) lets us intercept the system back gesture too.

Code Examples

Example 1 — Frequency

Showing an interstitial on every back press annoys users and lowers ratings. A simple rule: show one at most every few minutes, or every third action. Keep a counter or a DateTime lastShown in AdState and skip the ad when it is too soon.


Common Beginner Mistakes

1. Loading when about to show — the ad is not ready; load early.

2. Showing the same ad twice — an ad object is single-use; load a new one.

3. Showing at app start or on a button tap that expects a result — policy and user experience problems. Show at natural breaks only.

4. Forgetting to continue navigation — if the ad fails, the user must still be able to leave.


Best Practices

  • Show interstitials between tasks, never in the middle of one
  • Always continue the flow whether or not the ad shows
  • Limit the frequency

Mini Challenge

Add a counter in AdState so the interstitial shows only on every second visit to the screen.


Practice Questions

  1. Why load in initState?
  2. Why can an interstitial only be used once?
  3. What happens if the ad is not loaded when Back is pressed?
  4. What is PopScope for here?

Practice Answers

  1. So it is ready when needed.
  2. The SDK releases it after showing.
  3. The screen closes immediately.
  4. To handle the system back gesture.

Key Takeaways

  • Load early, show at natural pauses
  • Callbacks continue the user's flow
  • Dispose and reload after each show