原生平台相关范例:
从 0.10.1 开始,不再支持旧版自渲染方式加载广告。所有可请求广告为穿山甲官网目前可创建广告位。
因自渲染广告位,官方不推荐使用,且API不会经常变动,后续会单独提交一个自渲染版本出来。如果你仍需要该功能,可使用0.9.1或在其基础上修改。
- 不再需要传入isExpress参数
- BannerView, FeedView, SplashView均需要包一层限制大小的Widget, 可选Container, SizeBox, AspectRatio, Expanded等
- BannerView, FeedView, SplashView的控制点击实现变动,可参考example进行更改。
- 从v0.10.1开始不再提供自渲染广告位加载,后续会在新的分支实现,敬请期待。
- Android依赖方式变更,查看 如何配置
[Android] 3.5.0.0 (理论上3.5+都支持)
[iOS] 3.4.2.8 (理论上3.4+都支持)
注:如果出现高版本不兼容问题,可联系我升级适配,或者使用上面指定版本。
dependencies:
# 添加依赖
pangle_flutter: latest
-
iOS版本依赖配置
本项目默认集成
Ads-CN
, 如果你是国内APP,无需额外配置;如果你是海外APP,请参照如下配置:打开你flutter应用ios项目下的
Podfile
,在target 'Runner do
上面添加如下代码即可(如果不熟悉Podfile,也可以参考本项目example/ios/Podfile里面的配置)。
def flutter_install_ios_plugin_pods(ios_application_path = nil) # defined_in_file is set by CocoaPods and is a Pathname to the Podfile. ios_application_path ||= File.dirname(defined_in_file.realpath) if self.respond_to?(:defined_in_file) raise 'Could not find iOS application path' unless ios_application_path
# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
# referring to absolute paths on developers' machines.
symlink_dir = File.expand_path('.symlinks', ios_application_path)
system('rm', '-rf', symlink_dir) # Avoid the complication of dependencies like FileUtils.
symlink_plugins_dir = File.expand_path('plugins', symlink_dir)
system('mkdir', '-p', symlink_plugins_dir)
plugins_file = File.join(ios_application_path, '..', '.flutter-plugins-dependencies')
plugin_pods = flutter_parse_plugins_file(plugins_file)
plugin_pods.each do |plugin_hash|
plugin_name = plugin_hash['name']
plugin_path = plugin_hash['path']
if (plugin_name && plugin_path)
symlink = File.join(symlink_plugins_dir, plugin_name)
File.symlink(plugin_path, symlink)
pod plugin_name, :path => File.join('.symlinks', 'plugins', plugin_name, 'ios')
if plugin_name == 'pangle_flutter'
# cn表示国内,global表示海外
pod 'pangle_flutter/global', :path => File.join('.symlinks', 'plugins', plugin_name, 'ios')
end
end
end
end
- Android版本依赖配置
本项目默认依赖国内版本,如果你需要配置海外版本,以本插件example为例。在项目根目录下`local.properties`文件内配置一个属性,导入依赖方式同国内一样,[查看配置方法](SETUP.md)。
```properties
# cn表示国内,global表示海外
pangle_flutter.env=global
海外版本使用方法基本与国内一致,需注意部分配置属性海外并不适用,在调用时填入了不存在的参数也不会有什么影响。
pangle.init(
iOS: IOSConfig(
appId: kAppId,
logLevel: PangleLogLevel.error,
),
android: AndroidConfig(
appId: kAppId,
debug: false,
allowShowNotify: true,
allowShowPageWhenScreenLock: false,
/// 海外不存在该配置
directDownloadNetworkType: [
AndroidDirectDownloadNetworkType.k2G,
]),
)
- 创建一个Swift文件,名称随意
- 根据提示选择
Create Bridging Header
。如果没有提示,请自行搜索如何创建。
import 'package:pangle_flutter/pangle_flutter.dart';
/// 如果在runApp方法调用之前初始化,加入下面这句代码
WidgetsFlutterBinding.ensureInitialized();
/// 初始化,未列出所有参数
/// [kAppId] 申请穿山甲广告位后得到的appID
await pangle.init(
iOS: IOSConfig(appId: kAppId),
android: AndroidConfig(appId: kAppId),
);
/// 全屏类型
/// [kSplashId] 开屏广告ID, 对应Android的CodeId,对应iOS的slotID
await pangle.loadSplashAd(
iOS: IOSSplashConfig(slotId: kSplashId, isExpress: false),
android: AndroidSplashConfig(slotId: kSplashId, isExpress: 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: (){},
/// 广告被点击跳过
onSkip: (){},
/// 广告倒计时结束
onTimeOver: (){},
);
/// [kRewardedVideoId] 激励视频广告ID, 对应Android的CodeId,对应iOS的slotID
pangle.loadRewardVideoAd(
iOS: IOSRewardedVideoConfig(slotId: kRewardedVideoId),
android: AndroidRewardedVideoConfig(slotId: kRewardedVideoId),
);
基本覆盖了原生回调事件,现在点击右上角关闭[ x ]按钮,需要开发者手动移除,不再自动移除Item。
/// 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.updateTouchableBounds([Rect.zero]);
// 传入Rect.zero无影响
controller.updateRestrictedBounds([Rect.zero]);
},
onClick: () {},
),
),
- 控制可点击区域(默认可点击)
// 因iOS的EXPRESS类型的广告内部使用WebView渲染,而WebView与FlutterView存在部分点击事件冲突,故提供该解决方案
onBannerViewCreated: (BannerViewController controller){
// 禁止点击,传入一个Rect.zero即可
controller.updateTouchableBounds([Rect.zero]);
// 提供点击,传入空即可
controller.updateTouchableBounds([]);
// 额外不可点击区域(一般用于上面可点击范围上面,如可点击范围有一个悬浮按钮Widget)
controller.updateRestrictedBounds([Rect.zero]);
},
基本覆盖了原生回调事件,现在点击右上角关闭[ 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),
);
- 加载数据
/// 使用方法
/// 你的数据模型
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);
});
},
)
- 控制可点击区域(默认可点击)
// 因iOS的EXPRESS类型的广告内部使用WebView渲染,而WebView与FlutterView存在部分点击事件冲突,故提供该解决方案
// 1. 可点击区域key
final _bodyKey = GlobalKey();
// 不可点击区域key
final _otherKey = GlobalKey();
// 2.FeedView移动区域
Container(
key: _bodyKey,
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return _buildItem(index);
},
)),
// 可能覆盖在FeedView上的button
FloatingActionButton(
key: _otherKey,
),
// 3. 获取FeedViewController并限制点击范围
AspectRatio(
aspectRatio: 375 / 284.0,
child: FeedView(
id: item.feedId,
onFeedViewCreated: (controller) {
// 限制FeedView点击范围
_initConstraintBounds(controller);
},
onDislike: (option) {
// 移除FeedView两部曲
pangle.removeFeedAd([item.feedId]);
setState(() {
items.removeAt(index);
});
},
),
)
_initConstraintBounds(FeedViewController controller) {
if (!Platform.isIOS) {
return;
}
RenderBox bodyBox = _bodyKey.currentContext.findRenderObject();
final bodyBound = PangleHelper.fromRenderBox(bodyBox);
controller.updateTouchableBounds([bodyBound]);
RenderBox otherBox = _otherKey.currentContext.findRenderObject();
final otherBound = PangleHelper.fromRenderBox(otherBox);
controller.updateRestrictedBounds([otherBound]);
}
// 4.清除缓存
// 可选,点击不喜欢即右上角叉时清除
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');
}
final result = await pangle.loadInterstitialAd(
iOS: IOSInterstitialAdConfig(
slotId: kInterstitialId
/// 该宽高为你申请的广告位宽高,请根据实际情况赋值
expressSize: PangleExpressSize(width: width, height: height),
),
android: AndroidInterstitialAdConfig(
slotId: kInterstitialId,
),
);
print(jsonEncode(result));
- iOS信息流广告的点击事件需要传入
rootViewController
,使用的是(UIApplication.shared.delegate?.window??.rootViewController)!
,暂未发现问题。 BannerView
、FeedView
通过Hybrid Composition
实现。在安卓上,PlatformView
最低支持API 19。
提交issue即可。