Native Ad Integration in Flutter(Using Kotlin and Swift)

Hello Guys,

Today, we discuss how to implement native ads in flutter using Kotlin and swift.

Introduction:

Native ads are ad riches that are presented to users via UI components that are native to the platform. a native ad is also known as customized ads. These are the ads that match an application’s contents.

For Example, if A app has a list of items displayed to the user you can insert a native ad similar look at your list of items.

Now let’s start adding native ads to the flutter project.

1. Create A New Flutter Project

Create a new project in your favorite IDE. In my case, I am using Android Studio IDE to build flutter projects.

2. Add dependencies

In your project, open pubspec.yaml file & add the below dependencies under the dependencies section.

dependencies:
google_mobile_ads: ^0.13.2

Note: This package has not yet been updated with null security features so please check that your project environment is less than sdk version 2.12.0. If not less than 2.12.0, so then replaced it with 2.7.0

environment:
  sdk: ">=2.8.0 <3.0.0"

3. Platform requirements setup needed

  • Android Setup :

    Open the Android folder in Android Studio

    Right-click on the Android folder of your project,

    android > Flutter > Open the android module in android studio.

     1. Adding google services classpath in your build.gradle file

    open[project]/android/build.gradle file.

    dependencies {
            classpath 'com.android.tools.build:gradle:4.1.0'
            classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
            classpath 'com.google.gms:google-services:4.3.0'     // add this google services 4.3.0
        }

     

       2. Adding google services classpath in your build.gradle file

    open[project]/android/app/build.gradle file.

    apply plugin: 'com.android.application'
    apply plugin: 'kotlin-android'
    apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
    apply plugin: 'com.google.gms.google-services' //add this line

     3. Add admob App ID in Android-Manifest.xml

    <manifest ....... >
    <application
         android:label="nativead"
         android:icon="@mipmap/ic_launcher">
        <!-- Sample AdMob App ID: ca-app-pub-3940256099942544~3347511713 -->
        <meta-data
            android:name="com.google.android.gms.ads.APPLICATION_ID"
            android:value="ca-app-pub-3940256099942544~3347511713"/>  <!-- your admob adpp id
      </application>
    </manifest>
    

    Now Adding Native Structure to Your Project

     4. Create a Native Ad Layout in Your Project

    I’ve created a folder called “layout” in open[project]/android/app/src/main/res this directory. Inside this folder, I developed a “list tile native ad.xml” file and add native ad layout design.

    <?xml version="1.0" encoding="utf-8"?>
    <com.google.android.gms.ads.nativead.NativeAdView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <TextView
                android:id="@+id/tv_list_tile_native_ad_attribution_small"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="#F19938"
                android:text="Ad"
                android:textColor="#FFFFFF"
                android:textSize="12sp" />
    
            <ImageView
                android:id="@+id/iv_list_tile_native_ad_icon"
                android:layout_width="48dp"
                android:layout_height="48dp"
                android:layout_gravity="center_vertical"
                android:layout_marginStart="16dp"
                android:layout_marginLeft="16dp"
                android:scaleType="fitXY"
                tools:background="#EDEDED" />
    
            <TextView
                android:id="@+id/tv_list_tile_native_ad_attribution_large"
                android:layout_width="48dp"
                android:layout_height="48dp"
                android:layout_gravity="center_vertical"
                android:layout_marginStart="16dp"
                android:layout_marginLeft="16dp"
                android:background="#F19938"
                android:gravity="center"
                android:text="Ad"
                android:textColor="#FFFFFF"
                android:visibility="invisible" />
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:layout_marginStart="80dp"
                android:layout_marginLeft="80dp"
                android:layout_marginEnd="16dp"
                android:layout_marginRight="16dp"
                android:orientation="vertical">
    
                <TextView
                    android:id="@+id/tv_list_tile_native_ad_headline"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:ellipsize="end"
                    android:lines="1"
                    android:maxLines="1"
                    android:textColor="#FFFFFF"
                    android:textSize="16sp"
                    tools:text="Headline" />
    
                <TextView
                    android:id="@+id/tv_list_tile_native_ad_body"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:ellipsize="end"
                    android:lines="1"
                    android:maxLines="1"
                    android:textColor="#828282"
                    android:textSize="14sp"
                    tools:text="body" />
    
            </LinearLayout>
    
        </FrameLayout>
    
    </com.google.android.gms.ads.nativead.NativeAdView>

    A class that implements a NativeAdFactory is required for the Android implementation of the Google Mobile Ads plugin. A NativeAdFactory has a function that returns a NativeAdView from a NativeAd and custom settings.

    The factoryId used to add the factory to GoogleMobileAdsPlugin must match when generating the NativeAd in Dart. The name of the factoryId in the following code snippet is a factory example. Now Open  open[project]/android/app/src/main/java this path and create new file name as ListTileNativeAdFactory.kt do some functionality in this file . like below.

    package com.example.nativeads
    import android.content.Context
    import android.view.LayoutInflater
    import android.view.View
    import android.widget.ImageView
    import android.widget.TextView
    import com.google.android.gms.ads.nativead.NativeAd
    import com.google.android.gms.ads.nativead.NativeAdView
    import com.example.nativeads.R
    import io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin
    
    class ListTileNativeAdFactory(val context: Context) : GoogleMobileAdsPlugin.NativeAdFactory {
    
        override fun createNativeAd(
            nativeAd: NativeAd,
            customOptions: MutableMap<String, Any>?
        ): NativeAdView {
            val nativeAdView = LayoutInflater.from(context)
                .inflate(R.layout.list_tile_native_ad, null) as NativeAdView
    
            with(nativeAdView) {
                val attributionViewSmall =
                    findViewById<TextView>(R.id.tv_list_tile_native_ad_attribution_small)
                val attributionViewLarge =
                    findViewById<TextView>(R.id.tv_list_tile_native_ad_attribution_large)
    
                val iconView = findViewById<ImageView>(R.id.iv_list_tile_native_ad_icon)
                val icon = nativeAd.icon
                if (icon != null) {
                    attributionViewSmall.visibility = View.VISIBLE
                    attributionViewLarge.visibility = View.INVISIBLE
                    iconView.setImageDrawable(icon.drawable)
                } else {
                    attributionViewSmall.visibility = View.INVISIBLE
                    attributionViewLarge.visibility = View.VISIBLE
                }
                this.iconView = iconView
    
                val headlineView = findViewById<TextView>(R.id.tv_list_tile_native_ad_headline)
                headlineView.text = nativeAd.headline
                this.headlineView = headlineView
    
                val bodyView = findViewById<TextView>(R.id.tv_list_tile_native_ad_body)
                with(bodyView) {
                    text = nativeAd.body
                    visibility = if (nativeAd.body.isNotEmpty()) View.VISIBLE else View.INVISIBLE
                }
                this.bodyView = bodyView
    
                setNativeAd(nativeAd)
            }
    
            return nativeAdView
        }
    }

    In MainActivity, each NativeAdFactory must be registered with a factoryId, which is a unique String identifier. configureFlutterEngine(FlutterEngine) Each unique native ad layout used by your app can have its own NativeAdFactory, or all layouts can be handled by a single NativeAdFactory.

    When using add-to-app, the NativeAdFactory should also be unregistered in cleanUpFlutterEngine(engine).

    The following is an example of what MainActivity.java should look like:

    package com.example.nativeads
    
    import io.flutter.embedding.android.FlutterActivity
    import com.example.nativeads.ListTileNativeAdFactory
    import io.flutter.embedding.engine.FlutterEngine
    import io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin
    
    class MainActivity: FlutterActivity() {
        override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
            super.configureFlutterEngine(flutterEngine)
    
            // TODO: Register the ListTileNativeAdFactory
            GoogleMobileAdsPlugin.registerNativeAdFactory(
                flutterEngine, "listTile", ListTileNativeAdFactory(context))
        }
    
        override fun cleanUpFlutterEngine(flutterEngine: FlutterEngine) {
            super.cleanUpFlutterEngine(flutterEngine)
    
            // TODO: Unregister the ListTileNativeAdFactory
            GoogleMobileAdsPlugin.unregisterNativeAdFactory(flutterEngine, "listTile")
        }
    }

    We’ve implemented native ad functionality on the Android side. So, let’s have a look at the iOS side and add some native ad functionality.

  • iOS  Setup :

