Skip to main content

Troubleshooting Guide

info

Work through this guide before contacting support. Start by raising the log level — most problems announce themselves.

TeadsLog.level = LogLevel.debug;

Quick Checklist​

  • TeadsSdk.configure() called once at app start, and it did not throw
  • articleUrl provided on every placement
  • The widget ID matches the placement type
  • Nothing overlays the placement — not even a transparent widget
  • Android: Teads Maven repository added, AdMob APPLICATION_ID present, NDK pinned
  • iOS: platform :ios, '15.0', pod install run, NSUserTrackingUsageDescription present
  • Tested on a real device, not only a simulator or emulator

Installation Issues​

Android: Could not resolve tv.teads.sdk.android:sdk​

The Teads Maven repository is missing, or your build blocks project-level repositories.

// android/build.gradle
allprojects {
repositories {
google()
mavenCentral()
maven { url "https://teads.jfrog.io/artifactory/SDKAndroid-maven-prod" }
}
}

If android/settings.gradle sets repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS), the repository must go in settings.gradle's dependencyResolutionManagement block instead.

Then:

cd android && ./gradlew clean && cd ..
flutter clean && flutter pub get

Android: the app crashes at launch, before any Dart runs​

The AdMob application ID is missing. The native SDK's transitive play-services-ads dependency has a content provider that crashes at process start without one.

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

This is required even if you do not use AdMob. See Installation.

Android: NDK errors or a version-mismatch warning​

Pin the NDK explicitly in android/app/build.gradle:

android {
ndkVersion = "27.1.12297006" // or newer
}

Android: Unsupported class file major version, or other JDK errors​

Use JDK 17 or 21. Android Studio's bundled JBR normally works with no setup. Otherwise:

export JAVA_HOME=/path/to/jdk21
tip

Prefer JAVA_HOME or ~/.gradle/gradle.properties over committing org.gradle.java.home to your repository — a hardcoded JDK path breaks the build on every other machine and on CI.

Android: minSdkVersion conflict​

The plugin requires minSdk 24. Raise your app's value; do not override the plugin's.

iOS: pod install cannot find TeadsSDK​

sudo gem install cocoapods
rm -rf ios/Pods ios/Podfile.lock
cd ios && pod install

If it persists, clear the CocoaPods cache: pod cache clean --all.

iOS: missing-framework errors in Xcode​

Open ios/Runner.xcworkspace, not ios/Runner.xcodeproj. Then Product → Clean Build Folder, and if needed rm -rf ~/Library/Developer/Xcode/DerivedData.

iOS: deployment-target errors​

platform :ios, '15.0' in the Podfile, and the Runner target's IPHONEOS_DEPLOYMENT_TARGET at 15.0 or higher. Also raise any post_install hook that pins IPHONEOS_DEPLOYMENT_TARGET — a hook left behind at 14.0 puts the pods below the app linking them, which surfaces as a compatibility warning from CocoaPods rather than a build failure.

No Ad Appears​

Work down this list in order.

1. Did configure succeed?​

try {
await TeadsSdk.configure('YOUR_APP_KEY');
} catch (e) {
debugPrint('Teads configure failed: $e');
}

2. Does the widget ID match the placement?​

The widget ID selects the ad format

Pointing Banner or Interstitial at Feed's MB_1 serves a feed, which looks exactly like a broken placement. This is the single most common false bug report.

Use MB_10 for Banner, INT_MW_1 for Interstitial, MB_1 for Feed.

3. Is the Feed's widgetIndex non-zero?​

A Feed placement with widgetIndex: 1 (or higher) never loads unless a widgetIndex: 0 placement loads on the same page. No error is reported — it simply waits forever.

Set widgetIndex: 0 unless you deliberately coordinate multiple Feeds.

4. Is it a no-fill?​

No-fill is a normal outcome, not a bug. Check onFailed:

onFailed: (error) => debugPrint('No ad: ${error.reason}'),

5. Is the placement actually laid out?​

  • Not inside a zero-height or zero-width parent
  • Not inside a collapsed Visibility or Offstage
  • Not clipped by a fixed-size SizedBox smaller than the creative

6. Android emulator only: is DNS resolving?​

Android emulators often cannot resolve the Feed's asset domain

The emulator's default DNS resolver frequently fails on widgets.outbrainimg.com with ERR_NAME_NOT_RESOLVED, which looks exactly like a broken Feed. The host machine resolves it fine — the failure is inside the emulator.

Boot the emulator with explicit DNS servers:

emulator -avd <name> -dns-server 8.8.8.8,8.8.4.4

Real devices are not expected to hit this.

Ad Appears But Behaves Wrongly​

Clicks do nothing​

  • Look for an overlay over the placement — including a transparent Container, a gradient scrim, or a Stack sibling drawn on top
  • Look for an ancestor IgnorePointer or AbsorbPointer
  • Test on a real device; touch handling in emulators is less reliable

The browser opens twice on click​

You are opening click.url from onClicked. The SDK already does. Use the callback for analytics only.

The one exception is FeedPlacement.onOrganicClick, which exists so you can navigate.

The placement does not resize​

The widgets resize themselves as the creative reports its height. If yours does not:

  • Remove any fixed-height wrapper
  • Make sure the placement is in a scrollable or flexible parent
  • Check onEvent for TeadsAdEvent.heightUpdated

Feed fullscreen video breaks the layout​

Do not remove widgets from the tree while fullscreen

Collapse siblings with Visibility(maintainState: true) or a zero height. Removing a sibling re-parents the WebView and it stops painting entirely.

Also fold your own chrome away in onFullScreenChange — hide the app bar, stop the surrounding scroll view moving, and hide the system bars.

