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

πŸ“± How to Integrate Google AdMob Native Ads in Flutter (with Full Code & Custom Layout)

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 XML layout in Android, 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 android/app/src/main/AndroidManifest.xml:

Inside the <application> tag, add:

xml

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

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

Β 

Set the Minimum SDK Version

In android/app/build.gradle, ensure:

gradle

minSdkVersion 23
  

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

Create the following XML file:

πŸ“ Path: android/app/src/main/res/layout/native_ads_medium.xml

xml

<com.google.android.gms.ads.nativead.NativeAdView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/uadview" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#FFFFFF">
    <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" android:minHeight="50dp" android:orientation="vertical">
        <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content">
            <LinearLayout android:id="@+id/icone" android:layout_width="match_parent" android:layout_height="55dp" android:layout_above="@+id/qwer" android:layout_marginBottom="10dp" android:orientation="horizontal" android:paddingRight="4dp">
                <ImageView android:id="@+id/native_ad_icon" android:layout_width="50dp" android:layout_height="50dp" android:layout_margin="2.5dp" android:adjustViewBounds="true"/>
                <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent">
                    <LinearLayout android:layout_width="match_parent" android:layout_height="50dp" android:layout_marginLeft="4dp" android:orientation="vertical" android:paddingTop="4dp">
                        <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:orientation="horizontal">
                            <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="2dp" android:layout_marginTop="2dp" android:layout_marginRight="2dp" android:layout_marginBottom="2dp" android:background="#FF8400" android:paddingLeft="3dp" android:paddingTop="2dp" android:paddingRight="3dp" android:paddingBottom="2dp" android:text="Ad" android:textColor="#fff" android:textSize="7dp"/>
                            <TextView android:id="@+id/native_ad_headline" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:layout_marginTop="2dp" android:ellipsize="end" android:maxLines="1" android:textSize="14sp"/>
                        </LinearLayout>
                        <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:visibility="visible">
                            <TextView android:id="@+id/native_ad_body" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="3dp" android:ellipsize="end" android:gravity="bottom" android:maxLines="1" android:textSize="12sp"/>
                            <RatingBar android:id="@+id/ad_stars" style="?android:attr/ratingBarStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" android:isIndicator="true" android:numStars="5" android:stepSize="0.5" android:visibility="gone"/>
                        </LinearLayout>
                    </LinearLayout>
                </RelativeLayout>
            </LinearLayout>

            <LinearLayout android:id="@+id/qwer" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true">
                <Button android:id="@+id/native_ad_button" android:layout_width="match_parent" android:layout_height="45dp" android:layout_centerVertical="true" android:layout_marginLeft="5dp" android:layout_marginRight="5dp" android:layout_marginBottom="5dp" android:background="@drawable/install_btn" android:gravity="center" android:paddingLeft="10dp" android:paddingRight="10dp" android:textAllCaps="false" android:textColor="#fff" android:textSize="16dp"/>
            </LinearLayout>

            <LinearLayout android:id="@+id/vdhara" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@id/icone" android:gravity="center" android:orientation="vertical">
                <com.google.android.gms.ads.nativead.MediaView android:id="@+id/native_ad_media" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center_horizontal"/>
            </LinearLayout>

            <RelativeLayout android:id="@+id/btnview" android:layout_width="match_parent" android:layout_height="50dp" android:layout_alignParentBottom="true" android:layout_gravity="end" android:orientation="horizontal" android:paddingLeft="10dp" android:paddingTop="10dp" android:paddingRight="6dp" android:paddingBottom="10dp" android:visibility="gone">
                <TextView android:id="@+id/ad_advertiser" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginEnd="10dp" android:layout_marginRight="10dp" android:ellipsize="end" android:maxLines="2" android:textSize="12sp" android:visibility="visible"/>
                <TextView android:id="@+id/ad_price" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingStart="5dp" android:paddingLeft="5dp" android:paddingEnd="5dp" android:paddingRight="5dp" android:textSize="12sp" android:visibility="gone"/>
                <TextView android:id="@+id/ad_store" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingStart="5dp" android:paddingLeft="5dp" android:paddingEnd="5dp" android:paddingRight="5dp" android:textSize="12sp" android:visibility="gone"/>
                <TextView android:layout_width="wrap_content" android:layout_height="30dp" android:layout_alignParentRight="true" android:background="@drawable/install_btn" android:gravity="center" android:paddingLeft="10dp" android:paddingRight="10dp" android:textAllCaps="false" android:textColor="#fff" android:textSize="13sp"/>
            </RelativeLayout>
        </RelativeLayout>
    </LinearLayout>
</com.google.android.gms.ads.nativead.NativeAdView>
  

🎨 Step 4: Add Button Background Drawable

Create:

πŸ“ Path: android/app/src/main/res/drawable/install_btn.xml

xml

<shape xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:aapt="http://schemas.android.com/aapt" android:shape="rectangle">
    <gradient android:startColor="#F2A000" android:endColor="#FFA800" android:angle="270"/>
    <corners android:radius="12dp"/>
