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

Flutter集成Unity Ads启动自动展示广告失效问题排查

Flutter集成Unity Ads冷启动插屏/横幅广告不展示修复方案

核心问题定位

当前实现存在3个致命逻辑错误,直接导致冷启动场景下广告无法正常展示:

  • 广告加载、展示逻辑全部写在runApp()执行前:冷启动阶段Flutter尚未完成根Widget挂载,Unity Ads插屏依赖的原生前台页面上下文不存在,广告找不到可挂载的容器自然无法弹出;热重载时页面已经处于前台挂载状态,因此可以正常触发展示,和观测到的现象完全一致。
  • 错误将UnityBannerAd当作异步方法调用:UnityBannerAd是可渲染的UI组件,不是返回Future的异步接口,在main函数中用await等待组件实例化没有任何意义——该组件从未被插入到页面的Widget渲染树中,永远不会在界面上显示。
  • 广告展示时机未和加载状态对齐:广告初始化、素材拉取是异步过程,串行在load方法后立刻调用showVideoAd,冷启动时如果素材未完成拉取就触发展示,会直接返回展示失败。

具体修复步骤

1. 精简main函数逻辑

main函数中只保留SDK初始化和应用启动逻辑,移除所有广告加载、展示、Banner实例化相关代码:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // 仅做SDK初始化,不在这里加载/展示广告
  UnityAds.init(
    gameId: AdHelper.addUnitId,
    onComplete: () => print('Initialization Complete'),
    onFailed: (error, message) =>
        print('Initialization Failed: $error $message'),
  );
  runApp(const MyApp());
}

2. 在首页生命周期中处理插屏广告加载与自动展示

将插屏加载、展示逻辑移到首页PageViewDemo的状态类中,等首帧渲染完成、页面完全挂载后再加载广告,确认广告加载成功后再触发展示:

class _PageViewDemoState extends State<PageViewDemo> {
  bool _interstitialLoaded = false;
  final PageController _controller = PageController(initialPage: 0);

  @override
  void initState() {
    super.initState();
    // 等首帧渲染完成、页面完全挂载后再执行广告逻辑
    WidgetsBinding.instance.addPostFrameCallback((_) async {
      // 加载插屏广告
      UnityAds.load(
        placementId: AdHelper.interstitialId,
        onComplete: (placementId) {
          print('Load Complete $placementId');
          _interstitialLoaded = true;
          // 加载完成后再展示广告
          _showInterstitialAd();
        },
        onFailed: (placementId, error, message) {
          print('Load Failed $placementId: $error $message');
          _interstitialLoaded = false;
        },
      );
    });
  }

  // 抽离插屏展示方法,增加加载状态校验
  void _showInterstitialAd() {
    if (!_interstitialLoaded) return;
    UnityAds.showVideoAd(
      placementId: AdHelper.interstitialId,
      onStart: (placementId) => print('Video Ad $placementId started'),
      onClick: (placementId) => print('Video Ad $placementId click'),
      onSkipped: (placementId) => print('Video Ad $placementId skipped'),
      onComplete: (placementId) {
        print('Video Ad $placementId completed');
        _interstitialLoaded = false;
      },
      onFailed: (placementId, error, message) {
        print('Video Ad $placementId failed: $error $message');
        _interstitialLoaded = false;
      },
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  final List images = [
    "https://google.com",
    "https://google.com",
    "https://google.com",
    "https://google.com",
  ];

  // 原有build逻辑在第三步修改
}

3. 将Banner广告嵌入到页面渲染树实现常驻展示

Banner作为UI组件必须放入Widget树才能渲染,直接在Scaffold中添加Banner组件即可实现底部常驻效果:

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Center(
        child: SizedBox.fromSize(
          size: const Size.fromHeight(648),
          child: PageView.builder(
            allowImplicitScrolling: true,
            controller: PageController(viewportFraction: 1),
            itemCount: images.length,
            itemBuilder: (BuildContext context, int index) {
              return Padding(
                padding: const EdgeInsets.symmetric(
                  vertical: 0,
                  horizontal: 0,
                ),
                child: Material(
                  elevation: 5,
                  borderRadius: BorderRadius.circular(1),
                  child: Stack(
                    fit: StackFit.expand,
                    children: [
                      Image.network(
                        images[index],
                        fit: BoxFit.contain,
                      ),
                      DecoratedBox(
                        decoration: BoxDecoration(
                          gradient: LinearGradient(
                            begin: FractionalOffset.bottomCenter,
                            end: FractionalOffset.topCenter,
                            colors: [
                              const Color(0x00000000).withOpacity(0.0),
                              const Color(0xff000000).withOpacity(0.01),
                            ],
                          ),
                        ),
                      )
                    ],
                  ),
                ),
              );
            },
          ),
        )),
    // 底部添加常驻Banner广告
    bottomNavigationBar: SizedBox(
      height: 50, // 根据Banner标准尺寸调整高度
      child: UnityBannerAd(
        placementId: AdHelper.bannerId,
        onLoad: (placementId) => print('Banner loaded: $placementId'),
        onClick: (placementId) => print('Banner clicked: $placementId'),
        onFailed: (placementId, error, message) =>
            print('Banner Ad $placementId failed: $error $message'),
      ),
    ),
  );
}

注意事项

  • 测试阶段请使用Unity Ads后台提供的测试Placement ID,正式广告位需要等应用在Unity后台审核通过后才会有稳定填充,无填充时广告不会展示,可通过onFailed回调打印的错误码定位具体原因。
  • 插屏广告展示必须保证应用处于前台状态,不要在应用切后台、页面未挂载的生命周期阶段调用展示方法。

内容的提问来源于stack exchange,提问作者Terrence Skill

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 01:15:29