Randomize
Randomize

Reputation: 9103

How to fix the issue "Ad.load to be called before AdWidget is inserted into the tree" issue with Flutter / Admob?

I am using google_mobile_ads: ^1.1.0 version on Flutter and following the video here:

https://www.youtube.com/watch?v=m0d_pbgeeG8

Apart some small changes respect to the video (I guess not fully up to date compare to the most recent API changes) I have now the following code:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final initFuture = MobileAds.instance.initialize();
  final adState = AdState(initFuture);
  await SystemChrome.setPreferredOrientations(<DeviceOrientation>[
    DeviceOrientation.portraitUp,
    DeviceOrientation.portraitDown
  ]).then((_) => runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => Something()),
        ...some other ChangeNotifierProvider...,
        Provider<AdState>(create: (_) =>  adState)
      ],
      child: const MyApp()
    ),
  ));
}

with AdState:

import 'dart:io';

import 'package:google_mobile_ads/google_mobile_ads.dart';

class AdState {
  Future<InitializationStatus> initialisation;

  AdState(this.initialisation);

  String get bannerAdUnitId => Platform.isAndroid
      ? 'ca-app-pub-3940256099942544/6300978111'
      : 'ca-app-pub-3940256099942544/2934735716'; // ios

  BannerAdListener get adListener => _adListener;

  final BannerAdListener _adListener = BannerAdListener(

    // Called when an ad is successfully received.
    onAdLoaded: (Ad ad) => print('Ad loaded: ${ad.adUnitId}.'),

    onAdClosed: (Ad ad) {
      ad.dispose();
      print('Ad closed: ${ad.adUnitId}.');
    },

    // Called when an ad request failed.
    onAdFailedToLoad: (Ad ad, LoadAdError error) {
      ad.dispose();
      print('Ad failed to load: : ${ad.adUnitId}, $error');
    },

    // Called when an ad opens an overlay that covers the screen.
    onAdOpened: (Ad ad) => print('Ad opened: ${ad.adUnitId}.'),

    // Called when an impression occurs on the ad.
    onAdImpression: (Ad ad) => print('Ad impression: ${ad.adUnitId}.'),
  );
}

then in the home page widget state class:

BannerAd? banner;

@override
  void didChangeDependencies() {
    super.didChangeDependencies();
    final adState = Provider.of<AdState>(context);
    adState.initialisation.then((status) {
      setState(() {
        banner = BannerAd(
            adUnitId: adState.bannerAdUnitId,
            size: AdSize.banner,
            request: const AdRequest(),
            listener: adState.adListener
        )..load();
      });
    });
  }

@override
Widget build(BuildContext context) {
...somewhere in the middle...
if (banner == null)
  const SizedBox(height: 50)
else
  SizedBox (height:50, child: AdWidget(ad: banner!)),
....
}

The error I am getting is:

AdWidget requires Ad.load to be called before AdWidget is inserted into the tree

load() method is called in the didChangeDependencies() method above but of course it returns a Future so I think it may still not being there when the build() is being run. How can I fix that?

Upvotes: 2

Views: 4865

Answers (2)

Justus Lolwerikoi
Justus Lolwerikoi

Reputation: 883

Faced the same problem, this was my fix, although the try block has a lot of codes, you can fix this to your liking.

BannerAd? bottomBannerAd;
bool isBannerAdReady = false;

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

@override
void dispose() {
  bottomBannerAd!.dispose();
  isBannerAdReady = false;
  super.dispose();
}

createBottomBannerAd() {
  try {
    bottomBannerAd = BannerAd(
      adUnitId: AdHelper.bannerAdUnitId,
      size: AdSize.banner,
      request: const AdRequest(),
      listener: BannerAdListener(
        onAdLoaded: (_) {
          setState(() {
            isBannerAdReady = true;
          });
        },
        onAdFailedToLoad: (ad, error) {
          ad.dispose();
        },
      ),
    );
    bottomBannerAd!.load();
  }
  catch (e){
    bottomBannerAd = null;
  }
}



bottomNavigationBar: isBannerAdReady && bottomBannerAd != null
                        ? SizedBox(
                            height: bottomBannerAd!.size.height.toDouble(),
                            width: bottomBannerAd!.size.width.toDouble(),
                            child: AdWidget(
                              ad: bottomBannerAd!,
                            ),
                          )
                        : null,

Upvotes: 0

ישו אוהב אותך
ישו אוהב אותך

Reputation: 29814

You can use the AdWidget only after BannerAd is loaded not aftar BannerAd is not null.

So, try using a flag for loaded state like in the following snippet:

BannerAd? banner;
bool _bannerIsLoaded = false;

@override
  void didChangeDependencies() {
    super.didChangeDependencies();
    final adState = Provider.of<AdState>(context);
    adState.initialisation.then((status) {
      setState(() {
        banner = BannerAd(
            adUnitId: adState.bannerAdUnitId,
            size: AdSize.banner,
            request: const AdRequest(),
            listener: adState.adListener
        )..load();
       
        _bannerIsLoaded = true;
      });
    });
  }

@override
Widget build(BuildContext context) {
...somewhere in the middle...
if (banner != null && _bannerIsLoaded)
  SizedBox (height:50, child: AdWidget(ad: banner!)),
else
  const SizedBox(height: 50)
  
....
}

Upvotes: 5

Related Questions