概览

CloudX Flutter SDK 设置和核心功能概览

pub package

CloudX Flutter SDK 可让您通过横幅、MREC、插屏和激励视频广告,在 iOS 和 Android 上为 Flutter 应用变现。

安装

要求

要求版本
Dart SDK>=2.17.1 <4.0.0
Flutter>=3.0.0
iOS13.0+,Xcode 16.0+,Swift 6.0+
AndroidminSdk 23

Dart 版本范围特意放宽,使仍在较旧 Flutter LTS 线上的应用无需升级整个工具链即可接入 cloudx_flutter。您的应用实际的 iOS 下限取插件与所用 Flutter 版本中较高的一个:Flutter 3.47 所带引擎要求 iOS 15.0,因此使用该版本的应用需要在 Podfile 中设置 platform :ios, '15.0'。

个别适配器会抬高上述下限:部分适配器要求比插件本身更高的 compileSdk、minSdk、Kotlin 或 Xcode 版本,各适配器页面会给出各自的要求。较新的 Flutter 版本大多可以满足,因为 Flutter 3.47 要求 Gradle 8.14、AGP 8.11.1 和 Kotlin 2.2.20。设置版本下限前,请查阅您所启用适配器的页面。

在 pubspec.yaml 中添加 SDK:

dependencies:
  cloudx_flutter: ^3.9.0

然后运行:

flutter pub get

原生 SDK 版本

插件为每个平台固定一个 CloudX 原生 SDK。插件自身版本跟随 iOS CloudXCore 线;Android 的固定版本在更新日志中按版本列出。

平台依赖版本
Androidio.cloudx:sdk4.7.0
iOSCloudXCore3.9.0

iOS 设置

CloudXCore pod 会通过插件的 podspec 自动引入。广告网络适配器是独立的 pod,请按启用的广告网络逐个添加到 ios/Podfile 的 target 'Runner' 块中。

target 'Runner' do
  # ... 现有 Flutter 配置 ...

  # CloudX 广告网络适配器(按需添加)
  pod 'CloudXMetaAdapter', '~> 6.22.0.0'            # Meta Audience Network 6.22.0
  pod 'CloudXVungleAdapter', '~> 7.7.6.0'           # VungleAds 7.7.6
  pod 'CloudXInMobiAdapter', '~> 11.4.1.0'          # InMobiSDK 11.4.1
  pod 'CloudXMintegralAdapter', '~> 8.1.6.0'        # MintegralAdSDK 8.1.6
  pod 'CloudXUnityAdsAdapter', '~> 4.19.0.0'        # UnityAds 4.19.0
  pod 'CloudXMagniteAdapterV2', '~> 1.0.0.1'        # MagniteSDK 1.0.0
  pod 'CloudXMobileFuseAdapter', '~> 1.11.0.1'      # MobileFuseSDK 1.11.0
  pod 'CloudXMolocoAdapter', '~> 4.8.0.0'           # MolocoSDKiOS 4.8.0
  pod 'CloudXVerveAdapter', '~> 3.9.0.0'            # HyBid 3.9.0
  pod 'CloudXDigitalTurbineAdapter', '~> 8.4.8.0'   # Fyber Marketplace SDK 8.4.8
  pod 'CloudXGoogleWaterfallAdapter', '~> 13.6.0.4' # Google Mobile Ads SDK 13.6.0
  pod 'CloudXPangleAdapter', '~> 8.2.0.7.0'         # Ads-Global(Pangle / 字节跳动)8.2.0.7
  pod 'CloudXTaurusXAdapter', '~> 1.18.2.0'         # TaurusX SDK 1.18.2
end

各适配器页面给出该网络自身的要求:iOS 与 Xcode 版本下限、Info.plist 配置项、SKAdNetwork 标识符以及已知问题。iOS 集成指南为原生应用列出的是同一批 pod,因此 Flutter 应用安装的内容与 iOS 应用完全一致。

适配器按 <网络 SDK 版本>.<适配器修订号> 独立编号,与 CloudXCore 各自演进,因此适配器版本无需与核心版本一致。SDK 至少需要一个适配器才能投放广告。

Google 适配器需要在 ios/Runner/Info.plist 中以 GADApplicationIdentifier 提供您的 AdMob 应用 ID。google_mobile_ads 包单独使用时同样需要该项,因此只要应用中包含其中之一就请添加:

