Flutter实现onPressed等待广告加载完成的问题求助
解决Flutter插屏广告点击按钮后等待加载的问题
问题核心
点击按钮时广告尚未加载完成,当前代码仅做一次_isAdLoaded判断,若此时为false则后续流程完全中断,加载弹窗无法关闭也不会继续执行广告展示和页面跳转。
解决方案:通过Completer等待广告加载状态变更
我们可以用Completer来监听广告加载的完成事件,确保点击按钮后能等待广告加载完成再执行后续操作,同时处理加载失败的边界情况。
步骤1:添加状态监听变量
在_BusTestState类中添加Completer变量:
Completer<void>? _adLoadCompleter;
步骤2:初始化与绑定广告加载事件
修改_initAd()方法,初始化Completer并在广告加载成功/失败时完成它:
void _initAd() { _adLoadCompleter = Completer(); // 初始化Completer InterstitialAd.load( adUnitId: 'ca-app-pub-3940256099942544/1033173712', request: AdRequest(), adLoadCallback: InterstitialAdLoadCallback( onAdLoaded: onAdLoaded, onAdFailedToLoad: (error) { print(error); _adLoadCompleter?.completeError(error); // 加载失败时标记错误 }, ), ); } void onAdLoaded(InterstitialAd ad) { _interstitialAd = ad; _isAdLoaded = true; _adLoadCompleter?.complete(); // 加载完成时完成Completer _interstitialAd.fullScreenContentCallback = FullScreenContentCallback( onAdDismissedFullScreenContent: (ad) { _initAd(); // 重新加载广告时重置Completer ad.dispose(); }, onAdFailedToShowFullScreenContent: (ad, error) { print(error); _initAd(); // 重新加载广告时重置Completer ad.dispose(); }, ); }
步骤3:修改按钮点击逻辑
更新按钮的onPressed方法,等待广告加载完成后再执行操作:
onPressed: () async { adScreenLoadingDialog(context); try { // 等待广告加载完成,设置10秒超时避免无限等待 await _adLoadCompleter?.future.timeout(const Duration(seconds: 10)); if (_isAdLoaded && _interstitialAd != null) { await _interstitialAd!.show(); Navigator.pop(context); Navigator.push( context, MaterialPageRoute(builder: (context) => BusTest1()), ); } else { throw Exception('广告未加载完成'); } } catch (e) { Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('广告加载失败,请稍后重试')), ); } },
完整修改后的代码
import 'package:flutter/material.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; class BusTest extends StatefulWidget { @override _BusTestState createState() => _BusTestState(); } class _BusTestState extends State<BusTest> { InterstitialAd? _interstitialAd; bool _isAdLoaded = false; Completer<void>? _adLoadCompleter; // 新增Completer void _initAd() { _adLoadCompleter = Completer(); InterstitialAd.load( adUnitId: 'ca-app-pub-3940256099942544/1033173712', request: AdRequest(), adLoadCallback: InterstitialAdLoadCallback( onAdLoaded: onAdLoaded, onAdFailedToLoad: (error) { print(error); _adLoadCompleter?.completeError(error); }, ), ); } void onAdLoaded(InterstitialAd ad) { _interstitialAd = ad; _isAdLoaded = true; _adLoadCompleter?.complete(); _interstitialAd!.fullScreenContentCallback = FullScreenContentCallback( onAdDismissedFullScreenContent: (ad) { _initAd(); ad.dispose(); }, onAdFailedToShowFullScreenContent: (ad, error) { print(error); _initAd(); ad.dispose(); }, ); } @override void initState() { super.initState(); _initAd(); } @override void dispose() { _interstitialAd?.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( centerTitle: true, title: const Text( "Bus Test", style: TextStyle(fontSize: 20), ), ), body: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16), child: Column( children: [ ElevatedButton( style: ButtonStyle( shape: MaterialStateProperty.all( RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), maximumSize: MaterialStateProperty.resolveWith((states) => const Size(double.infinity, 60)), padding: MaterialStateProperty.all(EdgeInsets.zero), backgroundColor: MaterialStateProperty.resolveWith((states) { return states.contains(MaterialState.pressed) ? const Color(0xff795368) : const Color(0xff3E2A35); }), ), onPressed: () async { adScreenLoadingDialog(context); try { await _adLoadCompleter?.future.timeout(const Duration(seconds: 10)); if (_isAdLoaded && _interstitialAd != null) { await _interstitialAd!.show(); Navigator.pop(context); Navigator.push( context, MaterialPageRoute(builder: (context) => BusTest1()), ); } else { throw Exception('广告未加载完成'); } } catch (e) { Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('广告加载失败,请稍后重试')), ); } }, child: const SizedBox( height: 40, child: Center( child: Text( 'Bus Test 1', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15), ), ), ), ), const SizedBox(height: 10), // 其余两个按钮重复相同逻辑即可 ElevatedButton( style: ButtonStyle( shape: MaterialStateProperty.all( RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), maximumSize: MaterialStateProperty.resolveWith((states) => const Size(double.infinity, 60)), padding: MaterialStateProperty.all(EdgeInsets.zero), backgroundColor: MaterialStateProperty.resolveWith((states) { return states.contains(MaterialState.pressed) ? const Color(0xff795368) : const Color(0xff3E2A35); }), ), onPressed: () async { adScreenLoadingDialog(context); try { await _adLoadCompleter?.future.timeout(const Duration(seconds: 10)); if (_isAdLoaded && _interstitialAd != null) { await _interstitialAd!.show(); Navigator.pop(context); Navigator.push( context, MaterialPageRoute(builder: (context) => BusTest2()), ); } else { throw Exception('广告未加载完成'); } } catch (e) { Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('广告加载失败,请稍后重试')), ); } }, child: const SizedBox( height: 40, child: Center( child: Text( 'Bus Test 2', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15), ), ), ), ), const SizedBox(height: 10), ElevatedButton( style: ButtonStyle( shape: MaterialStateProperty.all( RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), maximumSize: MaterialStateProperty.resolveWith((states) => const Size(double.infinity, 60)), padding: MaterialStateProperty.all(EdgeInsets.zero), backgroundColor: MaterialStateProperty.resolveWith((states) { return states.contains(MaterialState.pressed) ? const Color(0xff795368) : const Color(0xff3E2A35); }), ), onPressed: () async { adScreenLoadingDialog(context); try { await _adLoadCompleter?.future.timeout(const Duration(seconds: 10)); if (_isAdLoaded && _interstitialAd != null) { await _interstitialAd!.show(); Navigator.pop(context); Navigator.push( context, MaterialPageRoute(builder: (context) => BusTest3()), ); } else { throw Exception('广告未加载完成'); } } catch (e) { Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('广告加载失败,请稍后重试')), ); } }, child: const SizedBox( height: 40, child: Center( child: Text( 'Bus Test 3', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15), ), ), ), ), const SizedBox(height: 10), ], ), ), ], ), ), ); } } void adScreenLoadingDialog(BuildContext context) { showDialog( barrierDismissible: false, context: context, builder: (context) { return const AlertDialog( elevation: 0, backgroundColor: Colors.transparent, content: SizedBox( height: 100, width: 100, child: SpinKitCircle( color: Color(0xffd88820), size: 100, ), ), ); }, ); } // 占位页面类,实际项目中替换为真实页面 class BusTest1 extends StatelessWidget { @override Widget build(BuildContext context) => Scaffold(appBar: AppBar(title: const Text('Bus Test 1'))); } class BusTest2 extends StatelessWidget { @override Widget build(BuildContext context) => Scaffold(appBar: AppBar(title: const Text('Bus Test 2'))); } class BusTest3 extends StatelessWidget { @override Widget build(BuildContext context) => Scaffold(appBar: AppBar(title: const Text('Bus Test 3'))); }
关键优化点
- 等待机制:通过
Completer实现异步等待,确保广告加载完成后再执行后续操作 - 错误处理:捕获加载失败和超时异常,关闭弹窗并提示用户
- 资源管理:广告展示后及时释放资源,重新加载广告时重置
Completer - 超时控制:添加10秒超时,避免用户无限等待
内容的提问来源于stack exchange,提问作者H Amr
相关产品推荐
相关产品推荐