First and foremost, we’ve added the Google Admob key to the iOS folder. as a result, go to open[project]/iOS/Runner/info. Add the AdMob key to this file and save it.

info.plist :

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

Now we’ll use Swift to create a layout for a native ad. Now Create a new file named ListTileNativeAdView.xib under open[project]/iOS/Runner and perform some functionality in this file. as in the example below.

ListTileNativeAdView.xib

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="retina5_5" orientation="portrait" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
        <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" id="iN0-l3-epB" customClass="GADNativeAdView">
            <rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
            <subviews>
                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="aDH-L2-ViC" userLabel="icon">
                    <rect key="frame" x="16" y="344" width="48" height="48"/>
                    <constraints>
                        <constraint firstAttribute="width" constant="48" id="Ii0-QX-7Bv"/>
                        <constraint firstAttribute="height" constant="48" id="bLX-V9-GK7"/>
                    </constraints>
                </imageView>
                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Ad" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="FVf-yB-1vd" userLabel="attribution">
                    <rect key="frame" x="0.0" y="0.0" width="16" height="15"/>
                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                    <color key="backgroundColor" systemColor="systemOrangeColor"/>
                    <fontDescription key="fontDescription" type="system" pointSize="12"/>
                    <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                    <nil key="highlightedColor"/>
                </label>
                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AF1-3a-kBu" userLabel="headline">
                    <rect key="frame" x="80" y="348" width="298" height="19.666666666666686"/>
                    <fontDescription key="fontDescription" type="system" pointSize="16"/>
                    <nil key="textColor"/>
                    <nil key="highlightedColor"/>
                </label>
                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="SqM-pP-iR7" userLabel="body">
                    <rect key="frame" x="80" y="371" width="298" height="17"/>
                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
                    <color key="textColor" systemColor="systemGrayColor"/>
                    <nil key="highlightedColor"/>
                </label>
            </subviews>
            <color key="backgroundColor" red="0.98039871450000005" green="0.98038035629999998" blue="0.98039287330000002" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
            <constraints>
                <constraint firstAttribute="trailingMargin" secondItem="SqM-pP-iR7" secondAttribute="trailing" constant="16" id="WZu-qp-hh3"/>
                <constraint firstItem="SqM-pP-iR7" firstAttribute="bottom" secondItem="aDH-L2-ViC" secondAttribute="bottom" constant="-4" id="Yeo-Tn-OgQ"/>
                <constraint firstItem="aDH-L2-ViC" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="aY2-Z1-3G7"/>
                <constraint firstItem="AF1-3a-kBu" firstAttribute="top" secondItem="aDH-L2-ViC" secondAttribute="top" constant="4" id="mwE-qj-92K"/>
                <constraint firstItem="aDH-L2-ViC" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="16.000000000000114" id="n9m-Dv-DAB"/>
                <constraint firstAttribute="trailingMargin" secondItem="AF1-3a-kBu" secondAttribute="trailing" constant="16" id="pog-io-Jsp"/>
                <constraint firstItem="SqM-pP-iR7" firstAttribute="leading" secondItem="aDH-L2-ViC" secondAttribute="trailing" constant="16" id="wqd-KX-n5Y"/>
                <constraint firstItem="AF1-3a-kBu" firstAttribute="leading" secondItem="aDH-L2-ViC" secondAttribute="trailing" constant="16" id="xaD-rv-wJ2"/>
            </constraints>
            <connections>
                <outlet property="bodyView" destination="SqM-pP-iR7" id="X3G-1T-dMs"/>
                <outlet property="headlineView" destination="AF1-3a-kBu" id="FCc-oH-TDT"/>
                <outlet property="iconView" destination="aDH-L2-ViC" id="VUa-bH-mKt"/>
            </connections>
            <point key="canvasLocation" x="137.68115942028987" y="152.44565217391306"/>
        </view>
    </objects>
    <resources>
        <systemColor name="systemGrayColor">
            <color red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
        </systemColor>
        <systemColor name="systemOrangeColor">
            <color red="1" green="0.58431372549019611" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
        </systemColor>
    </resources>
