magic_ad_sdk

English | 简体中文

MagicAdSDK Flutter plugin for Android and iOS.

This package provides a Flutter MethodChannel bridge for MagicAdSDK, including SDK initialization, ad loading, ad display, PlatformView-based ad rendering, event streams, and object lifecycle management.

Features

  • SDK initialization and logging configuration
  • Splash ads
  • Rewarded video ads
  • Interstitial ads
  • Banner ads with Flutter PlatformView embedding
  • Feed ads with Flutter PlatformView embedding
  • Self-render feed ads with Flutter PlatformView embedding
  • Ad lifecycle events through broadcast streams
  • Per-ad-object lifecycle management with objectID

Requirements

Flutter and Dart:

  • Flutter: >=3.10.0
  • Dart SDK: >=3.0.0 <4.0.0

Android:

  • minSdk: 21
  • compileSdk: 34
  • Java: 11
  • Kotlin Gradle Plugin: 2.1.0
  • Android Gradle Plugin: 8.9.1
  • MagicAdSDK Android: com.magic.ad.sdk:magicadsdk:0.0.63

iOS:

  • iOS deployment target: 13.0
  • Swift: 5.0
  • MagicAdSDK iOS: MagicAdSDK 0.0.63

Installation

Add the package to your Flutter app:

dependencies:
  magic_ad_sdk: ^0.0.1

Then import the unified API entry:

import 'package:magic_ad_sdk/magic_index.dart';

Android Setup

MagicAdSDK is resolved from the Magic Maven repository. Add the repository to your host Android project.

For newer Flutter Android templates, configure repositories in android/settings.gradle or the root Gradle file used by your app:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        maven {
            url "https://mv.magicacid.cn/repository/maven-public/"
            allowInsecureProtocol = true
        }
        google()
        mavenCentral()
    }
}

If your app needs to load HTTP ad assets, configure Android cleartext traffic for the required asset domains in your host app. One common approach is to add a network_security_config.xml file and reference it from AndroidManifest.xml:

<application
    android:networkSecurityConfig="@xml/network_security_config">
</application>

iOS Setup

The plugin podspec depends on MagicAdSDK 0.0.63, which is integrated through CocoaPods when your Flutter app installs pods.

Set the minimum deployment target to iOS 13.0 or later in your app's ios/Podfile:

platform :ios, '13.0'

Then install pods:

cd ios
pod install

If your app uses IDFA or location-based capabilities, request the required permissions in the host app and pass the resulting configuration to MagicAdSDK according to your app's privacy and compliance requirements.

Initialize SDK

Register event streams before initialization if you need to observe init status:

final initSubscription =
    MagicListenerManager.initEventHandler.listen((event) {
  if (event.initStatus == MagicInitStatus.initSuccess) {
    debugPrint('MagicAdSDK initialized');
  } else if (event.initStatus == MagicInitStatus.initFailed) {
    debugPrint('MagicAdSDK init failed: ${event.errorMessage}');
  }
});

await MagicInitManger.initMagicSDK(appId: 'your_app_id');
await MagicInitManger.setLogEnabled(logEnabled: true);

Ad Object Lifecycle

Each loadXxx call returns an independent ad object with its own objectID. Multiple ads can be loaded for the same placement without overwriting each other.

Always destroy ad objects when they are no longer needed:

await adObject.destroy();

Recommended lifecycle rules:

  • Store the returned ad object while the page owns it.
  • Filter stream events by event.adObject?.objectID.
  • Use getPrice() after the ad has loaded if you need the native bid price value.
  • Call destroy() in dispose, on route exit, or before reusing an ad slot.
  • If a page exits while loadXxx is still awaiting, destroy the returned object immediately once the future completes.
  • When an ad sends a closed event, clear the reference held by your page.

Splash Ads

MagicSplashAdObject? splashAd;

final splashSubscription =
    MagicListenerManager.splashEventHandler.listen((event) {
  if (event.adObject?.objectID != splashAd?.objectID) return;

  if (event.isClosed) {
    splashAd = null;
  }
});

