Skip to main content

CloudX Flutter SDK

pub package GitHub A Flutter plugin for the CloudX Mobile Ads platform. Monetize your Flutter apps with banner, MREC, and interstitial ads across iOS and Android.

Features

  • Banner Ads (320x50) - Widget-based and programmatic positioning
  • MREC Ads (300x250) - Medium Rectangle ads with flexible placement
  • Interstitial Ads - Full-screen ads for natural transition points
  • Privacy Compliance - Built-in support for CCPA, GPP, GDPR, and COPPA
  • Auto-Refresh - Automatic ad refresh with server-side configuration
  • Revenue Tracking - Access to eCPM and winning bidder information
  • User Targeting - First-party data integration via key-value pairs

Platform Support

PlatformStatusMinimum Version
Android✅ Production ReadyAPI 21 (Android 5.0)
iOS⚠️ Alpha/ExperimentaliOS 14.0
Note: iOS support requires setting allowIosExperimental: true during initialization.

Installation

Add this to your pubspec.yaml:
dependencies:
  cloudx_flutter: ^0.18.0
Then run:
flutter pub get
Alternative: Git Dependency For the latest development version from GitHub:
dependencies:
  cloudx_flutter:
    git:
      url: https://github.com/cloudx-io/cloudx-flutter.git
      ref: v0.11.0  # Use specific version tag
      path: cloudx_flutter_sdk

iOS Setup

The SDK requires iOS 14.0+. Update your ios/Podfile:
platform :ios, '14.0'
Then install pods:
cd ios && pod install

Android Setup

No additional configuration required. Minimum SDK is automatically set to API 21.

Ad Network Adapters

The CloudX SDK requires ad network adapters to serve ads. Adapters are not included by default - you must add them to your project based on which ad networks you want to support.

Android Adapters

Add adapters to your app’s android/app/build.gradle:
dependencies {
    // CloudX Adapters - add the adapters you want to use
    implementation 'io.cloudx:adapter-meta:0.6.1'      // Meta Audience Network
    implementation 'io.cloudx:adapter-cloudx:0.6.1'    // CloudX Network
    // Add other adapters as needed
}
Or if using Kotlin DSL (build.gradle.kts):
dependencies {
    // CloudX Adapters - add the adapters you want to use
    implementation("io.cloudx:adapter-meta:0.6.1")      // Meta Audience Network
    implementation("io.cloudx:adapter-cloudx:0.6.1")    // CloudX Network
    // Add other adapters as needed
}

iOS Adapters

Add adapters to your app’s ios/Podfile:
target 'Runner' do
  use_frameworks!
  use_modular_headers!

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

  # CloudX Adapters - add the adapters you want to use
  pod 'CloudXMetaAdapter', '~> 1.1.0'    # Meta Audience Network
  pod 'CloudXAdapter', '~> 1.1.0'        # CloudX Network
  # Add other adapters as needed
end
Then run:
cd ios && pod install
Available Adapters:
  • Meta Audience Network - adapter-meta (Android) / CloudXMetaAdapter (iOS)
  • CloudX Network - adapter-cloudx (Android) / CloudXAdapter (iOS)
  • More adapters coming soon

Quick Start

1. Initialize the SDK

Initialize CloudX before creating any ads:
import 'package:cloudx_flutter/cloudx.dart';

// Optional: Enable verbose logging (development only)
await CloudX.setLoggingEnabled(true);

// Optional: Set environment (default: production)
await CloudX.setEnvironment('production'); // 'dev', 'staging', or 'production'

// Initialize the SDK
final success = await CloudX.initialize(
  appKey: 'YOUR_APP_KEY',
  allowIosExperimental: true, // Required for iOS
);

if (success) {
  print('CloudX SDK initialized successfully');
} else {
  print('Failed to initialize CloudX SDK');
}

2. Banner Ads