</document>

The Google Mobile Ads plugin for iOS requires a class that implements the FLTNativeAdFactory interface. A GADNativeAd and custom settings are passed to a method in a FLTNativeAdFactory, which returns a GADNativeAdView. This is the GADNativeAdView that appears in your app. The factoryID must match the one used to add the factory to FLTGoogleMobileAdsPlugin when generating the NativeAd in Dart. The name of the factoryID in the following code snippet is adFactoryExample. FLTNativeAdFactory is an example.

Now open this path in open[project]/iOS/Runner and create a new file named ListTileNativeAdFactory.swift.In this file, Swift does certain functionality. as in the example below.

import google_mobile_ads

class ListTileNativeAdFactory : FLTNativeAdFactory {
    
    func createNativeAd(_ nativeAd: GADNativeAd,
                        customOptions: [AnyHashable : Any]? = nil) -> GADNativeAdView? {
        let nibView = Bundle.main.loadNibNamed("ListTileNativeAdView", owner: nil, options: nil)!.first
        let nativeAdView = nibView as! GADNativeAdView
        
        (nativeAdView.headlineView as! UILabel).text = nativeAd.headline
        
        (nativeAdView.bodyView as! UILabel).text = nativeAd.body
        nativeAdView.bodyView!.isHidden = nativeAd.body == nil
        
        (nativeAdView.iconView as! UIImageView).image = nativeAd.icon?.image
        nativeAdView.iconView!.isHidden = nativeAd.icon == nil
        
        nativeAdView.callToActionView?.isUserInteractionEnabled = false

        nativeAdView.nativeAd = nativeAd
        
        return nativeAdView
    }
}

