How to Integrate Google AdMob Native Ads in Flutter For iOS (with Full Code & Custom Layout)

πŸ“± How to Integrate Google AdMob Native Ads in Flutter for iOS

Keywords: AdMob Flutter Native Ads, Flutter Native Ad Example, Google Mobile Ads Integration Flutter, AdMob Layout XML, Flutter Monetization Guide, Custom Native AdView Flutter

πŸš€ Final Output Preview

Watch the full implementation in this YouTube tutorial:

✨ Introduction

Monetizing your Flutter apps with Google AdMob Native Ads is a powerful way to boost your income while offering a seamless user experience. Native ads blend into your app’s layout, making them appear as part of the UI instead of typical intrusive banners.

In this post, we’ll walk through a complete working example of how to integrate native ads in Flutter using a custom XIB layout in iOS, and how to load and display them from your Dart code.

This guide is beginner-friendly and uses the official google_mobile_ads Flutter plugin from Google.

🧩 Prerequisites

  • Flutter 3.19 or higher

  • Android device or emulator

  • Google AdMob account

πŸ“¦ Step 1: Add the google_mobile_ads Package

In your pubspec.yaml:

YAML

dependencies:
  google_mobile_ads: ^6.0.0
  

Install it:

Terminal

flutter pub get
  

πŸ›  Step 2: Configure Android for AdMob

Edit ios/Runner/Info.plist:

Inside the <dict> tag, add:

plist

<key>GADApplicationIdentifier</key>
<string>ca-app-pub-3940256099942544~3347511713</string>
  

πŸ“Œ Note: Replace this with your own AdMob App ID when deploying your app.
πŸ”— Test App & Ad Unit IDs

Β 

Set the Minimum iOS Version

In ios/Podfile, ensure:

pod

platform :ios, '12.0'
use_frameworks!
  

πŸ§‘β€πŸ’» Step 3: Add Native Ad Layout in iOS

Create the following XIB file:

πŸ“ Path: ios/Runner/listTileMedium.xib

