native_googleads 0.0.3
native_googleads: ^0.0.3 copied to clipboard
A Flutter plugin for integrating Google Mobile Ads (AdMob) using native platform implementations. Supports interstitial and rewarded ads with comprehensive callbacks.
Native Google Ads #
A Flutter plugin for integrating Google Mobile Ads (AdMob) using native platform implementations. This plugin provides a simple and efficient way to display banner, native, interstitial, and rewarded ads in your Flutter applications.
Features #
- ✅ Native Implementation - Direct integration with Google Mobile Ads SDK
- ✅ iOS & Android Support - Full support for both platforms
- ✅ Banner Ads - Display banner ads in various sizes including adaptive
- ✅ Native Ads - Fully customizable native ads with templates and styling options
- ✅ Interstitial Ads - Full-screen ads at natural transition points
- ✅ Rewarded Ads - Reward users for watching video ads
- ✅ Platform Views - Native ad rendering using platform-specific views
- ✅ Comprehensive Callbacks - Full lifecycle event handling
- ✅ Swift Package Manager - Modern iOS dependency management
- ✅ Kotlin Support - Modern Android implementation
Platform Support #
Platform | Minimum Version | Architecture |
---|---|---|
Android | API 24 (7.0) | Kotlin |
iOS | 13.0 | Swift |
Installation #
Add native_googleads
to your pubspec.yaml
:
dependencies:
native_googleads: ^0.0.3
Then run:
flutter pub get
Platform Setup #
Android Setup #
1. Update android/app/build.gradle
Ensure your minimum SDK version is at least 24:
android {
defaultConfig {
minSdkVersion 24
// ... other configurations
}
}
2. Add AdMob App ID to AndroidManifest.xml
Add your AdMob App ID to android/app/src/main/AndroidManifest.xml
:
<manifest>
<application>
<!-- Add your AdMob App ID here -->
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy"/>
</application>
</manifest>
Note: Use test App ID
ca-app-pub-3940256099942544~3347511713
for development
iOS Setup #
1. Update iOS Deployment Target (or Swift Package Manager)
In your ios/Podfile
, ensure the platform version is at least 13.0:
platform :ios, '13.0'
2. Add AdMob App ID to Info.plist
Add the following to your ios/Runner/Info.plist
:
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy</string>
<!-- For iOS 14+ App Tracking Transparency -->
<key>NSUserTrackingUsageDescription</key>
<string>This identifier will be used to deliver personalized ads to you.</string>
<!-- Required SKAdNetwork identifiers -->
<key>SKAdNetworkItems</key>
<array>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>cstr6suwn9.skadnetwork</string>
</dict>
<!-- Add other SKAdNetwork identifiers as needed -->
</array>
Note: Use test App ID
ca-app-pub-3940256099942544~1458002511
for development
3. Update iOS Pods
cd ios
pod install
Usage #
Basic Usage #
import 'package:native_googleads/native_googleads.dart';
// Get the singleton instance
final ads = NativeGoogleads.instance;
// Initialize with App ID (use platform test App ID during development)
await ads.initialize(appId: 'your-app-id');
// Load and show a banner ad (programmatic API)
final bannerId = await ads.loadBannerAd(
adUnitId: 'your-banner-ad-unit-id',
size: BannerAdSize.adaptive,
);
if (bannerId != null) {
await ads.showBannerAd(bannerId);
}
// Load and show a native ad (programmatic API)
final nativeAdId = await ads.loadNativeAd(
adUnitId: 'your-native-ad-unit-id',
);
if (nativeAdId != null) {
await ads.showNativeAd(nativeAdId);
}
// Preload and show interstitial ad
await ads.preloadInterstitialAd(
adUnitId: 'your-interstitial-ad-unit-id',
);
await ads.showInterstitialAd(adUnitId: 'your-interstitial-ad-unit-id');
// Preload and show rewarded ad
await ads.preloadRewardedAd(
adUnitId: 'your-rewarded-ad-unit-id',
);
await ads.showRewardedAd(adUnitId: 'your-rewarded-ad-unit-id');
Caching #
This plugin preloads and caches full-screen ads at the platform level per adUnitId
.
- Preload: call
preloadInterstitialAd(adUnitId)
orpreloadRewardedAd(adUnitId)
ahead of time. - Check: use
isInterstitialReady(adUnitId)
orisRewardedReady(adUnitId)
before showing. - Show: call
showInterstitialAd(adUnitId: ...)
orshowRewardedAd(adUnitId: ...)
. - Auto-preload: after an ad is dismissed or fails to show, the native layer auto-preloads the next ad for the same
adUnitId
to keep a warm cache.
Best practices:
- Preload during app start or at natural pauses (e.g., level start).
- Always check readiness for smoother UX; if not ready, delay or show alternative UI.
- Use one
adUnitId
per placement; the cache holds one ready ad per type peradUnitId
.
Preload → Check → Show (with auto-preload) #
import 'dart:io';
import 'package:native_googleads/native_googleads.dart';
final ads = NativeGoogleads.instance;
late final String interstitialId;
Future<void> initAds() async {
interstitialId = Platform.isAndroid
? AdTestIds.androidInterstitial
: AdTestIds.iosInterstitial;
// 1) Initialize and preload early
await ads.initialize(appId: Platform.isAndroid
? AdTestIds.androidAppId
: AdTestIds.iosAppId);
await ads.preloadInterstitialAd(adUnitId: interstitialId);
// Optional: listen for lifecycle to reflect auto-preload
ads.setAdCallbacks(
onAdDismissed: (type) async {
if (type == 'interstitial') {
await Future.delayed(const Duration(milliseconds: 200));
final ready = await ads.isInterstitialReady(interstitialId);
// Update UI state accordingly
}
},
);
}
Future<void> maybeShowInterstitial() async {
// 2) Check readiness before showing
final ready = await ads.isInterstitialReady(interstitialId);
if (!ready) {
await ads.preloadInterstitialAd(adUnitId: interstitialId);
return;
}
// 3) Show when ready
await ads.showInterstitialAd(adUnitId: interstitialId);
}
Use the same pattern for rewarded ads with preloadRewardedAd
, isRewardedReady
, and showRewardedAd(adUnitId: ...)
.
Using Test Ads #
For development, always use test ad unit IDs:
import 'dart:io';
// Use test IDs during development
final testAdUnitId = Platform.isAndroid
? AdTestIds.androidInterstitial // Test interstitial for Android
: AdTestIds.iosInterstitial; // Test interstitial for iOS
await ads.preloadInterstitialAd(adUnitId: testAdUnitId);
Available test IDs:
AdTestIds.androidInterstitial
/AdTestIds.iosInterstitial
AdTestIds.androidRewarded
/AdTestIds.iosRewarded
AdTestIds.androidBanner
/AdTestIds.iosBanner
AdTestIds.androidNativeAdvanced
/AdTestIds.iosNativeAdvanced
Setting Up Callbacks #
ads.setAdCallbacks(
onAdDismissed: (adType) {
print('Ad dismissed: $adType');
// Handle ad dismissal
},
onAdShowed: (adType) {
print('Ad showed: $adType');
// Handle ad display
},
onAdFailedToShow: (adType, error) {
print('Ad failed: $adType, error: $error');
// Handle ad failure
},
onUserEarnedReward: (type, amount) {
print('Reward earned: $amount $type');
// Grant reward to user
},
);
Working with Banner Ads #
Banner ads can be displayed in various sizes:
// Available banner sizes
BannerAdSize.banner // 320x50
BannerAdSize.largeBanner // 320x100
BannerAdSize.mediumRectangle // 300x250
BannerAdSize.fullBanner // 468x60
BannerAdSize.leaderboard // 728x90
BannerAdSize.adaptive // Adaptive size based on device width
// Load a banner ad (programmatic API)
final bannerId = await ads.loadBannerAd(
adUnitId: Platform.isAndroid
? AdTestIds.androidBanner
: AdTestIds.iosBanner,
size: BannerAdSize.adaptive,
);
// Show the banner (programmatic API)
if (bannerId != null) {
await ads.showBannerAd(bannerId);
}
// Hide the banner (keeps it loaded)
await ads.hideBannerAd(bannerId);
// Dispose when no longer needed
await ads.disposeBannerAd(bannerId);
You can also use the BannerAdWidget
for easier integration:
BannerAdWidget(
adUnitId: AdTestIds.androidBanner,
size: BannerAdSize.adaptive,
onAdLoaded: () => print('Banner loaded'),
onAdFailedToLoad: (error) => print('Banner failed: $error'),
)
Note:
- When using
BannerAdWidget
, do not callshowBannerAd
/hideBannerAd
— the PlatformView handles attaching/detaching the banner. - On iOS, the programmatic
showBannerAd
attaches the banner to the root view controller, not inside a widget tree. - If you preloaded via
loadBannerAd(...)
, you can render that ad by passingpreloadedBannerId
to the widget:BannerAdWidget(preloadedBannerId: bannerId, ...)
.
Working with Native Ads #
Native ads allow you to customize the ad appearance to match your app:
// Load a native ad (programmatic API)
final nativeAdId = await ads.loadNativeAd(
adUnitId: Platform.isAndroid
? AdTestIds.androidNativeAdvanced
: AdTestIds.iosNativeAdvanced,
);
// Show the native ad (programmatic API)
if (nativeAdId != null) {
await ads.showNativeAd(nativeAdId);
}
// Dispose when no longer needed
await ads.disposeNativeAd(nativeAdId);
Use the NativeAdWidget
for easier integration:
NativeAdWidget(
adUnitId: AdTestIds.androidNativeAdvanced,
height: 300,
backgroundColor: Colors.white,
onAdLoaded: () => print('Native ad loaded'),
onAdFailedToLoad: (error) => print('Native ad failed: $error'),
)
Customizing Native Ads (New in 0.0.3)
Customize the appearance of native ads with NativeAdStyle
:
NativeAdWidget(
adUnitId: AdTestIds.androidNativeAdvanced,
height: 350,
style: NativeAdStyle(
// Typography customization
headlineStyle: NativeAdTextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
textColor: Colors.black87,
),
bodyStyle: NativeAdTextStyle(
fontSize: 14,
textColor: Colors.black54,
),
// Layout customization
backgroundColor: Colors.white,
mainBackgroundColor: Colors.grey[50],
cornerRadius: 12,
padding: EdgeInsets.all(16),
// Media customization
mediaStyle: NativeAdMediaStyle(
aspectRatio: 16 / 9,
cornerRadius: 8,
),
// Call-to-action button customization
callToActionStyle: NativeAdButtonStyle(
backgroundColor: Colors.blue,
textColor: Colors.white,
cornerRadius: 6,
),
),
onAdLoaded: () => print('Styled native ad loaded'),
)
Using Native Ad Templates (New in 0.0.3)
Choose from predefined templates for consistent native ad layouts:
NativeAdWidget(
adUnitId: AdTestIds.androidNativeAdvanced,
template: NativeAdTemplate.medium, // small, medium, banner, or large
height: 320,
onAdLoaded: () => print('Template native ad loaded'),
)
Full-Screen Native Ads (New in 0.0.3)
Display native ads in full-screen mode for immersive experiences:
NativeAdWidget(
adUnitId: AdTestIds.androidNativeAdvanced,
isFullScreen: true,
style: NativeAdStyle(
backgroundColor: Colors.black,
headlineStyle: NativeAdTextStyle(
fontSize: 24,
textColor: Colors.white,
),
// ... other style properties
),
onAdLoaded: () => print('Full-screen native ad loaded'),
)
Note:
- If you preloaded via
loadNativeAd(...)
, you can render that ad by passingpreloadedNativeAdId
to the widget:NativeAdWidget(preloadedNativeAdId: nativeId, ...)
.
Complete Example #
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:native_googleads/native_googleads.dart';
class AdExample extends StatefulWidget {
@override
_AdExampleState createState() => _AdExampleState();
}
class _AdExampleState extends State<AdExample> {
final NativeGoogleads _ads = NativeGoogleads.instance;
bool _interstitialReady = false;
bool _rewardedReady = false;
int _rewardAmount = 0;
@override
void initState() {
super.initState();
_initializeAds();
}
Future<void> _initializeAds() async {
// Set up callbacks
_ads.setAdCallbacks(
onAdDismissed: (adType) {
setState(() {
if (adType == 'interstitial') _interstitialReady = false;
if (adType == 'rewarded') _rewardedReady = false;
});
},
onUserEarnedReward: (type, amount) {
setState(() {
_rewardAmount += amount;
});
},
);
// Initialize with test App ID for development
await _ads.initialize(appId: Platform.isAndroid
? AdTestIds.androidAppId
: AdTestIds.iosAppId);
}
Future<void> _preloadInterstitialAd() async {
final adUnitId = Platform.isAndroid
? AdTestIds.androidInterstitial
: AdTestIds.iosInterstitial;
final success = await _ads.preloadInterstitialAd(adUnitId: adUnitId);
setState(() => _interstitialReady = success);
}
Future<void> _showInterstitialAd() async {
if (_interstitialReady) {
await _ads.showInterstitialAd(adUnitId: Platform.isAndroid
? AdTestIds.androidInterstitial
: AdTestIds.iosInterstitial);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Native Google Ads Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Rewards: $_rewardAmount'),
ElevatedButton(
onPressed: _preloadInterstitialAd,
child: Text('Preload Interstitial'),
),
ElevatedButton(
onPressed: _interstitialReady ? _showInterstitialAd : null,
child: Text('Show Interstitial'),
),
],
),
),
);
}
}
API Reference #
NativeGoogleads #
Method | Description | Parameters | Returns |
---|---|---|---|
initialize |
Initialize the ads SDK | appId: String? |
Future<Map<String, dynamic>?> |
loadBannerAd |
Load a banner ad | adUnitId: String , size: BannerAdSize |
Future<String?> |
showBannerAd |
Show loaded banner | bannerId: String |
Future<bool> |
hideBannerAd |
Hide loaded banner | bannerId: String |
Future<bool> |
disposeBannerAd |
Dispose banner ad | bannerId: String |
Future<bool> |
loadNativeAd |
Load a native ad | adUnitId: String |
Future<String?> |
showNativeAd |
Show loaded native ad | nativeAdId: String |
Future<bool> |
disposeNativeAd |
Dispose native ad | nativeAdId: String |
Future<bool> |
preloadInterstitialAd |
Preload an interstitial | adUnitId: String |
Future<bool> |
isInterstitialReady |
Check if interstitial ready | adUnitId: String |
Future<bool> |
showInterstitialAd |
Show preloaded interstitial | adUnitId: String |
Future<bool> |
preloadRewardedAd |
Preload a rewarded ad | adUnitId: String |
Future<bool> |
isRewardedReady |
Check if rewarded is ready | adUnitId: String |
Future<bool> |
showRewardedAd |
Show preloaded rewarded ad | adUnitId: String |
Future<bool> |
setAdCallbacks |
Set ad event callbacks | Various callbacks | void |
BannerAdSize #
Enum for banner ad sizes:
Size | Description | Dimensions | Notes |
---|---|---|---|
banner |
Standard banner | 320x50 | Works on all devices |
largeBanner |
Large banner | 320x100 | Works on all devices |
mediumRectangle |
Medium rectangle | 300x250 | Works on most devices |
fullBanner |
Full banner | 468x60 | Requires tablet or landscape |
leaderboard |
Leaderboard | 728x90 | Requires tablet or large screen |
adaptive |
Adaptive banner | Width based on device | Recommended for best fit |
Note: The widget automatically validates banner sizes and will use a smaller size if the requested size doesn't fit the screen width. For example:
- Leaderboard (728px) on phones → uses Adaptive size
- Full Banner (468px) on small phones → uses Banner size
Testing #
The plugin includes test ad unit IDs from Google. Always use test ads during development:
// Use test ad unit IDs
final testInterstitial = Platform.isAndroid
? AdTestIds.androidInterstitial
: AdTestIds.iosInterstitial;
Production ID Validation #
To help prevent accidental use of Google test ad unit IDs in release:
- Default: warns in release if a test ad unit ID is used.
- Strict mode: throws an error in release when a test ad unit ID is used.
- Disable: you can disable the check if needed.
// Warn-only (default)
NativeGoogleads.instance.setAdIdValidationPolicy(
disallowTestIdsInRelease: true,
strict: false,
);
// Strict: throw in release when using test IDs
NativeGoogleads.instance.setAdIdValidationPolicy(strict: true);
// Disable check
NativeGoogleads.instance.setAdIdValidationPolicy(
disallowTestIdsInRelease: false,
);
Troubleshooting #
Common Issues #
-
Ads not loading
- Ensure you have a valid AdMob account
- Verify your App ID and Ad Unit IDs are correct
- Check internet connectivity
- For iOS, ensure App Tracking Transparency is handled
-
Build errors on Android
- Verify minimum SDK is 24 or higher
- Run
flutter clean
and rebuild
-
Build errors on iOS
- Ensure deployment target is iOS 13.0+
- Run
pod install
in the ios directory - Clean build folder in Xcode
-
Test ads not showing
- Use test App IDs and ad unit IDs
- Check console logs for error messages
- Ensure proper initialization
Migration Guide #
If you're migrating from another ads plugin:
- Remove the old plugin from
pubspec.yaml
- Follow the platform setup instructions above
- Replace initialization and ad loading code
- Update ad unit IDs and callbacks
- Test thoroughly with test ads first
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature
) - Commit your changes (
git commit -m 'Add some AmazingFeature'
) - Push to the branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
License #
This project is licensed under the MIT License - see the LICENSE file for details.
Support #
For issues and feature requests, please use the GitHub issue tracker.
Acknowledgments #
- Google Mobile Ads SDK team
- Flutter community
- All contributors
Changelog #
See CHANGELOG.md for version history.
iOS-Specific Features #
Timeout Mechanism for Pending Ad Results #
On iOS, the plugin includes an automatic timeout mechanism to prevent memory leaks from pending ad results that may never complete. This feature helps manage edge cases where ad loading fails without proper callbacks.
Default Behavior
- Default timeout: 30 seconds
- Automatic cleanup: Removes stale pending results after timeout
- Periodic cleanup: Runs every 60 seconds to clean up very old results
- Logging: Detailed logs in Xcode console for debugging
Configuration
You can configure the timeout duration programmatically:
// Access the method channel for iOS-specific features
final ads = NativeGoogleads.instance;
// Set custom timeout (iOS only)
if (Platform.isIOS) {
await ads.methodChannel.invokeMethod('setAdLoadTimeout', {
'timeout': 15.0, // Set to 15 seconds
});
}
Diagnostic Information
Get diagnostic information about pending results and ad state:
if (Platform.isIOS) {
final diagnosticInfo = await ads.methodChannel.invokeMethod('getDiagnosticInfo');
print('Pending results: ${diagnosticInfo['pendingResultsCount']}');
print('Active timers: ${diagnosticInfo['pendingTimersCount']}');
print('Current timeout: ${diagnosticInfo['currentTimeout']}s');
}
Testing the Timeout Mechanism
The example app includes a dedicated test page for the timeout mechanism:
- Navigate to "Timeout Mechanism Test" in the example app (iOS only)
- Set a short timeout (e.g., 5 seconds)
- Trigger a timeout test with invalid ad unit ID
- Check diagnostic info to verify cleanup
Implementation Details
- Each pending ad result gets a timeout timer
- When timeout occurs, the result receives an
AD_LOAD_TIMEOUT
error - All associated resources are automatically cleaned up
- Periodic cleanup removes results older than 2x timeout duration
- All timers are properly invalidated on plugin deallocation
This mechanism ensures your iOS app maintains optimal memory usage even in edge cases where ad loading encounters unexpected issues.
Made with ❤️ by [Your Name]