A factoryId (a unique String identification) must be assigned to each FLTNativeAdFactory in register. NativeAdFactory:factoryId:nativeAdFactory:.

Each unique native ad layout utilised by your app can have its own FLTNativeAdFactory, or a single FLTNativeAdFactory can handle all layouts. This is accomplished by calling register and importing FLTGoogleMobileAdsPlugin.h. using a FlutterPluginRegistry (a unique identity for the factory) and the factory itself NativeAdFactory:factoryId:nativeAdFactory:factoryId:nativeAdFactory:factoryId:nativeAdFactory:factoryId:nativeAdFactory:factoryId:nativeAdFa After GeneratedPluginRegistrant registerWithRegistry:self]; is called, the factory must be added.

It should look something like this if done in AppDelegate.swift :

import UIKit
import Flutter
import google_mobile_ads
import Firebase

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

    let listTileFactory = ListTileNativeAdFactory()
    FLTGoogleMobileAdsPlugin.registerNativeAdFactory(
        self, factoryId: "listTile", nativeAdFactory: listTileFactory)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

We’re going to Add Native Ads to Flutter now.

An adUnitId, a factoryId, an AdRequest, and a NativeAdListener are all required for a NativeAd. A native ad is created in the following way:

final NativeAd myNative = NativeAd(
  adUnitId: '<ad unit id>',
  factoryId: 'adFactoryExample',
  request: AdRequest(),
  listener: NativeAdListener(),
);