Option A: Widget-Based (Recommended) Embed banner ads directly in your widget tree:
import 'package:cloudx_flutter/cloudx.dart';

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('My App')),
      body: Column(
        children: [
          Expanded(child: Text('Your content here')),

          // Banner ad at the bottom
          CloudXBannerView(
            placementName: 'home_banner',
            listener: CloudXAdViewListener(
              onAdLoaded: (ad) => print('Banner loaded: ${ad.bidder}'),
              onAdLoadFailed: (error) => print('Banner failed: $error'),
              onAdDisplayed: (ad) => print('Banner displayed'),
              onAdDisplayFailed: (error) => print('Display failed: $error'),
              onAdClicked: (ad) => print('Banner clicked'),
              onAdHidden: (ad) => print('Banner hidden'),
              onAdExpanded: (ad) => print('Banner expanded'),
              onAdCollapsed: (ad) => print('Banner collapsed'),
            ),
          ),
        ],
      ),
    );
  }
}
Option B: Programmatic Positioning Create banners that overlay your content at specific screen positions:
// Create a banner ad
final adId = await CloudX.createBanner(
  placementName: 'home_banner',
  adId: 'banner_1', // Optional: auto-generated if not provided
  listener: CloudXAdViewListener(
    onAdLoaded: (ad) => print('Loaded: ${ad.revenue}'),
    onAdLoadFailed: (error) => print('Failed: $error'),
    onAdDisplayed: (ad) => print('Displayed'),
    onAdDisplayFailed: (error) => print('Failed to display'),
    onAdClicked: (ad) => print('Clicked'),
    onAdHidden: (ad) => print('Hidden'),
    onAdExpanded: (ad) => print('Expanded'),
    onAdCollapsed: (ad) => print('Collapsed'),
  ),
  position: AdViewPosition.bottomCenter, // Optional: for overlay banners
);

// Load the banner
await CloudX.loadBanner(adId: adId!);

// Show the banner (if using programmatic positioning)
await CloudX.showBanner(adId: adId);

// Hide when needed
await CloudX.hideBanner(adId: adId);

// Always destroy when done
await CloudX.destroyAd(adId: adId);
Auto-Refresh Control:
// Start auto-refresh (refresh interval configured server-side)
await CloudX.startAutoRefresh(adId: adId);

// Stop auto-refresh (IMPORTANT: call before destroying)
await CloudX.stopAutoRefresh(adId: adId);

3. MREC Ads (300x250)

Medium Rectangle ads work just like banners but with a larger size. Widget-Based:
CloudXMRECView(
  placementName: 'home_mrec',
  listener: CloudXAdViewListener(
    onAdLoaded: (ad) => print('MREC loaded'),
    onAdLoadFailed: (error) => print('MREC failed: $error'),
    onAdDisplayed: (ad) => print('MREC displayed'),
    onAdDisplayFailed: (error) => print('Display failed'),
    onAdClicked: (ad) => print('MREC clicked'),
    onAdHidden: (ad) => print('MREC hidden'),
    onAdExpanded: (ad) => print('MREC expanded'),
    onAdCollapsed: (ad) => print('MREC collapsed'),
  ),
)
Programmatic:
final adId = await CloudX.createMREC(
  placementName: 'home_mrec',
  listener: CloudXAdViewListener(...),
  position: AdViewPosition.centered,
);

await CloudX.loadMREC(adId: adId!);
await CloudX.showMREC(adId: adId);

// Check if ready
final isReady = await CloudX.isMRECReady(adId: adId);

// Always destroy when done
await CloudX.destroyAd(adId: adId);

4. Interstitial Ads

Full-screen ads shown at natural transition points:
// Create the interstitial
final adId = await CloudX.createInterstitial(
  placementName: 'level_complete',
  listener: CloudXInterstitialListener(
    onAdLoaded: (ad) => print('Interstitial ready'),
    onAdLoadFailed: (error) => print('Load failed: $error'),
    onAdDisplayed: (ad) => print('Interstitial showing'),
    onAdDisplayFailed: (error) => print('Show failed: $error'),
    onAdClicked: (ad) => print('Interstitial clicked'),
    onAdHidden: (ad) => print('Interstitial closed'),
  ),
);

// Load the interstitial
await CloudX.loadInterstitial(adId: adId!);

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

// Always destroy after showing
await CloudX.destroyAd(adId: adId);

Event Listeners

All ad types use listener objects with callback functions.

CloudXAdViewListener

Used for Banner and MREC ads:
CloudXAdViewListener(
  onAdLoaded: (CloudXAd ad) {
    // Ad successfully loaded
    print('Bidder: ${ad.bidder}');
    print('Revenue: \$${ad.revenue}');
  },
  onAdLoadFailed: (String error) {
    // Failed to load ad
  },
  onAdDisplayed: (CloudXAd ad) {
    // Ad is now visible to user
  },
  onAdDisplayFailed: (String error) {
    // Failed to display ad
  },
  onAdClicked: (CloudXAd ad) {
    // User clicked the ad
  },
  onAdHidden: (CloudXAd ad) {
    // Ad was hidden
  },
  onAdRevenuePaid: (CloudXAd ad) {
    // Revenue tracking (optional)
  },
  onAdExpanded: (CloudXAd ad) {
    // User expanded the ad
  },
  onAdCollapsed: (CloudXAd ad) {
    // User collapsed the ad
  },
)

CloudXInterstitialListener

