Reputation: 269
So I insert admob ads inside the List. And I added the functionality of infinity scroll inside the list view. So when the user scrolls to end of the list, new items are added into the list. With this items I also add admob ads inside it.
So when the users scroll to the end the new items and ads are added into the List. At that time the below exceptions are caught. So how to solve this exception.
======== Exception caught by widgets library =======================================================
The following assertion was thrown building AdWidget-[#53ef3](dirty, state: _AdWidgetState#850ac):
This AdWidget is already in the Widget tree
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.
The relevant error-causing widget was:
AdWidget-[#53ef3] file:///D:/flutter%20project/memer/lib/pages/TimeLinePage.dart:198:42
When the exception was thrown, this was the stack:
#0 _AdWidgetState.build (package:google_mobile_ads/src/ad_containers.dart:371:7)
#1 StatefulElement.build (package:flutter/src/widgets/framework.dart:4612:27)
#2 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4495:15)
#3 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4667:11)
#4 Element.rebuild (package:flutter/src/widgets/framework.dart:4189:5)
code:-
return ListView.builder(itemBuilder: (context, index){
//print(posts);
if(posts[index] is Post){
return posts[index];
}
else{
final Container adContainer = Container(
alignment: Alignment.center,
child: AdWidget(key: UniqueKey(), ad: posts[index] as BannerAd),//AdmobService.createBannerAd()..load()
height: 50,
);
return adContainer;
}
},itemCount: posts.length,
controller: scrollController,physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()));
}
Upvotes: 13
Views: 15576
Reputation: 1793
Adding a unique key
to each SizedBox
(or another widget) containing the AdWidget
did solve my problem.
You have to initialize and load the ads in the initState() method.
And here is how I used the banner ad key as the widget's unique key.
if (_bannerAd1 != null)
SizedBox(
key: Key(_bannerAd1!.adUnitId),
height: _bannerAd1?.size.height.toDouble(),
width: _bannerAd1?.size.width.toDouble(),
child: AdWidget(
ad: _bannerAd1!,
),
),
if (_bannerAd2 != null)
SizedBox(
key: Key(_bannerAd2!.adUnitId),
height: _bannerAd2?.size.height.toDouble(),
width: _bannerAd2?.size.width.toDouble(),
child: AdWidget(
ad: _bannerAd2!,
),
),
Upvotes: 2
Reputation: 51
Nothing above worked for me. In my case to show three adbanner in the same List I hade to create 3 independent variables to load 3 independent AdWidegt. See attached code to have an aproximation to resolve this problem. Note: resolved the error that we talks about, I have now an unresolved performance problem with banners when memory is deleted every time that banners disappears from the screen because scroll. This force rebuilds and disposings every time the scrolls occurs...
@override
Widget build (BuildContext context) {
final BannerAd? banner1 = _bannerAd1;
final BannerAd? banner2 = _bannerAd2;
final BannerAd? banner3 = _bannerAd3;
if(widget.index==2 && _bannerAd1isAvailable && banner1 != null ){
print(banner1.adUnitId);
return AdContainer(banner: banner1, numBanner: 1,);
}else if(widget.index==12 && _bannerAd2isAvailable && banner2 != null ){
print(banner2.adUnitId);
return AdContainer(banner: banner2, numBanner: 2,);
}else if(widget.index==22 && _bannerAd3isAvailable && banner3 != null ){
print(banner3.adUnitId);
return AdContainer(banner: banner3, numBanner: 3,);
}else{
return Container();
}
}
class AdContainer extends StatelessWidget {
const AdContainer({
Key? key,
required this.banner, required this.numBanner,
}) : super(key: key);
final int numBanner;
final BannerAd banner;
@override
Widget build(BuildContext context) {
final AdWidget object1 = AdWidget(ad: banner);
final AdWidget object2 = AdWidget(ad: banner);
final AdWidget object3 = AdWidget(ad: banner);
return Container(
width: double.infinity,
color: const Color.fromRGBO(13, 37, 63, 0.6),
padding: const EdgeInsets.symmetric(vertical: 20,horizontal: 20),
child: Container(
alignment: Alignment.center,
height: banner.size.height.toDouble(),
width: banner.size.width.toDouble(),
child: (numBanner==1)?object1:(numBanner==2)?object2:object3,
),
);
}
}
Upvotes: 0
Reputation: 87
You have to use UniqueKey like this
class BannarAdWidget extends StatefulWidget {
const BannarAdWidget({Key? key}) : super(key: key);
@override
State<BannarAdWidget> createState() => _BannarAdWidgetState();
}
class _BannarAdWidgetState extends State<BannarAdWidget> {
BannerAd? _bannerAd;
bool _bannerAdIsLoaded = false;
@override
void didChangeDependencies() {
// Create the ad objects and load ads.
_bannerAd = BannerAd(
size: AdSize.banner,
request: const AdRequest(),
adUnitId: AdmobHelper.bannerAdUnitId,
listener: BannerAdListener(
onAdLoaded: (Ad ad) {
print('$BannerAd loaded.');
setState(() => _bannerAdIsLoaded = true);
},
onAdFailedToLoad: (Ad ad, LoadAdError error) {
print('$BannerAd failedToLoad: $error');
ad.dispose();
},
onAdOpened: (Ad ad) => print('$BannerAd onAdOpened.'),
onAdClosed: (Ad ad) => print('$BannerAd onAdClosed.'),
),
)..load();
super.didChangeDependencies();
}
@override
void dispose() {
_bannerAd?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final BannerAd? bannerAd = _bannerAd;
if (_bannerAdIsLoaded && bannerAd != null) {
return SizedBox(
height: bannerAd.size.height.toDouble(),
width: bannerAd.size.width.toDouble(),
child: AdWidget(ad: bannerAd),
);
} else {
return const SizedBox.shrink();
}
}
class AdmobHelper {
static String get bannerAdUnitId {
if (Platform.isAndroid) {
return "ca-app-pub-3940256099942544/6300978111";
} else if (Platform.isIOS) {
return "ca-app-pub-3940256099942544/2934735716";
} else {
throw UnsupportedError('Unsupported platform');
}
}
}
Upvotes: 1
Reputation: 111
#Step-1: Make A Stateful Class Like Below:
import 'package:flutter/cupertino.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
class BannerAdmob extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return _BannerAdmobState();
}
}
class _BannerAdmobState extends State<BannerAdmob>{
late BannerAd _bannerAd;
bool _bannerReady = false;
@override
void initState() {
super.initState();
_bannerAd = BannerAd(
adUnitId: "ca-app-pub-3940256099942544/6300978111",
request: const AdRequest(),
size: AdSize.largeBanner,
listener: BannerAdListener(
onAdLoaded: (_) {
setState(() {
_bannerReady = true;
});
},
onAdFailedToLoad: (ad, err) {
setState(() {
_bannerReady = false;
});
ad.dispose();
},
),
);
_bannerAd.load();
}
@override
void dispose() {
super.dispose();
_bannerAd.dispose();
}
@override
Widget build(BuildContext context) {
return _bannerReady?SizedBox(
width: _bannerAd.size.width.toDouble(),
height: _bannerAd.size.height.toDouble(),
child: AdWidget(ad: _bannerAd),
):Container();
}
}
#Step-2: Use this as below:
@override
Widget build(BuildContext context){
return BannerAdmob();
}
Upvotes: 9
Reputation: 12343
When you want to add a new banner, you have to assign it a new ID:
BannerAd(adUnitId: 'somethingDifferentThanTheOneInTheTree')
As clearly stated by the error log:
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.
Upvotes: 4
Reputation: 127
I tried all of these, but only the following worked for me. I added myBanner.dispose()
to iniState
to each of my pages.
void initState(){
myBanner.dispose();
myBanner.load();
super.initState();
}
Upvotes: 3
Reputation: 448
As the above answer, just I will put my implementation here for newcomers from the future to be much easier for them.
Firstly, let's create a helper class called ad_helper to hide all stuff from our UI, Which contains two helper methods :
First method is buildBannerWidget which is a public method to build our ad widget as we wish.
Second method is _instantiateBanner which is a private method to build our bannerAd object every time we call buildBannerWidget method.
class Ads {
static BannerAd? _banner;
static Future<Widget> buildBannerWidget({
required BuildContext context,
}) async {
final mediaQuery = MediaQuery.of(context);
await _instantiateBanner(
mediaQuery.orientation,
mediaQuery.size.width.toInt(),
);
return Container(
width : MediaQuery.of(context).size.width,
height : 70,
child: AdWidget(ad: _banner!),
);
}
static Future<BannerAd> _instantiateBanner(orientation, width) async {
_banner = BannerAd(
adUnitId: BannerAd.testAdUnitId,
// size: AdSize.banner,
size: (await AdSize.getAnchoredAdaptiveBannerAdSize(orientation, width))!,
request: _getBannerAdRequest(),
listener: _buildListener(),
);
await _banner?.load();
return _banner!;
}
static AdRequest _getBannerAdRequest() {
return AdRequest();
}
static BannerAdListener _buildListener() {
return BannerAdListener(
onAdOpened: (Ad ad) {
print('${Constants.Tag} BannerAdListener onAdOpened ${ad.toString()}.');
},
onAdClosed: (Ad ad) {
print('${Constants.Tag} BannerAdListener onAdClosed ${ad.toString()}.');
},
onAdImpression: (Ad ad) {
print(
'${Constants.Tag} BannerAdListener onAdImpression ${ad.toString()}.');
},
onAdWillDismissScreen: (Ad ad) {
print(
'${Constants.Tag} BannerAdListener onAdWillDismissScreen ${ad.toString()}.');
},
onPaidEvent: (
Ad ad,
double valueMicros,
PrecisionType precision,
String currencyCode,
) {
print('${Constants.Tag} BannerAdListener PaidEvent ${ad.toString()}.');
},
onAdLoaded: (Ad ad) {
print('${Constants.Tag} BannerAdListener onAdLoaded ${ad.toString()}.');
},
onAdFailedToLoad: (Ad bannerAd, LoadAdError error) {
bannerAd.dispose();
print(
'${Constants.Tag} BannerAdListener onAdFailedToLoad error is ${error.responseInfo} | ${error.message} | ${error.code} | ${error.domain}');
},
);
}
static void disposeBanner() {
_banner?.dispose();
}
}
Second step is in our UI will be our widget:
FutureBuilder<Widget>(
future: Ads.buildBannerWidget(
context: context,
),
builder: (_, snapshot) {
if (!snapshot.hasData)return Text("No Banner yet");
return Container(
height: 90,
width: MediaQuery.of(context).size.width,
child: snapshot.data,
);
},
)
Upvotes: 3
Reputation: 887
In addition to Kafil Khan's answer, you can wrap the Container widget with StatefulBuilder
.
Example:
Widget bannerAdWidget() {
return StatefulBuilder(
builder: (context, setState) => Container(
child: AdWidget(ad: _bannerAd),
width: _bannerAd.size.width.toDouble(),
height: 100.0,
alignment: Alignment.center,
),
);
}
Upvotes: 18
Reputation: 111
The problem is that you're putting the same Widget again and again. You can fix this by creating a new StatefulWidget
class and returning the Adwidget, this will build that same widget multiple times, it works like a Builder. This solved my problem, hope it will work for you too! :)
You also don't have to provide multiple id's for a single ad unit.
Upvotes: 8