DuraIT.Avalonia.AdMob 0.3.1

dotnet add package DuraIT.Avalonia.AdMob --version 0.3.1
                    
NuGet\Install-Package DuraIT.Avalonia.AdMob -Version 0.3.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="DuraIT.Avalonia.AdMob" Version="0.3.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DuraIT.Avalonia.AdMob" Version="0.3.1" />
                    
Directory.Packages.props
<PackageReference Include="DuraIT.Avalonia.AdMob" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add DuraIT.Avalonia.AdMob --version 0.3.1
                    
#r "nuget: DuraIT.Avalonia.AdMob, 0.3.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package DuraIT.Avalonia.AdMob@0.3.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=DuraIT.Avalonia.AdMob&version=0.3.1
                    
Install as a Cake Addin
#tool nuget:?package=DuraIT.Avalonia.AdMob&version=0.3.1
                    
Install as a Cake Tool

<AdMob/>

DuraIT.Avalonia.AdMob

Free, open-source AdMob ads for Avalonia — banner and native in-feed controls, plus interstitial, rewarded, and app-open ads.

NuGet Downloads Quality Gate Coverage MIT


Avalonia ships no ad SDK, and AdMob/Meta/Unity provide MAUI plugins but nothing for Avalonia. This library hosts the native AdMob banner — Android AdView, iOS GADBannerView — inside the Avalonia visual tree through a NativeControlHost, so you drop one control into your XAML and get a real banner on both mobile heads. A native (in-feed) ad control renders inline the same way, blending into your own content. Full-screen interstitial, rewarded, rewarded interstitial, and app-open ads are supported too, presented by the native SDK on demand.

Found a bug or want a format that isn't here yet? Open an issue.

Platform support

Platform Target framework Renders
Android net10.0-android native AdView
iOS net10.0-ios native GADBannerView
Desktop net10.0 inert placeholder strip (so shared UI compiles and runs on the debug head)

Install

dotnet add package DuraIT.Avalonia.AdMob

Usage

1. Register the services

Call AddAdMob once during startup, wherever you build your service collection. It registers every ad format; inject only the ones you use:

using DuraIT.Avalonia.AdMob;

services.AddAdMob(options =>
{
    options.UseTestAds = true; // serve Google's sample test ads during development
});

Prefer to register a single format? Use AddAdMobBanner, AddAdMobInterstitial, AddAdMobRewarded, AddAdMobRewardedInterstitial, or AddAdMobAppOpen instead — they take the same arguments.

Keep UseTestAds = true throughout development — it substitutes Google's public sample ad units, so no real impressions or revenue are generated.

To validate a real ad unit before shipping without generating invalid traffic, register your device with Google instead and list it in TestDeviceIds — real ad units then serve test creatives to that device only:

services.AddAdMobBanner(options =>
{
    options.TestDeviceIds = ["33BE2250B43518CCDA7DE426D04EE231"]; // logged by the SDK on first run
});

2. Place the control

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:admob="using:DuraIT.Avalonia.AdMob.Platforms">
  <DockPanel>
    <admob:BannerAd DockPanel.Dock="Bottom" />
    
  </DockPanel>
</UserControl>

The control is a fixed 320×50 standard banner (Height = 50). With test ads enabled you can leave AdUnitId unset; for production, set your real banner ad unit id:

<admob:BannerAd AdUnitId="ca-app-pub-XXXXXXXXXXXXXXXX/YYYYYYYYYY" />

3. Configure your app id per head

AdMob requires your app id in the platform manifest — even in test mode. Use the sample app ids below during development and swap in your own for release.

AndroidAndroidManifest.xml:

<application>
  <meta-data
    android:name="com.google.android.gms.ads.APPLICATION_ID"
    android:value="ca-app-pub-3940256099942544~3347511713" />
</application>

iOSInfo.plist:

<key>GADApplicationIdentifier</key>
<string>ca-app-pub-3940256099942544~1458002511</string>
<key>SKAdNetworkItems</key>
<array>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>cstr6suwn9.skadnetwork</string>
  </dict>
  
</array>