splashAd = await MagicSplashManager.loadSplash(
  placementID: 'your_placement_id',
);

if (await splashAd?.ready() == true) {
  await splashAd?.show();
}

Rewarded Video Ads

MagicRewardedAdObject? rewardedAd;

final rewardedSubscription =
    MagicListenerManager.rewardedVideoEventHandler.listen((event) {
  if (event.adObject?.objectID != rewardedAd?.objectID) return;

  if (event.rewardedVideoStatus == MagicRewardedVideoStatus.rewarded) {
    debugPrint('Reward granted');
  }

  if (event.isClosed) {
    rewardedAd = null;
  }
});

rewardedAd = await MagicRewardedManager.loadRewardedVideo(
  placementID: 'your_placement_id',
);

if (await rewardedAd?.ready() == true) {
  await rewardedAd?.show();
}

Interstitial Ads

MagicInterstitialAdObject? interstitialAd;

final interstitialSubscription =
    MagicListenerManager.interstitialEventHandler.listen((event) {
  if (event.adObject?.objectID != interstitialAd?.objectID) return;

  if (event.isClosed) {
    interstitialAd = null;
  }
});

interstitialAd = await MagicInterstitialManager.loadInterstitialAd(
  placementID: 'your_placement_id',
);

if (await interstitialAd?.ready() == true) {
  await interstitialAd?.show();
}

Load a banner ad and embed it with MagicBannerAdView:

MagicBannerAdObject? bannerAd;

bannerAd = await MagicBannerManager.loadBannerAd(
  placementID: 'your_placement_id',
  width: 320,
  height: 50,
);
if (bannerAd != null)
  SizedBox(
    width: 320,
    height: 50,
    child: MagicBannerAdView(adObject: bannerAd!),
  )

Destroy the banner when it leaves the page:

await bannerAd?.destroy();
bannerAd = null;

Feed Ads

Load a feed ad and embed it with MagicFeedAdView:

MagicFeedAdObject? feedAd;

feedAd = await MagicFeedManager.loadFeedAd(
  placementID: 'your_placement_id',
  width: MediaQuery.sizeOf(context).width,
  height: 0,
);
if (feedAd != null)
  MagicFeedAdView(adObject: feedAd!)

You can query and control video state when supported by the native ad:

final isVideo = await feedAd?.isVideo();
final isPlaying = await feedAd?.isPlaying();
await feedAd?.pauseVideo();
await feedAd?.resumeVideo();

Self-render Feed Ads

Load a self-render feed ad and embed it with MagicSelfRenderFeedAdView:

MagicSelfRenderFeedAdObject? selfRenderFeedAd;

selfRenderFeedAd = await MagicFeedManager.loadSelfRenderFeedAd(
  placementID: 'your_placement_id',
  width: MediaQuery.sizeOf(context).width,
  height: 0,
);
if (selfRenderFeedAd != null)
  MagicSelfRenderFeedAdView(adObject: selfRenderFeedAd!)

Self-render feed video events are emitted through selfRenderFeedEventHandler.

Error Handling

Native loading failures are reported in two ways:

  • The Future returned by loadXxx throws a PlatformException.
  • The corresponding event stream emits a loadFailed event.
import 'package:flutter/services.dart';

try {
  final ad = await MagicSplashManager.loadSplash(
    placementID: 'your_placement_id',
  );
  await ad?.show();
} on PlatformException catch (error) {
  debugPrint('Load failed: ${error.code} ${error.message}');
}

Stream events also include fields such as errorCode, errorMessage, rawMap, and adObject.

Event Streams

Available event streams:

  • MagicListenerManager.initEventHandler
  • MagicListenerManager.splashEventHandler
  • MagicListenerManager.rewardedVideoEventHandler
  • MagicListenerManager.interstitialEventHandler
  • MagicListenerManager.bannerEventHandler
  • MagicListenerManager.feedEventHandler
  • MagicListenerManager.selfRenderFeedEventHandler

Because these streams are broadcast streams, multiple pages can receive the same type of event. Always filter events by the ad object's objectID when a page owns a specific ad.