</shape>
  

βš™οΈ Step 5: Setup NativeAdFactory in Java

Create NativeAdFactoryMedium.java in your package folder (in same folder where MainActivity.java is present):

java

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import com.google.android.gms.ads.nativead.MediaView;
import com.google.android.gms.ads.nativead.NativeAd;
import com.google.android.gms.ads.nativead.NativeAdView;

import java.util.Map;

import io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin;

public class NativeAdFactoryMedium implements GoogleMobileAdsPlugin.NativeAdFactory {
    private final Context context;

    public NativeAdFactoryMedium(Context context) {
        this.context = context;
    }

    @Override
    public NativeAdView createNativeAd(NativeAd nativeAd, Map customOptions) {
        NativeAdView adView = (NativeAdView) LayoutInflater.from(context)
            .inflate(R.layout.native_ads_medium, null);

        adView.setMediaView((MediaView) adView.findViewById(R.id.native_ad_media));
        adView.setHeadlineView(adView.findViewById(R.id.native_ad_headline));
        adView.setBodyView(adView.findViewById(R.id.native_ad_body));
        adView.setIconView(adView.findViewById(R.id.native_ad_icon));
        adView.setCallToActionView(adView.findViewById(R.id.native_ad_button));

        ((TextView) adView.getHeadlineView()).setText(nativeAd.getHeadline());
        ((TextView) adView.getBodyView()).setText(nativeAd.getBody());

        if (nativeAd.getIcon() != null) {
            ((ImageView) adView.getIconView()).setImageDrawable(nativeAd.getIcon().getDrawable());
        } else {
            adView.getIconView().setVisibility(View.GONE);
        }

        ((Button) adView.getCallToActionView()).setText(nativeAd.getCallToAction());
        adView.setNativeAd(nativeAd);

        return adView;
    }
}
  

Modify MainActivity.java

Add following code into your MainActivity.java file

java

import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin;

public class MainActivity extends FlutterActivity {
    @Override
    public void configureFlutterEngine(FlutterEngine flutterEngine) {
        super.configureFlutterEngine(flutterEngine);
        GoogleMobileAdsPlugin.registerNativeAdFactory(flutterEngine, "listTileMedium",
                new NativeAdFactoryMedium(this));
    }

    @Override
    public void cleanUpFlutterEngine(FlutterEngine flutterEngine) {
        super.cleanUpFlutterEngine(flutterEngine);
        GoogleMobileAdsPlugin.unregisterNativeAdFactory(flutterEngine, "listTileMedium");
    }
}
  

πŸ§ͺ 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: 300,
                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.

🎁 Bonus: Displaying Smaller Sized Native Ads in Flutter

If you want to display compact native adsβ€”ideal for list items, feed cards, or tighter spacesβ€”you can create a custom NativeAdFactory with a smaller layout. Here’s how to set it up step-by-step.

πŸ“ Step 1: Create a Smaller Native Ad Layout

Create a new layout file: res/layout/native_ads_small.xml. Below is an optimized, smaller design for native ads.Β 

xml

<com.google.android.gms.ads.nativead.NativeAdView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/uadview" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#FFFFFF">
    <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content">
        <LinearLayout android:id="@+id/ll_space" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#e9e9e9" android:visibility="gone">
            <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="7dp" android:gravity="center" android:text="Reserved For Ads Space" android:textColor="#b9b9b9" android:textSize="12dp"/>
        </LinearLayout>

        <LinearLayout android:layout_width="match_parent" android:layout_height="150dp" android:orientation="vertical">
            <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="2dp" android:layout_marginTop="2dp" android:layout_marginRight="2dp" android:layout_marginBottom="2dp" android:background="#FF8400" android:paddingLeft="3dp" android:paddingTop="2dp" android:paddingRight="3dp" android:paddingBottom="2dp" android:text="Ad" android:textColor="#fff" android:textSize="7dp"/>

            <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="2dp" android:layout_marginTop="1dp" android:layout_marginRight="2dp" android:layout_marginBottom="4dp" android:orientation="vertical">
                <com.google.android.gms.ads.nativead.MediaView android:id="@+id/ad_media" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center_horizontal" android:visibility="gone"/>

                <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:layout_marginTop="2dp" android:layout_marginRight="8dp" android:orientation="horizontal">
                    <ImageView android:id="@+id/native_ad_icon" android:layout_width="65dp" android:layout_height="65dp" android:layout_gravity="center" android:adjustViewBounds="true"/>

                    <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:layout_weight="1" android:orientation="vertical">
                        <TextView android:id="@+id/native_ad_headline" android:layout_width="match_parent" android:layout_height="wrap_content" android:maxLines="1" android:textColor="#000" android:textSize="15dp" android:textStyle="bold"/>

                        <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="2dp" android:gravity="center_vertical" android:orientation="horizontal">
                            <TextView android:id="@+id/ad_advertiser" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="bottom" android:textColor="#000" android:textSize="10dp" android:textStyle="normal" android:visibility="gone"/>
                            <RatingBar android:id="@+id/ad_stars" style="?android:attr/ratingBarStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="2dp" android:isIndicator="true" android:numStars="5" android:stepSize="0.5" android:visibility="gone"/>
                        </LinearLayout>

                        <TextView android:id="@+id/native_ad_body" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="2dp" android:layout_weight="1" android:maxLines="3" android:minLines="3" android:textSize="9dp" android:visibility="visible"/>

                        <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:orientation="horizontal" android:visibility="gone">
                            <TextView android:id="@+id/ad_price" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="4dp" android:paddingRight="4dp" android:textColor="#000" android:textSize="12dp"/>
                            <TextView android:id="@+id/ad_store" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:paddingLeft="4dp" android:paddingRight="4dp" android:textColor="#000" android:textSize="10dp"/>
                        </LinearLayout>
                    </LinearLayout>
                </LinearLayout>

                android:layout_width="match_parent" android:layout_height="wrap_content" app:cardCornerRadius="4dp" app:cardElevation="4dp" app:cardUseCompatPadding="true">
                <Button android:id="@+id/native_ad_button" android:layout_width="match_parent" android:layout_height="50dp" android:layout_marginTop="8dp" android:background="@drawable/install_btn" android:gravity="center" android:singleLine="true" android:textAllCaps="true" android:textColor="#fff" android:textSize="16dp" android:textStyle="bold"/>
            </LinearLayout>
        </LinearLayout>
    </FrameLayout>
