Displaying Different Types of Ads in Flutter

Flutter is a versatile framework for building cross-platform applications, and one common requirement is integrating advertisements for monetization. Displaying different types of ads can increase revenue streams and provide a better user experience by mixing various ad formats. This article guides you through displaying banner ads, native ads, and rewarded ads in your Flutter application.

Introduction to Ad Integration in Flutter

Integrating ads in your Flutter app allows you to monetize your application through various formats. Some common ad formats include banner ads (small, rectangular ads displayed at the top or bottom of the screen), native ads (ads that match the look and feel of your app), and rewarded ads (ads that users can watch in exchange for in-app rewards).

Why Use Different Types of Ads?

  • Increased Revenue: Mixing different ad formats can optimize revenue generation.
  • Better User Experience: Some ads are less intrusive and provide a better experience.
  • Variety: Prevents ad fatigue by showing different types of ads.

Prerequisites

Before diving into implementation, ensure you have:

  • Flutter SDK installed.
  • An active AdMob account and Ad Units set up.
  • The google_mobile_ads Flutter package installed.

Add the following dependency to your pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  google_mobile_ads: ^4.0.0

Then run flutter pub get to install the package.

Step-by-Step Implementation

Step 1: Initialize Mobile Ads

Before displaying any ads, initialize the Mobile Ads SDK. It’s best to do this when the app starts.

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: 'Ad Integration Demo',
      home: AdScreen(),
    );
  }
}

class AdScreen extends StatefulWidget {
  @override
  _AdScreenState createState() => _AdScreenState();
}

class _AdScreenState extends State {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Ad Integration Demo'),
      ),
      body: Center(
        child: Text('Displaying Ads'),
      ),
    );
  }
}

Step 2: Implement Banner Ads

Banner ads are rectangular ads that occupy a portion of the screen. Create a BannerAd object and load it.

class _AdScreenState extends State {
  BannerAd? _bannerAd;
  bool _isBannerAdReady = false;

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

  void _loadBannerAd() {
    _bannerAd = BannerAd(
      adUnitId: '', // Replace with your Ad Unit ID
      request: AdRequest(),
      size: AdSize.banner,
      listener: BannerAdListener(
        onAdLoaded: (Ad ad) {
          print('$BannerAd loaded.');
          setState(() {
            _isBannerAdReady = true;
          });
        },
        onAdFailedToLoad: (Ad ad, LoadAdError error) {
          print('$BannerAd failed to load: ${error.message}');
          ad.dispose();
        },
        onAdOpened: (Ad ad) => print('$BannerAd opened.'),
        onAdClosed: (Ad ad) => print('$BannerAd closed.'),
      ),
    );

    _bannerAd!.load();
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Ad Integration Demo'),
      ),
      body: Column(
        children: [
          Expanded(
            child: Center(
              child: Text('Displaying Ads'),
            ),
          ),
          if (_isBannerAdReady && _bannerAd != null)
            SizedBox(
              width: _bannerAd!.size.width.toDouble(),
              height: _bannerAd!.size.height.toDouble(),
              child: AdWidget(ad: _bannerAd!),
            ),
        ],
      ),
    );
  }
}

Replace <YOUR_BANNER_AD_UNIT_ID> with your actual Ad Unit ID.

Step 3: Implement Native Ads

Native ads match the look and feel of your application. Implement a NativeAd and use a NativeAdView to display it.

import 'package:flutter/material.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'dart:io' show Platform;

class _AdScreenState extends State {
  // Banner Ad implementation...
  NativeAd? _nativeAd;
  bool _isNativeAdReady = false;

  @override
  void initState() {
    super.initState();
    _loadBannerAd();
    _loadNativeAd();
  }