final subscription =
    MagicListenerManager.bannerEventHandler.listen((event) {
  if (event.adObject?.objectID != bannerAd?.objectID) return;
  debugPrint('Banner event: ${event.bannerStatus}');
});

Cancel stream subscriptions when the owning object or page is disposed:

await subscription.cancel();

Extra Parameters

Each ad loading API accepts an optional extraMap. Key helper methods are provided by each manager:

final ad = await MagicRewardedManager.loadRewardedVideo(
  placementID: 'your_placement_id',
  extraMap: {
    MagicRewardedManager.videoMuteKey(): true,
    MagicRewardedManager.bidFloorKey(): 10,
  },
);

Common helper methods include:

  • MagicSplashManager.bidFloorKey()
  • MagicSplashManager.videoMuteKey()
  • MagicRewardedManager.bidFloorKey()
  • MagicRewardedManager.videoMuteKey()
  • MagicInterstitialManager.bidFloorKey()
  • MagicInterstitialManager.videoMuteKey()
  • MagicBannerManager.bidFloorKey()
  • MagicBannerManager.videoMuteKey()
  • MagicFeedManager.bidFloorKey()
  • MagicFeedManager.autoPlayKey()
  • MagicFeedManager.videoMuteKey()
  • MagicCommon.getShowCustomExtKey()

API Overview

Initialization:

  • MagicInitManger.initMagicSDK({required appId})
  • MagicInitManger.getSDKVersion()
  • MagicInitManger.setLogEnabled({logEnabled})

Splash:

  • MagicSplashManager.loadSplash({placementID, extraMap})
  • MagicSplashAdObject.ready()
  • MagicSplashAdObject.getPrice()
  • MagicSplashAdObject.show()
  • MagicSplashAdObject.destroy()

Rewarded video:

  • MagicRewardedManager.loadRewardedVideo({placementID, extraMap})
  • MagicRewardedAdObject.ready()
  • MagicRewardedAdObject.getPrice()
  • MagicRewardedAdObject.show()
  • MagicRewardedAdObject.destroy()

Interstitial:

  • MagicInterstitialManager.loadInterstitialAd({placementID, extraMap})
  • MagicInterstitialAdObject.ready()
  • MagicInterstitialAdObject.getPrice()
  • MagicInterstitialAdObject.show()
  • MagicInterstitialAdObject.destroy()

Banner:

  • MagicBannerManager.loadBannerAd({placementID, width, height, extraMap})
  • MagicBannerAdView(adObject: bannerAd)
  • MagicBannerAdObject.realHeight()
  • MagicBannerAdObject.getPrice()
  • MagicBannerAdObject.destroy()

Feed:

  • MagicFeedManager.loadFeedAd({placementID, width, height, extraMap})
  • MagicFeedAdView(adObject: feedAd)
  • MagicFeedAdObject.realHeight()
  • MagicFeedAdObject.isVideo()
  • MagicFeedAdObject.isPlaying()
  • MagicFeedAdObject.pauseVideo()
  • MagicFeedAdObject.resumeVideo()
  • MagicFeedAdObject.getPrice()
  • MagicFeedAdObject.destroy()

Self-render feed:

  • MagicFeedManager.loadSelfRenderFeedAd({placementID, width, height, extraMap})
  • MagicSelfRenderFeedAdView(adObject: selfRenderFeedAd)
  • MagicSelfRenderFeedAdObject.realHeight()
  • MagicSelfRenderFeedAdObject.isVideo()
  • MagicSelfRenderFeedAdObject.isPlaying()
  • MagicSelfRenderFeedAdObject.pauseVideo()
  • MagicSelfRenderFeedAdObject.resumeVideo()
  • MagicSelfRenderFeedAdObject.getPrice()
  • MagicSelfRenderFeedAdObject.destroy()

Example App

The example/ directory contains a Flutter sample app showing SDK initialization, ad loading, PlatformView embedding, event handling, and object cleanup.

Run it with:

cd example
flutter pub get
flutter run

License

This package is released under the MIT License. See LICENSE for details.