🎯 广告类型
版本 GroMore

📰 信息流广告

信息流广告需要 先批量加载获取广告 ID,再结合 AdFeedWidget 在列表中渲染。

1. 加载与缓存 ID

import 'dart:collection';

class FeedAdRepository {
  final String posId;
  final Queue<int> _adIds = Queue<int>();

  FeedAdRepository(this.posId);

  Future<void> warmUp({int count = 3}) async {
    final ids = await GromoreAds.loadFeedAd(
      posId,
      width: 375,
      height: 300,
      count: count,
    );
    _adIds.addAll(ids);
  }

  int? take() => _adIds.isNotEmpty ? _adIds.removeFirst() : null;

  Future<void> clear() async {
    if (_adIds.isEmpty) return;
    await GromoreAds.clearFeedAd(_adIds.toList());
    _adIds.clear();
  }
}

🔁 loadFeedAd 返回的 List<int> 为原生缓存广告的唯一 ID,后续 widget 渲染必须传入。

可选参数说明

loadFeedAd 支持透传常见的聚合配置,只有在显式传值时才会影响原生 SDK:

参数类型说明
mutedIfCanbool首次展示时优先静音(Android/iOS 均生效)
volumedouble[Android] 自定义音量,范围 0.0~1.0,仅在 mutedIfCanfalse 时生效
bidNotifybool打开竞价结果回调,方便记录填充来源
scenarioIdString透传 GroMore 场景 ID,便于服务端报表归因
useSurfaceViewboolAndroid 独有:强制使用 SurfaceView 渲染视频素材
extraMap<String, dynamic>聚合额外参数,仅 Android 侧通过 MediationAdSlot#setExtraObject 生效;iOS 目前忽略

📌 未传递的字段会保留 GroMore SDK 默认值,避免出现“自以为是”的默认配置。

2. 在列表中渲染

class FeedListPage extends StatefulWidget {
  const FeedListPage({super.key});

  @override
  State<FeedListPage> createState() => _FeedListPageState();
}

class _FeedListPageState extends State<FeedListPage> {
  late final FeedAdRepository _repo;
  final List<_FeedItem> _items = [];

  @override
  void initState() {
    super.initState();
    _repo = FeedAdRepository('feed_pos_id');
    _loadData();
  }

  Future<void> _loadData() async {
    await _repo.warmUp(count: 2);

    const articles = [
      '新品上市', '热门专题', '运营活动', '使用技巧', '会员权益',
    ];

    for (var i = 0; i < articles.length; i++) {
      _items.add(_FeedItem.text(articles[i]));
      if ((i + 1) % 2 == 0) {
        final adId = _repo.take();
        if (adId != null) {
          _items.add(_FeedItem.ad(adId));
        }
      }
    }
    setState(() {});
  }

  @override
  void dispose() {
    _repo.clear();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: _items.length,
      itemBuilder: (context, index) {
        final item = _items[index];
        return item.when(
          text: (title) => ListTile(title: Text(title)),
          ad: (adId) => SizedBox(
            height: 300,
            child: AdFeedWidget(
              posId: _repo.posId,
              adId: adId,
              width: MediaQuery.of(context).size.width,
              height: 300,
              onAdError: (error) => debugPrint('feed ad error: $error'),
            ),
          ),
        );
      },
    );
  }
}

class _FeedItem {
  final int? adId;
  final String? title;

  const _FeedItem._({this.adId, this.title});

  static _FeedItem text(String title) => _FeedItem._(title: title);
  static _FeedItem ad(int adId) => _FeedItem._(adId: adId);

  T when<T>({required T Function(String) text, required T Function(int) ad}) {
    return adId != null ? ad(adId!) : text(title!);
  }
}

3. 清理

  • 调用 GromoreAds.clearFeedAd(adIds) 释放已不需要的广告。
  • 页面销毁时务必清理缓存的 ID,避免原生侧持有无效视图。

4. AdFeedWidget 参数

参数类型说明
posIdString信息流广告位 ID
adIdintloadFeedAd 返回的广告 ID
width / heightdouble期望的渲染尺寸(像素)
isVisiblebool是否渲染,默认 true
onAdLoaded 等回调同步原生侧渲染状态

掌握上述步骤即可将原生信息流广告无缝嵌入列表内容中。

需要进一步协助?

与 LightCore 技术顾问沟通,获取商业化策略与集成支持。