That's it — the control loads and displays the banner. There is no manual SDK-init call: the Google Mobile Ads SDK is initialized lazily, once consent allows it (see below).

Interstitial ads

An interstitial is a full-screen ad the native SDK presents on demand — there is no control to place in XAML. Inject IInterstitialAdService, load an ad ahead of the transition you want to interrupt, then present it at that point:

using DuraIT.Avalonia.AdMob;

public sealed class GameOverViewModel
{
    private readonly IInterstitialAdService _interstitial;

    public GameOverViewModel(IInterstitialAdService interstitial) => _interstitial = interstitial;

    // Kick off the load early — e.g. when the level starts — so the ad is ready by the transition.
    public Task PreloadAsync() => _interstitial.LoadAsync();

    public async Task ShowGameOverAsync()
    {
        if (_interstitial.IsReady)
        {
            await _interstitial.ShowAsync();
        }

        // Load the next one — an interstitial is single-use.
        await _interstitial.LoadAsync();
    }
}

LoadAsync resolves consent first and only requests an ad once it is allowed; with test ads enabled you can leave the ad unit unset, or pass your own: LoadAsync("ca-app-pub-XXXXXXXXXXXXXXXX/YYYYYYYYYY"). ShowAsync returns false when no ad is ready or the platform (desktop) has no ads, so callers never need a platform check. The app-id manifest setup above (step 3) is shared — an interstitial needs no extra platform configuration.

Rewarded ads

A rewarded ad grants the user something in-app (coins, a hint, an extra life) in exchange for watching it to completion. Inject IRewardedAdService, load ahead of time, and present it when the user opts in. Unlike an interstitial, ShowAsync returns an AdReward? — the reward is earned only if the user finishes the ad, so grant it only when the result is non-null:

using DuraIT.Avalonia.AdMob;

public sealed class ShopViewModel
{
    private readonly IRewardedAdService _rewarded;

    public ShopViewModel(IRewardedAdService rewarded) => _rewarded = rewarded;

    // Preload so the "Watch for coins" button can enable itself the moment an ad is ready.
    public Task PreloadAsync() => _rewarded.LoadAsync();

    public async Task WatchForCoinsAsync()
    {
        if (_rewarded.IsReady)
        {
            AdReward? reward = await _rewarded.ShowAsync();
            if (reward is not null)
            {
                GrantCoins(reward.Amount); // reward.Type / reward.Amount come from the ad-unit config
            }
        }

        // Load the next one — a rewarded ad is single-use.
        await _rewarded.LoadAsync();
    }
}

Rewarded interstitial works identically — inject IRewardedInterstitialAdService instead. It shows at a natural transition without the user opting in first, but still returns an AdReward? for watching to completion. Both formats share the same app-id manifest setup (step 3) and need no extra platform configuration.

App-open ads

An app-open ad is the full-screen ad shown while your app is loading or returning to the foreground. Inject IAppOpenAdService, preload one, and present it from your own foreground hook. Two things set it apart from an interstitial:

  • A loaded ad expires after four hours. IsReady turns false once it goes stale, so check it (or just reload) before showing.
  • The library never shows it for you. It presents on demand only — it does not subscribe to platform lifecycle events, so you decide when the foreground ad appears. This keeps the library out of your app's lifecycle and avoids showing an ad at the wrong moment (e.g. returning from your own consent dialog or an external payment sheet).
using DuraIT.Avalonia.AdMob;

public sealed class AppOpenAdCoordinator
{
    private readonly IAppOpenAdService _appOpen;

    public AppOpenAdCoordinator(IAppOpenAdService appOpen) => _appOpen = appOpen;

    // Call once at startup, then again after each show, so an ad is always warming up.
    public Task PreloadAsync() => _appOpen.LoadAsync();

    // Wire this to your app's "resumed from background" event.
    public async Task OnResumedAsync()
    {
        if (_appOpen.IsReady)
        {
            await _appOpen.ShowAsync();
        }

        // Load the next one — the ad is single-use, and a fresh load resets the four-hour clock.
        await _appOpen.LoadAsync();
    }
}

It shares the same app-id manifest setup (step 3) and needs no extra platform configuration.

