如何解决 Flutter / Admob 的 "Ad.load to be called before AdWidget is inserted into the tree" 问题?

How to fix the issue "Ad.load to be called before AdWidget is inserted into the tree" issue with Flutter / Admob?

我在 Flutter 上使用 google_mobile_ads: ^1.1.0 版本并在此处观看视频:

https://www.youtube.com/watch?v=m0d_pbgeeG8

除了视频方面的一些小改动(我想与最近的 API 改动相比还没有完全更新)我现在有以下代码:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final initFuture = MobileAds.instance.initialize();
  final adState = AdState(initFuture);
  await SystemChrome.setPreferredOrientations(<DeviceOrientation>[
    DeviceOrientation.portraitUp,
    DeviceOrientation.portraitDown
  ]).then((_) => runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => Something()),
        ...some other ChangeNotifierProvider...,
        Provider<AdState>(create: (_) =>  adState)
      ],
      child: const MyApp()
    ),
  ));
}

AdState:

import 'dart:io';

import 'package:google_mobile_ads/google_mobile_ads.dart';

class AdState {
  Future<InitializationStatus> initialisation;

  AdState(this.initialisation);

  String get bannerAdUnitId => Platform.isAndroid
      ? 'ca-app-pub-3940256099942544/6300978111'
      : 'ca-app-pub-3940256099942544/2934735716'; // ios

  BannerAdListener get adListener => _adListener;

  final BannerAdListener _adListener = BannerAdListener(

    // Called when an ad is successfully received.
    onAdLoaded: (Ad ad) => print('Ad loaded: ${ad.adUnitId}.'),

    onAdClosed: (Ad ad) {
      ad.dispose();
      print('Ad closed: ${ad.adUnitId}.');
    },

    // Called when an ad request failed.
    onAdFailedToLoad: (Ad ad, LoadAdError error) {
      ad.dispose();
      print('Ad failed to load: : ${ad.adUnitId}, $error');
    },

    // Called when an ad opens an overlay that covers the screen.
    onAdOpened: (Ad ad) => print('Ad opened: ${ad.adUnitId}.'),

    // Called when an impression occurs on the ad.
    onAdImpression: (Ad ad) => print('Ad impression: ${ad.adUnitId}.'),
  );
}

然后在首页widget状态class:

BannerAd? banner;

@override
  void didChangeDependencies() {
    super.didChangeDependencies();
    final adState = Provider.of<AdState>(context);
    adState.initialisation.then((status) {
      setState(() {
        banner = BannerAd(
            adUnitId: adState.bannerAdUnitId,
            size: AdSize.banner,
            request: const AdRequest(),
            listener: adState.adListener
        )..load();
      });
    });
  }

@override
Widget build(BuildContext context) {
...somewhere in the middle...
if (banner == null)
  const SizedBox(height: 50)
else
  SizedBox (height:50, child: AdWidget(ad: banner!)),
....
}

我得到的错误是:

AdWidget requires Ad.load to be called before AdWidget is inserted into the tree

load() 方法在上面的 didChangeDependencies() 方法中被调用,当然它 returns 一个 Future 所以我认为当 build() 正在 运行。我该如何解决?

您只能在 BannerAd 加载后才能使用 AdWidget,而不是 BannerAd 不为空。

因此,请尝试使用 flag 作为加载状态,如以下代码片段所示:

BannerAd? banner;
bool _bannerIsLoaded = false;

@override
  void didChangeDependencies() {
    super.didChangeDependencies();
    final adState = Provider.of<AdState>(context);
    adState.initialisation.then((status) {
      setState(() {
        banner = BannerAd(
            adUnitId: adState.bannerAdUnitId,
            size: AdSize.banner,
            request: const AdRequest(),
            listener: adState.adListener
        )..load();
       
        _bannerIsLoaded = true;
      });
    });
  }

@override
Widget build(BuildContext context) {
...somewhere in the middle...
if (banner != null && _bannerIsLoaded)
  SizedBox (height:50, child: AdWidget(ad: banner!)),
else
  const SizedBox(height: 50)
  
....
}