Skip to main content

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
ParameterTypeRequiredDescription
unitIdStringYUnit ID created in AdControl console
useCustomClickboolNWhether to use custom click handling (default: false)
listenerAdropNativeListenerNAd event listener

Properties

PropertyTypeDescription
isLoadedboolWhether the ad has finished loading
unitIdStringAd unit ID
creativeIdStringCreative ID
txIdStringTransaction ID
campaignIdStringCampaign ID
destinationURLStringDestination URL
propertiesAdropNativePropertiesNative ad properties
creativeSizeCreativeSizeCreative size
isBackfilledboolWhether it’s a backfill ad

Methods

MethodReturn TypeDescription
load()Future<void>Loads the ad

AdropNativeAdView

A widget that displays native ads on screen.

Constructor

AdropNativeAdView({
  required AdropNativeAd? ad,
  required Widget child,
})
Parameters
ParameterTypeRequiredDescription
adAdropNativeAd?YLoaded native ad object
childWidgetYChild widget to display ad content

AdropNativeProperties

Content properties of the native ad.

Properties

PropertyTypeDescription
headlineString?Ad headline
bodyString?Ad body text
creativeString?HTML creative content
assetString?Image asset URL
destinationURLString?URL to navigate on click
callToActionString?CTA button text
profileAdropNativeProfile?Advertiser profile information
extraMap<String, String>Additional custom fields
isBackfilledboolWhether it’s a backfill ad

AdropNativeProfile

PropertyTypeDescription
displayNameString?Advertiser name
displayLogoString?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

CallbackDescription
onAdReceivedCalled when ad is received successfully
onAdClickedCalled when ad is clicked
onAdImpressionCalled when ad impression occurs
onAdFailedToReceiveCalled 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();
  }
}

Using Extra Fields

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

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) {
          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