No title

In this guide you'll integrate Google AdMob banner ads into a Flutter app from scratch — clean architecture, proper service layer, and real working code. I follow this exact setup in the video above, so read through once, then build along.


What You'll Build

A Flutter app with a singleton AdMobService that loads and manages a banner ad. The ad sits pinned at the bottom of the screen. The setup works on both Android and iOS.

Test IDs: All ad unit IDs in this post are Google's official test IDs. They will never charge you and are safe to use during development. Swap them for your real IDs only before publishing.

Step 1 — Add the Dependency

Open pubspec.yaml and add the package:

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  google_mobile_ads: ^5.1.0

Then run:

flutter pub get

Step 2 — Android Setup

Open android/app/src/main/AndroidManifest.xml and add the AdMob App ID inside the <application> tag:

AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <application
      android:label="admobflutter"
      android:name="${applicationName}"
      android:icon="@mipmap/ic_launcher">

    <!-- AdMob App ID (test ID) -->
    <meta-data
        android:name="com.google.android.gms.ads.APPLICATION_ID"
        android:value="ca-app-pub-3940256099942544~3347511713"/>

    <activity
        android:name=".MainActivity"
        android:exported="true"
        android:launchMode="singleTop"
        android:taskAffinity=""
        android:theme="@style/LaunchTheme"
        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
        android:hardwareAccelerated="true"
        android:windowSoftInputMode="adjustResize">
      <meta-data
          android:name="io.flutter.embedding.android.NormalTheme"
          android:resource="@style/NormalTheme"/>
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
    </activity>

    <meta-data
        android:name="flutterEmbedding"
        android:value="2"/>
  </application>

  <queries>
    <intent>
      <action android:name="android.intent.action.PROCESS_TEXT"/>
      <data android:mimeType="text/plain"/>
    </intent>
  </queries>
</manifest>
Test App ID (Android): ca-app-pub-3940256099942544~3347511713

Step 3 — iOS Setup

Open ios/Runner/Info.plist and add the GADApplicationIdentifier key:

ios/Runner/Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>CADisableMinimumFrameDurationOnPhone</key>
  <true/>
  <key>CFBundleDevelopmentRegion</key>
  <string>$(DEVELOPMENT_LANGUAGE)</string>
  <key>CFBundleDisplayName</key>
  <string>Admobflutter</string>
  <key>CFBundleExecutable</key>
  <string>$(EXECUTABLE_NAME)</string>
  <key>CFBundleIdentifier</key>
  <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
  <key>CFBundleInfoDictionaryVersion</key>
  <string>6.0</string>
  <key>CFBundleName</key>
  <string>admobflutter</string>
  <key>CFBundlePackageType</key>
  <string>APPL</string>
  <key>CFBundleShortVersionString</key>
  <string>$(FLUTTER_BUILD_NAME)</string>
  <key>CFBundleSignature</key>
  <string>????</string>
  <key>CFBundleVersion</key>
  <string>$(FLUTTER_BUILD_NUMBER)</string>
  <key>LSRequiresIPhoneOS</key>
  <true/>

  <!-- AdMob App ID (test ID) -->
  <key>GADApplicationIdentifier</key>
  <string>ca-app-pub-3940256099942544~1458002754</string>

  <key>UIApplicationSceneManifest</key>
  <dict>
    <key>UIApplicationSupportsMultipleScenes</key>
    <false/>
    <key>UISceneConfigurations</key>
    <dict>
      <key>UIWindowSceneSessionRoleApplication</key>
      <array>
        <dict>
          <key>UISceneClassName</key>
          <string>UIWindowScene</string>
          <key>UISceneConfigurationName</key>
          <string>flutter</string>
          <key>UISceneDelegateClassName</key>
          <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
          <key>UISceneStoryboardFile</key>
          <string>Main</string>
        </dict>
      </array>
    </dict>
  </dict>

  <key>UIApplicationSupportsIndirectInputEvents</key>
  <true/>
  <key>UILaunchStoryboardName</key>
  <string>LaunchScreen</string>
  <key>UIMainStoryboardFile</key>
  <string>Main</string>
  <key>UISupportedInterfaceOrientations</key>
  <array>
    <string>UIInterfaceOrientationPortrait</string>
    <string>UIInterfaceOrientationLandscapeLeft</string>
    <string>UIInterfaceOrientationLandscapeRight</string>
  </array>
  <key>UISupportedInterfaceOrientations~ipad</key>
  <array>
    <string>UIInterfaceOrientationPortrait</string>
    <string>UIInterfaceOrientationPortraitUpsideDown</string>
    <string>UIInterfaceOrientationLandscapeLeft</string>
    <string>UIInterfaceOrientationLandscapeRight</string>
  </array>
