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

Lecture 004 — Banner Ads

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

A banner is the simplest ad: a strip that sits on screen while the user works. The one trick is to reserve its space before it loads, so the screen does not jump.

Contents

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


Prerequisites

Lectures 001–003; StatefulWidget, didChangeDependencies, bottomNavigationBar.


Connection With Previous Lectures

The SDK is initialised. The first ad we show is the banner, in the bottom bar of its own screen.


Learning Objectives

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

  • Create a BannerAd and load it
  • Show it with AdWidget
  • Reserve space so the layout does not jump
  • Dispose the ad with the screen

Lesson

1. The Helper

dart
// lib/admob/banner_ad_helper.dart
import 'package:flutter/material.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';

import 'admob_manager.dart';

class BannerAdHelper {
  BannerAd? _bannerAd;
  bool isLoaded = false;

  void loadBannerAd(
    BuildContext context,
    VoidCallback onAdLoaded, {
    int height = 100,
  }) {
    _bannerAd?.dispose();
    isLoaded = false;
    _bannerAd = BannerAd(
      size: AdSize(
        width: MediaQuery.of(context).size.width.truncate(),
        height: height,
      ),
      adUnitId: AdmobManager.bannerAdaptiveId,
      listener: BannerAdListener(
        onAdLoaded: (_) {
          isLoaded = true;
          onAdLoaded();
        },
        onAdFailedToLoad: (ad, error) {
          debugPrint('Banner failed: ${error.code} ${error.message}');
          ad.dispose();
        },
      ),
      request: const AdRequest(),
    )..load();
  }

  BannerAd? get bannerAd => isLoaded ? _bannerAd : null;

  void dispose() => _bannerAd?.dispose();

  /// Reserves the banner's height before it loads, so the layout never jumps.
  static Widget getBannerView({
    required bool isPurchase,
    required bool isLoaded,
    required BannerAd? bannerAd,
    double height = 100,
  }) {
    if (isPurchase) return const SizedBox();
    return Container(
      color: Colors.grey.shade100,
      padding: const EdgeInsets.symmetric(vertical: 5),
      height: isLoaded && bannerAd != null
          ? bannerAd.size.height.toDouble()
          : height,
      width: double.infinity,
      child: isLoaded && bannerAd != null
          ? AdWidget(ad: bannerAd)
          : const SizedBox(),
    );
  }
}
  • BannerAd(size:, adUnitId:, listener:, request:) — the ad object.
  • AdSize(width:, height:) — full screen width and a height we choose.
  • onAdLoaded sets isLoaded and tells the screen to rebuild; onAdFailedToLoad disposes the failed ad.
  • getBannerView returns a fixed-height container even before the ad loads. That reserved space is why the content above does not move when the ad arrives.
  • bannerAd returns null until loaded, so a screen cannot show a half-ready ad.

2. The Screen

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

import 'admob/banner_ad_helper.dart';

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

  @override
  State<ScreenBannerAds> createState() => _ScreenBannerAdsState();
}

class _ScreenBannerAdsState extends State<ScreenBannerAds> {
  final bannerAdHelper = BannerAdHelper();

  // didChangeDependencies, not initState: loading needs MediaQuery (the screen width).
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    bannerAdHelper.loadBannerAd(context, () {
      if (mounted) setState(() {});
    }, height: 200);
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Banner Ads')),
      bottomNavigationBar: BannerAdHelper.getBannerView(
        isPurchase: false,
        isLoaded: bannerAdHelper.isLoaded,
        bannerAd: bannerAdHelper.bannerAd,
        height: 200,
      ),
      body: const Center(child: Text('Banner sits at the bottom')),
    );
  }
}
  • didChangeDependencies instead of initState because we need MediaQuery for the width. That method can run more than once, so the helper disposes any previous ad before creating a new one.
  • if (mounted) setState(...) — the ad may load after the screen is closed.
  • dispose releases the ad's memory.

3. Adaptive Banners

A custom AdSize is fine for learning. For production, adaptive banners (AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(width)) choose the best height for the device and usually fill better.


Code Examples

Example 1 — Banner Height

Change height: 200 in both the helper call and getBannerView to 100. Run again: the reserved area and the ad shrink together. Keep the two numbers equal.


Common Beginner Mistakes

1. Not disposing — leaks memory; call dispose in the screen.

2. setState after dispose — guard with mounted.

3. Different heights for reserve and ad — the screen jumps when the ad loads.

4. Placing a banner inside a scrolling list without a fixed height — it throws a layout error.


Best Practices

  • Reserve the banner's height before it loads
  • Put banners where they do not sit next to buttons — accidental taps are against policy
  • One banner per screen is enough

Mini Challenge

Move the banner to the top using AppBar's bottom: or a Column at the top of the body.


Practice Questions

  1. Why didChangeDependencies here?
  2. Why a fixed-height container?
  3. What does bannerAd return before loading?
  4. What should dispose do?

Practice Answers

  1. MediaQuery is needed for the width.
  2. So the layout does not jump when the ad appears.
  3. null.
  4. Release the ad object.

Key Takeaways

  • Banner = BannerAd + AdWidget
  • Reserve the space; dispose the ad
  • Prefer adaptive banners in production