displayio_sdk 0.1.0 copy "displayio_sdk: ^0.1.0" to clipboard
displayio_sdk: ^0.1.0 copied to clipboard

Flutter plugin for the DIO (Display.io) ad SDKs — banner, infeed, interstitial, interscroller, audio and native ads on Android and iOS through one Dart API.

displayio_sdk #

The DIO (Display.io) Flutter plugin. It wraps the native DIO ad SDKs for Android and iOS, so a Flutter app loads and shows DIO ads through a single Dart API — every DIO ad format, one call site.

Status: all formats are implemented and device-verified on both platforms. The package is pre-1.0 — the API may still change.

Requirements #

Flutter >= 3.44.1 (Dart SDK ^3.12.1)
Android minSdk 24; pulls com.brandio.ads:sdk from https://maven.display.io/
iOS minimum deployment target 15.0; DIOSDK via Swift Package Manager

The Android artifact lives in DIO's own Maven repo, so add it to your app's settings.gradle.kts (dependencyResolutionManagement) or build.gradle.kts:

repositories {
    maven { url = uri("https://maven.display.io/") }
}

You need a DIO app id and placement ids from the DIO dashboard.

Install #

dependencies:
  displayio_sdk: ^0.1.0
import 'package:displayio_sdk/displayio_sdk.dart';

Quick start #

Initialize once at startup, then load a placement. The returned DioAd is a sealed type — switch on it to get the right presentation:

await DioSdk.instance.initialize(appId: 'APP_ID');

final ad = await DioSdk.instance.loadAd('PLACEMENT_ID');
switch (ad) {
  case DioInlineAd():       // banner / infeed / inline / interscroller
    DioAdView(ad: ad, reveal: true);
  case DioInterstitialAd():
    await ad.show();
  case DioAudioAd():        // InFlowAudio / InRing
    ad.play();
    ad.pause();
    ad.companionView;       // DioCompanionView? — null when absent
  case DioInGameAudioAd(): // the SDK's own square card, you position it
    Positioned(top: 16, right: 16, child: ad.cardView);
  case DioNativeAd():
    DioNativeAdView(ad: ad, child: /* your layout + slot widgets */);
}

The concrete ad type comes from ad.type, not from the placement type — an inline placement resolves at load time to a banner, infeed, or interscroller.

On iOS the first initialize call blocks the main thread briefly (the native SDK creates a WKWebView synchronously to read the user agent), which stalls Flutter rendering. Call it at startup / on a splash screen, and judge UI smoothness in profile or release builds, not debug.

Formats #

Format Presentation
Banner (HTML / video / audio) DioAdView
Infeed (video / display / audio) DioAdView
Interscroller (display / video / audio) DioAdView(reveal: true) — Dart-driven reveal parallax
Inline DioAdView (resolves to one of the above)
Interstitial (display / video / audio) ad.show()
InFlowAudio / InRing ad.play() / ad.pause() + optional companion view
In-Game Audio ad.cardView — the SDK's own card, positioned by you
Native DioNativeAdView + slot widgets

Native ads #

Native ads are publisher-rendered: the SDK gives you the text assets and fills its own media / icon / CTA slots; you build the layout with your own Flutter widgets.

DioNativeAdView(
  ad: ad,
  child: Column(
    children: [
      Text(ad.headline ?? ''),
      DioNativeIconView(ad: ad, size: 44),                // optional
      DioNativeMediaView(ad: ad, aspectRatio: 16 / 9),    // required — click + viewability root
      Text(ad.body ?? ''),
      DioNativeCtaView(ad: ad, text: ad.callToAction),    // optional, separately tracked CTA
    ],
  ),
);
  • DioNativeAdView registers and unregisters the slots for click and impression tracking; wrapping the layout in it is required.
  • Text (headline, body, callToAction, advertiser, price, privacy) is plain Flutter. There are no image URLs — icon and main image are rendered by the SDK into native slots, and click/viewability need real native views.
  • A Flutter-drawn CTA cannot be click-tracked; use DioNativeCtaView or rely on the media tap.

In-Game Audio #

An audio ad that the SDK renders as its own square card — gradient artwork, an animated equalizer, an AD badge, a progress ring and a native mute control. It is neither an inline ad (it takes no slot in the content flow) nor a headless audio ad (there is no play / pause — playback follows attachment and on-screen visibility, and the user mutes from the card).

You position the card yourself, which is the whole point of the format:

Stack(children: [
  gameSurface,
  Positioned(top: 16, right: 16, child: ad.cardView),
]);
ad.preferredSize;   // the card's natural (square) size — cardView uses it
await ad.dispose(); // or just remove the widget: detaching ends the ad

Resize it through the placement, not the widget:

await DioSdk.instance.loadAd('PID', options: const DioAdOptions(
  inGameAudio: DioInGameAudioConfig(customWidth: 160),  // the square's side
  audio: DioAudioControls(showSoundControl: true),      // iOS mute control
));

