pangle_flutter

Introduction: Flutter 版穿山甲 SDK,支持 Android、iOS。A Flutter plugin that supports Pangle SDK on Android and iOS.
More: Author   ReportBugs   
Tags:

Thanks for non-commercial open source development authorization by JetBrains.

pangle_flutter是一款集成了字节跳动穿山甲 Android 和 iOS SDK 的 Flutter 插件。

Platform Pub Package Build Status License: MIT

目录

原生平台相关范例:

版本迁移

  1. 不再需要传入 isExpress 参数
  2. BannerView, FeedView, SplashView 均需要包一层限制大小的 Widget, 可选 Container, SizeBox, AspectRatio, Expanded 等
  3. BannerView, FeedView, SplashView 的控制点击实现变动,可参考 example 进行更改。

SDK 对应版本

  • 已经内部依赖相关 sdk,无需额外导入。如需替换新的 sdk,请自行 fork 本项目更改依赖。

[Android] 5.0+

[iOS] 5.0+

注:如果出现高版本不兼容问题,可联系我升级适配,或者使用上面指定版本。

官方文档(需要登陆)

使用文档

范例截图

集成步骤

1. 添加 yaml 依赖

dependencies:
  # 添加依赖
  pangle_flutter: latest

2. Android 和 iOS 额外配置

  • iOS 版本依赖配置

    本项目默认集成Ads-CN, 如果你是国内 APP,无需额外配置;如果你是海外 APP,请参照如下配置:(已移除)

  • 使用说明

支持开屏广告、激励视频、全屏视频(新模板渲染插屏广告)、模板渲染信息流、模板渲染插屏、模板渲染 Banner。(如有自渲染广告位请联系我,或提交 Feature request)

1. 信息流广告

pangle_flutter

pangle_flutter

2. iOS 使用纯 OC 开发的项目导入该模块

  1. 创建一个 Swift 文件,名称随意
  2. 根据提示选择Create Bridging Header。如果没有提示,请自行搜索如何创建。

OC 导入 Swift 模块

使用说明

1. 初始化

import 'package:pangle_flutter/pangle_flutter.dart';
/// 如果在 runApp 方法调用之前初始化,加入下面这句代码
WidgetsFlutterBinding.ensureInitialized();
/// 初始化,未列出所有参数
/// [kAppId] 申请穿山甲广告位后得到的 appID
await pangle.init(
  iOS: IOSConfig(appId: kAppId),
  android: AndroidConfig(appId: kAppId),
);

2. 开屏广告

/// 全屏类型
/// [kSplashId] 开屏广告 ID, 对应 Android 的 CodeId,对应 iOS 的 slotID
await pangle.loadSplashAd(
  iOS: IOSSplashConfig(slotId: kSplashId, isExpress: false),
  android: AndroidSplashConfig(slotId: kSplashId, isExpress: false),
);


/// 自定义类型
/// AndroidViewSurface 支持
@override
void initState() {
  super.initState();
  SplashView.platform = SurfaceAndroidSplashView(hybridComposition: false);
}

/// 同 Widget 类用法
SplashView(
  iOS: IOSSplashConfig(slotId: kSplashId, isExpress: false),
  android: AndroidSplashConfig(slotId: kSplashId, isExpress: false),
  backgroundColor: Colors.white,
  /// 广告展示
  onShow: (){},
  /// 广告获取失败
  onError: (int code, String message){},
  /// 广告被点击
  onClick: (){},
  /// 广告已结束
  onClose: (){},
);

3. 激励视频广告

/// [kRewardedVideoId] 激励视频广告 ID, 对应 Android 的 CodeId,对应 iOS 的 slotID
pangle.loadRewardVideoAd(
   iOS: IOSRewardedVideoConfig(slotId: kRewardedVideoId),
   android: AndroidRewardedVideoConfig(slotId: kRewardedVideoId),
 );

4. Banner 广告

基本覆盖了原生回调事件,现在点击右上角关闭[ x ]按钮,需要开发者手动移除,不再自动移除 Item。

/// AndroidViewSurface 支持
@override
void initState() {
  super.initState();
  BannerView.platform = AndroidBannerView(hybridComposition: false);
}

/// Banner 通过 PlatformView 实现,使用方法同 Widget
/// [kBannerId] Banner 广告 ID, 对应 Android 的 CodeId,对应 iOS 的 slotID
BannerView(
  iOS: IOSBannerAdConfig(slotId: kBannerId),
  android: AndroidBannerAdConfig(slotId: kBannerId),
  // 还有其他回调,具体可导入查看
),


// 必须限定范围大小,可用 Expaned,Container,SizeBox,AspectRatio 等
Container(
  height: 260,
  child: BannerView(
    iOS: IOSBannerConfig(
      slotId: kBannerExpressId600x260,
      expressSize: PangleExpressSize(width: 600, height: 260),
    ),
    android: AndroidBannerConfig(
      slotId: kBannerExpressId600x260,
      expressSize: PangleExpressSize(width: 600, height: 260),
    ),
    onBannerViewCreated: (BannerViewController controller){
      // 传入[Rect.zero]与[]均为无额外点击区域
      controller.addTouchableBounds([Rect.zero]);
      // 清空额外点击区域
      controller.clearTouchableBounds();
    },
    onClick: () {},
  ),
),