  void _loadNativeAd() {
    _nativeAd = NativeAd(
      adUnitId: '', // Replace with your Ad Unit ID
      factoryId: 'adFactoryExample',
      request: AdRequest(),
      listener: NativeAdListener(
        onAdLoaded: (Ad ad) {
          print('$NativeAd loaded.');
          setState(() {
            _isNativeAdReady = true;
          });
        },
        onAdFailedToLoad: (Ad ad, LoadAdError error) {
          print('$NativeAd failed to load: ${error.message}');
          ad.dispose();
        },
        onAdOpened: (Ad ad) => print('$NativeAd opened.'),
        onAdClosed: (Ad ad) => print('$NativeAd closed.'),
      ),
    );

    _nativeAd!.load();
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Ad Integration Demo'),
      ),
      body: Column(
        children: [
          if (_isNativeAdReady && _nativeAd != null)
            Container(
              height: 300,
              child: AdWidget(ad: _nativeAd!),
              alignment: Alignment.center,
            ),
          Expanded(
            child: Center(
              child: Text('Displaying Ads'),
            ),
          ),
          if (_isBannerAdReady && _bannerAd != null)
            SizedBox(
              width: _bannerAd!.size.width.toDouble(),
              height: _bannerAd!.size.height.toDouble(),
              child: AdWidget(ad: _bannerAd!),
            ),
        ],
      ),
    );
  }
}

Remember to replace <YOUR_NATIVE_AD_UNIT_ID> with your Native Ad Unit ID.

You will need to register the NativeAdFactory, in MainActivity.kt (android/app/src/main/kotlin/your/package/name/MainActivity.kt)

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

class MainActivity: FlutterActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        // Pass the MainActivity instance to the plugin.
        GoogleMobileAdsPlugin.registerNativeAdFactory(flutterEngine, "adFactoryExample", MyNativeAdFactory(layoutInflater))
    }

    override fun onDestroy() {
        // Destroy the native ad factory
        GoogleMobileAdsPlugin.unregisterNativeAdFactory(flutterEngine, "adFactoryExample")
        super.onDestroy()
    }
}

You will also need to create the layout for your native ad, res/layout/native_ad_layout.xml:


<com.google.android.gms.ads.nativead.NativeAdView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="8dp">

        <TextView
            android:id="@+id/ad_headline"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="16sp"
            android:textStyle="bold"
            android:textColor="@android:color/black"/>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <ImageView
                android:id="@+id/ad_app_icon"
                android:layout_width="40dp"
                android:layout_height="40dp"
                android:adjustViewBounds="true"
                android:maxWidth="40dp"
                android:maxHeight="40dp"/>

            <TextView
                android:id="@+id/ad_body"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="14sp"
                android:layout_marginStart="8dp"
                android:layout_gravity="center_vertical"
                android:textColor="@android:color/darker_gray"/>
        </LinearLayout>

        <Button
            android:id="@+id/ad_call_to_action"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="end"/>
    </LinearLayout>
</com.google.android.gms.ads.nativead.NativeAdView>

Next, you will need to create MyNativeAdFactory:

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 io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin

class MyNativeAdFactory(private val layoutInflater: LayoutInflater): GoogleMobileAdsPlugin.NativeAdFactory {
    override fun createNativeAd(
        nativeAd: NativeAd,
        customOptions: MutableMap<String, Any>?
    ): NativeAdView {
        val adView = layoutInflater.inflate(R.layout.native_ad_layout, null) as NativeAdView

        // Set the media view.
        adView.mediaView = adView.findViewById(R.id.ad_media)

        // Set other ad assets.
        adView.headlineView = adView.findViewById(R.id.ad_headline)
        adView.bodyView = adView.findViewById(R.id.ad_body)
        adView.callToActionView = adView.findViewById(R.id.ad_call_to_action)
        adView.iconView = adView.findViewById(R.id.ad_app_icon)
        adView.priceView = adView.findViewById(R.id.ad_price)
        adView.starRatingView = adView.findViewById(R.id.ad_stars)
        adView.storeView = adView.findViewById(R.id.ad_store)
        adView.advertiserView = adView.findViewById(R.id.ad_advertiser)

        // The headline and mediaContent are guaranteed to be here.
        (adView.headlineView as TextView).text = nativeAd.headline
        adView.mediaView.mediaContent = nativeAd.mediaContent

        // These assets aren't guaranteed to be present. Check that they are before
        // showing or hiding them.
        if (nativeAd.body == null) {
            adView.bodyView.visibility = View.INVISIBLE
        } else {
            adView.bodyView.visibility = View.VISIBLE
            (adView.bodyView as TextView).text = nativeAd.body
        }

        if (nativeAd.callToAction == null) {
            adView.callToActionView.visibility = View.INVISIBLE
        } else {
            adView.callToActionView.visibility = View.VISIBLE
            (adView.callToActionView as TextView).text = nativeAd.callToAction
        }

        if (nativeAd.icon == null) {
            adView.iconView.visibility = View.GONE
        } else {
            (adView.iconView as ImageView).setImageDrawable(
                nativeAd.icon.drawable
            )
            adView.iconView.visibility = View.VISIBLE
        }

        // This method tells the Google Mobile Ads SDK that you have finished populating your
        // native ad view with this native ad.
        adView.setNativeAd(nativeAd)

        return adView
    }
}

