Skip to content

Flutter SDK ​

Flutter 插件 adbnk_sdk 封装了 ADBNK 的 Android 与 iOS 原生 SDK,渲染、点击处理和追踪均由各平台的原生 SDK 完成。Dart API 命名与 google_mobile_ads 一致:AdRequest、AdLoadCallback、FullScreenContentCallback 等。

版本:以你拿到的插件包为准,可通过 AdbnkSdk.sdkVersion 获取。

环境要求 ​

项目要求
Flutter3.0+(Dart 2.19+)
AndroidminSdk 21,compileSdk 34+
iOS12.0+

每个广告位还需要一个广告位 ID。在流量主后台创建 App SDK 广告位,进入广告位管理,点击获取代码。

安装 ​

插件及其依赖的原生 SDK 以 SDK 包形式交付。如需获取,请联系你的 ADBNK 客户经理。

把插件解压到你的项目中(例如 packages/adbnk_sdk),并以 path 依赖引入:

yaml
# pubspec.yaml
dependencies:
  adbnk_sdk:
    path: packages/adbnk_sdk
bash
flutter pub get

插件还需要 SDK 包里的原生 SDK 文件:

  • Android: 把 AAR 和 POM 放到 adbnk_sdk/android/libs/repo/app/adbnk/adbnk-sdk/<版本号>/(文件名为 adbnk-sdk-<版本号>.aar 与 .pom)。插件会把该目录当作本地 Maven 仓库。如果你的 settings.gradle 把 dependencyResolutionManagement 设为 PREFER_SETTINGS,还需在其中加入 maven { url = uri("<路径>/adbnk_sdk/android/libs/repo") }。
  • iOS: 把 AdbnkSDK.xcframework 放到 adbnk_sdk/ios/Frameworks/AdbnkSDK.xcframework,在 ios/Podfile 中设置 platform :ios, '12.0' 或更高,然后执行 pod install。

缺少原生文件时,构建会停止并打印期望的路径。

初始化 ​

dart
import 'package:adbnk_sdk/adbnk_sdk.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final status = await AdbnkSdk.initialize(
    appId: 'com.example.myapp',                 // your app identifier
  );
  // status.ready, status.sdkVersion
  runApp(const MyApp());
}

在 initialize 完成之前调用任何加载方法都会抛出 StateError。

AdbnkConfig 字段:

字段默认值说明
testModefalse见测试模式
gdprConsentnullGDPR 同意状态。null 表示未知,按未同意处理,见隐私与合规
coppaCompliancefalse将所有请求视为面向儿童

其他成员:AdbnkSdk.isInitialized、AdbnkSdk.sdkVersion、setGDPRConsent(bool)、setCOPPACompliance(bool)、setTestMode(bool)。

AdRequest ​

dart
final request = AdRequest.builder()
    .addKeyword('sports')
    .setContentUrl('https://example.com/article/123')
    .setNonPersonalizedAds(false)
    .setTagForChildDirectedTreatment(null)   // true / false / null = unspecified
    .setTagForUnderAgeOfConsent(null)
    .putExtra('placement', 'home_feed')
    .build();

const AdRequest.empty() 为默认请求。

广告格式 ​

格式Dart API
横幅 BannerBannerAdWidget(widget)
悬浮横幅(固定在顶部/底部)FloatBannerAd
原生 NativeNativeAdWidget(widget)/ NativeAd
插屏 InterstitialInterstitialAd
激励 RewardedRewardedAd
激励插屏 Rewarded interstitialRewardedInterstitialAd
开屏 SplashSplashAd
应用打开 App openAppOpenAd / AppOpenAdManager
弹窗 PopupPopupAd

全屏格式:通用流程 ​

插屏、激励、激励插屏、开屏、应用打开和弹窗广告的用法相同:

dart
static Future<void> load(
  String zoneId, {
  AdRequest? adRequest,
  required AdLoadCallback<T> adLoadCallback,
});
  1. 在 onAdLoaded 中设置 ad.fullScreenContentCallback。
  2. 调用 ad.show()。
  3. 广告关闭后调用 ad.dispose()。
dart
FullScreenContentCallback<T>(
  onAdShowedFullScreenContent: (ad) {},
  onAdDismissedFullScreenContent: (ad) {},
  onAdFailedToShowFullScreenContent: (ad, error) {},
  onAdImpression: (ad) {},
  onAdClicked: (ad) {},
)

每个广告对象只能展示一次,展示后 ad.isReady 变为 false。

每种格式同时只保留一个已加载广告

