Overview
Overview of CloudX Flutter SDK setup and core features
The CloudX Flutter SDK enables monetization of your Flutter apps with banner, MREC, interstitial, and rewarded ads on iOS and Android.
Installation
Requirements
| Requirement | Version |
|---|---|
| Dart SDK | >=2.17.1 <4.0.0 |
| Flutter | >=3.0.0 |
| iOS | 13.0+, Xcode 16.0+, Swift 6.0+ |
| Android | minSdk 23 |
The Dart range is intentionally wide so apps on older Flutter LTS lines can add cloudx_flutter without upgrading the whole toolchain. Your app’s own iOS floor is whichever is higher, the plugin’s or your Flutter version’s: Flutter 3.47 ships an engine that requires iOS 15.0, so an app on that line sets platform :ios, '15.0' in its Podfile.
Individual adapters raise these floors: some need a higher compileSdk, minSdk, Kotlin, or Xcode version than the plugin itself, and each adapter page states its own. Current Flutter releases clear most of them, since Flutter 3.47 enforces Gradle 8.14, AGP 8.11.1, and Kotlin 2.2.20. Check the pages for the adapters you enable before setting your floors.
Add the SDK to your pubspec.yaml:
dependencies:
cloudx_flutter: ^3.9.0Then run:
flutter pub getNative SDK versions
The plugin pins one native CloudX SDK per platform. Its own version follows the iOS CloudXCore line; the Android pin is listed per release in the changelog.
| Platform | Dependency | Version |
|---|---|---|
| Android | io.cloudx:sdk | 4.7.0 |
| iOS | CloudXCore | 3.9.0 |
iOS Setup
The CloudXCore pod is included automatically through the plugin’s podspec. Ad network adapters are separate pods that you add to your ios/Podfile, inside the target 'Runner' block, one per network you enable.
target 'Runner' do
# ... existing Flutter config ...
# CloudX ad network adapters (add as needed)
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 / ByteDance) 8.2.0.7
pod 'CloudXTaurusXAdapter', '~> 1.18.2.0' # TaurusX SDK 1.18.2
endEach adapter page carries that network’s own requirements: its iOS and Xcode floors, Info.plist entries, SKAdNetwork identifiers, and any known issues. The iOS integration guide lists the same pods for a native app, so a Flutter app installs exactly what an iOS app does.
Adapters are versioned <network-sdk-version>.<adapter-revision> and move independently of CloudXCore, so an adapter version never has to match the core version. At least one adapter is required for the SDK to serve ads.
The Google adapter needs your AdMob app ID as GADApplicationIdentifier in ios/Runner/Info.plist. So does the google_mobile_ads package on its own, so add this whenever your app carries either one:
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy</string>Then install pods:
cd ios && pod installApp Transport Security
The CloudX SDK does not require disabling App Transport Security. If a
mediated network serves assets over plain HTTP from a specific host, scope an
exception to that domain in your ios/Runner/Info.plist instead of allowing arbitrary
loads app-wide (a blanket NSAllowsArbitraryLoads weakens your app’s
transport security and can draw App Store review scrutiny):
<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 Setup
The plugin pulls in io.cloudx:sdk, so you only add the ad network adapters you need. Declare them in your app module’s dependencies block, typically android/app/build.gradle.kts, one per network you enable.
dependencies {
// CloudX ad network adapters (add as needed)
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
}Each adapter page carries that network’s own requirements: its compileSdk or minSdk floors, any extra Maven repository, manifest entries, and known issues. The Android integration guide lists the same artifacts for a native app, so a Flutter app declares exactly what an Android app does.
At least one adapter is required for the SDK to serve ads. Adapters are versioned <network-sdk-version>.<adapter-revision> and move independently of the core SDK.
Either Google adapter needs your Google app identification in android/app/src/main/AndroidManifest.xml, and an app taking AdMob demand declares its AdMob app ID. The google_mobile_ads package needs it too, with no CloudX Google adapter installed. Without one of the two forms below, initialization fails before the Google SDK starts:
<application>
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy" />
</application>An app taking Google Ad Manager demand only, with no AdMob app ID, declares the boolean com.google.android.gms.ads.AD_MANAGER_APP instead. The two forms are alternatives; do not set both.
<application>
<meta-data
android:name="com.google.android.gms.ads.AD_MANAGER_APP"
android:value="true" />
</application>Extra Maven repositories
Some networks publish their SDKs outside Maven Central, and their adapter pages give the repository URL to add. Declare those repositories wherever your Gradle template keeps them: allprojects { repositories { ... } } in the project-level android/build.gradle.kts or android/build.gradle, or dependencyResolutionManagement.repositories in android/settings.gradle.kts or android/settings.gradle. Flutter generates the Kotlin DSL names for new projects and the Groovy names for older ones. Newer templates centralize the repositories in the settings file, where a project-level declaration is ignored or rejected.
The Mintegral, Verve, Pangle, and TaurusX adapters each need one. Add them to the block your template already has, not to both. On a Groovy template the entries are the same URLs written as 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/") }
}
}allprojects {
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/") }
}
}Initialization
Initialize the SDK before loading any ads. Typically in your main widget’s initState:
import 'package:cloudx_flutter/cloudx.dart';
// Optional: enable verbose logging (development only)
CloudX.setMinLogLevel(CloudXLogLevel.verbose);
// Initialize with your app key
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() returns a CloudXInitializationResult. Check result.success. On failure errorCode, errorCodeName and message carry the native SDK’s own error, so a bad app key is distinguishable from a network failure; all three are null on success. The call never throws, and a second call returns the first call’s outcome.
Other initialization utilities:
final initialized = await CloudX.isInitialized();
final version = await CloudX.getVersion();Ad Formats
CloudX Flutter supports banner, MREC, interstitial, and rewarded ads. Use the format guides for implementation details:
Banner & MREC ads
Programmatic overlays at a fixed screen position, with refresh control.
Interstitial ads
Load and show full-screen interstitial placements.
Rewarded ads
Reward users after a completed rewarded ad view.
Ad Information (CloudXAd)
Ad callbacks receive a CloudXAd:
| Property | Type | Description |
|---|---|---|
adUnitId | String | The ad unit ID |
adFormat | CloudXAdFormat | The ad format; read adFormat.value for the reported name |
networkName | String | Name of the winning ad network |
networkPlacement | String? | Network-specific placement ID |
creativeId | String? | Creative identifier for creative-level issue reporting |
placement | String? | Custom placement set by your integration |
revenue | double | Revenue value in USD |
adValues | Map<String, String> | SDK-provided ad metadata, used by features such as Trusted Arbiter |
creativeId is null when the demand source does not report a creative id. It is distinct from the creativeId you pass in reportRevenueData, which describes an impression won outside the CloudX ad flow.
CloudXAdFormat carries the constants banner, mrec, interstitial, and rewarded.
Advanced Features
Per-Load Extra Parameters
Attach network or server-side configuration to a single ad load. Each format has its own setter, addressed by ad unit 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'],
);A value may be null, a bool, an int, a double, a String, or a list or map of those. Values are read when the load runs, so set them before it, and setting the same key again replaces the previous value.
Every format accepts the setter before its first load. A value set before the ad exists is held for that ad unit and applied when the ad is created, ahead of the load: for interstitial and rewarded that is the first loadInterstitial or loadRewarded, and for banner and MREC it is createBanner or createMrec, whose own load carries it too. Held values are dropped when the ad unit is destroyed.
CloudX returns the complete bag in the extra_parameters request-export column. The reserved tags key also controls tag-based routing; use another key for correlation metadata. Do not include raw personal data, secrets, or consent strings.
CloudX stores the bag only when the entire compact JSON object is at most 256 UTF-8 bytes. The limit is independent for each export column, user_key_values and app_key_values included, and counts all combined keys, values, quotes, separators and braces, not 256 bytes per key-value pair. A bag that is oversized or malformed, or one that privacy rules prohibit persisting, leaves its export cell empty; the auction itself is unaffected.
Error Handling
All error callbacks receive a CloudXError with code and message properties:
| Range | Category | Common Codes |
|---|---|---|
| 0 | General | internalError |
| 100-199 | Network | networkError, networkTimeout, networkServerError, networkNoConnection |
| 200-299 | Initialization | notInitialized, noAdaptersFound, sdkDisabled, invalidAppKey |
| 300-399 | Ad Loading | noFill, invalidAdUnit, adsDisabled, loadNotAllowedWhileShowing, loadFailed, loadRejectedConcurrency, loadRejectedTooManyConcurrentLoads |
| 400-499 | Display | adNotReady, adAlreadyShowing, dontKeepActivitiesEnabled, adNotFound, displayHostUnavailable |
| 600-699 | Adapter | adapterNoFill, adapterLoadTimeout, adapterTimeout, adapterInitializationError |
A load issued while a fullscreen ad of that format is on screen is rejected with loadNotAllowedWhileShowing (303). A load issued for an ad unit that already has one in flight can be rejected as well, and the two platforms number that differently: Android sends loadRejectedConcurrency (305) and iOS sends loadRejectedTooManyConcurrentLoads (306), neither reports the other’s value, so handle both. Load one ad at a time per ad unit and wait for its callback.
CloudXError.codeName carries the SDK’s own name for the code, such as NO_FILL, so a log line reads NO_FILL[302] rather than 302. It is null when the SDK reported a code the plugin cannot name.
See CloudXErrorCode for the full list of error codes.
Revenue Tracking
All ad formats provide revenue callbacks. CloudXAd includes adUnitId, adFormat, networkName, revenue (USD), optional placement, and optional networkPlacement:
CloudX.setInterstitialListener(CloudXInterstitialListener(
onAdRevenuePaid: (ad) {
trackRevenue(ad.revenue, ad.networkName, ad.adUnitId);
},
// ... other callbacks
));Publisher-Reported Revenue Data
CloudX.reportRevenueData forwards impression revenue that another mediation platform reported to your app, so CloudX learns what that demand actually pays. It is required for AdMob and Ad Manager Trusted Arbiter bids, which CloudX prices from that history.
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',
));| Field | Type | Description |
|---|---|---|
platform | CloudXRevenuePlatform | Required. adMob, gam, inMobi, topOn, or CloudXRevenuePlatform.custom('name') |
revenue | double | Required. Revenue for one impression, in currencyCode units |
adFormat | String | Required. For example banner, interstitial, rewarded |
currencyCode | String? | ISO 4217 code of revenue, for example USD |
precision | CloudXRevenuePrecision? | exact, estimated, publisherDefined, or undefined |
networkName | String? | Winning ad network, when known |
adUnitId | String? | The mediation platform’s ad unit id; report the id you passed to the arbiter bid |
thirdPartyAdPlacementId | String? | Network-side placement id |
creativeId | String? | Creative id from the network |
networkPlacement | String? | Network placement identifier |
countryCode | String? | User country, ISO 3166-1 alpha-2 |
userSegment | String? | Your own segment string |
The call returns false when the data was dropped, for example when the SDK is not initialized, the platform name is blank, or revenue is not a finite number. For Google demand, map PrecisionType.precise to exact, estimated to estimated, publisherProvided to publisherDefined, and anything else to undefined.
Trusted Arbiter
CloudX.arbiter compares a loaded CloudX ad with bids from platforms you mediate yourself and returns the platform to show. See Trusted Arbiter for the full flow, including when to run it and how to report Google paid events back to CloudX.
User Targeting
import 'package:cloudx_flutter/cloudx.dart';
// Set hashed user ID (pass null to clear)
CloudX.setHashedUserId('hashed-user-id');
// Set custom key-value pairs
CloudX.setUserKeyValue('age_group', '25-34');
CloudX.setAppKeyValue('app_version', '1.0.0');
// Clear all custom key-values
CloudX.clearAllKeyValues();The hashed user ID is a publisher-provided pseudonymous identifier. When applicable privacy signals allow it, CloudX captures the value at auction time and returns it as hashed_user_id in the request and impression activity exports. Use auction_id to join the exports. Use the ID to join CloudX activity to cohorts in your own user data. The export value is empty when no ID was set, persistence was suppressed by privacy signals, or the value exceeded 128 characters. Do not pass raw personal data.
Identity passthrough
Pass UID 2.0, EUID, LiveRamp, ID5, and Intent IQ with setUserKeyValue. Set each value when you have it, and again when it refreshes.
| Key | Pass this |
|---|---|
uidapi.com | UID2 advertising token, not the refresh token. Do not decrypt it. UID2 docs |
euid.eu | EUID advertising token, not the refresh token. Do not decrypt it. EUID docs |
liveramp.com | LiveRamp ATS envelope, not a RampID. LiveRamp docs |
id5-sync.com | ID5 universal UID. Do not pass 0. ID5 docs |
intentiq.com | Intent IQ ID (IIQ ID) from your Intent IQ integration. Intent IQ docs |
CloudX does not generate these IDs. Create them with UID2, EUID, LiveRamp ATS, ID5, or Intent IQ, then pass the string. This is separate from hashed user 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);Privacy Compliance
Reads IAB GPP, TCF v2, and US Privacy strings from NSUserDefaults / SharedPreferences (typically set by a CMP).
| Key | Standard | Description |
|---|---|---|
IABGPP_HDR_GppString | GPP | Global Privacy Platform string |
IABGPP_GppSID | GPP | Section IDs |
IABTCF_TCString | TCF v2 | GDPR consent string |
IABUSPrivacy_String | US Privacy | CCPA string |
Manual APIs: CloudX.setHasUserConsent(bool?) and CloudX.setDoNotSell(bool?) when you do not use a CMP, or to override until cleared. null removes the override; resolution order matches the native SDK (stored IAB strings and these overrides). Callable before CloudX.initialize().
CloudX.setHasUserConsent(true);
CloudX.setDoNotSell(false);
CloudX.setHasUserConsent(null);Test Mode
Test mode is server-controlled via device whitelisting:
- Initialize the SDK with verbose logging enabled
- Find your device advertising ID in the console logs
- Add the device to your whitelist on the CloudX dashboard
Debug Logging
import 'package:cloudx_flutter/cloudx.dart';
// Enable verbose logging (call before initialize)
CloudX.setMinLogLevel(CloudXLogLevel.verbose);
// Available levels: verbose, debug, info, warn, error, noneSupport
For support, contact support@cloudx.io