<key>GADApplicationIdentifier</key>
<string>ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy</string>

然后安装 pod:

cd ios && pod install

App Transport Security

CloudX SDK 不需要禁用应用传输安全(App Transport Security)。如果某个聚合广告网络通过特定域名的纯 HTTP 提供素材,请在 ios/Runner/Info.plist 中为该域名单独配置例外,而不要在整个应用范围允许任意加载(全局 NSAllowsArbitraryLoads 会削弱应用的传输安全,并可能引起 App Store 审核关注):

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSExceptionDomains</key>
  <dict>
    <key>example-ad-network.com</key>
    <dict>
      <key>NSExceptionAllowsInsecureHTTPLoads</key>
      <true/>
      <key>NSIncludesSubdomains</key>
      <true/>
    </dict>
  </dict>
</dict>

Android 设置

插件已经引入 io.cloudx:sdk,因此您只需添加需要的广告网络适配器,在应用模块的 dependencies 块中按启用的广告网络逐个声明,通常是 android/app/build.gradle.kts。

dependencies {
    // CloudX 广告网络适配器(按需添加)
    implementation("io.cloudx:adapter-bigo:6.0.1.0")             // BIGO Ads SDK 6.0.1
    implementation("io.cloudx:adapter-digitalturbine:8.4.7.1")   // Digital Turbine Marketplace SDK 8.4.7
    implementation("io.cloudx:adapter-googlewaterfall:25.4.0.0") // Google Mobile Ads SDK 25.4.0
    implementation("io.cloudx:adapter-inmobi:11.4.0.1")          // InMobi SDK 11.4.0
    implementation("io.cloudx:adapter-magnite:1.0.0.1")          // Magnite Ads SDK 1.0.0
    implementation("io.cloudx:adapter-meta:6.22.0.0")            // Meta Audience Network 6.22.0
    implementation("io.cloudx:adapter-mintegral:17.1.71.1")      // Mintegral SDK 17.1.71
    implementation("io.cloudx:adapter-mobilefuse:1.12.0.0")      // MobileFuse SDK 1.12.0
    implementation("io.cloudx:adapter-moloco:4.11.0.0")          // Moloco SDK 4.11.0
    implementation("io.cloudx:adapter-pangle:8.2.0.4.0")         // Pangle SDK 8.2.0.4
    implementation("io.cloudx:adapter-taurusx:1.18.3.0")         // TaurusX SDK 1.18.3
    implementation("io.cloudx:adapter-unityads:4.19.0.1")        // Unity Ads SDK 4.19.0
    implementation("io.cloudx:adapter-verve:3.9.0.1")            // Verve HyBid SDK 3.9.0
    implementation("io.cloudx:adapter-vungle:7.7.7.0")           // Vungle SDK 7.7.7
}

各适配器页面给出该网络自身的要求:compileSdk 或 minSdk 下限、额外的 Maven 仓库、manifest 配置以及已知问题。Android 集成指南为原生应用列出的是同一批制品,因此 Flutter 应用声明的内容与 Android 应用完全一致。

SDK 至少需要一个适配器才能投放广告。适配器按 <网络 SDK 版本>.<适配器修订号> 独立编号,与核心 SDK 各自演进。

任一 Google 适配器都需要在 android/app/src/main/AndroidManifest.xml 中声明 Google 应用标识,接入 AdMob 需求的应用填入自己的 AdMob 应用 ID。即使没有安装 CloudX 的 Google 适配器,google_mobile_ads 包也需要该声明。下面两种形式都没有时,初始化会在 Google SDK 启动之前失败:

<application>
  <meta-data
      android:name="com.google.android.gms.ads.APPLICATION_ID"
      android:value="ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy" />
</application>

仅接入 Google Ad Manager 需求、没有 AdMob 应用 ID 的应用,改为声明布尔值 com.google.android.gms.ads.AD_MANAGER_APP。两者是二选一,请勿同时设置。

<application>
  <meta-data
      android:name="com.google.android.gms.ads.AD_MANAGER_APP"
      android:value="true" />
</application>

额外的 Maven 仓库