Used for Interstitial ads:
CloudXInterstitialListener(
  onAdLoaded: (CloudXAd ad) {
    // Interstitial ready to show
  },
  onAdLoadFailed: (String error) {
    // Failed to load
  },
  onAdDisplayed: (CloudXAd ad) {
    // Interstitial is showing
  },
  onAdDisplayFailed: (String error) {
    // Failed to show
  },
  onAdClicked: (CloudXAd ad) {
    // User clicked
  },
  onAdHidden: (CloudXAd ad) {
    // User closed interstitial
  },
  onAdRevenuePaid: (CloudXAd ad) {
    // Revenue tracking (optional)
  },
)

CloudXAd Model

All callbacks receive a CloudXAd object with ad metadata:
class CloudXAd {
  final String? placementName;       // Your placement name
  final String? placementId;         // CloudX internal ID
  final String? bidder;              // Winning network (e.g., "meta", "vungle")
  final String? externalPlacementId; // Network-specific ID
  final double? revenue;             // eCPM revenue in USD
}

Ad Positioning

When using programmatic positioning, specify where ads should appear:
enum AdViewPosition {
  topCenter,
  topRight,
  centered,
  centerLeft,
  centerRight,
  bottomLeft,
  bottomCenter,
  bottomRight,
}
Example:
await CloudX.createBanner(
  placementName: 'banner',
  position: AdViewPosition.bottomCenter,
);

Privacy & Compliance

CloudX provides comprehensive privacy APIs to comply with regulations.

CCPA (California Consumer Privacy Act)

Fully supported in bid requests:
// Set CCPA privacy string (format: "1YNN")
// 1 = version, Y/N = opt-out-sale, Y/N = opt-out-sharing, Y/N = LSPA
await CloudX.setCCPAPrivacyString('1YNN');

GDPR (General Data Protection Regulation)

⚠️ Warning: Not yet supported by CloudX servers. Contact CloudX for GDPR support.
await CloudX.setIsUserConsent(true); // true = user consented

COPPA (Children’s Online Privacy Protection Act)

Clears user data but not yet included in bid requests (server limitation):
await CloudX.setIsAgeRestrictedUser(true); // true = age-restricted

GPP (Global Privacy Platform)

Comprehensive privacy framework (fully supported):
// Set GPP string
await CloudX.setGPPString('DBABMA~CPXxRfAPXxRfAAfKABENB...');

// Set section IDs (e.g., [7, 8] for US-National and US-CA)
await CloudX.setGPPSid([7, 8]);

// Get current values
final gppString = await CloudX.getGPPString();
final gppSid = await CloudX.getGPPSid();

User Targeting

Enhance ad targeting with first-party data.

User ID

// Set user ID (should be hashed for privacy)
await CloudX.setUserID('hashed-user-id');

Key-Value Targeting

User-Level Targeting (cleared by privacy regulations):
await CloudX.setUserKeyValue('age', '25');
await CloudX.setUserKeyValue('interests', 'gaming');
App-Level Targeting (persistent across privacy changes):
await CloudX.setAppKeyValue('app_version', '1.2.0');
await CloudX.setAppKeyValue('build_type', 'release');
Clear All:
await CloudX.clearAllKeyValues();

Lifecycle Management

Widget Lifecycle

For widget-based ads (CloudXBannerView, CloudXMRECView), the SDK automatically handles lifecycle.

Programmatic Lifecycle

For programmatically created ads, always call destroyAd() to prevent memory leaks:
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() {
    // CRITICAL: Always destroy ads
    if (_adId != null) {
      CloudX.destroyAd(adId: _adId!);
    }
    super.dispose();
  }

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

SDK Information

// Check platform support
final isSupported = await CloudX.isPlatformSupported();

// Get SDK version
final version = await CloudX.getVersion();

Advanced Features

Widget Controllers

Control banner/MREC widgets programmatically:
final controller = CloudXAdViewController();

CloudXBannerView(
  placementName: 'home_banner',
  controller: controller,
  listener: CloudXAdViewListener(...),
)

// Control auto-refresh
await controller.startAutoRefresh();
await controller.stopAutoRefresh();

// Check if controller is attached
if (controller.isAttached) {
  // Controller is ready
}

Best Practices

1. Always Destroy Ads

Memory leaks occur if you don’t destroy ads:
@override
void dispose() {
  CloudX.destroyAd(adId: myAdId);
  super.dispose();
}

2. Stop Auto-Refresh Before Destroying

For banner/MREC ads:
await CloudX.stopAutoRefresh(adId: adId);
await CloudX.destroyAd(adId: adId);

3. Check Ad Readiness

