In long or infinite scrolling lists, such as news feeds or social feeds,
creating a banner ad for every item position can lead to high memory
consumption and UI lag. For improved performance, use banner recycling to
maintain a small, fixed-size cache of BannerAd instances and reuse off-screen
ads as the user scrolls.
This page covers sizing an ad cache, checking whether a BannerAd instance is
off-screen, and re-assigning cached ads to list positions.
The following image shows how an off-screen banner ad returns to the cache and is reused in a list position as the user scrolls:
Prerequisites
Before you continue, use inline adaptive for scrolling banners.
Track assigned list positions
As your user scrolls, banner recycling keeps a limited number of BannerAd
instances in memory and moves these instances between list positions.
To know which ad to reuse, track which list position each ad in the cache occupies.
Maintain the following state in your widget:
- A fixed-size cache of
BannerAdinstances. The size you set the cache to is the maximum number of ads held in memory at once. - An ad interval that determines how frequently an ad appears in the list.
- A position
Mapclass that associates eachBannerAdinstance with the list position the instance occupies. When you recycle an ad, update the entry in thisMapclass to point to the latest position. - A size
Mapclass that stores the platform-resolvedAdSizeobject for each loaded ad. After the ad loads, inline adaptive banner heights are known.
The following example tracks the assigned list positions for each ad in the cache:
// Maximum number of banner ad instances kept in the cache.
static const int _cacheSize = 8;
// Spacing between banner ads. For example, index 0, 6, 12, and so on
// display ads.
static const int _adInterval = 6;
// The cache of allocated BannerAd instances.
final List<BannerAd> _banners = [];
// Maps each BannerAd instance to its currently assigned list item position.
final Map<BannerAd, int> _bannerPositions = {};
// Stores the platform-resolved size of each loaded banner ad.
final Map<BannerAd, AdSize> _bannerSizes = {};
Choose a cache size
Make the cache larger than the maximum number of ads that can appear on screen at the same time. If the cache is smaller than that size, the ad selected for recycling is often still visible, and your app allocates additional ads instead of reusing the ads.
To choose a size, do the following:
- Estimate how many ads fit on screen at once by dividing the viewport height by the height of one ad interval.
- Add space for ads that are still detaching during fast scrolls.
- If you see that ads selected for recycling are still on screen, increase the cache size.
The example on this page displays an ad every six rows and uses a cache of eight. This example leaves space for the few ads visible on screen at once.
Check whether an ad is on screen
All Google Mobile Ads Flutter Plugin classes that render platform views implement the AdWithView
class and provide an isMounted getter. Before re-assigning a cached ad to a
list position, check the isMounted getter to confirm the ad has scrolled
off-screen and is safe to reuse.
Retrieve and recycle a banner ad
When the list requests a banner for a given position, do the following:
- Reuse the ad already assigned to that position, if one exists.
- If the cache isn't full, request an ad.
- If the cache is full, select a cached ad and verify that the
isMountedgetter is thefalsevalue before re-assigning the ad. - Call the
dispose()method on every ad in the cache when the widget is removed from the widget tree.
The following example retrieves and recycles a banner ad:
BannerAd _getRecycledBannerAd(BuildContext context, int bannerPosition) {
// 1. If an ad is already mapped to this position, reuse it.
final BannerAd? existingBanner = _bannerPositions.entries
.firstWhereOrNull((entry) => entry.value == bannerPosition)
?.key;
if (existingBanner != null) {
return existingBanner;
}
// 2. If the cache is not full, allocate a new ad instance.
if (_banners.length < _cacheSize) {
final BannerAd newBanner = _createBannerAd(context);
_banners.add(newBanner);
_bannerPositions[newBanner] = bannerPosition;
return newBanner;
}
// 3. Select an existing ad from the cache using modulo arithmetic.
final BannerAd targetBanner = _banners[bannerPosition % _cacheSize];
// 4. Verify the ad is detached from the screen before recycling.
if (targetBanner.isMounted) {
// If still on screen during fast scrolls, allocate a temporary instance.
return _createBannerAd(context);
} else {
// Safe to reuse: reassign to the new position.
_bannerPositions[targetBanner] = bannerPosition;
return targetBanner;
}
}
For the complete example, see Google Mobile Ads Flutter Plugin example.