- Unlike other ad formats where the mediation SDK implements the ad view, native ads provide ad components for you to build the ad view directly.
- The key advantage is minimizing disruption by implementing layouts based on your UI/UX. However, you must include ad markers to prevent users from confusing ads with content.
4 Required Native Ad ElementsNative ads can be freely designed to match your app’s UI, but the following 4 elements are mandatory:
- Ad Label: Add “AD” or similar text so users clearly recognize it as an advertisement.
- AdChoices Icon: Automatically inserted by the DARO SDK. Ensure it’s not obscured by other UI elements.
- Ad Title: Display the ad’s headline.
- CTA (Call to Action): Include a CTA button such as “Install”, “Open”, “Download”. The size and format are flexible.
Registering Native Factory
To use native ads in Flutter, you must register a native ad factory on the native platform (iOS/Android). The factoryId is the identifier of the factory registered on the native platform.
Create Ad Layout XML
Create a native ad layout file in android/app/src/main/res/layout/.<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="400dp">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardCornerRadius="12dp"
app:cardElevation="4dp"
app:cardBackgroundColor="#1E1E1E">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="12dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="12dp">
<ImageView
android:id="@+id/ad_app_icon"
android:layout_width="56dp"
android:layout_height="56dp"
android:scaleType="centerCrop" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="12dp"
android:orientation="vertical">
<TextView
android:id="@+id/ad_headline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textStyle="bold"
android:maxLines="2"
android:ellipsize="end" />
<TextView
android:id="@+id/ad_advertiser"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="13sp"
android:layout_marginTop="4dp" />
</LinearLayout>
</LinearLayout>
<FrameLayout
android:id="@+id/ad_media"
android:layout_width="match_parent"
android:layout_height="180dp" />
<TextView
android:id="@+id/ad_body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:textSize="14sp"
android:maxLines="2"
android:ellipsize="end" />
<Button
android:id="@+id/ad_call_to_action"
android:layout_width="match_parent"
android:layout_height="44dp"
android:layout_marginTop="12dp"
android:textSize="16sp"
android:textStyle="bold" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</FrameLayout>
Implement DaroNativeAdFactory
Create a class that implements the DaroNativeAdFactory interface.import android.content.Context
import android.view.LayoutInflater
import android.view.View
import so.daro.flutter.native.DaroNativeAdFactory
import droom.daro.core.model.DaroNativeAdBinder
class SampleNativeAdFactory(private val context: Context) : DaroNativeAdFactory {
override fun createNativeAdView(): View {
return LayoutInflater.from(context).inflate(R.layout.sample_native_ad, null)
}
override fun createAdBinder(view: View): DaroNativeAdBinder {
return DaroNativeAdBinder.Builder(view)
.setTitleViewId(R.id.ad_headline)
.setBodyTextViewId(R.id.ad_body)
.setIconViewId(R.id.ad_app_icon)
.setMediaViewGroupId(R.id.ad_media)
.setCallToActionViewId(R.id.ad_call_to_action)
.build()
}
}
Register Factory in MainActivity
Register the factory in configureFlutterEngine of your MainActivity.import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import so.daro.flutter.DaroFlutterPlugin
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
DaroFlutterPlugin.registerNativeAdFactory(
SampleNativeAdFactory(this),
"sample_native_ad"
)
}
}
Implement DaroNativeAdFactory
Create a factory class that implements the DaroNativeAdFactory protocol and a custom view that inherits from DaroAdNativeView.SampleNativeAdFactory.swift
import UIKit
import daro_flutter
import Daro
class SampleNativeAdFactory: NSObject, DaroNativeAdFactory {
func createNativeAdView(unit: DaroAdUnit) -> DaroAdNativeView {
return SampleNativeAdContentView(unit: unit)
}
}
final class SampleNativeAdContentView: DaroAdNativeView {
init(unit: DaroAdUnit) {
super.init(unit: unit)
setupUI()
}
@available(*, unavailable)
public required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// Define UI elements
let iconImageView: UIImageView = {
let view = UIImageView()
view.contentMode = .scaleAspectFill
view.layer.cornerRadius = 8
view.clipsToBounds = true
return view
}()
let titleLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 16, weight: .bold)
label.numberOfLines = 2
return label
}()
let advertiserLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 13)
label.numberOfLines = 1
return label
}()
let bodyLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 14)
label.numberOfLines = 2
return label
}()
let mediaContentView = UIView()
let callToActionButton: UIButton = {
let button = UIButton()
button.titleLabel?.font = .systemFont(ofSize: 16, weight: .bold)
button.backgroundColor = .systemBlue
button.layer.cornerRadius = 8
return button
}()
private func setupUI() {
// Configure layout (AutoLayout, etc.)
// ...
// Bind views to Daro SDK
bindViews(
titleLabel: titleLabel,
advertiserLabel: advertiserLabel,
bodyLabel: bodyLabel,
iconImageView: iconImageView,
mediaContentView: mediaContentView,
callToActionButton: callToActionButton
)
}
}
Register Factory in AppDelegate
Register the factory in application(_:didFinishLaunchingWithOptions:) of your AppDelegate.import Flutter
import UIKit
import daro_flutter
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
DaroFlutterPlugin.registerNativeAdFactory(
SampleNativeAdFactory(),
factoryId: "sample_native_ad"
)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
The factoryId passed to DaroNativeAd must exactly match the factory ID registered on the native platform.
Implementing Native Ads
Native ads follow a Create Ad object → Load → Display widget → Dispose pattern.
import 'package:daro_flutter/daro_flutter.dart';
1. Create Ad Object
Create a DaroNativeAd object and configure callbacks.
final nativeAd = DaroNativeAd(
adUnitId: '{YOUR_AD_UNIT_ID}',
factoryId: '{YOUR_FACTORY_ID}',
onAdLoaded: (adInfo) {
print('Native ad loaded: ${adInfo.adUnitId}');
setState(() {
_isAdLoaded = true;
});
},
onAdFailedToLoad: (error) {
print('Native ad failed to load: ${error.message}');
nativeAd.dispose();
},
onAdClicked: (adInfo) {
print('Native ad clicked');
},
onAdImpression: (adInfo) {
print('Native ad impression');
},
);
2. Load the Ad
After the onAdLoaded callback fires, place the DaroNativeAdWidget in the widget tree.
@override
Widget build(BuildContext context) {
return Column(
children: [
// ... other widgets
if (_isAdLoaded)
DaroNativeAdWidget(ad: nativeAd),
],
);
}
Placing DaroNativeAdWidget in the widget tree before calling ad.load() will throw a FlutterError. Always mount the widget after the onAdLoaded callback.
4. Dispose Resources
Call dispose() to clean up resources when leaving the page or when the ad is no longer needed.
@override
void dispose() {
nativeAd.dispose();
super.dispose();
}
Callback Reference
| Callback | Description |
|---|
onAdLoaded | Called when the ad is successfully loaded |
onAdFailedToLoad | Called when ad loading fails |
onAdClicked | Called when the user clicks the ad |
onAdImpression | Called when an ad impression is recorded |
LINE Native Ads
- LINE native ads are compact native ad formats designed to be naturally inserted into feeds or lists.
- They are more compact than standard native ads, ideal for embedding between list items.
- The ad height is fixed at 36dp (Android) / 36pt (iOS).
- Styles can be customized to match your app’s UI.
Implementing LINE Native Ads
LINE native ads follow the same Create Ad object → Load → Display widget → Dispose pattern.
1. Create Ad Object
final lineNativeAd = DaroLineNativeAd(
adUnitId: '{YOUR_AD_UNIT_ID}',
onAdLoaded: (adInfo) {
print('Line native ad loaded: ${adInfo.adUnitId}');
setState(() {
_isAdLoaded = true;
});
},
onAdFailedToLoad: (error) {
print('Line native ad failed to load: ${error.message}');
lineNativeAd.dispose();
},
onAdClicked: (adInfo) {
print('Line native ad clicked');
},
onAdImpression: (adInfo) {
print('Line native ad impression');
},
);
2. Load the Ad
if (_isAdLoaded)
DaroLineNativeAdWidget(ad: lineNativeAd),
Placing DaroLineNativeAdWidget in the widget tree before calling ad.load() will throw a FlutterError. Always mount the widget after the onAdLoaded callback.
4. Dispose Resources
@override
void dispose() {
lineNativeAd.dispose();
super.dispose();
}
Callback Reference
| Callback | Description |
|---|
onAdLoaded | Called when the ad is successfully loaded |
onAdFailedToLoad | Called when ad loading fails |
onAdClicked | Called when the user clicks the ad |
onAdImpression | Called when an ad impression is recorded |
Style Customization
Use DaroLineNativeAdStyle to customize LINE native ad colors. Pass the style when creating the DaroLineNativeAd object.
final lineNativeAd = DaroLineNativeAd(
adUnitId: '{YOUR_AD_UNIT_ID}',
style: DaroLineNativeAdStyle(
backgroundColor: Colors.white,
contentColor: Colors.black,
adMarkLabelTextColor: Colors.white,
adMarkLabelBackgroundColor: Colors.grey,
),
onAdLoaded: (adInfo) {
print('Line native ad loaded');
setState(() {
_isAdLoaded = true;
});
},
onAdFailedToLoad: (error) {
print('Line native ad failed: ${error.message}');
lineNativeAd.dispose();
},
);
Style Properties
| Property | Type | Description |
|---|
backgroundColor | Color? | Overall ad background color |
contentColor | Color? | Ad content text color |
adMarkLabelTextColor | Color? | Ad mark label text color |
adMarkLabelBackgroundColor | Color? | Ad mark label background color |