You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于Google Flutter官方Codelab,如何在Flutter ListView中借助google_mobile_ads实现多组不同原生广告?

Hey there! I’ve tackled this exact scenario before with the google_mobile_ads plugin, so let me walk you through how to pull off showing different native ads at specific positions in your ListView.

Core Idea

The key is to merge your regular content items and ad entries into a single data list, then render each item based on its type. This lets you insert distinct native ads (with different ad unit IDs or layouts) wherever you need them.

Step 1: Define a Unified List Item Model

First, create a model to distinguish between regular content and different ad types. This helps the ListView know what to render for each entry:

enum ListItemType { content, nativeAdPromo, nativeAdSponsored }

class ListItem {
  final ListItemType type;
  final dynamic data; // Holds content text or ad unit ID

  ListItem(this.type, this.data);
}

Step 2: Build Your Combined Data Source

Generate a list that mixes your regular content with the ad positions you want. For example, insert a "promo" ad at index 2 and a "sponsored" ad at index 7:

List<ListItem> buildMixedList() {
  final items = <ListItem>[];
  
  // Add 10 regular content items
  for (int i = 0; i < 10; i++) {
    items.add(ListItem(ListItemType.content, "Regular Content Item $i"));
  }
  
  // Insert different ads at specific positions
  items.insert(2, ListItem(ListItemType.nativeAdPromo, "ca-app-pub-XXX/XXX"));
  items.insert(7, ListItem(ListItemType.nativeAdSponsored, "ca-app-pub-YYY/YYY"));
  
  return items;
}

Step 3: Handle Ad Loading & Caching

Create a way to load and cache native ads by their ad unit ID. This prevents redundant loads and keeps track of which ads are ready:

final Map<String, NativeAd?> _loadedAds = {};

void _loadAd(String adUnitId) {
  if (_loadedAds.containsKey(adUnitId)) return;

  final nativeAd = NativeAd(
    adUnitId: adUnitId,
    factoryId: adUnitId.contains("XXX") ? "promo_ad_factory" : "sponsored_ad_factory",
    request: const AdRequest(),
    listener: NativeAdListener(
      onAdLoaded: (ad) => setState(() => _loadedAds[adUnitId] = ad as NativeAd),
      onAdFailedToLoad: (ad, error) {
        ad.dispose();
        _loadedAds.remove(adUnitId);
        print("Ad failed to load: $error");
      },
    ),
  );

  nativeAd.load();
}

Note: Use different factoryId values if you want distinct layouts for each ad type (you'll need to configure these in your Android/iOS project).

Step 4: Render the ListView

In your build method, map each list item to the correct widget. Show loading states while ads are loading, and the ad once it's ready:

@override
Widget build(BuildContext context) {
  final mixedList = buildMixedList();
  
  // Preload ads when the widget initializes
  for (final item in mixedList) {
    if (item.type == ListItemType.nativeAdPromo || item.type == ListItemType.nativeAdSponsored) {
      _loadAd(item.data as String);
    }
  }

  return ListView.builder(
    itemCount: mixedList.length,
    itemBuilder: (context, index) {
      final item = mixedList[index];
      
      switch (item.type) {
        case ListItemType.content:
          return ListTile(title: Text(item.data as String));
        
        case ListItemType.nativeAdPromo:
        case ListItemType.nativeAdSponsored:
          final ad = _loadedAds[item.data as String];
          if (ad == null) {
            return const SizedBox(
              height: 120,
              child: Center(child: CircularProgressIndicator()),
            );
          }
          return Padding(
            padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
            child: NativeAdWidget(ad: ad),
          );
      }
    },
  );
}

Step 5: Clean Up Resources

Don't forget to dispose of loaded ads when the widget is destroyed to avoid memory leaks:

@override
void dispose() {
  for (final ad in _loadedAds.values) {
    ad?.dispose();
  }
  super.dispose();
}

Pro Tips

  • Lazy Loading: Instead of preloading all ads upfront, use a ScrollController or VisibilityDetector to load ads only when they're about to enter the viewport. This saves bandwidth and improves performance.
  • Error Handling: Replace loading indicators with a placeholder widget or skip the ad position entirely if an ad fails to load.
  • Custom Layouts: Use different factoryId values to style each ad type uniquely (configure these in your native project files).

内容的提问来源于stack exchange,提问作者Calvin

火山引擎 最新活动