Unity Ads Plugin
📢 Follow for updates: t.me/pavelzaichyk_dev
A Flutter plugin for integrating Unity Ads into your mobile applications. Easily display Unity Banner Ads and Unity Video Ads (Rewarded & Interstitial) with simple Dart APIs.
Features
- Display Unity Banner, Rewarded, and Interstitial Video Ads
- Simple initialization and ad loading
- Server-to-server reward callback support
- Privacy consent management
- Firebase Test Lab support (Android)
- Null safety
Table of Contents
Installation
Add the plugin to your pubspec.yaml:
dependencies:
unity_ads_plugin: ^<latest_version>
Then run:
flutter pub get
Usage Example
Here is a minimal example of using the plugin:
import 'package:unity_ads_plugin/unity_ads_plugin.dart';
void main() {
UnityAds.init(
gameId: 'YOUR_GAME_ID',
testMode: true,
onComplete: () => print('Unity Ads Initialized'),
onFailed: (error, message) => print('Unity Ads Initialization Failed: $error $message'),
);
}
For more detailed usage, see the Getting Started section.
Getting Started
1. Initialization
UnityAds.init(
gameId: 'PROJECT_GAME_ID',
onComplete: () => print('Initialization Complete'),
onFailed: (error, message) => print('Initialization Failed: $error $message'),
);
Set your Game ID.
For testing purposes, set testMode to true.
UnityAds.isInitialized() can be used to check if the SDK has initialized successfully.
Android only: To change ad behavior in Firebase Test Lab, use the firebaseTestLabMode parameter. Possible values:
| Mode | Description |
|---|---|
| disableAds | Ads are not displayed in Firebase Test Lab (default) |
| showAdsInTestMode | Ads are displayed in test mode. |
| showAds | Real ads are displayed if testMode is false. |
2. Show Rewarded/Interstitial Video Ad

Load a video ad before showing it.
UnityAds.load(
placementId: 'PLACEMENT_ID',
onComplete: (placementId) => print('Load Complete $placementId'),
onFailed: (placementId, error, message) => print('Load Failed $placementId: $error $message'),
);
Show a loaded ad.
UnityAds.showVideoAd(
placementId: 'PLACEMENT_ID',
onStart: (placementId) => print('Video Ad $placementId started'),
onClick: (placementId) => print('Video Ad $placementId click'),
onSkipped: (placementId) => print('Video Ad $placementId skipped'),
onComplete: (placementId) => print('Video Ad $placementId completed'),
onFailed: (placementId, error, message) => print('Video Ad $placementId failed: $error $message'),
);
Server-to-server Redeem Callbacks
UnityAds.showVideoAd has a serverId parameter. To use server-to-server callbacks, set this parameter. Read more at docs.unity.com.
3. Show Banner Ad

Place the UnityBannerAd widget in your app.
UnityBannerAd(
placementId: 'PLACEMENT_ID',
onLoad: (placementId) => print('Banner loaded: $placementId'),
onClick: (placementId) => print('Banner clicked: $placementId'),
onShown: (placementId) => print('Banner shown: $placementId'),
onFailed: (placementId, error, message) => print('Banner Ad $placementId failed: $error $message'),
)
Privacy Consent
Read more about privacy consent in the Unity Ads documentation.
Use the following code to pass the appropriate consent flags to the Unity Ads SDK:
UnityAds.setPrivacyConsent(<Privacy Consent type>, true)
Troubleshooting
Android: the ad shows up as a separate app in Recent Apps
Symptom. While a fullscreen ad (interstitial or rewarded) is playing, opening Recent Apps shows two cards for your app: your app and the ad. The user can swipe the ad away in one gesture, so the ad never completes and the impression is lost.
Why it happens. Android groups screens into tasks — one task is one card in Recent Apps. Which task a screen joins is decided by a label called task affinity: by default every screen in your app shares the same affinity (your package name) and therefore the same card.
Unity's ad screen is launched with FLAG_ACTIVITY_NEW_TASK. That flag doesn't mean "make a new
card" — it means "put me in the task I belong to", and Android picks that task by matching
affinities (Tasks and the back stack):
If there's an existing task with the same affinity as the new activity, the activity is launched into that task. If not, it begins a new task.
Since 2024 the Flutter app template stamps MainActivity with a blank affinity
(flutter#144018), while Unity's ad screen keeps
the default one. The labels don't match, so Android opens a second task — a second card.
Fix. Delete one line from your app's android/app/src/main/AndroidManifest.xml:
<activity
android:name=".MainActivity"
android:taskAffinity="" <!-- delete this line -->
...>
Both screens then share the default affinity, the ad joins your app's task, and Recent Apps shows a single card again. This is a change in your app — the plugin cannot override it, because the attribute lives in your manifest and the ad screen is declared inside Unity's SDK.
Trade-off. android:taskAffinity="" is there to block task hijacking, where a malicious
app already installed on the device claims your affinity and slips a fake screen (say, a login
form) in front of your user.
How much you give up depends on your minSdk. Per Android's own
StrandHogg / task affinity
page:
Android SDK versions 30 and newer (Android 11) contain the appropriate OS patches to avoid this vulnerability.
While it is possible to partially mitigate version 1 of the StrandHogg attack through individual application configuration, version 2 of the attack can only be prevented by this SDK version patch.
minSdk30 or higher — the OS already blocks both variants of the attack. The attribute buys you nothing; delete it.minSdk24–29 (Flutter's default is 24) — users on Android 7–10 lose partial protection against one variant of the attack. Android's recommended fix is raisingminSdkto 30, which also resolves the Recent Apps problem as a side effect.
Apps handling logins, payments, or personal data on Android 7–10 may prefer to keep the attribute and accept the lost impressions. That's a decision for your app, not the plugin.
What doesn't work. Adding android:excludeFromRecents="true" to Unity's ad activity (via
tools:replace) does not help — the ad still gets its own card while it is on screen. Neither
does setting a matching android:taskAffinity="" on Unity's activity: a blank affinity never
matches an existing task, so Android still creates a new one.
See issue #70.
Donate
If you find this package helpful and would like to support its continued development, please consider making a donation. Your contributions are greatly appreciated and motivate the further enhancement of this and other plugins.
Your support helps maintain and improve this package, ensuring it remains up-to-date and useful for the community.
Thank you for your generosity!