DioInGameAudioView(width:, height:) sizes the slot, not the creative — anything below preferredSize crops the progress ring and the mute control, which sit at the card's edges.

The mute control is asymmetric because the native SDKs are: on Android it is a view flag (DioInGameAudioView(showSoundControl: true), the default), on iOS a placement flag set before the request (DioAudioControls.showSoundControl). Showing it matters — with the control hidden, some creatives' decorative audio bars act as the ad's click target, so a tap meant to mute counts as a click.

If the creative has an end-card, the SDK swaps it into the same card; there is no separate companion widget.

Customization — DioAdOptions #

Per-load options span two axes: placement styling and the ORTB ad request. Every field is nullable and null means leave the native SDK default untouched.

final ad = await DioSdk.instance.loadAd('PID', options: DioAdOptions(
  // placement styling — only the group matching the resolved format applies
  audio: DioAudioControls(showSoundControl: false, accentColor: Color(0xFF00A2FF)),
  interscroller: DioInterscrollerConfig(headerText: 'Ad', showTapHint: false),
  infeed: DioInfeedConfig(fullWidth: true, ctaButtonInfeedColor: Colors.black),
  pureAudioAd: DioPureAudioAdConfig(autoRequestEnabled: false),
  inGameAudio: DioInGameAudioConfig(customWidth: 160),
  native: DioNativeRequestConfig(video: DioAssetParams(required: true)),
  // ORTB bid request / targeting — always applied
  request: DioAdRequestConfig(
    user: DioRequestUser(yob: 1990, gender: DioGender.female),
    bcat: ['IAB25'],
    tmax: 1000,
  ),
));

Ad metadata #

Each loaded ad carries a read-only snapshot of the bid response:

ad.metadata?.ecpm;
ad.metadata?.advertiserName;
ad.metadata?.creativeId;
ad.description; // "AdUnit: <type>, Placement id: <id>, Request id: <id>"

Placement listing (diagnostics) #

final placements = await DioSdk.instance.placements();
// [16209 (inGameAudio), 5133 (interstitial), 6363 (mediumrectangle), ...]

What the dashboard returned for the initialized app id — useful when a typo in the app id would otherwise only surface as a no-fill. It returns an empty list before initialize and on any native error, and never throws, so a diagnostic screen calling it from initState cannot break. DioPlacement.type is null for a unit this plugin does not model (the deprecated ones); rawType keeps the native string either way. It carries no ad state by design — the native SDK stays the single source of truth for that.

Server-to-server (ORTB) #

For a mediation / S2S flow, let the SDK build the bid request, POST it to your own exchange, and render the response you get back:

final requestJson = await DioSdk.instance.buildOrtbRequest('PID');
final token = await DioSdk.instance.token(); // DIO user token (user.buyeruid)

// ... POST requestJson to your exchange, receive ortbJson ...

final ad = await DioSdk.instance.loadAdFromOrtb('PID', ortbJson);

The result is an ordinary DioAd — the same widgets, show, events, metadata, and dispose apply. loadAdFromOrtb takes the raw ORTB JSON string; only the placement part of options applies (including native asset params, which the parser needs so asset ids match).

Known limitations #

  • iOS interscroller does not auto-pause/resume media on scroll visibility. That logic is scroll-driven inside the native SDK, and the plugin hosts the interscroller in a scroll view whose offset is pinned (the parallax is Dart-driven), so the SDK never sees a visibility change. Android is unaffected.
  • *TextSize options are Android-only — iOS folds text size into a font the SDK does not expose.
  • Custom fonts are not exposed. A Flutter-bundled font is invisible to native Typeface / UIFont without registering it in the native project, which would break the single-Dart-API promise.
  • interscroller / infeed styling applies only when the placement resolves directly to that format — reached through an inline placement, the concrete sub-placement is unknown at config time and the styling is skipped.
  • Audio output routing is the publisher's job on iOS (a global AVAudioSession); setAudioOutput is a no-op there. See example/ for a demo using the audio_session package.
  • Native ads use up to three platform views (media / icon / CTA) — mind the count in a scrolling feed, and note that viewability is measured on the media slot rather than the whole card.

Example #

example/ is a full test app covering every format, the customization and ORTB flows, and metadata:

cd example && flutter run

Placement inventory lives in example/lib/test_placements.dart and ad customization in example/lib/test_ad_config.dart.

License #

MIT — see LICENSE.

0
likes
0
points
208
downloads

Publisher

verified publisherdisplay.io

Weekly Downloads

Flutter plugin for the DIO (Display.io) ad SDKs — banner, infeed, interstitial, interscroller, audio and native ads on Android and iOS through one Dart API.

Homepage
Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on displayio_sdk

Packages that implement displayio_sdk