Overview
Native ads are customizable ads that blend naturally with your app’s content. You can configure the ad to match your app’s UI using AdropNativeAd and AdropNativeAdView.
Key Features
- Customizable layout to match app design
- Provides various elements like headline, body, CTA button, profile, etc.
- Supports image and HTML creative
- Custom click handling support
- Backfill ad support
Use the test unit ID in development: PUBLIC_TEST_UNIT_ID_NATIVE
AdropNativeAd
Constructor
AdropNativeAd({
required String unitId,
bool useCustomClick = false,
AdropNativeListener? listener,
})
Parameters
| Parameter | Type | Required | Description |
|---|
unitId | String | Y | Unit ID created in Ad Control Console |
useCustomClick | bool | N | Whether to use custom click handling (default: false) |
listener | AdropNativeListener | N | Ad event listener |
Properties
| Property | Type | Description |
|---|
isLoaded | bool | Whether the ad has finished loading |
unitId | String | Ad unit ID |
creativeId | String | Creative ID |
txId | String | Transaction ID |
campaignId | String | Campaign ID |
destinationURL | String | Destination URL |
properties | AdropNativeProperties | Native ad properties |
creativeSize | CreativeSize | Creative size |
isBackfilled | bool | Whether it’s a backfill ad |
browserTarget | BrowserTarget? | Browser target (external or internal) |
Methods
| Method | Return Type | Description |
|---|
load() | Future<void> | Loads the ad |
Unlike other ad types (AdropInterstitialAd, AdropRewardedAd, AdropPopupAd), AdropNativeAd does not provide a dispose() method.
To load a new ad, create a new AdropNativeAd instance. Resources from previous instances are automatically released by the garbage collector.
AdropNativeAdView
A widget that displays native ads on screen.
Constructor
AdropNativeAdView({
required AdropNativeAd? ad,
required Widget child,
})
Parameters
| Parameter | Type | Required | Description |
|---|
ad | AdropNativeAd? | Y | Loaded native ad object |
child | Widget | Y | Child widget to display ad content |
AdropNativeProperties
Content properties of the native ad.
Properties
| Property | Type | Description |
|---|
headline | String? | Ad headline |
body | String? | Ad body text |
creative | String? | HTML creative content |
asset | String? | Image asset URL |
destinationURL | String? | URL to navigate on click |
callToAction | String? | CTA button text |
profile | AdropNativeProfile? | Advertiser profile information |
extra | Map<String, String> | Additional custom fields |
isBackfilled | bool | Whether it’s a backfill ad |
AdropNativeProfile
| Property | Type | Description |
|---|
displayName | String? | Advertiser name |
displayLogo | String? | Advertiser logo image URL |
Basic Usage
import 'package:flutter/material.dart';
import 'package:adrop_ads_flutter/adrop_ads_flutter.dart';
class NativeAdExample extends StatefulWidget {
const NativeAdExample({super.key});
@override
State<NativeAdExample> createState() => _NativeAdExampleState();
}
class _NativeAdExampleState extends State<NativeAdExample> {
bool isLoaded = false;
AdropNativeAd? nativeAd;
@override
void initState() {
super.initState();
_createNativeAd();
}
void _createNativeAd() {
nativeAd = AdropNativeAd(
unitId: 'YOUR_UNIT_ID',
listener: AdropNativeListener(
onAdReceived: (ad) {
debugPrint('Native ad received successfully: ${ad.creativeId}');
setState(() {
isLoaded = true;
});
},
onAdClicked: (ad) {
debugPrint('Native ad clicked: ${ad.creativeId}');
},
onAdImpression: (ad) {
debugPrint('Native ad impression: ${ad.creativeId}');
},
onAdFailedToReceive: (ad, errorCode) {
debugPrint('Native ad failed to receive: $errorCode');
setState(() {
isLoaded = false;
});
},
),
);
// Load ad
nativeAd?.load();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Native Ad Example')),
body: SingleChildScrollView(
child: Column(
children: [
// Main content
const Padding(
padding: EdgeInsets.all(16),
child: Text('Main Content'),
),
// Native ad
if (isLoaded) _buildNativeAdView(),
],
),
),
);
}
Widget _buildNativeAdView() {
return AdropNativeAdView(
ad: nativeAd,
child: Container(
padding: const EdgeInsets.all(16),
margin: const EdgeInsets.all(16),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Advertiser profile
if (nativeAd?.properties.profile != null)
Row(
children: [
if (nativeAd?.properties.profile?.displayLogo != null)
Image.network(
nativeAd!.properties.profile!.displayLogo!,
width: 24,
height: 24,
),
const SizedBox(width: 8),
Text(nativeAd?.properties.profile?.displayName ?? ''),
],
),
const SizedBox(height: 8),
// Headline
if (nativeAd?.properties.headline != null)
Text(
nativeAd!.properties.headline!,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
// Body
if (nativeAd?.properties.body != null)
Text(nativeAd!.properties.body!),
const SizedBox(height: 8),
// Image asset
if (nativeAd?.properties.asset != null)
Image.network(
nativeAd!.properties.asset!,
width: double.infinity,
fit: BoxFit.cover,
),
const SizedBox(height: 8),
// CTA button
if (nativeAd?.properties.callToAction != null)
ElevatedButton(
onPressed: () {},
child: Text(nativeAd!.properties.callToAction!),
),
],
),
),
);
}
}
AdropNativeListener
Listener to handle native ad events.
Callbacks
AdropNativeListener(
onAdReceived: (AdropNativeAd ad) {
// Ad received successfully
},
onAdClicked: (AdropNativeAd ad) {
// Ad clicked
},
onAdImpression: (AdropNativeAd ad) {
// Ad impression
},
onAdFailedToReceive: (AdropNativeAd ad, AdropErrorCode errorCode) {
// Ad failed to receive
},
)
Callback Descriptions
| Callback | Description |
|---|
onAdReceived | Called when ad is received successfully |
onAdClicked | Called when ad is clicked |
onAdImpression | Called when ad impression occurs |
onAdFailedToReceive | Called when ad fails to receive |
Custom Click Handling
Use useCustomClick when video creative or custom click behavior is needed.
nativeAd = AdropNativeAd(
unitId: 'YOUR_UNIT_ID',
useCustomClick: true, // Enable custom click
listener: AdropNativeListener(
onAdReceived: (ad) {
setState(() {
isLoaded = true;
});
},
onAdClicked: (ad) {
// Handle custom click action
debugPrint('Ad clicked: ${ad.destinationURL}');
},
),
);
When useCustomClick is true, child widget click events are handled as ad clicks.
Displaying HTML Creative
If the native ad includes HTML creative, you can display it using WebView.
import 'package:webview_flutter/webview_flutter.dart';
class NativeWithWebView extends StatefulWidget {
const NativeWithWebView({super.key});
@override
State<NativeWithWebView> createState() => _NativeWithWebViewState();
}
class _NativeWithWebViewState extends State<NativeWithWebView> {
bool isLoaded = false;
AdropNativeAd? nativeAd;
late final WebViewController webViewController;
@override
void initState() {
super.initState();
// Initialize WebView controller
webViewController = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted);
_createNativeAd();
}
void _createNativeAd() {
nativeAd = AdropNativeAd(
unitId: 'YOUR_UNIT_ID',
useCustomClick: true,
listener: AdropNativeListener(
onAdReceived: (ad) {
// Load HTML creative
if (ad.properties.creative != null) {
webViewController.loadHtmlString(ad.properties.creative!);
}
setState(() {
isLoaded = true;
});
},
),
);
nativeAd?.load();
}
@override
Widget build(BuildContext context) {
if (!isLoaded) return const SizedBox.shrink();
return AdropNativeAdView(
ad: nativeAd,
child: Container(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Profile
Row(
children: [
if (nativeAd?.properties.profile?.displayLogo != null)
Image.network(
nativeAd!.properties.profile!.displayLogo!,
width: 24,
height: 24,
),
const SizedBox(width: 8),
Text(nativeAd?.properties.profile?.displayName ?? ''),
],
),
const SizedBox(height: 8),
// Headline
if (nativeAd?.properties.headline != null)
Text(
nativeAd!.properties.headline!,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
// HTML creative
SizedBox(
width: MediaQuery.of(context).size.width,
height: 300,
child: WebViewWidget(controller: webViewController),
),
// CTA button
if (nativeAd?.properties.callToAction != null)
Padding(
padding: const EdgeInsets.only(top: 8),
child: ElevatedButton(
onPressed: () {},
child: Text(nativeAd!.properties.callToAction!),
),
),
],
),
),
);
}
}
To use HTML creative, you need to add the webview_flutter package.flutter pub add webview_flutter
Handling Backfill Ads
You can check and handle backfill ads using the isBackfilled property.
Widget _buildCreativeView() {
if (nativeAd?.isBackfilled == true) {
// Backfill ad: Use image asset
return Image.network(
nativeAd?.properties.asset ?? '',
width: double.infinity,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return const Center(child: CircularProgressIndicator());
},
errorBuilder: (context, error, stackTrace) {
return const Icon(Icons.error);
},
);
} else if (nativeAd?.properties.creative != null) {
// Regular ad: Use HTML creative
return SizedBox(
width: double.infinity,
height: 300,
child: WebViewWidget(controller: webViewController),
);
} else {
return const SizedBox.shrink();
}
}
Additional fields defined by publishers can be accessed from the extra map.
onAdReceived: (ad) {
// Access extra fields
final customField = ad.properties.extra['customFieldKey'];
if (customField != null) {
debugPrint('Custom field: $customField');
}
setState(() {
isLoaded = true;
});
}
Error Handling
When backfill ads are configured, the backfillNoFill error code is returned when both direct ads and backfill ads are unavailable.
onAdFailedToReceive: (ad, errorCode) {
if (errorCode == AdropErrorCode.backfillNoFill) {
debugPrint('No backfill ads available');
}
}
General Error Handling
class _NativeAdState extends State<NativeAdWidget> {
bool isLoaded = false;
AdropErrorCode? errorCode;
AdropNativeAd? nativeAd;
@override
void initState() {
super.initState();
nativeAd = AdropNativeAd(
unitId: 'YOUR_UNIT_ID',
listener: AdropNativeListener(
onAdReceived: (ad) {
setState(() {
isLoaded = true;
errorCode = null;
});
},
onAdFailedToReceive: (ad, error) {
// Handle backfill-related errors
if (error == AdropErrorCode.backfillNoFill) {
debugPrint('No backfill ads available');
}
setState(() {
isLoaded = false;
errorCode = error;
});
},
),
);
nativeAd?.load();
}
@override
Widget build(BuildContext context) {
if (isLoaded) {
return _buildNativeAdView();
} else if (errorCode != null) {
return Text('Ad load failed: ${errorCode?.code}');
} else {
return const SizedBox.shrink();
}
}
Widget _buildNativeAdView() {
// Native ad UI composition
return AdropNativeAdView(
ad: nativeAd,
child: Container(
// ...
),
);
}
}
Best Practices
1. Recreating Ads
To load a new ad, create a new AdropNativeAd instance.
void resetAd() {
setState(() {
isLoaded = false;
});
nativeAd = AdropNativeAd(
unitId: 'YOUR_UNIT_ID',
listener: AdropNativeListener(
onAdReceived: (ad) {
setState(() {
isLoaded = true;
});
},
),
);
nativeAd?.load();
}
2. Conditional Rendering
Display an appropriate placeholder until the ad loads.
Widget buildNativeAd() {
if (isLoaded && nativeAd != null) {
return _buildNativeAdView();
} else if (isLoading) {
return const SizedBox(
height: 200,
child: Center(child: CircularProgressIndicator()),
);
} else {
return const SizedBox.shrink();
}
}
3. Responsive Layout
Configure layout to adapt to various screen sizes.
Widget _buildNativeAdView() {
return AdropNativeAdView(
ad: nativeAd,
child: LayoutBuilder(
builder: (context, constraints) {
return Container(
width: constraints.maxWidth,
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Ad content
],
),
);
},
),
);
}
4. Null Check for Ad Properties
Native ad properties can be null, so always check them.
Widget _buildHeadline() {
final headline = nativeAd?.properties.headline;
if (headline == null || headline.isEmpty) {
return const SizedBox.shrink();
}
return Text(
headline,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
);
}
Next Steps
Interstitial Ads
Implement full-screen interstitial ads
Rewarded Ads
Implement rewarded ads that provide rewards
Popup Ads
Implement popup-style ads
Reference
Reference types, methods, and error codes