</com.google.android.gms.ads.nativead.NativeAdView>
  

πŸ› οΈ Step 2: Create NativeAdFactorySmall.java

This class inflates your custom layout and binds ad data:

java

public class NativeAdFactorySmall implements GoogleMobileAdsPlugin.NativeAdFactory {
    private final Context context;

    NativeAdFactorySmall(Context context) {
        this.context = context;
    }

    public NativeAdView createNativeAd(NativeAd nativeAd, Map customOptions) {
        NativeAdView adView = (NativeAdView) LayoutInflater.from(context)
                .inflate(R.layout.native_ads_small, null);

        adView.setIconView(adView.findViewById(R.id.native_ad_icon));
        if (nativeAd.getIcon() != null) {
            ((ImageView) adView.getIconView()).setImageDrawable(nativeAd.getIcon().getDrawable());
        } else {
            adView.getIconView().setVisibility(View.GONE);
        }

        adView.setCallToActionView(adView.findViewById(R.id.native_ad_button));
        if (nativeAd.getCallToAction() != null) {
            ((Button) adView.getCallToActionView()).setText(nativeAd.getCallToAction());
        } else {
            adView.getCallToActionView().setVisibility(View.GONE);
        }

        adView.setHeadlineView(adView.findViewById(R.id.native_ad_headline));
        ((TextView) adView.getHeadlineView()).setText(nativeAd.getHeadline());

        adView.setBodyView(adView.findViewById(R.id.native_ad_body));
        if (nativeAd.getBody() != null) {
            ((TextView) adView.getBodyView()).setText(nativeAd.getBody());
        } else {
            adView.getBodyView().setVisibility(View.GONE);
        }

        adView.setNativeAd(nativeAd);
        return adView;
    }
}
  

πŸ”§ Step 3: Register in MainActivity.java

Update your MainActivity to register and clean up the native ad factory:

java

public class MainActivity extends FlutterActivity {
    @Override
    public void configureFlutterEngine(FlutterEngine flutterEngine) {
        super.configureFlutterEngine(flutterEngine);

        // TODO: Register the ListTileNativeAdFactory
        GoogleMobileAdsPlugin.registerNativeAdFactory(flutterEngine, "listTile",
                new NativeAdFactorySmall(getContext()));
        GoogleMobileAdsPlugin.registerNativeAdFactory(flutterEngine, "listTileMedium",
                new NativeAdFactoryMedium(getContext()));
    }

    @Override
    public void cleanUpFlutterEngine(FlutterEngine flutterEngine) {
        super.cleanUpFlutterEngine(flutterEngine);

        // TODO: Unregister the ListTileNativeAdFactory
        GoogleMobileAdsPlugin.unregisterNativeAdFactory(flutterEngine, "listTile");
        GoogleMobileAdsPlugin.unregisterNativeAdFactory(flutterEngine, "listTileMedium");
    }
}
  

βœ… Flutter Usage Example

In your Dart code:

dart

NativeAd(
  adUnitId: 'your-admob-ad-unit-id',
  factoryId: 'listTile', // Must match the one you registered
  listener: NativeAdListener(
    onAdLoaded: (_) => print('Ad loaded!'),
    onAdFailedToLoad: (ad, error) {
      print('Ad failed to load: $error');
      ad.dispose();
    },
  ),
  request: AdRequest(),
),
  

Leave a Reply

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