You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 06:44:52