部分广告网络的 SDK 不在 Maven Central 上发布,对应适配器页面会给出需要添加的仓库地址。请按您的 Gradle 模板声明仓库的位置添加:项目级 android/build.gradle.kts 或 android/build.gradle 的 allprojects { repositories { ... } },或 android/settings.gradle.kts 或 android/settings.gradle 的 dependencyResolutionManagement.repositories。新建项目由 Flutter 生成 Kotlin DSL 文件名,较早的项目则是 Groovy 文件名。较新的模板会把仓库集中在 settings 文件中声明,此时项目级声明会被忽略或拒绝。

Mintegral、Verve、Pangle 与 TaurusX 适配器各需要一个。请添加到您的模板已有的那个块中,不要两处都加。Groovy 模板下的条目是同样的地址,写成 maven { url '...' }。

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        // io.cloudx:adapter-mintegral
        maven { url = uri("https://dl-maven-android.mintegral.com/repository/mbridge_android_sdk_oversea") }
        // io.cloudx:adapter-verve
        maven { url = uri("https://verve.jfrog.io/artifactory/verve-gradle-release") }
        // io.cloudx:adapter-pangle
        maven { url = uri("https://artifact.bytedance.com/repository/pangle") }
        // io.cloudx:adapter-taurusx
        maven { url = uri("https://artifact.taurusx.com/artifactory/taurusx-sdk/") }
    }
}

初始化

在加载任何广告之前初始化 SDK。通常放在主 Widget 的 initState 中:

import 'package:cloudx_flutter/cloudx.dart';

// 可选:启用详细日志(仅用于开发)
CloudX.setMinLogLevel(CloudXLogLevel.verbose);

// 使用您的应用密钥初始化
final result = await CloudX.initialize(appKey: 'YOUR_APP_KEY');
if (result.success) {
  print('CloudX initialized');
} else {
  print('CloudX init failed: ${result.errorCodeName} ${result.message}');
}

CloudX.initialize() 返回 CloudXInitializationResult。请检查 result.success。失败时,errorCode、errorCodeName 和 message 会带上原生 SDK 自身的错误信息,因此可以区分应用密钥错误与网络失败;成功时这三个字段均为 null。该调用不会抛出异常,重复调用会返回首次调用的结果。

其他初始化工具方法:

final initialized = await CloudX.isInitialized();
final version = await CloudX.getVersion();

广告格式

CloudX Flutter 支持横幅广告、MREC、插屏广告和激励视频广告。具体接入方式请参阅各格式指南:

广告信息(CloudXAd)

广告回调会收到一个 CloudXAd 对象:

属性类型说明
adUnitIdString广告单元 ID
adFormatCloudXAdFormat广告格式;用 adFormat.value 读取上报的名称
networkNameString竞得的广告网络名称
networkPlacementString?广告网络侧的广告位 ID
creativeIdString?用于素材级问题排查的素材标识符
placementString?您的集成设置的自定义广告位
revenuedouble收入(美元)
adValuesMap<String, String>SDK 提供的广告元数据,供 Trusted Arbiter 等功能使用

当需求方未返回素材 ID 时,creativeId 为 null。它与您在 reportRevenueData 中传入的 creativeId 不同,后者描述的是在 CloudX 广告流程之外竞得的展示。

CloudXAdFormat 包含 banner、mrec、interstitial 和 rewarded 四个常量。

高级功能

单次加载的额外参数

为单次广告加载附加广告网络或服务端配置。每种格式都有各自的设置方法,并通过广告单元 ID 定位:

CloudX.setBannerExtraParameter(
  adUnitId: bannerAdUnitId,
  key: 'requestId',
  value: 'request-456',
);
CloudX.setMrecExtraParameter(
  adUnitId: mrecAdUnitId,
  key: 'section',
  value: 'article_body',
);
CloudX.setInterstitialExtraParameter(
  adUnitId: interstitialAdUnitId,
  key: 'bidFloor',
  value: 0.5,
);
CloudX.setRewardedExtraParameter(
  adUnitId: rewardedAdUnitId,
  key: 'keywords',
  value: ['sports', 'scores'],
);

value 可以是 null、bool、int、double、String,或由这些类型构成的列表和映射。这些值在加载运行时被读取,因此请在加载之前设置;对同一个键再次设置会覆盖此前的值。

