Brian Tum
Brian Tum

Reputation: 51

How to show multiple google native ad in flutter

I am trying to call an ad dynamically in a listview but throws this error :

If you placed this AdWidget in a list, make sure you create a new instance in the builder function with a unique ad object.
Make sure you are not using the same ad object in more than one AdWidget. 

Here is the code

Import package google_mobile_ads

import 'package:google_mobile_ads/google_mobile_ads.dart';

Then instantiated the package

NativeAd _nativeAd;

final Completer<NativeAd> nativeAdCompleter = Completer<NativeAd>();

I function to load the Ads

loadAd(){
    _nativeAd = NativeAd(
      adUnitId: "ca-app-pub-3940256099942544/1044960115",
      request: AdRequest(),
      factoryId: 'adFactoryExample',
      listener: AdListener(
        onAdLoaded: (Ad ad) {
          print('$NativeAd loaded.');
          nativeAdCompleter.complete(ad as NativeAd);
        },
        onAdFailedToLoad: (Ad ad, LoadAdError error) {
          ad.dispose();
          print('$NativeAd failedToLoad: $error');
          nativeAdCompleter.completeError(null);
        },
        onAdOpened: (Ad ad) => print('$NativeAd onAdOpened.'),
        onAdClosed: (Ad ad) => print('$NativeAd onAdClosed.'),
        onApplicationExit: (Ad ad) => print('$NativeAd onApplicationExit.'),
      ),
    );
    Future<void>.delayed(Duration(seconds: 1), () => _nativeAd?.load());
  }

Then to show the add I did this in a switch case statement

case 'ad':
                                  loadAd();

                                  return FutureBuilder<NativeAd>(
                                    future: nativeAdCompleter.future,
                                    builder: (BuildContext context, AsyncSnapshot<NativeAd> snapshot) {
                                      Widget child;

                                      switch (snapshot.connectionState) {
                                        case ConnectionState.none:
                                        case ConnectionState.waiting:
                                        case ConnectionState.active:
                                          child = Container();
                                          break;
                                        case ConnectionState.done:
                                          if (snapshot.hasData) {
                                            child = AdWidget(ad: _nativeAd);
                                          } else {
                                            child = Text('Error loading $NativeAd');
                                          }
                                      }

                                      return Scaffold(
                                        body: Container(
                                          width: double.infinity,
                                          height: double.infinity,
                                          margin: EdgeInsets.only(top: 100, left: 5, right: 5, bottom: 70),
                                          child: Center(child: child),
                                          color: Colors.black,
                                        ),
                                      );
                                    },
                                  );
                                  break;

This integration throws the above exception while showing ads from the second ad

Upvotes: 5

Views: 2406

Answers (2)

Microaum
Microaum

Reputation: 21

To show multiple google native ads in flutter ListView you need to focus on the below 3 steps.

I assume you already follow the process to show Native Ads in a flutter. Create Native ads

Step 1: Create a different Future function as below in a separate dart file.

static Future<Widget> getNativeAdTest({
    required BuildContext context,
  }) async {
    bool isAdLoaded = false;
    NativeAd _listAd = NativeAd(
      adUnitId: AdHelper.nativeAdUnitId,
      factoryId: "listTile",
      request: const AdRequest(),
      listener: NativeAdListener(onAdLoaded: (ad) {
        isAdLoaded = true;
        if (kDebugMode) {
          // print("ad load");
        }
      }, onAdFailedToLoad: (ad, error) {
        // _listAd.dispose();
        if (kDebugMode) {
          print("faild to load add ${error.message}");
        }
      }),
    );
    await _listAd.load();
    await Future.delayed(const Duration(seconds: 4));
    return AdWidget(
      ad: _listAd,
      key: Key(_listAd.hashCode.toString()),
    );
  }

Step 2: Use ListView.separated() itemBuilder will show list content and separatorBuilder will show Ads.(you can skip if you want to use as any child)

Step 3: Use FutureBuilder() in the child widget as below to get data after ad load.

return FutureBuilder(
      future: AdHelper.getNativeAdTest(context: context),
      builder: (BuildContext context, snapshot) {
        if (snapshot.hasData) {
          AdWidget ad = snapshot.data as AdWidget;
          print("snap: $snapshot");
          return Container(
            height: 90,
            child: ad,
          );
        } else {
          return Container(
              alignment: Alignment.topCenter,
              margin: const EdgeInsets.only(top: 20),
              child: const CircularProgressIndicator(
                value: 0.8,
              ));
        }
      });

Upvotes: 2

nmats
nmats

Reputation: 23

I'm really not sure how you have implemented the widget since you only show the part of you class but I believe it should work if you made the isolated widget for Native ad and place initialisation of NativeAd in initState like this.

Then you can use NativeAdWidget in your list view.

class NativeAdWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => NativeAdState();
}

class NativeAdState extends State<NativeAdWidget> {
  NativeAd _nativeAd;
  final Completer<NativeAd> nativeAdCompleter = Completer<NativeAd>();

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

    _nativeAd = NativeAd(
      adUnitId: Platform.isAndroid ? getIt<Vars>().androidNativeId : getIt<Vars>().iosNativeId,
      request: const AdRequest(nonPersonalizedAds: true),
      customOptions: <String, Object>{},
      factoryId: getIt<Vars>().admobFactoryId,
      listener: AdListener(
        onAdLoaded: (Ad ad) {
          nativeAdCompleter.complete(ad as NativeAd);
        },
        onAdFailedToLoad: (Ad ad, LoadAdError err) {
          unawaited(HandleError.logError(err.message));
          ad.dispose();
          nativeAdCompleter.completeError(null);
        },
        onAdOpened: (Ad ad) => print('$ad onAdOpened.'),
        onAdClosed: (Ad ad) => print('$ad onAdClosed.'),
        onApplicationExit: (Ad ad) => print('$ad onApplicationExit.'),
      ),
    );

    _nativeAd?.load();
  }

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

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<NativeAd>(
      future: nativeAdCompleter.future,
      builder: (BuildContext context, AsyncSnapshot<NativeAd> snapshot) {
        Widget child;

        switch (snapshot.connectionState) {
          case ConnectionState.none:
          case ConnectionState.waiting:
          case ConnectionState.active:
            child = Container();
            break;
          case ConnectionState.done:
            if (snapshot.hasData) {
              child = AdWidget(ad: _nativeAd);
            } else {
              child = Text('Error loading ad');
            }
        }

        return Container(
          height: 330,
          child: child,
          color: const Color(0xFFFFFFFF),
        );
      },
    );
  }
}

Upvotes: 1

Related Questions