跳转到主要内容

CloudX Flutter SDK

pub package GitHub 用于 CloudX 移动广告平台的 Flutter 插件。在 iOS 和 Android 上通过横幅、MREC 和插屏广告实现 Flutter 应用变现。

功能

  • 横幅广告 (320x50) - 基于 Widget 和程序化布局
  • MREC 广告 (300x250) - 中等矩形广告,灵活放置
  • 插屏广告 - 全屏广告,适用于自然过渡点
  • 隐私合规 - 内置支持 CCPA、GPP、GDPR 和 COPPA
  • 自动刷新 - 服务器端配置的自动广告刷新
  • 收入追踪 - 访问 eCPM 和获胜竞价方信息
  • 用户定向 - 通过键值对进行第一方数据集成

平台支持

平台状态最低版本
Android✅ 生产就绪API 21 (Android 5.0)
iOS⚠️ Alpha/实验性iOS 14.0
注意: iOS 支持需要在初始化时设置 allowIosExperimental: true

安装

添加到您的 pubspec.yaml
dependencies:
  cloudx_flutter: ^0.18.0
然后运行:
flutter pub get

快速开始

1. 初始化 SDK

在创建任何广告之前初始化 CloudX:
import 'package:cloudx_flutter/cloudx.dart';

// 可选:启用详细日志(仅开发)
await CloudX.setLoggingEnabled(true);

// 可选:设置环境(默认:production)
await CloudX.setEnvironment('production'); // 'dev'、'staging' 或 'production'

// 初始化 SDK
final success = await CloudX.initialize(
  appKey: 'YOUR_APP_KEY',
  allowIosExperimental: true, // iOS 必需
);

if (success) {
  print('CloudX SDK 初始化成功');
} else {
  print('CloudX SDK 初始化失败');
}

2. 横幅广告

选项 A:基于 Widget(推荐) 直接在 widget 树中嵌入横幅广告:
import 'package:cloudx_flutter/cloudx.dart';

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('我的应用')),
      body: Column(
        children: [
          Expanded(child: Text('您的内容在这里')),

          // 底部横幅广告
          CloudXBannerView(
            placementName: 'home_banner',
            listener: CloudXAdViewListener(
              onAdLoaded: (ad) => print('横幅已加载: ${ad.bidder}'),
              onAdLoadFailed: (error) => print('横幅失败: $error'),
              onAdDisplayed: (ad) => print('横幅已展示'),
              onAdDisplayFailed: (error) => print('展示失败: $error'),
              onAdClicked: (ad) => print('横幅被点击'),
              onAdHidden: (ad) => print('横幅已隐藏'),
              onAdExpanded: (ad) => print('横幅已展开'),
              onAdCollapsed: (ad) => print('横幅已折叠'),
            ),
          ),
        ],
      ),
    );
  }
}
选项 B:程序化布局 创建在特定屏幕位置覆盖内容的横幅:
// 创建横幅广告
final adId = await CloudX.createBanner(
  placementName: 'home_banner',
  adId: 'banner_1', // 可选:如未提供则自动生成
  listener: CloudXAdViewListener(
    onAdLoaded: (ad) => print('已加载: ${ad.revenue}'),
    onAdLoadFailed: (error) => print('失败: $error'),
    onAdDisplayed: (ad) => print('已展示'),
    onAdDisplayFailed: (error) => print('展示失败'),
    onAdClicked: (ad) => print('被点击'),
    onAdHidden: (ad) => print('已隐藏'),
    onAdExpanded: (ad) => print('已展开'),
    onAdCollapsed: (ad) => print('已折叠'),
  ),
  position: AdViewPosition.bottomCenter, // 可选:用于覆盖横幅
);

// 加载横幅
await CloudX.loadBanner(adId: adId!);

// 显示横幅(如果使用程序化布局)
await CloudX.showBanner(adId: adId);

// 需要时隐藏
await CloudX.hideBanner(adId: adId);

// 完成时始终销毁
await CloudX.destroyAd(adId: adId);

3. MREC 广告 (300x250)

中等矩形广告与横幅的工作方式相同,但尺寸更大。 基于 Widget:
CloudXMRECView(
  placementName: 'home_mrec',
  listener: CloudXAdViewListener(
    onAdLoaded: (ad) => print('MREC 已加载'),
    onAdLoadFailed: (error) => print('MREC 失败: $error'),
    onAdDisplayed: (ad) => print('MREC 已展示'),
    onAdDisplayFailed: (error) => print('展示失败'),
    onAdClicked: (ad) => print('MREC 被点击'),
    onAdHidden: (ad) => print('MREC 已隐藏'),
    onAdExpanded: (ad) => print('MREC 已展开'),
    onAdCollapsed: (ad) => print('MREC 已折叠'),
  ),
)

4. 插屏广告