xib

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
    <device id="retina4_7" orientation="portrait" appearance="light"/>
    <dependencies>
        <deployment version="2048" identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
        <capability name="System colors in document resources" minToolsVersion="11.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <view contentMode="scaleToFill" ambiguous="YES" id="iN0-l3-epB" customClass="GADNativeAdView">
            <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
            <subviews>
                <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="iNa-bH-h1m">
                    <rect key="frame" x="16" y="16" width="49" height="49"/>
                    <constraints>
                        <constraint firstAttribute="height" constant="49" id="ICz-3W-FQf"/>
                        <constraint firstAttribute="width" constant="49" id="vY6-8D-xIn"/>
                    </constraints>
                </imageView>
                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Advertiser" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GTT-Yh-eSq">
                    <rect key="frame" x="96" y="42" width="132.5" height="17"/>
                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
                    <color key="textColor" systemColor="darkTextColor"/>
                    <nil key="highlightedColor"/>
                </label>
                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" misplaced="YES" text="Body that is really really long and can take up to two lines or sometimes even more." textAlignment="justified" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="PEQ-D9-2Vv">
                    <rect key="frame" x="16" y="68" width="343" height="34"/>
                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
                    <color key="textColor" systemColor="darkTextColor"/>
                    <nil key="highlightedColor"/>
                </label>
                <button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="E5w-YA-UY8">
                    <rect key="frame" x="17" y="292" width="342" height="39"/>
                    <constraints>
                        <constraint firstAttribute="height" constant="39" id="wug-fF-cwS"/>
                    </constraints>
                    <fontDescription key="fontDescription" type="system" pointSize="18"/>
                    <state key="normal" title="Install">
                        <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                    </state>
                    <buttonConfiguration key="configuration" style="filled" title="Install">
                        <fontDescription key="titleFontDescription" type="system" pointSize="18"/>
                    </buttonConfiguration>
                </button>
                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="Headline" textAlignment="justified" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="beR-eV-DX1">
                    <rect key="frame" x="73" y="17" width="256" height="20.5"/>
                    <constraints>
                        <constraint firstAttribute="height" constant="20.5" id="6r8-Hu-d0y"/>
                    </constraints>
                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
                    <color key="textColor" systemColor="darkTextColor"/>
                    <nil key="highlightedColor"/>
                </label>
                <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" placeholderIntrinsicWidth="100" placeholderIntrinsicHeight="17" translatesAutoresizingMaskIntoConstraints="NO" id="2Of-AP-0h9">
                    <rect key="frame" x="230" y="43" width="129" height="17"/>
                    <constraints>
                        <constraint firstAttribute="height" constant="17" id="jBW-Cz-Kyc"/>
                        <constraint firstAttribute="width" constant="100" id="sXk-zk-NI0"/>
                    </constraints>
                </imageView>
                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Ad" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lp1-oz-XOs">
                    <rect key="frame" x="73" y="43" width="15" height="15"/>
                    <color key="backgroundColor" red="1" green="0.80000001190000003" blue="0.40000000600000002" alpha="1" colorSpace="calibratedRGB"/>
                    <constraints>
                        <constraint firstAttribute="height" constant="15" id="As4-Ou-Uju"/>
                    </constraints>
                    <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="11"/>
                    <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
                    <nil key="highlightedColor"/>
                </label>
                <view contentMode="scaleAspectFit" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fNp-yu-K4i" customClass="GADMediaView">
                    <rect key="frame" x="28" y="106" width="318" height="178"/>
                </view>
            </subviews>
            <color key="backgroundColor" systemColor="systemBackgroundColor"/>
            <color key="tintColor" systemColor="systemYellowColor"/>
            <constraints>
                <constraint firstItem="GTT-Yh-eSq" firstAttribute="leading" secondItem="beR-eV-DX1" secondAttribute="leading" constant="23" id="0sB-Mk-EU6"/>
                <constraint firstItem="E5w-YA-UY8" firstAttribute="top" secondItem="fNp-yu-K4i" secondAttribute="bottom" constant="9" id="3z0-yB-zZZ"/>
                <constraint firstItem="PEQ-D9-2Vv" firstAttribute="top" relation="greaterThanOrEqual" secondItem="iNa-bH-h1m" secondAttribute="bottom" id="4S3-p0-z6A"/>
                <constraint firstItem="lp1-oz-XOs" firstAttribute="top" secondItem="2Of-AP-0h9" secondAttribute="top" id="6db-p4-F3F"/>
                <constraint firstItem="E5w-YA-UY8" firstAttribute="centerX" secondItem="PEQ-D9-2Vv" secondAttribute="centerX" id="6m6-nt-ZQh"/>
                <constraint firstAttribute="trailing" secondItem="PEQ-D9-2Vv" secondAttribute="trailing" constant="9" id="8U0-Fb-3R7"/>
                <constraint firstItem="iNa-bH-h1m" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="16" id="9WK-zC-xET"/>
                <constraint firstAttribute="trailing" secondItem="beR-eV-DX1" secondAttribute="trailing" constant="16" id="BcE-do-dNl"/>
                <constraint firstItem="fNp-yu-K4i" firstAttribute="trailing" secondItem="PEQ-D9-2Vv" secondAttribute="trailing" id="Bev-f8-CJl"/>
                <constraint firstItem="PEQ-D9-2Vv" firstAttribute="top" secondItem="2Of-AP-0h9" secondAttribute="bottom" constant="9" id="CCg-xe-cKg"/>
                <constraint firstItem="2Of-AP-0h9" firstAttribute="top" secondItem="beR-eV-DX1" secondAttribute="bottom" constant="4.5" id="ESC-Pe-TXR"/>
                <constraint firstItem="fNp-yu-K4i" firstAttribute="top" secondItem="PEQ-D9-2Vv" secondAttribute="bottom" constant="1" id="GrW-yR-pzW"/>
                <constraint firstItem="iNa-bH-h1m" firstAttribute="bottom" secondItem="2Of-AP-0h9" secondAttribute="bottom" constant="6" id="GwM-y0-1du"/>
                <constraint firstItem="beR-eV-DX1" firstAttribute="leading" secondItem="iNa-bH-h1m" secondAttribute="trailing" constant="8" symbolic="YES" id="MRN-dd-Oip"/>
                <constraint firstItem="2Of-AP-0h9" firstAttribute="leading" secondItem="GTT-Yh-eSq" secondAttribute="trailing" constant="7.5" id="Med-Nd-wEo"/>
                <constraint firstItem="beR-eV-DX1" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="17" id="Mvs-eV-Wzb"/>
                <constraint firstItem="fNp-yu-K4i" firstAttribute="trailing" secondItem="2Of-AP-0h9" secondAttribute="trailing" id="Nlk-nt-eQp"/>
                <constraint firstItem="PEQ-D9-2Vv" firstAttribute="top" secondItem="lp1-oz-XOs" secondAttribute="bottom" constant="10" id="PGV-Mv-vJ4"/>
                <constraint firstItem="GTT-Yh-eSq" firstAttribute="centerY" secondItem="2Of-AP-0h9" secondAttribute="centerY" id="YgR-kp-age"/>
                <constraint firstItem="lp1-oz-XOs" firstAttribute="centerY" secondItem="GTT-Yh-eSq" secondAttribute="centerY" id="doz-tQ-lWI"/>
                <constraint firstAttribute="bottom" secondItem="fNp-yu-K4i" secondAttribute="bottom" constant="414" id="ePJ-kL-HEb"/>
                <constraint firstItem="fNp-yu-K4i" firstAttribute="leading" secondItem="PEQ-D9-2Vv" secondAttribute="leading" id="jHT-Zo-LDU"/>
                <constraint firstItem="fNp-yu-K4i" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="103" id="lkE-uR-4O2"/>
                <constraint firstItem="fNp-yu-K4i" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="centerX-mediaView"/>
                <constraint firstItem="fNp-yu-K4i" firstAttribute="width" constant="320" id="width-mediaView"/>
                <constraint firstItem="fNp-yu-K4i" firstAttribute="height" constant="180" id="height-mediaView"/>
                <constraint firstItem="iNa-bH-h1m" firstAttribute="leading" secondItem="PEQ-D9-2Vv" secondAttribute="leading" id="mof-5F-8vM"/>
                <constraint firstItem="E5w-YA-UY8" firstAttribute="trailing" secondItem="PEQ-D9-2Vv" secondAttribute="trailing" id="qic-Q1-D7x"/>
                <constraint firstItem="lp1-oz-XOs" firstAttribute="leading" secondItem="beR-eV-DX1" secondAttribute="leading" id="tah-VW-bKg"/>
                <constraint firstItem="lp1-oz-XOs" firstAttribute="top" secondItem="beR-eV-DX1" secondAttribute="bottom" constant="5.5" id="z4L-oH-pe2"/>
                <constraint firstItem="E5w-YA-UY8" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leadingMargin" constant="1" id="zEm-xr-gbL"/>
            </constraints>
            <connections>
                <outlet property="advertiserView" destination="GTT-Yh-eSq" id="bY8-5O-6fF"/>
                <outlet property="bodyView" destination="PEQ-D9-2Vv" id="Gpd-Q6-Byv"/>
                <outlet property="callToActionView" destination="E5w-YA-UY8" id="RCf-yK-s1x"/>
                <outlet property="headlineView" destination="beR-eV-DX1" id="d1E-ed-yel"/>
                <outlet property="iconView" destination="iNa-bH-h1m" id="gIe-xy-iwm"/>
                <outlet property="mediaView" destination="fNp-yu-K4i" id="624-ZP-L04"/>
                <outlet property="starRatingView" destination="2Of-AP-0h9" id="zCO-9D-S0V"/>
            </connections>
            <point key="canvasLocation" x="13.6" y="-5.8470764617691158"/>
        </view>
    </objects>
    <resources>
        <systemColor name="darkTextColor">
            <color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
        </systemColor>
        <systemColor name="systemBackgroundColor">
            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
        </systemColor>
        <systemColor name="systemYellowColor">
            <color red="1" green="0.80000000000000004" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
        </systemColor>
    </resources>
