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

Flutter PageView每5页插入全屏Admob广告尺寸异常问题

Flutter PageView实现全屏Admob插页广告方案

问题说明

在垂直滚动的PageView中展示短新闻,要求每滑动5页新闻后插入1个全屏Admob广告页,但当前广告仅显示312x100的小尺寸,无法占满整个页面。

核心原因

  1. 使用了AdSize.banner标准横幅尺寸,该尺寸固定为移动端通用的320x50,无法适配全屏需求;
  2. 广告容器未设置为占满PageView页面,且广告尺寸未匹配屏幕大小;
  3. 原代码中广告页的索引逻辑错误,会直接替换对应位置的新闻,而非插入独立的广告页。

解决方案步骤

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 01:40:27