CloudX Flutter SDK
A Flutter plugin for the CloudX Mobile Ads platform. Monetize your Flutter apps with banner, MREC, and interstitial ads on 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, 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
| Platform | Status | Minimum Version |
|---|---|---|
| Android | ✅ Production Ready | API 21 (Android 5.0) |
| iOS | ❌ Not Supported | iOS 14.0 |
Note: iOS SDK is not yet available for production use.
Installation
Add this to your pubspec.yaml:
dependencies:
cloudx_flutter: ^0.18.0
Then run:
flutter pub get
Android Setup
No additional configuration required. Minimum SDK is automatically set to API 21.
Ad Network Adapters (Android)
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.
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.8.0' // Meta Audience Network
implementation 'io.cloudx:adapter-cloudx:0.8.0' // 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.8.0") // Meta Audience Network
implementation("io.cloudx:adapter-cloudx:0.8.0") // CloudX Network
// Add other adapters as needed
}
Available Adapters:
- Meta Audience Network -
adapter-meta - CloudX Network -
adapter-cloudx - 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',
testMode: false, // Set to true to enable test ads (development only)
);
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", "admob")
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');
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 is not currently supported)
}
Common Issues
iOS: SDK Not Supported
Note: iOS SDK is currently not available for production use. The SDK will return false during initialization on iOS.
Banner Not Showing
Checklist:
- Did you call
loadBanner()? - For programmatic banners, did you call
showBanner()? - Is the ad view added to the widget tree?
- Check the
onAdLoadFailedcallback 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
Android
- Android API: 21 (Android 5.0) or higher
- Gradle: 8.0+
- CloudX Android SDK: 0.8.0
iOS
Note: iOS is not currently supported. The SDK will compile on iOS but all ad operations will fail gracefully.
API Reference
Initialization
initialize({required String appKey, bool testMode})→Future<bool>isPlatformSupported()→boolgetVersion()→Future<String>setEnvironment(String environment)→Future<void>setLoggingEnabled(bool enabled)→Future<void>
Banner Ads
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:
- Check the demo app for implementation examples
- Review the API Reference section above
License
This project is licensed under the MIT License. See the LICENSE file for details.
Libraries
- cloudx
- cloudx_flutter
- listeners/cloudx_ad_listener
- CloudX ad listener
- listeners/cloudx_ad_view_listener
- CloudX ad view listener
- listeners/cloudx_interstitial_listener
- CloudX interstitial ad listener
- listeners/cloudx_rewarded_interstitial_listener
- CloudX rewarded interstitial ad listener
- Position for programmatic ad view placement
- models/cloudx_ad
- Ad metadata model
- widgets/cloudx_ad_view_controller
- widgets/cloudx_mrec_view