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

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~XXXXXXXXXX with 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~XXXXXXXXXX with 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

  1. Go to AdMob Console
  2. Select your app or create a new one
  3. Go to "App settings"
  4. Copy your App ID (format: ca-app-pub-XXXXXXXXXXXXXXXX~XXXXXXXXXX)
  5. 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

  1. Create an AdMob account
  2. Add your app to AdMob
  3. Create ad units for each format you want
  4. Replace test IDs with your real ad unit IDs

License

MIT License - see LICENSE file for details.

Support