Factory ID:

On Android, the factoryId must match the one used to add the factory to GoogleMobileAdsPlugin and/or FLTGoogleMobileAdsPlugin. On iOS, the factoryId must match the one used to add the factory to the FLTGoogleMobileAdsPlugin. Both platforms can use the same factoryId or each can have their own.

Native ad events:

You can listen for lifecycle events, such as when an ad is loaded, using NativeAdListener. Each method is implemented in the following example, which also logs a message to the console:

final NativeAdListener listener = NativeAdListener(
 // Called when an ad is successfully received.
 onAdLoaded: (Ad ad) => print('Ad loaded.'),
 // Called when an ad request failed.
 onAdFailedToLoad: (Ad ad, LoadAdError error) {
   // Dispose the ad here to free resources.
   ad.dispose();
   print('Ad failed to load: $error');
 },
 // Called when an ad opens an overlay that covers the screen.
 onAdOpened: (Ad ad) => print('Ad opened.'),
 // Called when an ad removes an overlay that covers the screen.
 onAdClosed: (Ad ad) => print('Ad closed.'),
 // Called when an impression occurs on the ad.
 onAdImpression: (Ad ad) => print('Ad impression.'),
 // Called when a click is recorded for a NativeAd.
 onNativeAdClicked: (NativeAd ad) => print('Ad clicked.'),
);

Load Ad:

Before a NativeAd can be displayed on the screen, load() must be called after it has been created.

nativeAd.load();

Display Ad:

After executing load, you must create an AdWidget with a supported ad to display a NativeAd as a widget (). The widget can be created before executing load(), but it must be loaded before being added to the widget tree.

final AdWidget adWidget = AdWidget(ad: myBanner);

AdWidget is derived from the Widget class in Flutter and can be used just like any other widget. Make sure the widget is in a container with a specific width and height on iOS. Otherwise, your advertisement may not be shown.

Container(
  height: 60,
  alignment: Alignment.center,
  color: Colors.black,
  child: Container(
  height: 100,
  width: double.infinity,
  child: AdWidget(
  ad: nativeAd,
 )),
)

When an Ad has called load(), it must call dispose() when it no longer needs access to it. When calling dispose(), the recommended practise is to do it after the AdWidget has been removed from the widget tree or in the AdListener. Callback for when an ad fails to load.

The code for displaying a native ad is shown below.

main.dart

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

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  MobileAds.instance.initialize();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Native Ads Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomeScreen(),
    );
  }
}

class MyHomeScreen extends StatefulWidget {
  const MyHomeScreen({Key key}) : super(key: key);

  @override
  _MyHomeScreenState createState() => _MyHomeScreenState();
}

class _MyHomeScreenState extends State<MyHomeScreen> {
  var nativeAd;
  bool isLoaded = false;
  @override
  void initState() {
    loadAd();
    // nativeAd.load();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text("Native Ads")),
        body: isLoaded
            ? Container(
                height: 60,
                alignment: Alignment.center,
                color: Colors.black,
                child: Container(
                    height: 100,
                    width: double.infinity,
                    child: AdWidget(
                      ad: nativeAd,
                    )),
              )
            : Container());
  }

  void loadAd() {
    nativeAd = NativeAd(
      adUnitId: "ca-app-pub-3940256099942544/2247696110",
      factoryId: 'listTile',
      request: AdRequest(),
      listener: NativeAdListener(
        onAdLoaded: (_) {
          setState(() {
            isLoaded = true;
          });
        },
        onAdFailedToLoad: (ad, error) {
          print('Ad load failed (code=${error.code} message=${error.message})');
        },
      ),
    );
    nativeAd.load();
  }
}

That concludes the procedure. Native advertisements are now available in your app.

Submit a Comment

Your email address will not be published.

Subscribe

Select Categories