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

启动时调用main()方法后MaterialApp组件未更新的问题解决

问题描述

我有一个AdMob横幅组件,通过在main()中调用的方法实现条件显示。为了让它在所有屏幕上显示,我将其放在MaterialApp的Container子组件中,主内容组件绑定到home属性。

遇到的问题:即使调用方法成功加载了横幅,启动时横幅组件仍不显示,但刷新视图后会显示。需要修改代码让横幅组件与MaterialApp同时显示。

现有代码

MaterialApp构建代码

runApp(MaterialApp(
    home: MyHomePage(),
    // debugShowCheckedModeBanner: false,
    builder: (BuildContext context, Widget child) {
      child = EasyLoading.init()(context,child);
      AdLoader().loadBanner();
      return Container(
        color: resultDisabled ? Color(0xff77c2ff) : Color(0xFF55A13B),
        child: SafeArea(
          top: false,
          child: Column(
            children: [
              Expanded(child: child),
              if (AdLoader.anchoredBanner != null)
                AdLoader().bannerWidget(AdLoader.anchoredBanner),
            ],
          ),
        ),
      );
    },

加载横幅方法

Future<void> loadBanner() async {
    final bannerAd = BannerAd(
      adUnitId: Platform.isAndroid ? realUnitIdAndroid : realUnitIdiOS,
      size: AdSize.fullBanner,
      request: request,
      listener: BannerAdListener(
        // Called when an ad is successfully received.
        onAdLoaded: (Ad ad) {
          print('Ad loaded.');

          anchoredBanner = ad as BannerAd;
        },
        // Called when an ad request failed.
        onAdFailedToLoad: (Ad ad, LoadAdError error) {
          // Dispose the ad here to free resources.
          ad.dispose();
          print('Ad failed to load: $error');
        },
        // Called when an ad opens an overlay that covers the screen.
        onAdOpened: (Ad ad) => print('Ad opened.'),
        // Called when an ad removes an overlay that covers the screen.
        onAdClosed: (Ad ad) => print('Ad closed.'),
        // Called when an impression occurs on the ad.
        onAdImpression: (Ad ad) => print('Ad impression.'),
      ),
    );

    bannerAd.load();

  }

横幅组件代码

Widget bannerWidget(BannerAd ad) {
    return Container(
      color: Colors.green,
      width: anchoredBanner.size.width.toDouble(),
      height: anchoredBanner.size.height.toDouble(),
      child: AdWidget(ad: ad),
    );
  }
}
解决方法

问题根源在于Ad加载完成后更新anchoredBanner时,没有通知Flutter框架重建UI。以下是具体修改步骤:

  • 将AdLoader改为StatefulWidget:把广告的加载逻辑和状态管理放在组件内部,避免使用全局变量,通过setState()触发UI更新。
  • 在广告加载完成时触发状态更新:在onAdLoaded回调中调用setState(),让Flutter自动重建UI显示广告。
  • 规范资源释放:在组件销毁时释放广告资源,避免内存泄漏。

修改后的完整代码示例

1. 重构AdLoader为StatefulWidget

class AdLoader extends StatefulWidget {
  final Widget child;

  const AdLoader({Key? key, required this.child}) : super(key: key);

  @override
  State<AdLoader> createState() => _AdLoaderState();
}

class _AdLoaderState extends State<AdLoader> {
  BannerAd? anchoredBanner;

  @override
  void initState() {
    super.initState();
    loadBanner();
  }

  Future<void> loadBanner() async {
    final bannerAd = BannerAd(
      adUnitId: Platform.isAndroid ? realUnitIdAndroid : realUnitIdiOS,
      size: AdSize.fullBanner,
      request: const AdRequest(),
      listener: BannerAdListener(
        onAdLoaded: (Ad ad) {
          print('Ad loaded.');
          setState(() {
            anchoredBanner = ad as BannerAd;
          });
        },
        onAdFailedToLoad: (Ad ad, LoadAdError error) {
          ad.dispose();
          print('Ad failed to load: $error');
        },
        onAdOpened: (Ad ad) => print('Ad opened.'),
        onAdClosed: (Ad ad) => print('Ad closed.'),
        onAdImpression: (Ad ad) => print('Ad impression.'),
      ),
    );

    bannerAd.load();
  }

  Widget bannerWidget(BannerAd ad) {
    return Container(
      color: Colors.green,
      width: ad.size.width.toDouble(),
      height: ad.size.height.toDouble(),
      child: AdWidget(ad: ad),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: resultDisabled ? const Color(0xff77c2ff) : const Color(0xFF55A13B),
      child: SafeArea(
        top: false,
        child: Column(
          children: [
            Expanded(child: widget.child),
            if (anchoredBanner != null) bannerWidget(anchoredBanner!),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    anchoredBanner?.dispose();
    super.dispose();
  }
}

2. 更新MaterialApp的builder

runApp(MaterialApp(
  home: MyHomePage(),
  // debugShowCheckedModeBanner: false,
  builder: (BuildContext context, Widget child) {
    child = EasyLoading.init()(context, child);
    return AdLoader(child: child);
  },
));

关键说明

  • 广告状态由组件内部维护,加载完成后通过setState()触发UI重建,启动时广告加载完成会自动显示。
  • 组件销毁时调用dispose()释放广告资源,避免内存泄漏。
  • 移除全局变量的使用,状态管理更规范,符合Flutter的组件设计逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 04:00:37