每种格式都可以在首次加载之前调用设置方法。在广告尚未创建时设置的值会按广告单元暂存,并在创建广告时、加载之前应用:插屏广告和激励视频广告对应首次 loadInterstitial 或 loadRewarded,横幅广告和 MREC 则对应 createBanner 或 createMrec,由创建发起的那次加载同样会带上这些值。暂存的值会在广告单元销毁时一并丢弃。

CloudX 会在请求导出的 extra_parameters 列中返回完整的参数集合。保留键 tags 同时用于基于标签的路由;关联用的元数据请使用其他键名。请勿包含原始个人数据、密钥或同意串。

只有当整个紧凑 JSON 对象不超过 256 个 UTF-8 字节时,CloudX 才会存储该参数集合。该限制对每个导出列分别生效,user_key_values 和 app_key_values 同样适用,并且计入所有键、值、引号、分隔符和大括号的合计大小,并非每个键值对可各用 256 字节。参数集合过大、格式错误,或隐私规则禁止持久化时,对应的导出单元格为空;竞价本身不受影响。

错误处理

所有错误回调都会收到带有 code 和 message 属性的 CloudXError:

范围类别常见错误码
0通用internalError
100-199网络networkError, networkTimeout, networkServerError, networkNoConnection
200-299初始化notInitialized, noAdaptersFound, sdkDisabled, invalidAppKey
300-399广告加载noFill, invalidAdUnit, adsDisabled, loadNotAllowedWhileShowing, loadFailed, loadRejectedConcurrency, loadRejectedTooManyConcurrentLoads
400-499展示adNotReady, adAlreadyShowing, dontKeepActivitiesEnabled, adNotFound, displayHostUnavailable
600-699适配器adapterNoFill, adapterLoadTimeout, adapterTimeout, adapterInitializationError

在同一格式的全屏广告展示期间发起的加载会被拒绝,错误码为 loadNotAllowedWhileShowing(303)。当某个广告单元已有加载在进行中时,新发起的加载也可能被拒绝,而两个平台对此使用不同的编号:Android 为 loadRejectedConcurrency(305),iOS 为 loadRejectedTooManyConcurrentLoads(306),且互不上报对方的值,因此两个都要处理。请对每个广告单元一次只发起一次加载,并等待回调。

CloudXError.codeName 会带上 SDK 为该错误码定义的名称,例如 NO_FILL,因此日志会显示为 NO_FILL[302] 而不是 302。当插件无法为该错误码命名时,它为 null。

完整错误码列表见 CloudXErrorCode。

收入追踪

所有广告格式都提供收入回调。CloudXAd 包含 adUnitId、adFormat、networkName、revenue(美元)、可选 placement 以及可选 networkPlacement:

CloudX.setInterstitialListener(CloudXInterstitialListener(
  onAdRevenuePaid: (ad) {
    trackRevenue(ad.revenue, ad.networkName, ad.adUnitId);
  },
  // ... 其他回调
));

发布商上报的收入数据

CloudX.reportRevenueData 会把其他中介平台向您的应用上报的展示收入转发给 CloudX,使 CloudX 了解该需求方的实际价格。AdMob 和 Ad Manager 的 Trusted Arbiter 出价依赖这些历史数据来定价,因此该上报是必需的。

final accepted = await CloudX.reportRevenueData(CloudXRevenueData(
  platform: CloudXRevenuePlatform.adMob,
  revenue: 0.0123,
  adFormat: 'interstitial',
  currencyCode: 'USD',
  precision: CloudXRevenuePrecision.exact,
  networkName: 'admob',
  adUnitId: 'ca-app-pub-0000000000000000/1111111111',
));
字段类型说明
platformCloudXRevenuePlatform必填。adMob、gam、inMobi、topOn,或 CloudXRevenuePlatform.custom('name')
revenuedouble必填。单次展示的收入,单位为 currencyCode
adFormatString必填。例如 banner、interstitial、rewarded
currencyCodeString?revenue 的 ISO 4217 货币代码,例如 USD
precisionCloudXRevenuePrecision?exact、estimated、publisherDefined 或 undefined
networkNameString?竞得的广告网络(如已知)
adUnitIdString?该中介平台的广告单元 ID;请上报您传给出价的同一个 ID
thirdPartyAdPlacementIdString?广告网络侧的广告位 ID
creativeIdString?广告网络的素材 ID
networkPlacementString?广告网络广告位标识
countryCodeString?用户所在国家,ISO 3166-1 alpha-2
userSegmentString?您自定义的用户分层字符串

