Flutter原生广告加载异常求助:时而显示时而报错
问题排查与解决方案
这个错误的核心原因是广告实例的状态管理不一致:当广告被释放(dispose)或加载失败时,UI状态(_nativeAdIsLoaded)没有同步更新,导致Widget尝试渲染已失效的广告实例;另外,当Widget因配置变化(如深色模式切换)重建时,旧广告实例的清理逻辑也存在漏洞。
具体修复步骤
- 1. 同步广告状态与UI标记:在广告加载失败、释放时,重置
_nativeAdIsLoaded并清空_nativeAd引用 - 2. 处理Widget配置变化:重写
didUpdateWidget方法,当isDarkMode或isSmallSize变化时,销毁旧广告并重新加载适配新样式的广告 - 3. 统一广告释放逻辑:封装单独的方法处理广告释放,避免重复代码和状态遗漏
修改后的完整代码
class NativeAdWidget extends StatefulWidget { const NativeAdWidget({super.key, required this.isDarkMode, required this.isSmallSize}); final bool isDarkMode; final bool isSmallSize; @override NativeAdWidgetState createState() => NativeAdWidgetState(); } class NativeAdWidgetState extends State<NativeAdWidget> { NativeAd? _nativeAd; bool _nativeAdIsLoaded = false; final String _adUnitId = AdConfig.getNativeAdUnitId(); @override void initState() { loadAd(); super.initState(); } @override void didUpdateWidget(covariant NativeAdWidget oldWidget) { // 当深色模式或尺寸参数变化时,重新加载适配样式的广告 if (oldWidget.isDarkMode != widget.isDarkMode || oldWidget.isSmallSize != widget.isSmallSize) { _disposeAd(); loadAd(); } super.didUpdateWidget(oldWidget); } @override Widget build(BuildContext context) { final double minHeight = widget.isSmallSize ? 90.0 : 320.0; final double maxHeight = widget.isSmallSize ? 120.0 : 360.0; return ConstrainedBox( constraints: BoxConstraints( minWidth: 320.0, minHeight: minHeight, maxWidth: 400.0, maxHeight: maxHeight, ), child: _nativeAdIsLoaded && _nativeAd != null ? AdWidget(ad: _nativeAd!) : Container( color: Theme.of(context).colorScheme.onBackground, alignment: Alignment.center, child: const Text( 'Ad', style: TextStyle(fontSize: 20), ), ), ); } // 加载原生广告 void loadAd() { // 加载前先清理旧广告实例 if (_nativeAd != null) { _disposeAd(); } _nativeAd = NativeAd( adUnitId: _adUnitId, listener: NativeAdListener( onAdLoaded: (ad) { debugPrint('$NativeAd loaded.'); setState(() { _nativeAdIsLoaded = true; }); }, onAdFailedToLoad: (ad, error) { debugPrint('$NativeAd failed to load: $error'); // 加载失败时同步UI状态 setState(() { _nativeAdIsLoaded = false; }); ad.dispose(); }, onAdClosed: (ad) { // 广告关闭后重置状态 setState(() { _nativeAdIsLoaded = false; }); ad.dispose(); }, ), request: const AdRequest(), nativeAdOptions: NativeAdOptions( mediaAspectRatio: MediaAspectRatio.landscape, adChoicesPlacement: AdChoicesPlacement.topRightCorner, ), // 样式配置 nativeTemplateStyle: widget.isDarkMode ? nativeTemplateDark() : nativeTemplateLight()) ..load(); } // 统一处理广告释放逻辑 void _disposeAd() { if (_nativeAd != null) { _nativeAd!.dispose(); _nativeAd = null; setState(() { _nativeAdIsLoaded = false; }); } } @override void dispose() { _disposeAd(); super.dispose(); } // 深色模式样式 NativeTemplateStyle nativeTemplateDark() { final templateType = widget.isSmallSize ? TemplateType.small : TemplateType.medium; return NativeTemplateStyle( templateType: templateType, mainBackgroundColor: Colors.grey.shade800, cornerRadius: 10.0, callToActionTextStyle: NativeTemplateTextStyle(textColor: Colors.white, backgroundColor: Config.appThemeColor, style: NativeTemplateFontStyle.normal, size: 16.0), primaryTextStyle: NativeTemplateTextStyle(textColor: Colors.white, backgroundColor: Colors.transparent, style: NativeTemplateFontStyle.bold, size: 16.0), secondaryTextStyle: NativeTemplateTextStyle( textColor: Colors.grey.shade100, backgroundColor: Colors.transparent, style: NativeTemplateFontStyle.normal, size: 14.0), tertiaryTextStyle: NativeTemplateTextStyle( textColor: Colors.grey.shade100, backgroundColor: Colors.transparent, style: NativeTemplateFontStyle.normal, size: 14.0), ); } // 浅色模式样式 NativeTemplateStyle nativeTemplateLight() { final templateType = widget.isSmallSize ? TemplateType.small : TemplateType.medium; return NativeTemplateStyle( templateType: templateType, mainBackgroundColor: Colors.white, cornerRadius: 10.0, callToActionTextStyle: NativeTemplateTextStyle(textColor: Colors.white, backgroundColor: Config.appThemeColor, style: NativeTemplateFontStyle.normal, size: 16.0), primaryTextStyle: NativeTemplateTextStyle( textColor: Colors.grey.shade900, backgroundColor: Colors.transparent, style: NativeTemplateFontStyle.bold, size: 16.0), secondaryTextStyle: NativeTemplateTextStyle( textColor: Colors.blueGrey.shade600, backgroundColor: Colors.transparent, style: NativeTemplateFontStyle.normal, size: 14.0), tertiaryTextStyle: NativeTemplateTextStyle( textColor: Colors.blueGrey.shade500, backgroundColor: Colors.transparent, style: NativeTemplateFontStyle.normal, size: 14.0), ); } }
额外注意事项
- 确认测试广告ID正确性:Android原生测试ID为
ca-app-pub-3940256099942544/2247696110,iOS为ca-app-pub-3940256099942544/3986624511,避免因ID错误导致加载异常 - 广告加载存在随机性,测试时多切换页面、切换主题模式验证状态同步逻辑
内容的提问来源于stack exchange,提问作者sefa Göksoy
相关产品推荐
相关产品推荐