每种格式同一时间只持有一个已加载的广告,事件会派发给最近一次加载的实例。请先展示当前插屏,再加载下一个插屏。

插屏 ​

dart
InterstitialAd? _interstitial;

void loadInterstitial() {
  InterstitialAd.load(
    'YOUR_ZONE_ID',
    adLoadCallback: AdLoadCallback<InterstitialAd>(
      onAdLoaded: (ad) {
        ad.fullScreenContentCallback = FullScreenContentCallback<InterstitialAd>(
          onAdDismissedFullScreenContent: (ad) {
            ad.dispose();
            _interstitial = null;
            loadInterstitial();           // preload the next one
          },
          onAdFailedToShowFullScreenContent: (ad, error) {
            ad.dispose();
            _interstitial = null;
          },
        );
        _interstitial = ad;
      },
      onAdFailedToLoad: (error) => debugPrint('load failed: $error'),
    ),
  );
}

void showInterstitial() {
  if (_interstitial?.isReady ?? false) _interstitial!.show();
}

激励与激励插屏 ​

RewardedAd 与 RewardedInterstitialAd 的 API 相同。把奖励回调传给 show(),每个广告最多调用一次。

dart
RewardedAd.load(
  'YOUR_ZONE_ID',
  adLoadCallback: AdLoadCallback<RewardedAd>(
    onAdLoaded: (ad) {
      ad.fullScreenContentCallback = FullScreenContentCallback<RewardedAd>(
        onAdDismissedFullScreenContent: (ad) => ad.dispose(),
      );
      ad.show(onUserEarnedReward: (RewardItem reward) {
        grantReward(reward.amount, reward.type);
      });
    },
    onAdFailedToLoad: (error) {},
  ),
);

奖励数量和类型在流量主后台的广告位上配置。

开屏 ​

dart
SplashAd.load(
  'YOUR_ZONE_ID',
  adLoadCallback: AdLoadCallback<SplashAd>(
    onAdLoaded: (ad) {
      ad.fullScreenContentCallback = FullScreenContentCallback<SplashAd>(
        onAdDismissedFullScreenContent: (ad) { ad.dispose(); goToHome(); },
        onAdFailedToShowFullScreenContent: (ad, _) { ad.dispose(); goToHome(); },
      );
      ad.show();
    },
    onAdFailedToLoad: (_) => goToHome(),
  ),
);

应用打开 ​

AppOpenAd.load(...) 的用法与其他全屏格式相同。如需在 App 每次回到前台时展示应用打开广告,使用 AppOpenAdManager,并在生命周期监听中调用:

dart
class _AppState extends State<MyApp> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    AppOpenAdManager.instance.setup(zoneId: 'YOUR_ZONE_ID');
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed) {
      AppOpenAdManager.instance.showAdIfAvailable();
    }
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    AppOpenAdManager.instance.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => const MaterialApp(home: HomePage());
}

每次广告关闭后,管理器会预加载下一个广告;加载失败时会自动重试。

dart
PopupAd.load(
  'YOUR_ZONE_ID',
  adLoadCallback: AdLoadCallback<PopupAd>(
    onAdLoaded: (ad) {
      ad.fullScreenContentCallback = FullScreenContentCallback<PopupAd>(
        onAdDismissedFullScreenContent: (ad) => ad.dispose(),
      );
      ad.show();
    },
  ),
);

BannerAdWidget 是一个 platform view,创建时即加载广告。

dart
BannerAdWidget(
  zoneId: 'YOUR_ZONE_ID',
  size: BannerSize.banner,              // 320x50
  adRequest: AdRequest.empty(),         // optional
  callback: BannerAdCallbackWrapper(
    onLoaded: () {},
    onFailed: (AdError e) {},
    onImpression: () {},
    onClicked: () {},
  ),
)

BannerSize 取值:

值尺寸
banner320×50
largeBanner320×100
mediumRectangle300×250
fullBanner468×60
leaderboard728×90
adaptive屏幕宽度 × 50

请选择与广告位一致的尺寸。屏幕上可以同时存在多个横幅。

悬浮横幅 ​

悬浮横幅加载成功后会自动展示。

dart
final floatBanner = FloatBannerAd(
  zoneId: 'YOUR_ZONE_ID',
  position: FloatBannerPosition.bottom,
  height: 50,
  margin: 0,
  showCloseButton: true,
  refreshInterval: 0,                   // seconds, 0 = no refresh
);
await floatBanner.load(callback: BannerAdCallbackWrapper(onFailed: (e) {}));

