⚡ 高级功能
版本 GroMore
📊 事件处理
GromoreAds
提供了灵活的事件订阅系统,支持全局监听、按广告位过滤和按广告类型分类监听。
1. 基础用法:全局事件监听
使用 onEvent()
监听所有广告事件:
class MyAdPage extends StatefulWidget {
@override
State<MyAdPage> createState() => _MyAdPageState();
}
class _MyAdPageState extends State<MyAdPage> {
AdEventSubscription? _subscription;
@override
void initState() {
super.initState();
// 订阅全局广告事件
_subscription = GromoreAds.onEvent(
onEvent: (event) {
debugPrint('📌 ${event.action} - ${event.posId}');
// 处理特定事件
if (event.action == 'reward_video_completed') {
debugPrint('✅ 视频播放完成');
}
},
onError: (event) {
debugPrint('❌ 错误: ${event.message} (${event.code})');
},
onReward: (event) {
if (event.verified) {
debugPrint('🎁 发放奖励: ${event.rewardAmount}');
_grantReward(event.rewardAmount);
}
},
onEcpm: (event) {
debugPrint('💰 eCPM: ${event.ecpm} - ${event.networkName}');
},
);
}
@override
void dispose() {
_subscription?.cancel(); // 取消订阅
super.dispose();
}
void _grantReward(int? amount) {
// 发放奖励逻辑
}
}
2. 进阶用法:按广告位过滤
使用 onAdEvents(posId)
只监听特定广告位的事件:
class RewardVideoPage extends StatefulWidget {
@override
State<RewardVideoPage> createState() => _RewardVideoPageState();
}
class _RewardVideoPageState extends State<RewardVideoPage> {
static const String REWARD_POS_ID = 'reward_video_001';
AdEventSubscription? _subscription;
@override
void initState() {
super.initState();
// 只监听特定广告位的事件
_subscription = GromoreAds.onAdEvents(
REWARD_POS_ID,
onEvent: (event) {
// 这里只会收到 reward_video_001 的事件
debugPrint('事件: ${event.action}');
},
onError: (event) {
debugPrint('加载失败: ${event.message}');
_showRetryDialog();
},
);
// 加载广告
GromoreAds.loadRewardVideoAd(REWARD_POS_ID);
}
@override
void dispose() {
_subscription?.cancel();
super.dispose();
}
void _showRetryDialog() {
// 显示重试对话框
}
}
3. 便捷方法:按广告类型监听
插件为每种广告类型提供了便捷的监听方法:
3.1 激励视频广告
_subscription = GromoreAds.onRewardVideoEvents(
'reward_video_001',
onLoaded: (event) => debugPrint('✅ 加载成功'),
onShowed: (event) => debugPrint('👀 开始展示'),
onRewarded: (event) {
debugPrint('🎁 获得奖励: ${event.rewardAmount}');
_grantReward(event.rewardAmount);
},
onCompleted: (event) => debugPrint('✅ 播放完成'),
onSkipped: (event) => debugPrint('⏭️ 跳过视频'),
onClicked: (event) => debugPrint('👆 点击广告'),
onClosed: (event) => debugPrint('🚪 广告关闭'),
onError: (event) => debugPrint('❌ ${event.message}'),
);
3.2 开屏广告
_subscription = GromoreAds.onSplashEvents(
'splash_001',
onLoadSuccess: (event) => debugPrint('✅ 加载成功'),
onShow: (event) => debugPrint('👀 开始展示'),
onClicked: (event) => debugPrint('👆 点击广告'),
onClosed: (event) {
debugPrint('🚪 广告关闭');
_navigateToHomePage();
},
onSkip: (event) => debugPrint('⏭️ 跳过广告'),
onError: (event) => debugPrint('❌ ${event.message}'),
);
3.3 插屏广告
_subscription = GromoreAds.onInterstitialEvents(
'interstitial_001',
onLoadSuccess: (event) => debugPrint('✅ 加载成功'),
onShow: (event) => debugPrint('👀 开始展示'),
onClicked: (event) => debugPrint('👆 点击广告'),
onClosed: (event) => debugPrint('🚪 广告关闭'),
onError: (event) => debugPrint('❌ ${event.message}'),
);
3.4 信息流广告
_subscription = GromoreAds.onFeedEvents(
'feed_001',
onLoaded: (event) => debugPrint('✅ 加载成功'),
onLoadSuccess: (event) {
final count = event.extra?['count'] ?? 0;
debugPrint('✅ 批量加载成功: $count个');
},
onDestroyed: (event) => debugPrint('🗑️ 广告销毁'),
onError: (event) => debugPrint('❌ ${event.message}'),
);
3.5 Draw信息流广告
_subscription = GromoreAds.onDrawFeedEvents(
'draw_feed_001',
onLoaded: (event) => debugPrint('✅ 加载成功'),
onLoadSuccess: (event) {
final count = event.extra?['count'] ?? 0;
debugPrint('✅ 批量加载成功: $count个');
},
onDestroyed: (event) => debugPrint('🗑️ 广告销毁'),
onError: (event) => debugPrint('❌ ${event.message}'),
);
4. 多订阅支持
新版API支持同时创建多个订阅,不会相互覆盖:
class MultiAdPage extends StatefulWidget {
@override
State<MultiAdPage> createState() => _MultiAdPageState();
}
class _MultiAdPageState extends State<MultiAdPage> {
AdEventSubscription? _rewardSubscription;
AdEventSubscription? _splashSubscription;
AdEventSubscription? _globalSubscription;
@override
void initState() {
super.initState();
// 订阅1:监听激励视频
_rewardSubscription = GromoreAds.onRewardVideoEvents(
'reward_001',
onRewarded: (event) => _grantReward(event.rewardAmount),
);
// 订阅2:监听开屏广告
_splashSubscription = GromoreAds.onSplashEvents(
'splash_001',
onClosed: (event) => _navigateToHome(),
);
// 订阅3:全局监听用于统计
_globalSubscription = GromoreAds.onEvent(
onEvent: (event) => _logEvent(event),
);
}
@override
void dispose() {
_rewardSubscription?.cancel();
_splashSubscription?.cancel();
_globalSubscription?.cancel();
super.dispose();
}
}
💡 提示:每个订阅都是独立的,互不影响。记得在
dispose()
中取消所有订阅。
5. 事件数据结构
类型 | 触发场景 | 关键字段 |
---|---|---|
AdEvent | 所有事件基类 | action , posId , timestamp , extra |
AdErrorEvent | 加载/展示失败 | 继承 AdEvent ,额外包含 code , message |
AdRewardEvent | 激励验证 | 继承 AdEvent ,额外包含 rewardType , rewardAmount , verified |
AdEcpmEvent | 聚合 eCPM | 继承 AdEvent ,额外包含 ecpm , networkName , adnId , channel , subChannel , requestId , ritType , scenarioId , levelTag , biddingType , customData |
extra
字段会合并原生透传参数(若存在),可用于读取自定义回调数据。
6. 常见动作一览
广告 | 成功事件 | 失败事件 | 其他关键事件 |
---|---|---|---|
开屏 | splash_loaded , splash_showed | splash_load_fail , splash_render_fail | splash_clicked , splash_closed |
插屏 | interstitial_loaded , interstitial_showed | interstitial_load_fail | interstitial_clicked , interstitial_closed |
激励 | reward_video_loaded , reward_video_showed | reward_video_load_fail | reward_video_rewarded , reward_video_completed |
Banner | banner_loaded , banner_render_success | banner_load_fail , banner_render_fail | banner_clicked , banner_destroyed |
信息流 | feed_loaded , feed_showed | feed_load_fail | feed_clicked , feed_destroyed |
Draw | draw_feed_loaded , draw_feed_showed | draw_feed_load_fail | draw_feed_clicked , draw_feed_destroyed |
使用 event.action
与 AdEventAction
常量对比可以更安全地判断事件类型。
7. 实战:统计分析
使用多订阅实现实时广告统计:
class AdAnalytics {
final Map<String, _AdMetrics> _metrics = {};
final List<AdEventSubscription> _subscriptions = [];
void startTracking(List<String> posIds) {
for (final posId in posIds) {
// 为每个广告位创建订阅
final subscription = GromoreAds.onAdEvents(
posId,
onEvent: (event) {
final metrics = _metrics.putIfAbsent(posId, () => _AdMetrics());
if (event.action.endsWith('_loaded')) {
metrics.loaded++;
} else if (event.action.endsWith('_showed')) {
metrics.shown++;
} else if (event.action.endsWith('_clicked')) {
metrics.clicked++;
}
},
onError: (event) {
final metrics = _metrics.putIfAbsent(posId, () => _AdMetrics());
metrics.failed++;
},
);
_subscriptions.add(subscription);
}
}
void stopTracking() {
for (final sub in _subscriptions) {
sub.cancel();
}
_subscriptions.clear();
}
Map<String, dynamic> export() {
return _metrics.map((posId, value) => MapEntry(posId, value.toJson()));
}
}
class _AdMetrics {
int loaded = 0;
int shown = 0;
int clicked = 0;
int failed = 0;
double get ctr => shown > 0 ? (clicked / shown * 100) : 0;
double get fillRate => (loaded + failed) > 0
? (loaded / (loaded + failed) * 100) : 0;
Map<String, dynamic> toJson() => {
'loaded': loaded,
'shown': shown,
'clicked': clicked,
'failed': failed,
'ctr': ctr.toStringAsFixed(2),
'fillRate': fillRate.toStringAsFixed(2),
};
}
8. 最佳实践
8.1 订阅管理
class AdManager {
final List<AdEventSubscription> _subscriptions = [];
void addSubscription(AdEventSubscription subscription) {
_subscriptions.add(subscription);
}
void cancelAll() {
for (final sub in _subscriptions) {
sub.cancel();
}
_subscriptions.clear();
}
}
8.2 错误重试
void _handleError(AdErrorEvent event, String posId) {
final errorCode = event.code;
// 网络错误,3秒后重试
if (errorCode == -1000 || event.message.contains('network')) {
Future.delayed(Duration(seconds: 3), () {
GromoreAds.loadRewardVideoAd(posId);
});
}
// 广告位无填充,停止加载
else if (errorCode == -2000) {
debugPrint('广告位无填充,停止加载');
}
}
8.3 奖励验证
void _handleReward(AdRewardEvent event) {
// 仅在服务端验证通过时发放奖励
if (!event.verified) {
debugPrint('⚠️ 奖励验证失败,不发放奖励');
return;
}
// 发放奖励到用户账户
final userId = event.extra?['userId'];
final customData = event.extra?['customData'];
_apiService.grantReward(
userId: userId,
rewardType: event.rewardType,
rewardAmount: event.rewardAmount,
customData: customData,
);
}
9. 迁移指南
如果你正在从旧版API迁移,以下是对照表:
旧API | 新API |
---|---|
class MyListener extends OnAdEventListener | 使用函数回调 onEvent: (event) {} |
GromoreAds.onEventListener(listener) | GromoreAds.onEvent(...) |
GromoreAds.removeEventListener() | subscription.cancel() |
onSplashEvent(AdEvent event) | GromoreAds.onSplashEvents(posId, ...) |
单一监听器 | 支持多订阅 |
手动检查 posId | 自动 posId 过滤 |
迁移示例
旧代码:
class MyListener extends OnAdEventListener {
@override
void onRewardVideoEvent(AdEvent event) {
if (event.posId == 'my_pos_id') { // 手动检查posId
if (event.action == 'reward_video_completed') {
// 处理逻辑
}
}
}
}
await GromoreAds.onEventListener(MyListener());
新代码:
_subscription = GromoreAds.onRewardVideoEvents(
'my_pos_id', // 自动过滤posId
onCompleted: (event) {
// 处理逻辑
},
);
代码量减少 60%,更加简洁易读!