For interstitials, always check before showing:
final isReady = await CloudX.isInterstitialReady(adId: adId);
if (isReady) {
  await CloudX.showInterstitial(adId: adId);
}

4. Disable Logging in Production

// Only enable during development
if (kDebugMode) {
  await CloudX.setLoggingEnabled(true);
}

5. Handle Initialization Errors

final success = await CloudX.initialize(appKey: 'YOUR_KEY');
if (!success) {
  // Handle initialization failure
  // - Check network connection
  // - Verify app key is correct
  // - Check platform support (iOS requires experimental flag)
}

Common Issues

iOS: “Experimental API” Error

Solution: Set allowIosExperimental: true during initialization:
await CloudX.initialize(
  appKey: 'YOUR_KEY',
  allowIosExperimental: true,
);
Checklist:
  1. Did you call loadBanner()?
  2. For programmatic banners, did you call showBanner()?
  3. Is the ad view added to the widget tree?
  4. Check the onAdLoadFailed callback for errors

Memory Leaks

Solution: Always call destroyAd() in your widget’s dispose() method.

Auto-Refresh Not Stopping

Solution: Explicitly call stopAutoRefresh() before destroying:
await CloudX.stopAutoRefresh(adId: adId);
await CloudX.destroyAd(adId: adId);

Example App

A complete demo app is available in the GitHub repository under cloudx_flutter_demo_app/. It demonstrates:
  • SDK initialization with environment selection
  • All ad format implementations
  • Widget-based and programmatic approaches
  • Privacy compliance integration
  • User targeting setup
  • Proper lifecycle management
  • Event logging and debugging
Clone the repository and run the demo:
git clone https://github.com/cloudx-io/cloudx-flutter.git
cd cloudx-flutter/cloudx_flutter_demo_app
flutter pub get
flutter run

Requirements

Flutter

  • Flutter SDK: 3.0.0 or higher
  • Dart SDK: 3.0.0 or higher

iOS

  • iOS: 14.0 or higher
  • CocoaPods for dependency management
  • CloudXCore pod: ~> 1.1.40

Android

  • Android API: 21 (Android 5.0) or higher
  • Gradle: 8.0+
  • CloudX Android SDK: 0.6.1

API Reference

Initialization

  • initialize({required String appKey, bool allowIosExperimental})Future<bool>
  • isPlatformSupported()Future<bool>
  • getVersion()Future<String>
  • setEnvironment(String environment)Future<void>
  • setLoggingEnabled(bool enabled)Future<void>
  • createBanner({required String placementName, String? adId, CloudXAdViewListener? listener, AdViewPosition? position})Future<String?>
  • loadBanner({required String adId})Future<bool>
  • showBanner({required String adId})Future<bool>
  • hideBanner({required String adId})Future<bool>
  • startAutoRefresh({required String adId})Future<bool>
  • stopAutoRefresh({required String adId})Future<bool>

MREC Ads

  • createMREC({required String placementName, String? adId, CloudXAdViewListener? listener, AdViewPosition? position})Future<String?>
  • loadMREC({required String adId})Future<bool>
  • showMREC({required String adId})Future<bool>
  • isMRECReady({required String adId})Future<bool>

Interstitial Ads

  • createInterstitial({required String placementName, String? adId, CloudXInterstitialListener? listener})Future<String?>
  • loadInterstitial({required String adId})Future<bool>
  • showInterstitial({required String adId})Future<bool>
  • isInterstitialReady({required String adId})Future<bool>

Ad Lifecycle

  • destroyAd({required String adId})Future<bool>

Privacy

  • setCCPAPrivacyString(String? ccpaString)Future<void>
  • setIsUserConsent(bool hasConsent)Future<void>
  • setIsAgeRestrictedUser(bool isAgeRestricted)Future<void>
  • setGPPString(String? gppString)Future<void>
  • getGPPString()Future<String?>
  • setGPPSid(List<int>? sectionIds)Future<void>
  • getGPPSid()Future<List<int>?>

User Targeting

  • setUserID(String? userID)Future<void>
  • setUserKeyValue(String key, String value)Future<void>
  • setAppKeyValue(String key, String value)Future<void>
  • clearAllKeyValues()Future<void>

Roadmap (Future Versions)

The following features are planned for future releases:
  • ✅ Rewarded Ads (implementation exists, needs public API)
  • ✅ Native Ads with multiple sizes (implementation exists, needs public API)
  • ✅ Structured error handling with error codes
  • ✅ Granular log level control
  • ✅ App Open Ads

Support

For questions, issues, or feature requests:
  • Contact the CloudX team
  • Check the demo app for implementation examples

License

This project is licensed under the Business Source License 1.1. See the LICENSE file for details.