Overview

Overview of CloudX Flutter SDK setup and core features

pub package

The CloudX Flutter SDK enables monetization of your Flutter apps with banner, MREC, interstitial, and rewarded ads on iOS and Android.

Installation

Requirements

RequirementVersion
Dart SDK>=2.17.1 <4.0.0
Flutter>=3.0.0
iOS13.0+, Xcode 16.0+, Swift 6.0+
AndroidminSdk 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.0

Then run:

flutter pub get

Native 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.

PlatformDependencyVersion
Androidio.cloudx:sdk4.7.0
iOSCloudXCore3.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
end

Each 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 install

App 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/") }
    }
}

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:

Ad Information (CloudXAd)

Ad callbacks receive a CloudXAd:

PropertyTypeDescription
adUnitIdStringThe ad unit ID
adFormatCloudXAdFormatThe ad format; read adFormat.value for the reported name
networkNameStringName of the winning ad network
networkPlacementString?Network-specific placement ID
creativeIdString?Creative identifier for creative-level issue reporting
placementString?Custom placement set by your integration
revenuedoubleRevenue value in USD
adValuesMap<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:

RangeCategoryCommon Codes
0GeneralinternalError
100-199NetworknetworkError, networkTimeout, networkServerError, networkNoConnection
200-299InitializationnotInitialized, noAdaptersFound, sdkDisabled, invalidAppKey
300-399Ad LoadingnoFill, invalidAdUnit, adsDisabled, loadNotAllowedWhileShowing, loadFailed, loadRejectedConcurrency, loadRejectedTooManyConcurrentLoads
400-499DisplayadNotReady, adAlreadyShowing, dontKeepActivitiesEnabled, adNotFound, displayHostUnavailable
600-699AdapteradapterNoFill, 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',
));
FieldTypeDescription
platformCloudXRevenuePlatformRequired. adMob, gam, inMobi, topOn, or CloudXRevenuePlatform.custom('name')
revenuedoubleRequired. Revenue for one impression, in currencyCode units
adFormatStringRequired. For example banner, interstitial, rewarded
currencyCodeString?ISO 4217 code of revenue, for example USD
precisionCloudXRevenuePrecision?exact, estimated, publisherDefined, or undefined
networkNameString?Winning ad network, when known
adUnitIdString?The mediation platform’s ad unit id; report the id you passed to the arbiter bid
thirdPartyAdPlacementIdString?Network-side placement id
creativeIdString?Creative id from the network
networkPlacementString?Network placement identifier
countryCodeString?User country, ISO 3166-1 alpha-2
userSegmentString?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.

KeyPass this
uidapi.comUID2 advertising token, not the refresh token. Do not decrypt it. UID2 docs
euid.euEUID advertising token, not the refresh token. Do not decrypt it. EUID docs
liveramp.comLiveRamp ATS envelope, not a RampID. LiveRamp docs
id5-sync.comID5 universal UID. Do not pass 0. ID5 docs
intentiq.comIntent 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).

KeyStandardDescription
IABGPP_HDR_GppStringGPPGlobal Privacy Platform string
IABGPP_GppSIDGPPSection IDs
IABTCF_TCStringTCF v2GDPR consent string
IABUSPrivacy_StringUS PrivacyCCPA 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:

  1. Initialize the SDK with verbose logging enabled
  2. Find your device advertising ID in the console logs
  3. 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, none

Support

For support, contact support@cloudx.io