🎯 广告类型
版本 GroMore

📰 Draw信息流广告

Draw信息流广告是一种特殊的信息流广告类型,支持视频暂停控制、自定义渲染等高级功能,区别于传统信息流广告。

📋 与传统信息流广告的区别

特性传统信息流广告Draw信息流广告
API方法loadFeedAd()loadDrawFeedAd()
Widget组件AdFeedWidgetAdDrawFeedWidget
返回对象TTFeedAdTTDrawFeedAd
视频控制基础播放控制支持暂停/播放控制
自定义渲染标准模板支持自定义视频播放器
交互功能基础点击事件丰富的视频交互事件

Widget 组件方式(推荐)

// 在列表中嵌入Draw信息流广告
class DrawFeedPage extends StatefulWidget {
  @override
  _DrawFeedPageState createState() => _DrawFeedPageState();
}

class _DrawFeedPageState extends State<DrawFeedPage> {
  final List<dynamic> _feedItems = [];
  final List<int> _drawAdIds = [];
  
  @override
  void initState() {
    super.initState();
    _loadDrawFeedData();
  }
  
  void _loadDrawFeedData() async {
    // 加载Draw信息流广告
    try {
      final List<int> adIds = await GromoreAds.loadDrawFeedAd(
        'your_draw_feed_ad_id',
        width: 375,
        height: 300,
        count: 3,
      );
      
      setState(() {
        _drawAdIds.addAll(adIds);
        _buildFeedItems();
      });
    } catch (e) {
      print('Draw信息流广告加载失败: $e');
    }
  }
  
  void _buildFeedItems() {
    // 模拟加载内容数据
    final List<String> contentItems = [
      '内容1', '内容2', '内容3', '内容4', '内容5',
      '内容6', '内容7', '内容8', '内容9', '内容10'
    ];
    
    _feedItems.clear();
    
    // 在适当位置插入Draw广告
    int adIndex = 0;
    for (int i = 0; i < contentItems.length; i++) {
      _feedItems.add({'type': 'content', 'data': contentItems[i]});
      
      // 每4个内容后插入一个Draw广告
      if ((i + 1) % 4 == 0 && adIndex < _drawAdIds.length) {
        _feedItems.add({
          'type': 'draw_ad',
          'adId': _drawAdIds[adIndex],
          'posId': 'your_draw_feed_ad_id'
        });
        adIndex++;
      }
    }
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Draw信息流')),
      body: ListView.builder(
        itemCount: _feedItems.length,
        itemBuilder: (context, index) {
          final item = _feedItems[index];
          
          if (item['type'] == 'draw_ad') {
            // Draw广告项
            return Container(
              height: 300,
              margin: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
              child: AdDrawFeedWidget(
                posId: item['posId'],
                adId: item['adId'],
                width: MediaQuery.of(context).size.width - 32,
                height: 300,
                onAdLoaded: () {
                  print('Draw广告加载成功: ${item['adId']}');
                },
                onAdError: (error) {
                  print('Draw广告加载失败: $error');
                  // 广告加载失败时可以移除该项
                  setState(() {
                    _feedItems.removeAt(index);
                  });
                },
                onAdClicked: () {
                  print('Draw广告被点击: ${item['adId']}');
                },
                // Draw广告特有的视频回调
                onVideoPlay: () {
                  print('Draw广告视频开始播放: ${item['adId']}');
                },
                onVideoPause: () {
                  print('Draw广告视频暂停: ${item['adId']}');
                },
                onVideoComplete: () {
                  print('Draw广告视频播放完成: ${item['adId']}');
                },
              ),
            );
          } else {
            // 内容项
            return ListTile(
              title: Text(item['data']),
              subtitle: Text('这是一条内容描述'),
              leading: Icon(Icons.article),
            );
          }
        },
      ),
    );
  }
  
  @override
  void dispose() {
    // 页面销毁时清理Draw广告
    if (_drawAdIds.isNotEmpty) {
      GromoreAds.clearDrawFeedAd(_drawAdIds);
    }
    super.dispose();
  }
}

可选参数说明

loadDrawFeedAdloadFeedAd 共用同一套扩展参数,可按需开启聚合功能:

参数类型说明
mutedIfCanbool首次展示静音,兼容 Android / iOS
volumedouble[Android] 自定义音量(0.0~1.0),在 mutedIfCan=false 时生效
bidNotifybool启用竞价通知,获知实际填充渠道
scenarioIdString场景 ID,便于统计与归因
useSurfaceViewbool[Android] 视频采用 SurfaceView 呈现,降低过度绘制
extraMap<String, dynamic>[Android] 聚合扩展参数,通过 MediationAdSlot#setExtraObject 生效

同样地,未显式传入的字段将维持 GroMore 默认行为。

方法调用方式

class DrawFeedAdHelper {
  static final Map<String, List<int>> _loadedAds = {};
  
  // 加载Draw信息流广告
  static Future<List<int>> loadDrawFeedAd(String posId, {
    int width = 375,
    int height = 300,
    int count = 1,
  }) async {
    try {
      final List<int> adIds = await GromoreAds.loadDrawFeedAd(
        posId,
        width: width,
        height: height,
        count: count,
      );
      
      _loadedAds[posId] = adIds;
      print('Draw信息流广告 $posId 加载成功,获得${adIds.length}个广告');
      return adIds;
    } catch (e) {
      print('Draw信息流广告 $posId 加载异常: $e');
      return [];
    }
  }
  
