Add Inline Ads to Your Flutter App

Having ads can be super important for your apps, so let's take a look how we have use inline ads in our application

CODE

Initial setup

To first get started we need 2 packages, in this case it will be Google mobile ads and then we will use Riverpod to provide that to the rest of our application!

Of course I recommend reading the setup guide for the Google mobile ads as you need to do some platform specific tasks. To keep this as updated as possible I will not add those steps here but they will be outlined in the readme of the package!

The simple beginning

Now we need to create a class that will contain the data for our ads. This is the class we will provide to the rest of our application. You can see it as a kind of helper class just to make the handling of ads a bit easier!

class AdState {
  AdState({
    required this.initialization,
  });
  Future<InitializationStatus> initialization;

  String get bannerAdUnitId {
    if (Platform.isAndroid) {
      return 'ca-app-pub-3940256099942544/6300978111';
    } else {
      return 'ca-app-pub-3940256099942544/2934735716';
    }
  }

  AdListener get adListener => _adListener;

  AdListener _adListener = AdListener(
    // Called when an ad is successfully received.
    onAdLoaded: (Ad ad) => print('Ad loaded.'),
    // Called when an ad request failed.
    onAdFailedToLoad: (Ad ad, LoadAdError error) {
      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 ad is in the process of leaving the application.
    onApplicationExit: (Ad ad) => print('Left application.'),
  );
}

The bannerAdUnitId here represents some test IDs to show banners for Android and iOS. If you want your real ads you need to replace those with the ones you have!

Providing

So now we want to somehow create an instance of the AdState class to provide that with it's data. To do that is rather simple take a look at the code.

// The value here will be overridden in main
final adStateProvider = ScopedProvider<AdState>(null);

void main() {
  // Ensure that we have the binding setup to the platforms
  WidgetsFlutterBinding.ensureInitialized();
  final adsInitialization = MobileAds.instance.initialize();
  final adState = AdState(initialization: adsInitialization);
  runApp(
    ProviderScope(
      overrides: [
        adStateProvider.overrideWithValue(adState),
      ],
      child: MyApp(),
    ),
  );
}

So what we do here is that we initialize the ads to then get a Future back for when the ads are initialized. We take this value, add to the instance of our class and provide that class. Now we will be able to use this class throughout the application!

Provide a list

So what we will do here is that we want the application to display a list on countries and this list will then have some ads between, let's say every 5 items. To do this we first have to create out Country class.

class Country {
  Country(this.name, this.dialingCode);
  final String name;
  final String dialingCode;
}

Then in our application where we show the page we can for now just generate a dummy list, but of course this would be the real list that you would have.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter inline ads',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ListPage(
        // Generate a list of 50 countries
        countries: List.generate(50, (index) => Country('Sweden', '+46')),
      ),
    );
  }
}

Now we just have to make sure that our ListPage that we are going to create can take in this list and display it!

class ListPage extends StatefulWidget {
  ListPage({
    Key? key,
    required this.countries,
  }) : super(key: key);

  final List<Country> countries;
  @override
  _ListPageState createState() => _ListPageState();
}

class _ListPageState extends State<ListPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Inline Ads'),
      ),
      body: ListView.builder(
        itemBuilder: (context, index) {
          final item = widget.countries[index];

          return Card(
            child: ListTile(
              title: Text(item.name),
              trailing: Text(item.dialingCode),
            ),
          );
        },
        itemCount: widget.countries.length,
      ),
    );
  }
}

Now we will display a list of countries but we won't really use the ads we provided yet, so let us see how we can do that!

Use the Ads

We need to do a couple of things and I will first list them up and then show the implementation!

  • Have a state object (list) that we can add our ads into but also have the countries in it.
  • Copy of list into this state object.
  • Add our ads in to the list and also make sure that everytime the list changes we redo it.
  • Depending if we have an ad or a country in the list we display that one.

Now when we have that out of the way, let's see how that is implemented!

class ListPage extends StatefulWidget {
  ListPage({
    Key? key,
    required this.countries,
  }) : super(key: key);

  final List<Country> countries;
  @override
  _ListPageState createState() => _ListPageState();
}

class _ListPageState extends State<ListPage> {
  late List<Object> countriesWithAds;

  @override
  void initState() {
    super.initState();
    countriesWithAds = List.from(widget.countries);
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    final adState = context.read(adStateProvider);
    adState.initialization.then((value) {
      insertAdsToCountriesList(adState);
    });
  }

  void insertAdsToCountriesList(AdState adState) {
    setState(() {
      for (var i = countriesWithAds.length - 5; i >= 1; i -= 10) {
        countriesWithAds.insert(
          i,
          BannerAd(
            size: AdSize.banner,
            adUnitId: adState.bannerAdUnitId,
            listener: adState.adListener,
            request: AdRequest(),
          )..load(),
        );
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Inline Ads'),
      ),
      body: ListView.builder(
        itemBuilder: (context, index) {
          final item = countriesWithAds[index];
          if (item is Country) {
            return Card(
              child: ListTile(
                title: Text(item.name),
                trailing: Text(item.dialingCode),
              ),
            );
          } else {
            return Container(
              height: 50,
              child: AdWidget(ad: item as BannerAd),
            );
          }
        },
        itemCount: countriesWithAds.length,
      ),
    );
  }
}

Summary

So all we have to do was initialize the ads, provide them in out application, ad the number of ads we wanted in out list and check if the list item is either a Country or BannerAd to display it!