Flutter :- 这个 AdWidget 已经在 Widget 树中了。如何禁用此异常。这是什么意思?
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()));
}
当您想添加一个新横幅时,您必须为其分配一个新 ID:
BannerAd(adUnitId: 'somethingDifferentThanTheOneInTheTree')
正如错误日志中明确指出的那样:
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.
问题是您一次又一次地放置同一个小部件。您可以通过创建一个新的 StatefulWidget
class 并返回 Adwidget 来解决这个问题,这将多次构建相同的小部件,它的工作方式类似于 Builder。这解决了我的问题,希望它也对你有用! :)
您也不必为单个广告单元提供多个 ID。
除了 Kafil Khan 的 ,您还可以使用 StatefulBuilder
包装容器小部件。
示例:
Widget bannerAdWidget() {
return StatefulBuilder(
builder: (context, setState) => Container(
child: AdWidget(ad: _bannerAd),
width: _bannerAd.size.width.toDouble(),
height: 100.0,
alignment: Alignment.center,
),
);
}
如上,只是我将我的实现放在这里,以便将来的新手对他们来说更容易。
首先,让我们创建一个名为 ad_helper 的助手 class 来隐藏我们 UI 的所有内容,
其中包含两个辅助方法:
第一个方法是 buildBannerWidget,它是一个 public 方法来按照我们的意愿构建我们的广告小部件。
第二个方法是 _instantiateBanner,它是一个私有方法,每次我们调用 buildBannerWidget 方法时都会构建我们的 bannerAd 对象。
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();
}
所以我在列表中插入了 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()));
}
当您想添加一个新横幅时,您必须为其分配一个新 ID:
BannerAd(adUnitId: 'somethingDifferentThanTheOneInTheTree')
正如错误日志中明确指出的那样:
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.
问题是您一次又一次地放置同一个小部件。您可以通过创建一个新的 StatefulWidget
class 并返回 Adwidget 来解决这个问题,这将多次构建相同的小部件,它的工作方式类似于 Builder。这解决了我的问题,希望它也对你有用! :)
您也不必为单个广告单元提供多个 ID。
除了 Kafil Khan 的 StatefulBuilder
包装容器小部件。
示例:
Widget bannerAdWidget() {
return StatefulBuilder(
builder: (context, setState) => Container(
child: AdWidget(ad: _bannerAd),
width: _bannerAd.size.width.toDouble(),
height: 100.0,
alignment: Alignment.center,
),
);
}
如上
首先,让我们创建一个名为 ad_helper 的助手 class 来隐藏我们 UI 的所有内容, 其中包含两个辅助方法:
第一个方法是 buildBannerWidget,它是一个 public 方法来按照我们的意愿构建我们的广告小部件。
第二个方法是 _instantiateBanner,它是一个私有方法,每次我们调用 buildBannerWidget 方法时都会构建我们的 bannerAd 对象。
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();
}