在自然过渡点显示的全屏广告:
// 创建插屏
final adId = await CloudX.createInterstitial(
  placementName: 'level_complete',
  listener: CloudXInterstitialListener(
    onAdLoaded: (ad) => print('插屏已准备'),
    onAdLoadFailed: (error) => print('加载失败: $error'),
    onAdDisplayed: (ad) => print('插屏正在展示'),
    onAdDisplayFailed: (error) => print('展示失败: $error'),
    onAdClicked: (ad) => print('插屏被点击'),
    onAdHidden: (ad) => print('插屏已关闭'),
  ),
);

// 加载插屏
await CloudX.loadInterstitial(adId: adId!);

// 展示前检查是否准备好
final isReady = await CloudX.isInterstitialReady(adId: adId);
if (isReady) {
  await CloudX.showInterstitial(adId: adId);
}

// 展示后始终销毁
await CloudX.destroyAd(adId: adId);

隐私与合规

CCPA(加州消费者隐私法案)

竞价请求中完全支持:
// 设置 CCPA 隐私字符串(格式:"1YNN")
await CloudX.setCCPAPrivacyString('1YNN');

GDPR(通用数据保护条例)

⚠️ 警告: CloudX 服务器尚不支持。联系 CloudX 了解 GDPR 支持。
await CloudX.setIsUserConsent(true); // true = 用户已同意

GPP(全球隐私平台)

全面的隐私框架(完全支持):
// 设置 GPP 字符串
await CloudX.setGPPString('DBABMA~CPXxRfAPXxRfAAfKABENB...');

// 设置部分 ID(例如,[7, 8] 代表 US-National 和 US-CA)
await CloudX.setGPPSid([7, 8]);

用户定向

用户 ID

// 设置用户 ID(应哈希处理以保护隐私)
await CloudX.setUserID('hashed-user-id');

键值定向

用户级定向(受隐私法规清除):
await CloudX.setUserKeyValue('age', '25');
await CloudX.setUserKeyValue('interests', 'gaming');
应用级定向(隐私更改后持久):
await CloudX.setAppKeyValue('app_version', '1.2.0');
await CloudX.setAppKeyValue('build_type', 'release');
清除所有:
await CloudX.clearAllKeyValues();

生命周期管理

Widget 生命周期

对于基于 Widget 的广告(CloudXBannerViewCloudXMRECView),SDK 自动处理生命周期。

程序化生命周期

对于程序化创建的广告,始终调用 destroyAd() 以防止内存泄漏:
class MyAdScreen extends StatefulWidget {
  @override
  _MyAdScreenState createState() => _MyAdScreenState();
}

class _MyAdScreenState extends State<MyAdScreen> {
  String? _adId;

  @override
  void initState() {
    super.initState();
    _loadAd();
  }

  Future<void> _loadAd() async {
    _adId = await CloudX.createInterstitial(
      placementName: 'my_interstitial',
      listener: CloudXInterstitialListener(...),
    );
    await CloudX.loadInterstitial(adId: _adId!);
  }

  @override
  void dispose() {
    // 关键:始终销毁广告
    if (_adId != null) {
      CloudX.destroyAd(adId: _adId!);
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(...);
  }
}

最佳实践

1. 始终销毁广告

不销毁广告会导致内存泄漏:
@override
void dispose() {
  CloudX.destroyAd(adId: myAdId);
  super.dispose();
}

2. 销毁前停止自动刷新

对于横幅/MREC 广告:
await CloudX.stopAutoRefresh(adId: adId);
await CloudX.destroyAd(adId: adId);

3. 检查广告准备状态

对于插屏,展示前始终检查:
final isReady = await CloudX.isInterstitialReady(adId: adId);
if (isReady) {
  await CloudX.showInterstitial(adId: adId);
}

4. 生产环境禁用日志

// 仅在开发期间启用
if (kDebugMode) {
  await CloudX.setLoggingEnabled(true);
}

常见问题

iOS:“Experimental API” 错误

解决方案: 在初始化时设置 allowIosExperimental: true
await CloudX.initialize(
  appKey: 'YOUR_KEY',
  allowIosExperimental: true,
);

横幅不显示

检查清单:
  1. 您是否调用了 loadBanner()
  2. 对于程序化横幅,您是否调用了 showBanner()
  3. 广告视图是否添加到 widget 树?
  4. 检查 onAdLoadFailed 回调是否有错误

内存泄漏

解决方案: 始终在 widget 的 dispose() 方法中调用 destroyAd()

要求

Flutter

  • Flutter SDK:3.0.0 或更高
  • Dart SDK:3.0.0 或更高

iOS

  • iOS:14.0 或更高
  • CocoaPods 用于依赖管理
  • CloudXCore pod:~> 1.1.40

Android

  • Android API:21 (Android 5.0) 或更高
  • Gradle:8.0+
  • CloudX Android SDK:0.6.1

技术支持

如有问题、问题或功能请求:
  • 联系 CloudX 团队
  • 查看演示应用了解实现示例

许可证

本项目根据 Business Source License 1.1 许可。详见 LICENSE 文件。