  // 销毁Draw信息流广告
  static Future<void> destroyDrawFeedAd(String posId) async {
    final List<int>? adIds = _loadedAds[posId];
    if (adIds == null || adIds.isEmpty) return;
    
    try {
      await GromoreAds.clearDrawFeedAd(adIds);
      _loadedAds.remove(posId);
      print('Draw信息流广告 $posId 已销毁');
    } catch (e) {
      print('Draw信息流广告 $posId 销毁异常: $e');
    }
  }
  
  // 批量销毁所有Draw信息流广告
  static Future<void> destroyAllDrawFeedAds() async {
    for (String posId in _loadedAds.keys.toList()) {
      await destroyDrawFeedAd(posId);
    }
  }
}

自适应尺寸Draw信息流

class AdaptiveDrawFeedAd extends StatelessWidget {
  final String posId;
  final int adId;
  final double aspectRatio; // 宽高比
  
  const AdaptiveDrawFeedAd({
    Key? key,
    required this.posId,
    required this.adId,
    this.aspectRatio = 1.25, // 默认1.25:1
  }) : super(key: key);
  
  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final adWidth = constraints.maxWidth;
        final adHeight = adWidth / aspectRatio;
        
        return Container(
          width: adWidth,
          height: adHeight,
          margin: EdgeInsets.symmetric(vertical: 8),
          child: AdDrawFeedWidget(
            posId: posId,
            adId: adId,
            width: adWidth,
            height: adHeight,
            onAdLoaded: () {
              print('自适应Draw广告加载成功: $posId');
            },
            onAdError: (error) {
              print('自适应Draw广告加载失败: $error');
            },
            onVideoPlay: () {
              print('自适应Draw广告视频开始播放');
            },
            onVideoPause: () {
              print('自适应Draw广告视频暂停');
            },
          ),
        );
      },
    );
  }
}

AdDrawFeedWidget 参数

参数类型必需说明
posIdStringDraw信息流广告位 ID
adIdint广告数据ID(从loadDrawFeedAd返回)
widthdouble宽度,默认 375
heightdouble高度,默认 300
isVisiblebool是否可见,默认 true
onAdLoadedVoidCallback?广告加载成功回调
onAdErrorFunction(String)?广告加载失败回调
onAdClickedVoidCallback?广告点击回调
onAdClosedVoidCallback?广告关闭回调
onVideoPlayVoidCallback?Draw特有:视频播放回调
onVideoPauseVoidCallback?Draw特有:视频暂停回调
onVideoCompleteVoidCallback?Draw特有:视频播放完成回调

Draw广告特殊功能

1. 视频控制

AdDrawFeedWidget(
  posId: 'your_draw_feed_ad_id',
  adId: adId,
  onVideoPlay: () {
    // 视频开始播放时的处理
    print('Draw广告视频开始播放');
  },
  onVideoPause: () {
    // 视频暂停时的处理
    print('Draw广告视频暂停');
  },
  onVideoComplete: () {
    // 视频播放完成时的处理
    print('Draw广告视频播放完成');
  },
)

2. 与内容融合的Draw广告容器

// 创建与内容风格一致的Draw广告容器
Widget _buildDrawFeedItem(dynamic item, int index) {
  if (item['type'] == 'draw_ad') {
    return Container(
      margin: EdgeInsets.all(12),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
        boxShadow: [
          BoxShadow(
            color: Colors.grey.withOpacity(0.1),
            spreadRadius: 1,
            blurRadius: 5,
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // Draw广告标识
          Padding(
            padding: EdgeInsets.all(8),
            child: Row(
              children: [
                Icon(Icons.play_circle, size: 12, color: Colors.blue),
                SizedBox(width: 4),
                Text('Draw广告', style: TextStyle(fontSize: 10, color: Colors.grey)),
              ],
            ),
          ),
          // Draw广告内容
          AdDrawFeedWidget(
            posId: item['posId'],
            adId: item['adId'],
            width: MediaQuery.of(context).size.width - 24,
            height: 280,
            onAdLoaded: () => print('Draw广告加载完成'),
            onVideoPlay: () => print('Draw视频开始播放'),
          ),
        ],
      ),
    );
  } else {
    // 普通内容项
    return _buildContentItem(item);
  }
}

常见尺寸和比例

类型尺寸 (宽x高)宽高比适用场景
标准Draw375x3001.25:1通用Draw信息流
大屏Draw375x2801.34:1强视觉冲击
横版Draw375x2101.78:1横版视频内容
方形Draw300x3001:1方形视频展示

最佳实践

  1. 广告密度: Draw广告相比普通信息流广告更具视觉冲击力,建议控制在 1:4 到 1:6 之间
  2. 尺寸选择: Draw广告建议使用较大尺寸以充分展示视频内容
  3. 加载策略: Draw广告加载时间可能较长,建议提前预加载
  4. 交互处理: 利用Draw广告的视频回调优化用户交互体验
  5. 资源管理: 及时清理不需要的Draw广告实例以释放内存

⚠️ 注意事项

  1. 区分使用: Draw信息流广告与传统信息流广告是两种不同的广告类型,请勿混用
  2. API区分: 使用 loadDrawFeedAd()AdDrawFeedWidget,而非 loadFeedAd()
  3. 性能优化: Draw广告通常包含视频内容,注意内存和性能管理
  4. 事件处理: 充分利用Draw广告特有的视频事件回调
  5. 兼容性: 确保广告位配置支持Draw广告类型

下一步

需要进一步协助?

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