Native ads

A native (in-feed) ad is one you place inline in your own layout — a list, a feed, between content cards — instead of a fixed banner strip. Like the banner, it is a control you drop into XAML:

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:admob="using:DuraIT.Avalonia.AdMob.Platforms">
  <StackPanel>
    
    <admob:NativeAd CornerRadius="8" Padding="12" />
    
  </StackPanel>
</UserControl>

With test ads enabled you can leave AdUnitId unset; for production, set your real native ad unit id: <admob:NativeAd AdUnitId="ca-app-pub-XXXXXXXXXXXXXXXX/YYYYYYYYYY" />. Registration is the same as every other format — AddAdMob covers it, or use AddAdMobNative if native is the only format you use. It shares the app-id manifest setup (step 3) and needs no extra platform configuration.

Why you style it with properties, not a template

Unlike an ordinary Avalonia control, a native ad's assets (headline, icon, media, call-to-action, etc.) cannot be composed in your own XAML template. AdMob only counts an impression or click — and stays policy-compliant — when each asset is a real native view registered with the platform SDK's asset wrapper (NativeAdView on Android, GADNativeAdView on iOS). The control therefore renders a fixed native template internally and exposes styling through properties instead. Styles are read once, when the native view is created; changing a style property after the ad has rendered does not restyle it live.

Property Type Purpose
AdUnitId string? Native ad unit to load (substituted with a sample unit when test ads are on).
ShowIcon, ShowMedia, ShowBody, ShowAdvertiser, ShowStarRating, ShowPrice, ShowStore bool Whether each optional asset is shown when the ad provides one. All default to true.
CardBackground Color? Card background color. null (default) keeps the platform's own default.
HeadlineForeground, BodyForeground Color? Text colors. null (default) keeps the platform defaults.
CallToActionBackground, CallToActionForeground Color? Call-to-action button colors. null (default) keeps the platform defaults.
HeadlineFontSize, BodyFontSize double Text sizes in device-independent pixels. Default 16 / 14.
CornerRadius double Card corner radius. Default 0.
Padding Thickness Padding between the card edge and its assets. Default none.

For users in regulated regions (e.g. the EEA), the control requests consent through Google's User Messaging Platform before any ad is requested, presents the consent form if one is required, and only then initializes the SDK — Google's documented conditional-initialization pattern. If the consent service can't be reached, it fails open (a transient network hiccup won't permanently block ads).

Once real ads ship, Google requires a persistent way for users to change their choice. IBannerAdService exposes it:

public sealed class SettingsViewModel
{
    private readonly IBannerAdService _bannerAds;

    public SettingsViewModel(IBannerAdService bannerAds) => _bannerAds = bannerAds;

    // Show a "Manage ad consent" button only where a privacy-options entry point applies.
    public bool CanManageConsent => _bannerAds.IsPrivacyOptionsRequired;

    public Task ManageConsentAsync() => _bannerAds.ShowPrivacyOptionsAsync();
}

If your app targets users under the age of consent, set options.TagForUnderAgeOfConsent = true.

Logging (optional)

Pass an ILoggerFactory to surface ad-load outcomes (loaded, failed, blocked-by-consent):

services.AddAdMobBanner(
    options => options.UseTestAds = true,
    loggerFactory); // e.g. new SerilogLoggerFactory()

Without one, ad-load logging is silently discarded.

Testing gotchas

  • Ad-blocking VPNs/DNS break test ads. Proton VPN NetShield (and similar) return NXDOMAIN for Google's ad domains, so the request fails as an invalid request / no fill with a blank banner. Turn ad-blocking off while testing. The iOS simulator uses your Mac's network stack, so it inherits any host-level VPN/DNS block.
  • Test-ad throttling. Hammering a test ad unit (≈10+ rapid loads) makes Google return "no fill" for a cooldown period. Not a bug — space out your requests.

License

MIT © Durable IT Solutions

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-android36.0 is compatible.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-ios26.0 is compatible.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.3.1 0 8/3/2026
0.3.0 39 7/31/2026
0.2.0 35 7/30/2026
0.1.0 115 7/23/2026