Step 4: Implement Rewarded Ads

Rewarded ads provide in-app rewards to users who watch the ad. Implement a RewardedAd object and handle reward events.

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

class _AdScreenState extends State {
  RewardedAd? _rewardedAd;
  bool _isRewardedAdReady = false;

  @override
  void initState() {
    super.initState();
    _loadBannerAd();
    _loadNativeAd();
    _loadRewardedAd();
  }

  void _loadRewardedAd() {
    RewardedAd.load(
      adUnitId: '', // Replace with your Ad Unit ID
      request: AdRequest(),
      rewardedAdLoadCallback: RewardedAdLoadCallback(
        onAdLoaded: (RewardedAd ad) {
          print('$RewardedAd loaded.');
          setState(() {
            _rewardedAd = ad;
            _isRewardedAdReady = true;
          });
          _rewardedAd!.fullScreenContentCallback = FullScreenContentCallback(
            onAdShowedFullScreenContent: (Ad ad) =>
                print('$ad onAdShowedFullScreenContent.'),
            onAdDismissedFullScreenContent: (Ad ad) {
              print('$ad onAdDismissedFullScreenContent.');
              setState(() {
                _isRewardedAdReady = false;
              });
              _loadRewardedAd();
            },
            onAdFailedToShowFullScreenContent: (Ad ad, LoadAdError error) {
              print('$ad onAdFailedToShowFullScreenContent: ${error.message}');
              setState(() {
                _isRewardedAdReady = false;
              });
            },
          );
        },
        onAdFailedToLoad: (LoadAdError error) {
          print('RewardedAd failed to load: ${error.message}');
          setState(() {
            _isRewardedAdReady = false;
          });
        },
      ),
    );
  }

  void _showRewardedAd() {
    if (_isRewardedAdReady && _rewardedAd != null) {
      _rewardedAd!.show(
        onUserEarnedReward: (Ad ad, RewardItem reward) {
          print(
              '$ad with reward ${reward.amount} ${reward.type}');
        },
      );
    } else {
      print('Rewarded Ad is not ready yet!');
    }
  }

  @override
  void dispose() {
    _bannerAd?.dispose();
    _nativeAd?.dispose();
    _rewardedAd?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Ad Integration Demo'),
      ),
      body: Column(
        children: [
          if (_isNativeAdReady && _nativeAd != null)
            Container(
              height: 300,
              child: AdWidget(ad: _nativeAd!),
              alignment: Alignment.center,
            ),
          Expanded(
            child: Center(
              child: ElevatedButton(
                onPressed: _showRewardedAd,
                child: Text('Show Rewarded Ad'),
              ),
            ),
          ),
          if (_isBannerAdReady && _bannerAd != null)
            SizedBox(
              width: _bannerAd!.size.width.toDouble(),
              height: _bannerAd!.size.height.toDouble(),
              child: AdWidget(ad: _bannerAd!),
            ),
        ],
      ),
    );
  }
}

Remember to replace <YOUR_REWARDED_AD_UNIT_ID> with your Rewarded Ad Unit ID.

Best Practices for Ad Integration

  • Don’t Be Intrusive: Avoid placing ads in areas that disrupt user experience.
  • Frequency Capping: Implement frequency capping to limit how often users see ads.
  • Testing: Test different ad placements and formats to optimize performance.

Conclusion

Displaying different types of ads in Flutter is an effective strategy for monetizing your app while maintaining a good user experience. By integrating banner ads, native ads, and rewarded ads, you can optimize revenue and keep users engaged. Following the steps outlined in this guide will help you seamlessly integrate these ad formats into your Flutter application.