[英]Flutter :- This AdWidget is already in the Widget tree. How to disable this exception. And what does it mean?
所以我在列表中插入了 admob 個廣告。 我在列表視圖中添加了無限滾動的功能。 因此,當用戶滾動到列表末尾時,新項目將添加到列表中。 對於這些項目,我還在其中添加了 admob 個廣告。
因此,當用戶滾動到末尾時,新項目和廣告將添加到列表中。 那時捕獲了以下異常。 那么如何解決這個異常。
======== 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)
代碼:-
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()));
}
除了 Kafil Khan 的回答之外,您還可以使用StatefulBuilder
包裝 Container 小部件。
例子:
Widget bannerAdWidget() {
return StatefulBuilder(
builder: (context, setState) => Container(
child: AdWidget(ad: _bannerAd),
width: _bannerAd.size.width.toDouble(),
height: 100.0,
alignment: Alignment.center,
),
);
}
問題是您一次又一次地放置相同的小部件。 您可以通過創建一個新的StatefulWidget
class 並返回 Adwidget 來解決此問題,這將多次構建相同的小部件,它的工作方式類似於 Builder。 這解決了我的問題,希望它也對你有用::)
您也不必為單個廣告單元提供多個 ID。
當您想添加新橫幅時,您必須為其分配一個新 ID:
BannerAd(adUnitId: 'somethingDifferentThanTheOneInTheTree')
正如錯誤日志中明確指出的那樣:
如果您將此 AdWidget 放在列表中,請確保在構建器 function 中創建一個具有唯一廣告 object 的新實例。 確保您沒有在多個 AdWidget 中使用相同的廣告 object。
正如上面的答案,我將把我的實現放在這里,讓未來的新手更容易。
首先,讓我們創建一個名為 ad_helper 的助手 class 來隱藏我們 UI 中的所有內容,其中包含兩個助手方法:
第一種方法是 buildBannerWidget,它是一種公共方法,可以根據需要構建我們的廣告小部件。
第二種方法是 _instantiateBanner,它是每次調用 buildBannerWidget 方法時構建bannerAd object 的私有方法。
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();
}
}
第二步是在我們的 UI 中成為我們的小部件:
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,
);
},
)
我嘗試了所有這些,但只有以下對我有用。 我將myBanner.dispose()
添加到我的每個頁面的iniState
中。
void initState(){
myBanner.dispose();
myBanner.load();
super.initState();
}
#Step-1:制作有狀態的 Class,如下所示:
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:使用如下:
@override
Widget build(BuildContext context){
return BannerAdmob();
}
你必須像這樣使用 UniqueKey
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');
}
}
}
向包含AdWidget
的每個SizedBox
(或另一個小部件)添加唯一key
確實解決了我的問題。
您必須在 initState() 方法中初始化和加載廣告。
下面是我如何使用橫幅廣告鍵作為小部件的唯一鍵。
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!,
),
),
以上沒有對我有用。 在我的情況下,要在同一個列表中顯示三個廣告橫幅,我必須創建 3 個獨立變量來加載 3 個獨立的 AdWidegt。 請參閱附加代碼以近似解決此問題。 注意:解決了我們討論的錯誤,當每次橫幅因滾動從屏幕上消失時刪除 memory 時,我現在遇到橫幅未解決的性能問題。 每次卷軸發生時,這股力量都會重建和處置……
@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,
),
);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.