Feed renders as a large black area​

Rare, and specific to Android's texture-layer rendering path at very large content heights. The plugin hosts Feed's WebView as a real Android view by default precisely to avoid this, so you should not see it unless that default has been overridden.

Rotation is not reflected in a Banner (iOS)​

You have not forwarded viewWillTransition. Android needs nothing; UIKit only hands the transition coordinator to a real view controller:

override func viewWillTransition(
to size: CGSize,
with coordinator: UIViewControllerTransitionCoordinator
) {
super.viewWillTransition(to: size, with: coordinator)
TeadsFlutterPlugin.forwardViewWillTransition(to: size, with: coordinator)
}

Two Banners conflict​

One live BannerPlacement at a time

A process-wide flag in the native SDK means the last wrapped Banner or Feed placement created wins. Keep to one live BannerPlacement.

Interstitial Issues​

show() returns false​

There was no ad to show. Either load() has not completed, or the ad expired.

await interstitial.load();
// Wait for onReady, then:
if (await interstitial.isReady) {
await interstitial.show();
}

The ad was ready and now is not​

Ads expire on a native TTL — roughly an hour by default. Never cache isReady; re-check it immediately before show().

Memory grows across screens​

You are not disposing. InterstitialPlacement holds a native WebView and an Open Measurement session:


void dispose() {
_interstitial?.dispose();
super.dispose();
}

Recommendations Issues​

fetch returns an empty list​

  • Confirm the widget ID is a recommendations ID (SDK_1, RECS_1, TEST_RECS), not a Feed one
  • Confirm the articleUrl is reachable
  • An empty result is a valid response, not necessarily a fault

No viewable impressions, so no revenue​

You must drive RecommendationsViewabilityReporter yourself — the SDK cannot see a list it did not render. See Recommendations Viewability.

The most common cause is creating a new GlobalKey on every build, which restarts measurement each frame so nothing ever reaches the threshold. Cache the keys.

A click is reported for an item nobody tapped​

You are calling registerClick too early. It reports the click as well as resolving the URL, so calling it after fetch to pre-compute URLs reports a phantom click for every organic item.

Call it only in your onTap.

Privacy Issues​

The ATT prompt never appears​

  • NSUserTrackingUsageDescription must be in Info.plist — without it iOS denies the request instead of prompting
  • Request it after the first frame, not before runApp
  • iOS only asks once per install. Reinstall the app to test again

The app hangs on a white screen at launch (physical iPhone)​

You are awaiting requestTrackingAuthorization() before runApp. The completion handler never fires while there is no UI on screen. The Simulator does not reproduce this. Move the call into your first screen's post-frame callback.

Show your CMP before creating placements. Privacy signals are read when a placement assembles its request.

If you set TeadsPrivacy overrides, they win over your CMP's stored values until you call clearAllOverrides().

Testing & Debugging​

Raise the log level​

TeadsLog.level = LogLevel.debug;

Lines are tagged by component — Feed, Banner, Interstitial, Recommendations, RecommendationsViewability. See Logging.

Native logs​

The wrapped placements (Media, Banner, Interstitial) also log natively:

# Android
adb logcat | grep -i teads

On iOS, filter the Xcode console for Teads.

Inspect network traffic​

Use a proxy such as Charles or Proxyman. Note that all SDK network calls go through the platform HTTP stacks, so they honour the OS proxy settings and are visible.

Widget tests​

Placements that use platform views cannot render in flutter test. Use the test doubles:

import 'package:teads_flutter/teads_flutter_mocks.dart';

TeadsSdk.instance = MockTeadsSdk();
TeadsPrivacy.instance = MockTeadsPrivacy();
Recommendations.instance = MockRecommendations(recommendations: [...]);

This is a separate import from the main library on purpose, so test scaffolding never ships in production code.

Always test on real devices​

Emulators and simulators misrepresent DNS, touch handling, video playback and rendering performance. Everything in this guide assumes a real-device check before you conclude anything.

Known Limitations​

Stated plainly so you can judge what matters for your app:

  • One live BannerPlacement at a time. A process-wide flag in the native SDK means the last wrapped Banner or Feed placement created wins.
  • Media Native is not implemented.
  • Feed's dark mode is the only config field that applies to a loaded placement. Every other field takes effect when the page is recreated.
  • Recommendations viewability measurement is not a general occlusion check — see its limitations.
  • Feed autoplays muted video on iOS. Deliberate: a muted video plays on its own, while anything with sound still requires a user action.

Getting Help​

If nothing above applies:

  1. Re-check the Prerequisites checklist
  2. Confirm you are on the latest version available to you
  3. Reproduce in a minimal app, so plugin interactions are ruled out
  4. Contact support with logs, versions and reproduction steps

Quick Reference​

Installation checklist​

  • Dependency added, flutter pub get run
  • Android: Teads Maven repository
  • Android: AdMob APPLICATION_ID
  • Android: NDK pinned, minSdk 24+
  • iOS: platform :ios, '15.0', pod install
  • iOS: NSUserTrackingUsageDescription
  • Both platforms build

Integration checklist​

  • TeadsSdk.configure() once at start
  • ATT requested after the first frame (iOS)
  • articleUrl on every placement
  • Correct widget ID per placement
  • onFailed wired up; onUnsupported wired up for Feed
  • InterstitialPlacement.dispose() called
  • RecommendationsViewabilityReporter.dispose() called
  • Tested on real iOS and Android devices

tip

Pro tip: for a placement that shows nothing, check the widget ID and the Feed widgetIndex first. Between them they explain most "no ad" reports.