google_ads_helper 1.0.1
google_ads_helper: ^1.0.1 copied to clipboard
A comprehensive Flutter plugin for Google Mobile Ads with easy integration, smart initialization, revenue tracking, and professional widgets.
Google Ads Helper Plugin #
A comprehensive Flutter plugin for Google Mobile Ads with easy integration, smart initialization, revenue tracking, and professional UI widgets.
Features #
✅ Easy Integration - One-line initialization
✅ Smart Loading - Only loads ads with provided IDs
✅ Count-Based Control - Show interstitials every N calls
✅ Auto-Reload - Automatically loads next ad after showing
✅ Revenue Tracking - Built-in revenue calculation and tracking
✅ Professional Widgets - Drop-in UI components
✅ Automatic Retry - Handles ad loading failures gracefully
✅ Debug Helpers - Development tools for testing
✅ Production Ready - Comprehensive error handling
Installation #
Add this to your pubspec.yaml:
dependencies:
google_ads_helper: ^1.0.0
Then run:
flutter pub get
Quick Start #
1. Initialize in main.dart #
import 'package:flutter/material.dart';
import 'package:google_ads_helper/google_ads_helper.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize for reward app (most common)
await SimpleAdInitializer.initializeForRewardApp(
rewardedAdUnitId: 'ca-app-pub-XXXXXXXX/XXXXXXXX', // Your real ID
interstitialAdUnitId: 'ca-app-pub-XXXXXXXX/XXXXXXXX', // Your real ID
// testDeviceIds: ['YOUR_TEST_DEVICE_ID'], // For testing
);
runApp(MyApp());
}
2. Show Ads Anywhere #
// Show rewarded ad
AdHelper.showRewarded(
onRewardEarned: (reward) {
print('User earned ${reward.amount} ${reward.type}');
// Give user reward
},
onAdClosed: () => print('Ad closed'),
);
// Show interstitial (with count-based control)
AdHelper.showInterstitial(
onInterstitialShown: () => print('Ad shown'),
onAdClosed: () => print('Ad closed'),
);
3. Add Banner Ads #
// Simple banner widget
BannerAdWidget(
height: 60,
margin: EdgeInsets.all(10),
)
Initialization Options #
For Reward Apps #
await SimpleAdInitializer.initializeForRewardApp(
rewardedAdUnitId: 'your-rewarded-id',
interstitialAdUnitId: 'your-interstitial-id', // Optional
);
For Game Apps #
await SimpleAdInitializer.initializeForGameApp(
interstitialAdUnitId: 'your-interstitial-id',
rewardedAdUnitId: 'your-rewarded-id',
bannerAdUnitId: 'your-banner-id', // Optional
);
Custom Initialization #
await SimpleAdInitializer.initializeWithYourAdIds(
rewardedAdUnitId: 'your-rewarded-id',
interstitialAdUnitId: 'your-interstitial-id',
bannerAdUnitId: 'your-banner-id',
nativeAdUnitId: 'your-native-id',
testDeviceIds: ['test-device-id'],
onAdLoaded: (adType) => print('$adType loaded'),
onAdError: (error) => print('Error: $error'),
);
Interstitial Count Control #
The plugin has smart interstitial frequency control - ads only show every 3rd call by default:
// This will only show ad every 3rd call
AdHelper.showInterstitial(
onInterstitialShown: () => print('Ad shown!'),
);
// Force show (bypass count)
AdHelper.showInterstitial(
forceShow: true,
onInterstitialShown: () => print('Forced ad!'),
);
// Configure frequency
AdHelper.setInterstitialFrequency(5); // Show every 5th call
// Check if next call will show ad
if (AdHelper.willInterstitialShowNext) {
print('Next call will show ad!');
}
// Reset count manually
AdHelper.resetInterstitialCount();
Widgets #
Banner Ad Widget #
BannerAdWidget(
height: 60,
margin: EdgeInsets.all(10),
backgroundColor: Colors.grey[100],
)
Native Ad Widget #
NativeAdWidget(
height: 200,
margin: EdgeInsets.all(10),
borderRadius: BorderRadius.circular(10),
)
Smart Ad Container #
SmartAdContainer(
height: 200,
preferredAdType: AdType.native,
fallbackAdTypes: [AdType.banner],
)
Revenue Tracking #
// Track ad shows
AdRevenueTracker.trackAdShown(AdType.rewarded);
// Get revenue report
final report = AdRevenueTracker.getRevenueReport();
print('Total Revenue: ${report['estimatedRevenue']}');
print('User Rewards: ${report['userRewards']}');
print('App Profit: ${report['appProfit']}');
// Calculate specific revenue
double rewardedRevenue = AdHelper.calculateEstimatedRevenue(
AdType.rewarded, 10, // 10 rewarded ads
);
Debug Helpers #
// Print ad status
AdDebugHelper.printAdStatus();
// Print revenue report
AdDebugHelper.printRevenueReport();
// Check ad availability
print('Ads ready: ${AdHelper.adsStatus}');
print('Ads loading: ${AdHelper.loadingStatus}');
Test Ad Unit IDs #
For testing, the plugin automatically uses Google's test IDs:
| Ad Format | Test ID |
|---|---|
| Banner | ca-app-pub-3940256099942544/6300978111 |
| Interstitial | ca-app-pub-3940256099942544/1033173712 |
| Rewarded | ca-app-pub-3940256099942544/5224354917 |
| Native | ca-app-pub-3940256099942544/2247696110 |
Advanced Usage #
Ad Status Monitoring #
// Check if ads are ready
bool anyAdReady = AdHelper.hasAnyAdReady;
Map<String, bool> adStatus = AdHelper.adsStatus;
Map<String, bool> loadingStatus = AdHelper.loadingStatus;
print('Banner ready: ${adStatus['banner']}');
print('Interstitial ready: ${adStatus['interstitial']}');
print('Rewarded ready: ${adStatus['rewarded']}');
print('Native ready: ${adStatus['native']}');
// Check loading states
if (loadingStatus['rewarded'] == true) {
print('Rewarded ad is currently loading...');
}
Manual Ad Loading #
// Load specific ad types manually
GoogleAdsHelper.instance.loadBannerAd();
GoogleAdsHelper.instance.loadInterstitialAd();
GoogleAdsHelper.instance.loadRewardedAd();
GoogleAdsHelper.instance.loadNativeAd();
// Load all configured ads
GoogleAdsHelper.instance.loadAllAds();
// Reload specific ad type
GoogleAdsHelper.instance.reloadBannerAd();
GoogleAdsHelper.instance.reloadInterstitialAd();
Advanced Interstitial Controls #
// Get current interstitial statistics
int currentCount = AdHelper.interstitialCallCount;
int frequency = GoogleAdsHelper.instance.interstitialShowEveryCount;
bool willShowNext = AdHelper.willInterstitialShowNext;
print('Interstitial calls: $currentCount/$frequency');
print('Will show on next call: $willShowNext');
// Set custom frequency (default is 3)
AdHelper.setInterstitialFrequency(5); // Show every 5th call
AdHelper.setInterstitialFrequency(1); // Show every call
// Reset count manually (useful for special events)
AdHelper.resetInterstitialCount();
// Force show regardless of count
AdHelper.showInterstitial(
forceShow: true,
onInterstitialShown: () => print('Forced interstitial shown!'),
);
Runtime Ad Configuration #
// Update ad unit IDs at runtime
GoogleAdsHelper.instance.updateAdUnitIds(
bannerAdUnitId: 'ca-app-pub-XXXXXXXX/XXXXXXXX',
interstitialAdUnitId: 'ca-app-pub-XXXXXXXX/XXXXXXXX',
rewardedAdUnitId: 'ca-app-pub-XXXXXXXX/XXXXXXXX',
nativeAdUnitId: 'ca-app-pub-XXXXXXXX/XXXXXXXX',
);
// Add new ad types later
await SimpleAdInitializer.addAdType(
bannerAdUnitId: 'ca-app-pub-XXXXXXXX/XXXXXXXX',
);
Custom Ad Callbacks #
// Initialize with global callbacks
await GoogleAdsHelper.instance.initialize(
rewardedAdUnitId: 'your-rewarded-id',
interstitialAdUnitId: 'your-interstitial-id',
onAdLoaded: (adType) {
print('✅ $adType ad loaded successfully');
// Update UI, show buttons, etc.
},
onAdError: (error) {
print('❌ Ad error: $error');
// Handle error, show fallback content
},
onAdShown: (adType) {
print('📱 $adType ad shown');
// Track analytics, pause game, etc.
},
onAdClosed: (adType) {
print('🔒 $adType ad closed');
// Resume game, update UI, etc.
},
);
Revenue Tracking & Analytics #
// Track when ads are shown (call this in your ad callbacks)
AdRevenueTracker.trackAdShown(AdType.rewarded);
AdRevenueTracker.trackAdShown(AdType.interstitial);
AdRevenueTracker.trackAdShown(AdType.banner);
AdRevenueTracker.trackAdShown(AdType.native);
// Get individual ad counts
int rewardedCount = AdRevenueTracker.getAdCount(AdType.rewarded);
int interstitialCount = AdRevenueTracker.getAdCount(AdType.interstitial);
int totalAds = AdRevenueTracker.getTotalAdCount();
// Calculate revenue
double rewardedRevenue = AdHelper.calculateEstimatedRevenue(AdType.rewarded, rewardedCount);
double interstitialRevenue = AdHelper.calculateEstimatedRevenue(AdType.interstitial, interstitialCount);
// Calculate user rewards (what you pay to users)
double userRewards = AdHelper.calculateUserReward(AdType.rewarded, rewardedCount);
// Get comprehensive report
Map<String, dynamic> report = AdRevenueTracker.getRevenueReport();
print('Total Ads: ${report['totalAds']}');
print('Estimated Revenue: \$${report['estimatedRevenue'].toStringAsFixed(2)}');
print('User Rewards: \$${report['userRewards'].toStringAsFixed(2)}');
print('App Profit: \$${report['appProfit'].toStringAsFixed(2)}');
// Reset tracking (useful for daily/weekly reports)
AdRevenueTracker.reset();
Advanced Widget Usage #
// Banner with custom styling
BannerAdWidget(
height: 60,
adSize: AdSize.banner, // or AdSize.largeBanner, AdSize.mediumRectangle
margin: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
backgroundColor: Colors.grey[100],
)
// Native ad with custom appearance
NativeAdWidget(
height: 250,
margin: EdgeInsets.all(16),
borderRadius: BorderRadius.circular(12),
)
// Smart container that adapts to available ads
SmartAdContainer(
height: 200,
preferredAdType: AdType.native,
fallbackAdTypes: [AdType.banner], // Falls back if native not available
)
// Custom loading indicator
AdLoadingIndicator(
adType: AdType.rewarded,
customLoadingWidget: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(color: Colors.blue),
SizedBox(height: 8),
Text('Loading reward...', style: TextStyle(color: Colors.blue)),
],
),
)
Debug & Development Tools #
// Print detailed ad status (development only)
AdDebugHelper.printAdStatus();
// Output:
// 🔍 Ad Status Debug Info:
// banner: ✅ Ready (State: AdState.loaded)
// interstitial: 🔄 Loading... (State: AdState.loading)
// rewarded: ❌ Failed/Not Loaded (State: AdState.failed)
// native: ✅ Ready (State: AdState.loaded)
// Print revenue report
AdDebugHelper.printRevenueReport();
// Output:
// 💰 Revenue Report:
// Total Ads Shown: 25
// - Banner: 10
// - Interstitial: 8
// - Rewarded: 5
// - Native: 2
// Estimated Revenue: $3.45
// User Rewards: $1.95
// App Profit: $1.50
// Get ad states programmatically
Map<String, AdState> states = AdHelper.adStates;
if (states['rewarded'] == AdState.loaded) {
// Show rewarded button
} else if (states['rewarded'] == AdState.loading) {
// Show loading indicator
} else {
// Show "ad not available" message
}
Complete Integration Example #
import 'package:flutter/material.dart';
import 'package:google_ads_helper/google_ads_helper.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize with comprehensive callbacks
await SimpleAdInitializer.initializeForRewardApp(
rewardedAdUnitId: 'ca-app-pub-XXXXXXXX/XXXXXXXX',
interstitialAdUnitId: 'ca-app-pub-XXXXXXXX/XXXXXXXX',
onAdLoaded: (adType) {
print('🎉 $adType ad ready!');
},
onAdError: (error) {
print('❌ Ad error: $error');
},
);
// Set custom interstitial frequency
AdHelper.setInterstitialFrequency(4); // Show every 4th call
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: AdvancedRewardScreen(),
);
}
}
class AdvancedRewardScreen extends StatefulWidget {
@override
_AdvancedRewardScreenState createState() => _AdvancedRewardScreenState();
}
class _AdvancedRewardScreenState extends State<AdvancedRewardScreen> {
int coins = 100;
int totalAdsWatched = 0;
double totalEarnings = 0.0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Advanced Google Ads Helper'),
actions: [
IconButton(
icon: Icon(Icons.info),
onPressed: () => _showAdStatus(),
),
],
),
body: Column(
children: [
// Banner ad with custom styling
Container(
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(8),
),
margin: EdgeInsets.all(16),
child: BannerAdWidget(
height: 60,
backgroundColor: Colors.transparent,
),
),
// Stats display
Container(
padding: EdgeInsets.all(16),
margin: EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue[100]!, Colors.blue[50]!],
),
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildStat('Coins', coins.toString()),
_buildStat('Ads Watched', totalAdsWatched.toString()),
_buildStat('Earnings', '\$${totalEarnings.toStringAsFixed(2)}'),
],
),
),
SizedBox(height: 20),
// Ad buttons with smart states
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: [
// Rewarded ad button
_buildAdButton(
title: 'Watch Rewarded Ad',
subtitle: '+100 coins',
icon: Icons.play_circle_filled,
color: Colors.green,
isReady: AdHelper.adsStatus['rewarded'] ?? false,
isLoading: AdHelper.loadingStatus['rewarded'] ?? false,
onPressed: () => _showRewardedAd(),
),
SizedBox(height: 16),
// Interstitial button with count info
_buildAdButton(
title: 'Complete Task',
subtitle: '+50 coins • ${_getInterstitialInfo()}',
icon: Icons.task_alt,
color: Colors.blue,
isReady: true, // Always available for tasks
isLoading: false,
onPressed: () => _completeTask(),
),
SizedBox(height: 16),
// Force interstitial button
_buildAdButton(
title: 'Watch Interstitial (Bonus)',
subtitle: '+25 coins • Force show',
icon: Icons.video_library,
color: Colors.orange,
isReady: AdHelper.adsStatus['interstitial'] ?? false,
isLoading: AdHelper.loadingStatus['interstitial'] ?? false,
onPressed: () => _showBonusInterstitial(),
),
Spacer(),
// Revenue report button
TextButton.icon(
onPressed: () => _showRevenueReport(),
icon: Icon(Icons.analytics),
label: Text('View Revenue Report'),
),
],
),
),
),
// Native ad at bottom
Container(
margin: EdgeInsets.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey[300]!),
),
child: NativeAdWidget(
height: 150,
borderRadius: BorderRadius.circular(12),
),
),
],
),
);
}
Widget _buildStat(String label, String value) {
return Column(
children: [
Text(
value,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.blue[800],
),
),
Text(
label,
style: TextStyle(
fontSize: 12,
color: Colors.blue[600],
),
),
],
);
}
Widget _buildAdButton({
required String title,
required String subtitle,
required IconData icon,
required Color color,
required bool isReady,
required bool isLoading,
required VoidCallback onPressed,
}) {
return Container(
width: double.infinity,
child: ElevatedButton(
onPressed: isReady && !isLoading ? onPressed : null,
style: ElevatedButton.styleFrom(
backgroundColor: color,
padding: EdgeInsets.all(16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: Row(
children: [
Icon(icon, size: 32, color: Colors.white),
SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
Text(
isLoading ? 'Loading...' : subtitle,
style: TextStyle(
fontSize: 12,
color: Colors.white70,
),
),
],
),
),
if (isLoading)
SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
],
),
),
);
}
String _getInterstitialInfo() {
int count = AdHelper.interstitialCallCount;
int frequency = GoogleAdsHelper.instance.interstitialShowEveryCount;
if (AdHelper.willInterstitialShowNext) {
return 'Ad will show!';
} else {
return '${frequency - count} more for ad';
}
}
void _showRewardedAd() {
AdHelper.showRewarded(
onRewardEarned: (reward) {
setState(() {
coins += 100;
totalAdsWatched++;
totalEarnings += 0.35; // Estimated rewarded ad revenue
});
AdRevenueTracker.trackAdShown(AdType.rewarded);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('🎉 Earned 100 coins!'),
backgroundColor: Colors.green,
),
);
},
onAdNotReady: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('⏳ Rewarded ad not ready yet')),
);
},
onAdFailedToShow: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('❌ Failed to show rewarded ad')),
);
},
);
}
void _completeTask() {
// Always complete the task
setState(() {
coins += 50;
});
// Try to show interstitial (respects count-based control)
bool adShown = AdHelper.showInterstitial(
onInterstitialShown: () {
print('📱 Task completion interstitial shown');
},
onAdClosed: () {
setState(() {
totalAdsWatched++;
totalEarnings += 0.15; // Estimated interstitial revenue
});
AdRevenueTracker.trackAdShown(AdType.interstitial);
},
);
String message = adShown ?
'✅ Task completed! +50 coins (Ad shown)' :
'✅ Task completed! +50 coins';
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
}
void _showBonusInterstitial() {
AdHelper.showInterstitial(
forceShow: true, // Bypass count control
onInterstitialShown: () {
print('📱 Bonus interstitial shown');
},
onAdClosed: () {
setState(() {
coins += 25;
totalAdsWatched++;
totalEarnings += 0.15;
});
AdRevenueTracker.trackAdShown(AdType.interstitial);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('🎉 Bonus earned! +25 coins'),
backgroundColor: Colors.orange,
),
);
},
onAdNotReady: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('⏳ Interstitial ad not ready')),
);
},
);
}
void _showAdStatus() {
final status = AdHelper.adsStatus;
final loading = AdHelper.loadingStatus;
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Ad Status'),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildStatusRow('Banner', status['banner']!, loading['banner']!),
_buildStatusRow('Interstitial', status['interstitial']!, loading['interstitial']!),
_buildStatusRow('Rewarded', status['rewarded']!, loading['rewarded']!),
_buildStatusRow('Native', status['native']!, loading['native']!),
SizedBox(height: 16),
Text('Interstitial: ${AdHelper.interstitialCallCount}/${GoogleAdsHelper.instance.interstitialShowEveryCount}'),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('OK'),
),
],
),
);
}
Widget _buildStatusRow(String name, bool isReady, bool isLoading) {
String status = isLoading ? '🔄 Loading' : (isReady ? '✅ Ready' : '❌ Failed');
return Padding(
padding: EdgeInsets.symmetric(vertical: 2),
child: Text('$name: $status'),
);
}
void _showRevenueReport() {
final report = AdRevenueTracker.getRevenueReport();
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Revenue Report'),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Total Ads: ${report['totalAds']}'),
Text('Banner Ads: ${report['bannerAds']}'),
Text('Interstitial Ads: ${report['interstitialAds']}'),
Text('Rewarded Ads: ${report['rewardedAds']}'),
Text('Native Ads: ${report['nativeAds']}'),
Divider(),
Text('Estimated Revenue: \$${report['estimatedRevenue'].toStringAsFixed(2)}'),
Text('User Rewards: \$${report['userRewards'].toStringAsFixed(2)}'),
Text('App Profit: \$${report['appProfit'].toStringAsFixed(2)}'),
],
),
actions: [
TextButton(
onPressed: () {
AdRevenueTracker.reset();
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Revenue tracking reset')),
);
},
child: Text('Reset'),
),
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('OK'),
),
],
),
);
}
}
Platform Setup #
Android Configuration #
Add the following to your android/app/src/main/AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Internet permissions for ads -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:label="your_app_name"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<!-- Add your AdMob App ID -->
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-XXXXXXXXXXXXXXXX~XXXXXXXXXX"/>
<!-- MainActivity and other configurations... -->
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Intent filters and other activity config... -->
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
Important Notes:
- Replace
ca-app-pub-XXXXXXXXXXXXXXXX~XXXXXXXXXXwith your actual AdMob App ID - Your AdMob App ID is different from Ad Unit IDs (it ends with
~followed by 10 digits) - Find your App ID in the AdMob console under App settings
iOS Configuration #
Add the following to your ios/Runner/Info.plist:
<dict>
<!-- Other plist entries... -->
<!-- Add your AdMob App ID -->
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-XXXXXXXXXXXXXXXX~XXXXXXXXXX</string>
<!-- App Transport Security (allow ads) -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<!-- SKAdNetwork for iOS 14+ attribution -->
<key>SKAdNetworkItems</key>
<array>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>cstr6suwn9.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>4fzdc2evr5.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>2fnua5tdw4.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>ydx93a7ass.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>5a6flpkh64.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>p78axxw29g.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>v72qych5uu.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>ludvb6z3bs.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>cp8zw746q7.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>3sh42y64q3.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>c6k4g5qg8m.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>s39g8k73mm.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>3qy4746246.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>f38h382jlk.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>hs6bdukanm.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>v4nxqhlyqp.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>wzmmz9fp6w.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>yclnxrl5pm.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>t38b2kh725.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>7ug5zh24hu.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>9rd848q2bz.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>y5ghdn5j9k.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>n6fk4nfna4.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>v9wttpbfk9.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>n38lu8286q.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>47vhws6wlr.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>kbd757ywx3.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>9t245vhmpl.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>eh6m2bh4zr.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>a2p9lx4ktl.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>22mmun2rn5.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>4468km3ulz.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>2u9pt9hc89.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>8s468mfl3y.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>klf5c3l5u5.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>ppxm28t8ap.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>ecpz2srf59.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>uw77j35x4d.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>pwa73g5rt2.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>mlmmfzh3r3.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>578prtvx9j.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>4dzt52r2t5.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>e5fvkxwrpn.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>8c4e2ghe7u.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>zq492l623r.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>3rd42ekr43.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>3s53sq3u65.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>f73kdq92p3.skadnetwork</string>
</dict>
</array>
</dict>
Important Notes:
- Replace
ca-app-pub-XXXXXXXXXXXXXXXX~XXXXXXXXXXwith your actual AdMob App ID - The SKAdNetwork list above includes major ad networks for better monetization
- This configuration is required for iOS 14+ ad attribution
Getting Your AdMob App ID #
- Go to AdMob Console
- Select your app or create a new one
- Go to "App settings"
- Copy your App ID (format:
ca-app-pub-XXXXXXXXXXXXXXXX~XXXXXXXXXX) - Use this App ID in both Android and iOS configurations
Minimum SDK Requirements #
- Flutter SDK: >=3.3.3 <4.0.0
- Google Mobile Ads: ^5.2.0
- Android: minSdkVersion 21
- iOS: 12.0+
Build Configuration #
For Android, ensure your android/app/build.gradle has:
android {
compileSdkVersion 34
defaultConfig {
minSdkVersion 21
targetSdkVersion 34
// other configurations...
}
}
For iOS, ensure your ios/Podfile has:
platform :ios, '12.0'
Getting Your Ad Unit IDs #
- Create an AdMob account
- Add your app to AdMob
- Create ad units for each format you want
- Replace test IDs with your real ad unit IDs
License #
MIT License - see LICENSE file for details.