5. 信息流广告

基本覆盖了原生回调事件,现在点击右上角关闭[ x ]按钮,需要开发者手动移除,不再自动移除 Item。

  • 获取信息流数据
/// 信息流实现逻辑
/// 首先进行网络请求,得到信息流数据
///
/// PangleFeedAd 相应字段: 
/// [code] 响应码,0 成功,-1 失败
/// [message] 错误时,调试信息
/// [count] 获得信息流数量,一般同上面传入的 count,最终结果以此 count 为主
/// [data] (string list) 用于展示信息流广告的键 id
 PangleFeedAd feedAd = await pangle.loadFeedAd(
   iOS: IOSFeedAdConfig(slotId: kFeedId, count: 2),
   android: AndroidFeedAdConfig(slotId: kFeedId, count: 2),
 );
  • 加载数据
/// AndroidViewSurface 支持
@override
void initState() {
  super.initState();
  FeedView.platform = AndroidFeedView(hybridComposition: false);
}

/// 使用方法
/// 你的数据模型
class Item {
  /// 添加字段
  final String feedId;
}
final items = <Item>[];
final feedAdDatas = feedAd.data;
final items = Item(feedId: feedAdDatas[0]);
items.insert(Random().nextInt(items.length), item);
/// Widget 使用
FeedView(
  id: item.feedId,
  onRemove: () {
    setState(() {
      items.removeAt(index);
    });
  },
)
  • 清除缓存
 AspectRatio(
   aspectRatio: 375 / 284.0,
   child: FeedView(
     id: item.feedId,
     onDislike: (option) {
       // 1.移除 FeedView 两步

       // 可在 dispose 方法处移除所有(此处可选)
       pangle.removeFeedAd([item.feedId]);
       // 移除界面上的显示
       setState(() {
         items.removeAt(index);
       });
     },
   ),
 )

// 可选,点击不喜欢即右上角叉时清除
pangle.removeFeedAd([item.feedId]);

// 必须
@override
void dispose() {
  /// 不关心返回值
  pangle.removeFeedAd(feedIds);
  /// 关心返回值
  /// _removeFeedAd();
  super.dispose();
}

/// 移除广告
_removeFeedAd() async {
  /// 返回移除个数
  int count = await pangle.removeFeedAd(feedIds);
  print('Feed Ad Removed: $count');
}

6. 插屏广告

 final result = await pangle.loadInterstitialAd(
   iOS: IOSInterstitialAdConfig(
     slotId: kInterstitialId

     /// 该宽高为你申请的广告位宽高,请根据实际情况赋值
     expressSize: PangleExpressSize(width: width, height: height),
   ),
   android: AndroidInterstitialAdConfig(
     slotId: kInterstitialId,
   ),
 );
print(jsonEncode(result));

7. 点击穿透

本方案适用于SplashViewFeedViewBannerView

当我们点击覆盖在广告 View 上方的 Widget 时,最优先响应该事件的 View 是用来渲染被原生广告遮挡的 Widget layer 的 FlutterOverlayView,而 FlutterOverlayView 在初始化时被禁用了用户交互响应userInteractionEnabled=NO,所以点击事件就会在广告 View 上被响应。因采用方案Flutter 原生广告优化处理了点击穿透问题,广告可点击区域存在屏蔽过度的情况,故增加方法添加额外点击区域。

  • 添加可点击区域(此处使用 FeedView 作为范例)
// 1.广告不可点击区域 key
final _otherKey = GlobalKey();
// 2.可能覆盖在 FeedView 上的 button
FloatingActionButton(
  key: _otherKey,
),
// 3. 获取 FeedViewController 并添加点击范围
 AspectRatio(
   aspectRatio: 375 / 284.0,
   child: FeedView(
     id: item.feedId,
     onFeedViewCreated: (controller) {
       // 限制 FeedView 点击范围
       _initConstraintBounds(controller);
     },
   ),
 )

_initConstraintBounds(FeedViewController controller) {
  if (!Platform.isIOS) {
    return;
  }

  RenderBox otherBox = _otherKey.currentContext.findRenderObject();
  final otherBound = PangleHelper.fromRenderBox(otherBox);
  final targetBound = Rect.fromLTWH(
    0,
    otherBound.top,
    kPangleScreenWidth - otherBound.width,
    otherBound.height,
  );
  controller.addTouchableBound(targetBound);
}

8. 其他广告

另外已实现全屏视频广告、新模板渲染插屏,使用方式大同小异。

贡献

  • 有任何更好的实现方式或增加额外的功能,提交PR
  • 有任何使用上的问题,提交 issue

交流

提交 issue 即可。

感谢赞助

BokAugust

Apps
About Me
GitHub: Trinea
Facebook: Dev Tools