</document>
  

βš™οΈ Step 4: Setup NativeAdFactory in Swift

Create NativeAdFactory.swift in your ios/Runner folder:

swift

import Foundation
import google_mobile_ads
import UIKit

class NativeAdFactory: NSObject, FLTNativeAdFactory {
  func createNativeAd(_ nativeAd: NativeAd, customOptions: [AnyHashable : Any]?) -> NativeAdView {
    guard let nibObjects = Bundle.main.loadNibNamed("listTileMedium", owner: nil, options: nil),
          let adView = nibObjects.first as? NativeAdView else {
      fatalError("Could not load nib file for native ad view")
    }

    (adView.headlineView as? UILabel)?.text = nativeAd.headline
    (adView.bodyView as? UILabel)?.text = nativeAd.body
    adView.bodyView?.isHidden = nativeAd.body == nil

    (adView.callToActionView as? UIButton)?.setTitle(nativeAd.callToAction, for: .normal)
    adView.callToActionView?.isHidden = nativeAd.callToAction == nil

    (adView.iconView as? UIImageView)?.image = nativeAd.icon?.image
    adView.iconView?.isHidden = nativeAd.icon == nil

    (adView.advertiserView as? UILabel)?.text = nativeAd.advertiser
    adView.advertiserView?.isHidden = nativeAd.advertiser == nil

    adView.nativeAd = nativeAd

    return adView
  }
}
  