当数据被丢弃时该调用返回 false,例如 SDK 尚未初始化、平台名称为空,或 revenue 不是有限数值。对于 Google 需求方,请将 PrecisionType.precise 映射为 exact,estimated 映射为 estimated,publisherProvided 映射为 publisherDefined,其余映射为 undefined。

Trusted Arbiter

CloudX.arbiter 会把已加载的 CloudX 广告与您自行接入平台的出价进行比较,并返回应当展示的平台。完整流程(包括何时运行以及如何把 Google 付费事件回传给 CloudX)参见 Trusted Arbiter。

用户定向

import 'package:cloudx_flutter/cloudx.dart';

// 设置哈希用户 ID(传 null 可清除)
CloudX.setHashedUserId('hashed-user-id');

// 设置自定义键值对
CloudX.setUserKeyValue('age_group', '25-34');
CloudX.setAppKeyValue('app_version', '1.0.0');

// 清除所有自定义键值对
CloudX.clearAllKeyValues();

哈希用户 ID 是由发布商提供的假名标识符。当适用的隐私信号允许时,CloudX 会在请求和展示活动导出中以 hashed_user_id 返回竞价时记录的值。使用 auction_id 关联这些导出。您可以使用该 ID 将 CloudX 活动与自己的用户数据分群关联。未设置 ID、隐私信号阻止持久化或值超过 128 个字符时,导出值为空。请勿传递未经哈希的个人数据。

身份透传

通过 setUserKeyValue 传入 UID 2.0、EUID、LiveRamp、ID5 和 Intent IQ。有值时设置一次,刷新后再设一次。

键传入内容
uidapi.comUID2 广告 token,不要传 refresh token。不要解密。UID2 文档
euid.euEUID 广告 token,不要传 refresh token。不要解密。EUID 文档
liveramp.comLiveRamp ATS 信封,不要传 RampID。LiveRamp 文档
id5-sync.comID5 通用 UID。不要传 0。ID5 文档
intentiq.comIntent IQ ID(IIQ ID),来自你的 Intent IQ 集成。Intent IQ 文档

CloudX 不会生成这些 ID。请先用 UID2、EUID、LiveRamp ATS、ID5 或 Intent IQ 生成,再把字符串传进来。这与哈希用户 ID 不是同一回事。

CloudX.setUserKeyValue('uidapi.com', uid2Token);
CloudX.setUserKeyValue('euid.eu', euidToken);
CloudX.setUserKeyValue('liveramp.com', liveRampEnvelope);
CloudX.setUserKeyValue('id5-sync.com', id5Id);
CloudX.setUserKeyValue('intentiq.com', intentIQId);

隐私合规

从 NSUserDefaults / SharedPreferences 读取 IAB GPP、TCF v2 和 US Privacy 字符串(通常由 CMP 写入)。

键标准说明
IABGPP_HDR_GppStringGPP全球隐私平台字符串
IABGPP_GppSIDGPP部分 ID
IABTCF_TCStringTCF v2GDPR 同意字符串
IABUSPrivacy_StringUS PrivacyCCPA 字符串

手动 API: 当您不使用 CMP,或需要覆盖直到清除时,使用 CloudX.setHasUserConsent(bool?) 和 CloudX.setDoNotSell(bool?)。null 会移除覆盖;解析顺序与原生 SDK 一致(已存储的 IAB 字符串以及这些覆盖)。可在 CloudX.initialize() 之前调用。

CloudX.setHasUserConsent(true);
CloudX.setDoNotSell(false);
CloudX.setHasUserConsent(null);

测试模式

测试模式由服务端控制,通过设备白名单实现:

  1. 启用详细日志后初始化 SDK
  2. 在控制台日志中找到设备广告 ID
  3. 在 CloudX 控制台将该设备加入白名单

调试日志

import 'package:cloudx_flutter/cloudx.dart';

// 启用详细日志(在 initialize 之前调用)
CloudX.setMinLogLevel(CloudXLogLevel.verbose);

// 可用级别:verbose、debug、info、warn、error、none

技术支持

如需支持,请联系 support@cloudx.io