Flutter PageView每5页插入全屏Admob广告尺寸异常问题
Flutter PageView实现全屏Admob插页广告方案
问题说明
在垂直滚动的PageView中展示短新闻,要求每滑动5页新闻后插入1个全屏Admob广告页,但当前广告仅显示312x100的小尺寸,无法占满整个页面。
核心原因
- 使用了
AdSize.banner标准横幅尺寸,该尺寸固定为移动端通用的320x50,无法适配全屏需求; - 广告容器未设置为占满PageView页面,且广告尺寸未匹配屏幕大小;
- 原代码中广告页的索引逻辑错误,会直接替换对应位置的新闻,而非插入独立的广告页。
解决方案步骤
1. 修正广告尺寸与初始化逻辑
将BannerAd的尺寸改为自适应容器的AdSize.fluid,或根据屏幕宽高自定义合规尺寸。由于initState中无法获取MediaQuery,将广告初始化移至didChangeDependencies中:
@override void didChangeDependencies() { super.didChangeDependencies(); final screenSize = MediaQuery.of(context).size; _bannerAd = BannerAd( adUnitId: AdHelper.bannerAdUnitId, request: AdRequest(), // 使用fluid尺寸自适应容器大小,或自定义适配屏幕的尺寸 size: AdSize.fluid, listener: BannerAdListener( onAdLoaded: (_) { setState(() { _isBannerAdReady = true; }); }, onAdFailedToLoad: (ad, err) { print('Failed to load a banner ad: ${err.message}'); _isBannerAdReady = false; ad.dispose(); }, ), ); _bannerAd.load(); }
2. 调整广告页容器为全屏
在广告页的Widget中,设置容器宽高为屏幕尺寸,让AdWidget填充满容器:
if (isAdPage(index)) { return Container( width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, child: _isBannerAdReady ? AdWidget(ad: _bannerAd) : Center(child: Text("加载广告中...")), ); }
3. 修正新闻与广告的索引逻辑
原代码会将新闻位置替换为广告,需重新计算总页数和实际新闻索引:
- 总页数 = 新闻数 + 插入的广告数(每5个新闻插1个广告)
- 判断广告页:
(index + 1) % 6 == 0(5个新闻+1个广告为一组,第6、12...页是广告) - 实际新闻索引 = index - 已出现的广告数量(广告数量 = index ~/ 6)
新增计算方法并修改PageView.builder逻辑:
// 计算总页数 int get totalPages { int adCount = _newsList.length ~/5; if (_newsList.length %5 ==0 && _newsList.isNotEmpty) { adCount -=1; } return _newsList.length + adCount + (hasReachedEnd ?0 :1); } // 判断是否为广告页 bool isAdPage(int index) { if (index == totalPages -1) { return false; } return (index +1) %6 ==0; }
4. 清理资源
在dispose方法中销毁广告和控制器,避免内存泄漏:
@override void dispose() { _bannerAd.dispose(); _pageController.dispose(); super.dispose(); }
完整修改后的代码
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; import 'Helper/ad_helper.dart'; import 'Models/news.dart'; import 'package:http/http.dart' as http; class Tedd extends StatefulWidget { @override _TeddState createState() => _TeddState(); } class _TeddState extends State<Tedd> { List<NewsModel> _newsList = []; bool isLoading = true; late BannerAd _bannerAd; bool _isBannerAdReady = false; int currentPage = 1; bool hasReachedEnd = false; PageController _pageController = PageController(initialPage: 0); _getAllNews(currentPage) async { var articles = await http.get(Uri.parse( "https://pkbhai.com/myprojects/kids-stories/api/all-stories?page=${currentPage}")); var result = json.decode(articles.body); var newDataLength = result['data'].length; if (newDataLength == 0) { setState(() { hasReachedEnd = true; }); } result['data'].forEach((data) { var news = NewsModel(); news.id = data["id"]; news.articleTitle = data["name"]; news.articleDetails = data["details"]; if (mounted) { setState(() { _newsList.add(news); }); } }); setState(() { isLoading = true; }); } void handleNext() { _pageController.addListener(() async { // 计算当前实际新闻位置,避免广告页触发加载 int currentIndex = _pageController.page?.toInt() ?? 0; int actualNewsIndex = currentIndex - (currentIndex ~/6); if (actualNewsIndex == _newsList.length -1) { setState(() { currentPage +=1; }); _getAllNews(currentPage); } }); } @override void didChangeDependencies() { super.didChangeDependencies(); final screenSize = MediaQuery.of(context).size; _bannerAd = BannerAd( adUnitId: AdHelper.bannerAdUnitId, request: AdRequest(), size: AdSize.fluid, listener: BannerAdListener( onAdLoaded: (_) { setState(() { _isBannerAdReady = true; }); }, onAdFailedToLoad: (ad, err) { print('Failed to load a banner ad: ${err.message}'); _isBannerAdReady = false; ad.dispose(); }, ), ); _bannerAd.load(); } @override void initState() { _getAllNews(currentPage); handleNext(); super.initState(); } // 计算总页数 int get totalPages { int adCount = _newsList.length ~/5; if (_newsList.length %5 ==0 && _newsList.isNotEmpty) { adCount -=1; } return _newsList.length + adCount + (hasReachedEnd ?0 :1); } // 判断是否为广告页 bool isAdPage(int index) { if (index == totalPages -1) { return false; } return (index +1) %6 ==0; } @override Widget build(BuildContext context) { return Scaffold( body: _newsList.length >0 ? PageView.builder( scrollDirection: Axis.vertical, controller: _pageController, itemCount: totalPages, itemBuilder: (context, index) { // 处理加载和末尾状态 if (index == totalPages -1 && !hasReachedEnd) { return Center(child: CircularProgressIndicator()); } if (index == totalPages -1 && hasReachedEnd) { return Container(color: Colors.red); } // 广告页 if (isAdPage(index)) { return Container( width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, child: _isBannerAdReady ? AdWidget(ad: _bannerAd) : Center(child: Text("加载广告中...")), ); } // 新闻页 int newsIndex = index - (index ~/6); return Container( child: Padding( padding: const EdgeInsets.all(8.0), child: Container( color: Colors.blue.shade400, height: MediaQuery.of(context).size.height, child: PageView( reverse: true, children: [ Text( _newsList[newsIndex].articleDetails!, maxLines:4, ), Text(_newsList[newsIndex].articleTitle!), ], ), ), ), ); }, ) : Center(child: CircularProgressIndicator()), ); } @override void dispose() { _bannerAd.dispose(); _pageController.dispose(); super.dispose(); } }
内容的提问来源于stack exchange,提问作者Pramod Yadav
相关产品推荐
相关产品推荐

