cloudx_flutter 3.9.0 copy "cloudx_flutter: ^3.9.0" to clipboard
cloudx_flutter: ^3.9.0 copied to clipboard

Complete Flutter SDK wrapper for CloudX Core with targeting APIs and full ad lifecycle callbacks

CloudX Flutter SDK #

pub package GitHub

A Flutter plugin for the CloudX Mobile Ads platform. Monetize your Flutter apps with banner, MREC, interstitial, and rewarded ads on Android and iOS.

Installation #

Requires Flutter 3.0.0+, Dart 2.17.1+, Android API 23+, and iOS 13.0+. Your app's 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, which is why the apps in this repo set platform :ios, '15.0'.

Add this 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 alongside it in the changelog.

Platform Dependency Version
Android io.cloudx:sdk 4.7.0
iOS CloudXCore 3.9.0

Ad-network adapters are not bundled. Add the ones you need to your host app; each adapter is versioned as <network-sdk-version>.<adapter-revision> and moves independently of the core SDK. The versions below are the ones the CloudX docs publish for the pinned cores.

Android Setup #

The plugin pulls in io.cloudx:sdk, which requires compileSdk 36 and Kotlin 2.1 or newer in the host app. Minimum SDK is API 23. Current Flutter releases raise the floor further (Flutter 3.47 enforces Gradle 8.14, AGP 8.11.1 and Kotlin 2.2.20); the demo app in this repository builds with those versions.

Add ad network adapters to your app's android/app/build.gradle.kts:

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

AdMob Estimated demand is available on Android only. Use exactly one Google adapter per app: adapter-googlewaterfall (Google Mobile Ads SDK, above) or adapter-admob (io.cloudx:adapter-admob:1.3.0.4, GMA Next-Gen SDK). They ship the same classes and cannot be co-installed. Either Google adapter needs your AdMob app ID in AndroidManifest.xml:

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

Mintegral, Verve, Pangle and TaurusX ship their network SDKs outside Maven Central. Add the repositories you need to the project-level android/build.gradle.kts:

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

iOS Setup #

The CloudXCore pod is included automatically through the plugin's podspec.

Add ad network adapter pods to your app's ios/Podfile inside the target 'Runner' do block:

target 'Runner' do
  use_frameworks! :linkage => :static
  use_modular_headers!

  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))

  # Adapters for ad networks (add as needed)
  pod 'CloudXMetaAdapter', '~> 6.22.0.0'            # Meta Audience Network 6.22.0
  pod 'CloudXVungleAdapter', '~> 7.7.6.0'           # Vungle SDK 7.7.6
  pod 'CloudXInMobiAdapter', '~> 11.4.1.0'          # InMobi SDK 11.4.1
  pod 'CloudXMintegralAdapter', '~> 8.1.6.0'        # Mintegral SDK 8.1.6
  pod 'CloudXUnityAdsAdapter', '~> 4.19.0.0'        # Unity Ads SDK 4.19.0
  pod 'CloudXMagniteAdapterV2', '~> 1.0.0.1'        # Magnite SDK 1.0.0
  pod 'CloudXMobileFuseAdapter', '~> 1.11.0.1'      # MobileFuse SDK 1.11.0
  pod 'CloudXMolocoAdapter', '~> 4.8.0.0'           # Moloco SDK 4.8.0
  pod 'CloudXVerveAdapter', '~> 3.9.0.0'            # Verve HyBid SDK 3.9.0
  pod 'CloudXDigitalTurbineAdapter', '~> 8.4.8.0'   # Digital Turbine 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'         # Pangle SDK 8.2.0.7
  pod 'CloudXTaurusXAdapter', '~> 1.18.2.0'         # TaurusX SDK 1.18.2
end

CloudXGoogleWaterfallAdapter needs your AdMob app ID as GADApplicationIdentifier in ios/Runner/Info.plist, and every network benefits from the SKAdNetworkItems list the CloudX iOS integration guide publishes.

Then run cd ios && pod install.

Initialization #

Initialize CloudX before creating any ads:

import 'package:cloudx_flutter/cloudx.dart';

// Optional: Enable verbose logging (development only)
CloudX.setMinLogLevel(CloudXLogLevel.verbose);