</dict>
</plist>
Test App ID (iOS): ca-app-pub-3940256099942544~1458002754

Step 4 — Create the AdMob Service

Create lib/services/admob_service.dart. This is a singleton — one instance across the whole app, handles loading, disposing, and exposing the banner ad.

lib/services/admob_service.dart
import 'package:google_mobile_ads/google_mobile_ads.dart';

class AdMobService {
  static final AdMobService _instance = AdMobService._internal();

  factory AdMobService() {
    return _instance;
  }

  AdMobService._internal();

  // ✅ Google's official test banner ad unit ID
  // Replace with your real ID before publishing to the store
  static const String bannerAdUnitId = 'ca-app-pub-3940256099942544/6300978111';

  BannerAd? _bannerAd;
  bool _isBannerAdReady = false;

  /// Initialize Mobile Ads SDK — call this once in main()
  Future<void> initialize() async {
    await MobileAds.instance.initialize();
  }

  /// Load the banner ad
  void loadBannerAd({
    required Function(BannerAd) onAdLoaded,
    required Function(BannerAd, LoadAdError) onAdFailedToLoad,
  }) {
    _bannerAd = BannerAd(
      adUnitId: bannerAdUnitId,
      size: AdSize.banner,
      request: const AdRequest(),
      listener: BannerAdListener(
        onAdLoaded: (ad) {
          _isBannerAdReady = true;
          onAdLoaded(ad as BannerAd);
        },
        onAdFailedToLoad: (ad, error) {
          ad.dispose();
          onAdFailedToLoad(ad as BannerAd, error);
        },
      ),
    )..load();
  }

  BannerAd? getBannerAd() => _bannerAd;

  bool isBannerAdReady() => _isBannerAdReady;

  void disposeBannerAd() {
    _bannerAd?.dispose();
    _isBannerAdReady = false;
  }

  void disposeAll() {
    _bannerAd?.dispose();
  }
}

Step 5 — Initialize in main.dart

Initialize the SDK before runApp. One call, no extra config needed.

lib/main.dart
import 'package:flutter/material.dart';
import 'services/admob_service.dart';
import 'pages/home_page.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize AdMob SDK once at startup
  final adMobService = AdMobService();
  await adMobService.initialize();

  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Monetized App',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
        appBarTheme: const AppBarTheme(
          centerTitle: false,
          elevation: 0,
        ),
      ),
      home: const HomePage(),
    );
  }
}

Step 6 — Display the Banner Ad

Create lib/pages/home_page.dart. The banner is pinned at the bottom using a Stack with Positioned. Content scrolls above it with bottom padding so nothing hides behind the ad.