// floatBanner.hide();  floatBanner.show();  floatBanner.isShowing
// When you no longer need it:
floatBanner.dispose();

原生 ​

使用 NativeAdWidget 时,你提供布局,widget 负责加载、记录曝光和处理点击:

dart
NativeAdWidget(
  zoneId: 'YOUR_ZONE_ID',
  builder: (context, NativeAdData? ad, bool isLoaded) {
    if (!isLoaded || ad == null) return const SizedBox.shrink();
    return Card(
      child: ListTile(
        leading: ad.iconUrl != null ? Image.network(ad.iconUrl!) : null,
        title: Text(ad.headline ?? ''),
        subtitle: Text(ad.body ?? ''),
        trailing: Text(ad.callToAction ?? ''),
      ),
    );
  },
)

点击该 widget 会打开广告。请始终在布局中展示 ad.sponsoredLabel("广告"标识)。

NativeAdData 字段:

字段说明
headline广告标题
body广告正文
callToAction行动按钮文案
iconUrl图标图片 URL
imageUrl主图 URL
advertiser广告主名称
rating评分
price价格
store应用商店
sponsoredLabel"广告"标识文案

如需完全自行控制,可直接使用 NativeAd:

  1. 调用 await ad.load(callback: ...)。
  2. 读取 ad.adData。
  3. 广告真实展示给用户时调用 ad.recordImpression()。
  4. 仅在用户真实点击时调用 ad.recordClick()。
  5. 用完后调用 ad.dispose()。

点击处理 ​

无需自行处理点击。原生 SDK 记录点击后,若广告带有 deeplink 则打开它;没有 deeplink 或无法打开时,改为打开落地页。

隐私与合规 ​

SDK 从不弹出同意框或权限框。由你的 App 收集同意,再把结果传给 SDK。

GDPR(欧洲经济区/英国用户)。 用你的 CMP 收集同意,然后调用:

dart
await AdbnkSdk.setGDPRConsent(userConsented);

请在加载广告之前完成。未设置时同意状态为未知,SDK 按未同意处理。

非个性化广告:

dart
AdRequest.builder().setNonPersonalizedAds(true)

COPPA / 面向儿童的 App:

dart
await AdbnkSdk.setCOPPACompliance(true);                   // whole app
AdRequest.builder().setTagForChildDirectedTreatment(true); // per request
AdRequest.builder().setTagForUnderAgeOfConsent(true);

iOS ATT。 SDK 不弹出 ATT 授权框。如需请求用户授权追踪:

  1. 在 ios/Runner/Info.plist 中添加 NSUserTrackingUsageDescription。
  2. 使用你选择的 ATT 插件请求授权。

用户未授权追踪时,SDK 不读取 IDFA,广告照常投放。详见 iOS 隐私与合规。

商店披露。 Google Play 数据安全与 App Store 隐私标签以原生 SDK 为准,见 Android 和 iOS 页面。

测试模式 ​

  • testMode: true(或 AdbnkSdk.setTestMode(true))会把请求标记为测试请求。ADBNK 目前不提供专用测试素材,开发期间看到的是真实广告。请勿在自己的设备上反复点击广告。
  • SDK 自动处理流量质量保护,无需任何配置。

错误码 ​

AdError 包含 code 和 message。

来自原生 SDK 的错误保留各平台的错误码:

Dart 层自身产生的错误:

错误码含义
1001广告位 ID 无效
1002无填充(原生广告未返回素材时也使用此码)
1003网络错误
1004内部错误(message 中包含原生错误码)
1005广告已过期或未就绪,例如在加载完成前或已展示后调用了 show()
1006广告已展示过
1007已有加载在进行中
1008不支持的广告格式
1009当前平台没有可用的原生插件

常见问题 ​

横幅在桌面端或 Web 上不显示。 插件只支持 Android 和 iOS。在其他平台上,BannerAdWidget 渲染为空白区域,不会加载广告,调用原生 SDK 会失败(返回 AdError 时错误码为 1009)。

回调触发到了错误的广告上。 每种全屏格式只保留一个已加载的广告。请在上一个广告展示或 dispose 之后再加载下一个。

还能使用 loadWithCallback 和 *AdCallback 类吗? 仍可使用,但已废弃。请改用静态方法 load(..., adLoadCallback:) 配合 FullScreenContentCallback。

排查信息。 全屏广告提供 ad.responseInfo?.responseId,联系支持时请附上该 ID。

按 MIT 许可发布的文档内容。