// Initialize the SDK
final result = await CloudX.initialize(
  appKey: 'YOUR_APP_KEY',
);

if (result.success) {
  print('CloudX SDK initialized successfully');
} else {
  print('Failed to initialize CloudX SDK: ${result.errorCode} ${result.message}');
}

Ad Integration #

Banners use the programmatic overlay approach — they overlay your content at a fixed screen position and stay in place while content scrolls underneath.

const adUnitId = 'home_banner';

// Set up listener before creating ads
CloudX.setBannerListener(CloudXAdViewListener(
  onAdLoaded: (ad) => print('Banner loaded from ${ad.networkName}'),
  onAdLoadFailed: (adUnitId, error) => print('Failed: ${error.message}'),
  onAdClicked: (ad) => print('Clicked'),
  onAdRevenuePaid: (ad) => print('Revenue: ${ad.revenue}'),  // optional
));

// Create and show
CloudX.createBanner(
  adUnitId: adUnitId,
  position: CloudXAdViewPosition.bottomCenter,
);
CloudX.showBanner(adUnitId: adUnitId);

// Hide when needed
CloudX.hideBanner(adUnitId: adUnitId);

// Always destroy when done (e.g., in dispose)
CloudX.destroyBanner(adUnitId: adUnitId);

Banner ads auto-refresh by default. To control refresh manually:

CloudX.stopBannerAutoRefresh(adUnitId: adUnitId);
CloudX.loadBanner(adUnitId: adUnitId);         // Manually load a new ad
CloudX.startBannerAutoRefresh(adUnitId: adUnitId); // Re-enable auto-refresh

Optional placement and custom data for tracking:

CloudX.setBannerPlacement(adUnitId: adUnitId, placement: 'home_screen');
CloudX.setBannerCustomData(adUnitId: adUnitId, customData: 'level:5,coins:100');

Position is fixed when the banner is created. To move one, destroy it and create it again at the new position; that starts a new auction.

You can attach extra parameters to the auction. Set them before the load, and before createBanner to reach the first auction:

CloudX.setBannerExtraParameter(
  adUnitId: adUnitId,
  key: 'requestId',
  value: 'request-456',
);

A value stays on the ad until you set the same key again or clear it, so it applies to later loads and to auto-refresh auctions, not only the next one. Pass value: null to clear a key.

The same setter exists for every format: setMrecExtraParameter, setInterstitialExtraParameter and setRewardedExtraParameter.

MREC Ads (300x250) #

MREC ads work identically to banners but with a larger size. Set up a separate listener with CloudX.setMrecListener(...).

const adUnitId = 'home_mrec';

CloudX.setMrecListener(CloudXAdViewListener(
  onAdLoaded: (ad) => print('MREC loaded from ${ad.networkName}'),
  onAdLoadFailed: (adUnitId, error) => print('Failed: ${error.message}'),
  onAdClicked: (ad) => print('Clicked'),
  onAdRevenuePaid: (ad) => print('Revenue: ${ad.revenue}'),  // optional
));

CloudX.createMrec(
  adUnitId: adUnitId,
  position: CloudXAdViewPosition.centered,
);
CloudX.showMrec(adUnitId: adUnitId);

// Always destroy when done (e.g., in dispose)
CloudX.destroyMrec(adUnitId: adUnitId);

MREC ads also auto-refresh by default. Use the same refresh control methods as banner ads.

Widget-Based Ads (Inline) - not public #

The CloudXAdView widget embedded an ad in the Flutter widget tree so it scrolled with content. It is no longer exported from cloudx.dart and is not part of the public API. A widget ad accepts only the ad unit, the format and the auto-refresh flag, so placement, custom data and extra parameters cannot be set on one, and the setBanner... setters do not reach it either. Use the positioned createBanner / createMrec APIs above.

The code is still in the package and both native bridges still register the platform view, so the sections below describe what it does today. If you need inline ads, ask us and we will finish it.

Nothing below compiles from the public import alone. cloudx.dart does not export the widget, so reaching it means importing the unexported file directly, which is unsupported and can change or disappear in any release:

// Unsupported: not exported from package:cloudx_flutter/cloudx.dart
import 'package:cloudx_flutter/cloudx_ad_view.dart';