Modify AppDelegate.swift

Add following code into your AppDelegate.swift file

java

import UIKit
import Flutter
import google_mobile_ads

@main
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {

  GeneratedPluginRegistrant.register(with: self)
    let factory = NativeAdFactory()

    // Pass 'self' as the registry, not binaryMessenger
    FLTGoogleMobileAdsPlugin.registerNativeAdFactory(
      self,
      factoryId: "listTileMedium",
      nativeAdFactory: factory
    )


    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}
  

πŸ§ͺ Step 6: Use NativeAd in Flutter Code

Now you can use NativeAd from google_mobile_ads package as following. just load native ad with factory id listTileMedium and show it anywhere you want:

dart

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

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

  @override
  State createState() => _HomeScreenState();
}

class _HomeScreenState extends State {
  NativeAd? nativeAd;
  bool isNativeAdLoaded = false;

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

  void loadNativeAd() {
    nativeAd = NativeAd(
      adUnitId: 'ca-app-pub-3940256099942544/2247696110', // Test Native Ad Unit
      factoryId: 'listTileMedium',
      request: const AdRequest(),
      listener: NativeAdListener(
        onAdLoaded: (ad) {
          setState(() => isNativeAdLoaded = true);
        },
        onAdFailedToLoad: (ad, error) {
          ad.dispose();
          debugPrint('Native Ad failed: $error');
        },
      ),
    )..load();
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Native Ad Example')),
      body: ListView(
        children: [
          const SizedBox(height: 20),
          Center(child: Text('Google AdMob Native Ad')),
          const SizedBox(height: 20),
           if (isNativeAdLoaded)
              Container(
                height: 340,
                margin: const EdgeInsets.all(15),
                padding: const EdgeInsets.all(5),
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(15),
                  color: Colors.white,
                ),
                child: AdWidget(ad: nativeAd!),
              )
        ],
      ),
    );
  }
}
  

βœ… Best Practices & Important Notes

πŸ“Œ Conclusion

With this full setup, you've successfully integrated Google AdMob Native Ads in Flutter using a custom XML layout, Dart logic, and Java factory registration. Native ads provide a sleek and efficient monetization method by blending ads directly into your UI.

Leave a Reply

Your email address will not be published. Required fields are marked *