lib/pages/home_page.dart
import 'package:flutter/material.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import '../services/admob_service.dart';

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  late AdMobService _adMobService;
  bool _isBannerAdReady = false;

  @override
  void initState() {
    super.initState();
    _adMobService = AdMobService();
    _loadBannerAd();
  }

  void _loadBannerAd() {
    _adMobService.loadBannerAd(
      onAdLoaded: (_) {
        setState(() {
          _isBannerAdReady = true;
        });
      },
      onAdFailedToLoad: (ad, error) {
        debugPrint('Banner Ad failed to load: ${error.message}');
      },
    );
  }

  @override
  void dispose() {
    _adMobService.disposeBannerAd();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('Monetized App'),
        elevation: 2,
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Stack(
          children: [
            SingleChildScrollView(
              child: Column(
                children: [
                  _buildHeaderSection(),
                  Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 16.0),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        const SizedBox(height: 20),
                        _buildTitleSection(),
                        const SizedBox(height: 24),
                        _buildFeatureCards(),
                        const SizedBox(height: 100),
                      ],
                    ),
                  ),
                ],
              ),
            ),
            Positioned(
              bottom: 0,
              left: 0,
              right: 0,
              child: _buildBannerAdWidget(),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildHeaderSection() {
    return Container(
      width: double.infinity,
      height: 250,
      decoration: BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
          colors: [Colors.deepPurple.shade400, Colors.deepPurple.shade800],
        ),
      ),
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Icon(Icons.monetization_on, size: 80, color: Colors.white),
            const SizedBox(height: 16),
            const Text(
              'Monetized App',
              style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold, color: Colors.white),
            ),
            const SizedBox(height: 8),
            const Text(
              'Powered by Google AdMob',
              style: TextStyle(fontSize: 16, color: Colors.white70),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildTitleSection() {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const Text(
          'Welcome to Your App',
          style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 12),
        Text(
          'A professional monetized app with seamless Google Mobile Ads integration.',
          style: TextStyle(fontSize: 16, color: Colors.grey.shade600, height: 1.5),
        ),
      ],
    );
  }

  Widget _buildFeatureCards() {
    return Column(
      children: [
        _buildFeatureCard(icon: Icons.monetization_on, title: 'Ad Integration', description: 'Seamless Google Mobile Ads integration'),
        const SizedBox(height: 12),
        _buildFeatureCard(icon: Icons.design_services, title: 'Professional UI', description: 'Clean and modern interface design'),
        const SizedBox(height: 12),
        _buildFeatureCard(icon: Icons.trending_up, title: 'Revenue Ready', description: 'Start earning from day one'),
        const SizedBox(height: 12),
        _buildFeatureCard(icon: Icons.architecture, title: 'Clean Architecture', description: 'Abstracted AdMob service layer'),
      ],
    );
  }

  Widget _buildFeatureCard({required IconData icon, required String title, required String description}) {
    return Card(
      elevation: 2,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Row(
          children: [
            Container(
              padding: const EdgeInsets.all(12),
              decoration: BoxDecoration(
                color: Colors.deepPurple.withOpacity(0.1),
                borderRadius: BorderRadius.circular(8),
              ),
              child: Icon(icon, size: 32, color: Colors.deepPurple),
            ),
            const SizedBox(width: 16),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(title, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                  const SizedBox(height: 4),
                  Text(description, style: TextStyle(fontSize: 14, color: Colors.grey.shade600)),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildBannerAdWidget() {
    if (!_isBannerAdReady) return const SizedBox();
    final bannerAd = _adMobService.getBannerAd();
    if (bannerAd == null) return const SizedBox();
    return Container(
      color: Colors.grey.shade100,
      child: SizedBox(
        height: bannerAd.size.height.toDouble(),
        width: bannerAd.size.width.toDouble(),
        child: AdWidget(ad: bannerAd),
      ),
    );
  }
}

Project Structure

Your final file structure:

lib/
├── main.dart
├── services/
│   └── admob_service.dart
└── pages/
    └── home_page.dart

Test IDs Reference

All IDs used in this post are Google's official test suite. Never use real ad unit IDs during development:

# Android App ID
ca-app-pub-3940256099942544~3347511713

# iOS App ID
ca-app-pub-3940256099942544~1458002754

# Banner Ad Unit ID (works on both platforms)
ca-app-pub-3940256099942544/6300978111
Before publishing: Go to admob.google.com, create your app, generate real ad unit IDs, and replace the test IDs above. That's the only change needed to go live.

That's the whole setup. Run the app and you'll see a test banner ad at the bottom of the screen. Swap in real ad unit IDs before publishing and that banner starts generating revenue. Scale it up — add interstitial ads, rewarded ads, more screens — and earnings scale with it.

Post a Comment

Previous Post Next Post