CloudXAdView(
  adUnitId: 'feed_banner',
  adFormat: CloudXAdFormat.banner,  // or CloudXAdFormat.mrec
  listener: CloudXAdViewListener(
    onAdLoaded: (ad) => print('Loaded from ${ad.networkName}'),
    onAdLoadFailed: (adUnitId, error) => print('Failed: ${error.message}'),
    onAdClicked: (ad) => print('Clicked'),
    onAdRevenuePaid: (ad) => print('Revenue: ${ad.revenue}'),
  ),
)

Key differences from programmatic overlay ads:

  • Embeds inline in the widget tree (scrolls with content)
  • Each widget has its own per-instance listener (not global)
  • Auto-loads on creation
  • Automatically destroyed when the widget is removed from the tree

Auto-refresh is enabled by default. Control it via the isAutoRefreshEnabled parameter:

CloudXAdView(
  adUnitId: 'feed_banner',
  adFormat: CloudXAdFormat.banner,
  isAutoRefreshEnabled: false,  // disable auto-refresh
)

Changing isAutoRefreshEnabled dynamically (e.g., via setState) will start or stop auto-refresh without recreating the ad view.

Interstitial Ads #

Full-screen ads shown at natural transition points.

Note: Interstitials don't need a create call. The loadInterstitial method auto-creates the instance.

const adUnitId = 'level_complete';

CloudX.setInterstitialListener(CloudXInterstitialListener(
  onAdLoaded: (ad) => print('Interstitial loaded'),
  onAdLoadFailed: (adUnitId, error) => print('Failed: ${error.message}'),
  onAdDisplayed: (ad) => print('Displayed'),
  onAdDisplayFailed: (ad, error) => print('Display failed: ${error.message}'),
  onAdClicked: (ad) => print('Clicked'),
  onAdHidden: (ad) {
    print('Hidden');
    // Reload for next use
    CloudX.loadInterstitial(adUnitId: adUnitId);
  },
  onAdRevenuePaid: (ad) => print('Revenue: ${ad.revenue}'),  // optional
));

// Load
CloudX.loadInterstitial(adUnitId: adUnitId);

// Check if ready before showing
final isReady = await CloudX.isInterstitialReady(adUnitId: adUnitId);
if (isReady) {
  CloudX.showInterstitial(adUnitId: adUnitId);

  // Or with optional placement and custom data for tracking
  // CloudX.showInterstitial(adUnitId: adUnitId, placement: 'level_complete', customData: 'level:5');
}

// Always destroy when done (e.g., in dispose)
CloudX.destroyInterstitial(adUnitId: adUnitId);

Rewarded Ads #

Full-screen ads that grant users a reward upon completion.

Note: Like interstitials, rewarded ads don't need a create call. The loadRewarded method auto-creates the instance.

const adUnitId = 'rewarded_coins';

CloudX.setRewardedListener(CloudXRewardedListener(
  onAdLoaded: (ad) => print('Rewarded loaded'),
  onAdLoadFailed: (adUnitId, error) => print('Failed: ${error.message}'),
  onAdDisplayed: (ad) => print('Displayed'),
  onAdDisplayFailed: (ad, error) => print('Display failed: ${error.message}'),
  onAdClicked: (ad) => print('Clicked'),
  onAdHidden: (ad) {
    print('Hidden');
    // Reload for next use
    CloudX.loadRewarded(adUnitId: adUnitId);
  },
  onAdReceivedReward: (ad, reward) {
    print('Earned ${reward.amount} ${reward.label}');
    // Grant the reward to the user
  },
  onAdRevenuePaid: (ad) => print('Revenue: ${ad.revenue}'),  // optional
));

// Load
CloudX.loadRewarded(adUnitId: adUnitId);

// Check if ready before showing
final isReady = await CloudX.isRewardedReady(adUnitId: adUnitId);
if (isReady) {
  CloudX.showRewarded(adUnitId: adUnitId);

  // Or with optional placement and custom data for tracking
  // CloudX.showRewarded(adUnitId: adUnitId, placement: 'bonus_coins', customData: 'level:5');
}

// Always destroy when done (e.g., in dispose)
CloudX.destroyRewarded(adUnitId: adUnitId);

Ad Information (CloudXAd) #

The CloudXAd object is passed to listener callbacks and contains information about the loaded/displayed ad:

Property Type Description
adFormat CloudXAdFormat Ad format. Not an enum: the constants are the formats this plugin can load (banner, mrec, interstitial, rewarded), and a format the native SDKs report without a constant here (for example native, app_open) arrives carrying its reported value.
adUnitId String The ad unit ID
networkName String Name of the winning ad network
networkPlacement String? Network-specific placement ID
placement String? Custom placement set via setPlacement()
revenue double Impression-level revenue in USD
onAdLoaded: (ad) {
  print('Ad format: ${ad.adFormat}');
  print('Network: ${ad.networkName}');
  print('Revenue: ${ad.revenue}');
},

Error Handling #

All SDK errors are returned as CloudXError objects in listener callbacks:

Property Type Description
code int Error code (see categories below)
codeName String? The SDK's own name for the code, such as NO_FILL
message String Human-readable description

Error Code Categories

Range Category Common Codes
0 General internalError
100-199 Network networkError, networkTimeout, networkServerError, networkNoConnection
200-299 Initialization notInitialized, sdkDisabled, noAdaptersFound, invalidAppKey
300-399 Ad Loading noFill, invalidAdUnit, adsDisabled, loadFailed, loadNotAllowedWhileShowing, loadRejectedConcurrency (Android) / loadRejectedTooManyConcurrentLoads (iOS)
400-499 Display adNotReady, adAlreadyShowing, adNotFound, displayHostUnavailable
600-699 Adapter adapterNoFill, adapterTimeout, adapterLoadTimeout, adapterInitializationError

See CloudXErrorCode for the full list of error codes.

Advanced Features #

Debug Logging #

// Enable verbose logging (call before initialize)
CloudX.setMinLogLevel(CloudXLogLevel.verbose);

// Only enable during development
if (kDebugMode) {
  CloudX.setMinLogLevel(CloudXLogLevel.verbose);
}

Impression-Level Revenue Tracking #

Use the optional onAdRevenuePaid callback on any listener to receive impression-level revenue (ILR) data. The CloudXAd object contains the revenue value in USD and the winning network name.

onAdRevenuePaid: (ad) {
  print('Revenue: ${ad.revenue} from ${ad.networkName}');
},

Works with all ad formats (banner, MREC, interstitial, rewarded) and widget-based ads.

Trusted Arbiter (TPA) #

Trusted Arbiter compares a loaded CloudX ad with bids from other platforms you mediate yourself and returns the platform to show. Load every candidate in parallel, build one CloudXArbiterBid per loaded ad, run the arbiter once they have all settled (loaded or failed), store the result, and show the stored winner at the placement without another network call. Never run the arbiter on the show path.

CloudXArbiterResult? nextWinner;

// Runs once both candidates have loaded, ahead of the placement.
Future<void> prepareWinner(CloudXAd cloudXAd, String adMobAdUnitId) async {
  nextWinner = await CloudX.arbiter(CloudXArbiterConfiguration(bids: [
    CloudXArbiterBid.cloudX(cloudXAd), // the CloudXAd from onAdLoaded, unchanged
    CloudXArbiterBid.adMob(adUnitId: adMobAdUnitId),
  ]));
}

// Runs at the placement. No network call here.
Future<void> showInterstitial(String cloudXAdUnitId) async {
  final winner = nextWinner?.platform;
  nextWinner = null;
  if (winner == CloudXArbiterPlatform.cloudX) {
    CloudX.showInterstitial(adUnitId: cloudXAdUnitId);
  } else if (winner == CloudXArbiterPlatform.adMob) {
    // show your AdMob interstitial
  } // CloudXArbiterPlatform.none: no winner, carry on without an ad
}

The native SDK owns the timeout and the fallback and always completes: a single bid wins without a service call, and on timeout, error or an unavailable service the highest locally comparable USD bid wins. Do not wrap the call in your own timer or compare revenue yourself. Bids are also available for LevelPlay (CloudXArbiterBid.levelPlay), PubMatic (.pubMatic), Google Ad Manager (.gam) and any other mediator (.custom).

AdMob and Ad Manager bids carry no price. CloudX prices them from the revenue you forward after each Google impression, so forwarding Google's paid events is a required part of the integration. With google_mobile_ads, onPaidEvent reports valueMicros on both platforms:

ad.onPaidEvent = (ad, valueMicros, precision, currencyCode) {
  CloudX.reportRevenueData(CloudXRevenueData(
    platform: CloudXRevenuePlatform.adMob, // .gam for Ad Manager
    revenue: valueMicros / 1000000.0,
    adFormat: 'interstitial',
    currencyCode: currencyCode,
    precision: precision == PrecisionType.precise
        ? CloudXRevenuePrecision.exact
        : CloudXRevenuePrecision.estimated,
    adUnitId: ad.adUnitId, // the id you passed to CloudXArbiterBid.adMob
  ));
};

Running google_mobile_ads and CloudXGoogleWaterfallAdapter in the same iOS app. Both pin Google-Mobile-Ads-SDK to an exact version, so they install together only when the two pins name the same one; check the adapter's podspec against the google_mobile_ads release you are on before adding either. The demo pairs google_mobile_ads 7.0.0 with CloudXGoogleWaterfallAdapter ~> 12.14.0.3, both on Google Mobile Ads 12.14.0. An app that takes its Google demand only through CloudX has no such constraint and should use the current adapter.

The demo app's TPA tab (cloudx_flutter_demo_app/lib/arbiter/) is a complete interstitial and rewarded cycle against AdMob that can be copied into an app: arbiter_fullscreen_controller.dart holds the format-agnostic flow, and the interstitial and rewarded controllers add only their format's SDK calls. Trusted Arbiter must be enabled for your app in the CloudX dashboard; until it is, the SDK still answers every call from its local fallback, in which an AdMob bid with no reported revenue history cannot win.

Test Mode #

Test mode is server-controlled via device whitelisting. This provides better security and control over which devices receive test ads.

To enable test mode:

  1. Initialize the SDK with logging enabled and check the console for your device advertising ID
  2. Copy the advertising ID and add it to your device whitelist on the CloudX server dashboard
  3. The SDK will automatically configure adapters for test mode and include the test flag in bid requests

Note: Test mode is determined by the server, so you don't need to change any code between development and production builds.

Privacy Compliance #

The CloudX SDK supports GDPR and CCPA privacy compliance by reading standard IAB privacy strings from platform storage (SharedPreferences on Android, NSUserDefaults on iOS). These values are typically set automatically by your Consent Management Platform (CMP) such as Google UMP, OneTrust, or Sourcepoint.

How It Works

The SDK automatically detects user location and reads consent signals:

  1. EU Users (GDPR): Checks TCF v2 consent for purposes 1-4 and vendor consent (CloudX Vendor ID: 1510)
  2. US Users (CCPA): Checks for sale/sharing opt-out signals
  3. Other Regions: No restrictions applied

When consent is denied or user opts out, the SDK removes PII from ad requests:

  • Advertising ID is cleared
  • Geo coordinates (lat/lon) are removed
  • User key-values are not sent
  • Hashed user ID is excluded

Supported Privacy Keys

Key Standard Description
IABGPP_HDR_GppString GPP Global Privacy Platform string (modern)
IABGPP_GppSID GPP Section IDs (e.g., "2" for EU, "7" for US-National, "8" for US-CA)
IABTCF_TCString TCF v2 GDPR consent string (legacy)
IABTCF_gdprApplies TCF v2 Whether GDPR applies (1 = yes, 0 = no)
IABUSPrivacy_String US Privacy CCPA privacy string (legacy, e.g., "1YNN")

Note: The SDK prioritizes GPP (modern standard) over legacy TCF/US Privacy strings when both are available.

User Targeting #

// Set hashed user ID for targeting
CloudX.setHashedUserId('hashed-user-id');

// Set custom user key-value pairs
CloudX.setUserKeyValue('age', '25');
CloudX.setUserKeyValue('interests', 'gaming');

// Set custom app key-value pairs
CloudX.setAppKeyValue('app_version', '1.2.0');
CloudX.setAppKeyValue('build_type', 'release');

// Clear all custom key-values
CloudX.clearAllKeyValues();

Support #

For support, contact support@cloudx.io

4
likes
140
points
144
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

Complete Flutter SDK wrapper for CloudX Core with targeting APIs and full ad lifecycle callbacks

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter

More

Packages that depend on cloudx_flutter

Packages that implement cloudx_flutter