启动时调用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
相关产品推荐
相关产品推荐

