# Suyi聚合广告SDK Flutter 插件

[TOC]

## 1.1 概述

尊敬的开发者朋友，欢迎您使用Suyi聚合广告SDK。通过本文档，您可以在几分钟之内轻松完成广告的集成过程。

操作系统： iOS 9.0 及以上版本，Android 4.4 及以上版本，

运行设备：iPhone （iPad上可能部分广告正常展示，但是存在填充很低或者平台不支持等问题，建议不要在iPad上展示广告），Android

## 2.1 SDK导入

首先需要导入主SDK

```dart
dependencies:
  ad_suyi_flutter_sdk: {library version}
```

然后需要导入各平台SDK，

### 2.1.1 iOS在项目的podfile中增加如下内容，可以根据实际需要选择性导入平台

```ruby
pod 'ADSuyiSDK','~> 4.0.1.06131' # 主SDK 必选
pod 'ADSuyiSDK/ADSuyiSDKPlatforms/tianmu' # 天目  #必选
pod 'ADSuyiSDK/ADSuyiSDKPlatforms/gdt' # 优量汇(广点通）
pod 'ADSuyiSDK/ADSuyiSDKPlatforms/baidu' # 百度
pod 'ADSuyiSDK/ADSuyiSDKPlatforms/ks' # 快手
pod 'ADSuyiSDK/ADSuyiSDKPlatforms/jad' # 京媒，白名单需添加京东openapp.jdmobile
pod 'ADSuyiSDK/ADSuyiSDKPlatforms/iqy' # 爱奇艺

# ab二选一
# a.不需要gromore
pod 'ADSuyiSDK/ADSuyiSDKPlatforms/bu' # 穿山甲(头条)
# b.需要gromore
# pod 'ADSuyiSDK/ADSuyiSDKPlatforms/bu-without' # 穿山甲(头条)
# pod 'ADSuyiSDK/ADSuyiSDKPlatforms/gromore' # gromore
```

### 2.1.2 Android在项目中增加如下内容，可以根据实际需要选择性导入平台

#### 2.1.2.1. 在android根目录build.gradle中添加suyi仓库
```java
allprojects {
    repositories {
        ...
        google()
        jcenter()
        mavenCentral()
        // 添加以下仓库地址
        // ADSuyi远程仓库
        maven { url "https://maven.admobile.top/repository/maven-releases/" }
        // 如果添加了云码广告，需要添加云码的远程仓库依赖
        maven { url 'http://maven.aliyun.com/nexus/content/repositories/releases/' }
        // 如果添加了华为联盟广告，需要添加华为联盟的远程仓库依赖
        maven { url 'https://developer.huawei.com/repo/' }
    }
}
```
#### 2.1.2.2. OAID支持
导入安全联盟的OAID支持库 oaid_sdk_1.0.25.aar，可在Demo的libs目录下找到，强烈建议使用和Demo中一样版本的OAID库（包括项目中已存在的依赖的oaid版本）；
将Demo中assets文件夹下的supplierconfig.json文件复制到自己的assets目录下并按照supplierconfig.json文件中的说明进行OAID的 AppId 配置，supplierconfig.json文件名不可修改；
#### 2.1.2.3. 在android/app目录build.gradle中添加相关依赖
```java
// support支持库，如果是AndroidX请使用对应的支持库
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support:design:28.0.0'

// ADSuyiSdk核心库必须导入
implementation 'cn.admobiletop.adsuyi.ad:core:4.0.1.05072'

// 天目适配器，必选
implementation 'cn.admobiletop.adsuyi.ad.adapter:tianmu:3.0.1.06131'

// 优量汇（广点通）适配器，可选
implementation 'cn.admobiletop.adsuyi.ad.adapter:gdt:4.640.1510.05133'

// 穿山甲（头条）适配器，可选
implementation 'cn.admobiletop.adsuyi.ad.adapter:toutiao:6.9.0.6.06131'

// 百度适配器，可选。仅支持AndroidX版本
implementation 'cn.admobiletop.adsuyi.ad.adapter:baidu-androidx:9.391.05141'

// 快手适配器，可选
implementation 'cn.admobiletop.adsuyi.ad.adapter:ksadbase:4.4.20.1.06131'

// 米盟适配器，可选。仅支持AndroidX版本
implementation 'cn.admobiletop.adsuyi.ad.adapter:mimo-androidx:5.3.3.12103'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.github.bumptech.glide:glide:4.9.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'

// 华为适配器，可选
implementation 'cn.admobiletop.adsuyi.ad.adapter:hwpps:13.4.76.300.01031'

// 京媒适配器，可选。仅支持AndroidX版本
implementation 'cn.admobiletop.adsuyi.ad.adapter:jadyun-androidx:2.6.32.06131'

// 章鱼适配器，可选的
implementation 'cn.admobiletop.adsuyi.ad.adapter:octopus:1.6.3.6.05141'

// 爱奇艺适配器，可选
implementation 'cn.admobiletop.adsuyi.ad.adapter:iqy:1.16.200.85.06131'

// gromoreAdapterSdk，可选的。如使用gromore的其他渠道，请联系开发者。
// gromore已与穿山甲合并，此处无需重复导入穿山甲。
implementation 'cn.admobiletop.adsuyi.ad.adapter:gromore-without:6.9.0.6.06191'
implementation "com.pangle.cn:mediation-gdt-adapter:4.575.1445.1" //广点通 adapter
// 有gromore其他渠道需求，请联系开发者。

```

#### 2.1.2.4. 广告位创建注意事项
由于使用flutter方式对接，信息流广告可能存在无法展示广告问题，需要在创建优量汇、百度、快手渠道时，创建接近10：9比例的广告样式，可以达到最好的展示效果，否则会出现留白情况。

## 3.1 工程环境配置

### 3.1.1 IOS 工程环境配置

#### 3.1.1.1. info.plist 添加支持 Http访问字段

```
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
```

#### 3.1.1.2. Info.plist 添加定位权限字段（使用ADMobGenLocation可不设置）

```
NSLocationWhenInUseUsageDescription
NSLocationAlwaysAndWhenInUseUsageDeion
```

#### 3.1.1.3. Info.plist 添加获取本地网络权限字段
```
    <key>Privacy - Local Network Usage Description</key>
    <string>广告投放及广告监测归因、反作弊</string>
    <key>Bonjour services</key>
    <array>
        <string>_apple-mobdev2._tcp.local</string>
    </array>
```

#### 3.1.1.4. Info.plist推荐设置白名单，可提高广告收益

```
<key>LSApplicationQueriesSchemes</key>
    <array>
        <!--  电商及生活   -->
        <string>com.suning.SuningEBuy</string> <!--  苏宁  -->
        <string>openapp.jdmobile</string> <!--  京东  -->  
        <string>openjd</string> <!--  京东  --> 
        <string>jdmobile</string> <!--  京东  --> 
        <string>vmall</string>
        <string>vipshop</string>  <!--  维品汇  -->  
        <string>suning</string> <!--  苏宁  --> 
        <string>yohobuy</string> <!--  有货  --> 
        <string>kaola</string> <!--  网易考拉  --> 
        <string>yanxuan</string> <!--  网易严选  --> 
        <string>wbmain</string>  <!--  58同城  --> 
        <string>dianping</string>  <!--  大众点评  --> 
        <string>imeituan</string>  <!--  美团  --> 
        <string>beibeiapp</string> <!--  贝贝  --> 
        <string>taobao</string> <!--  淘宝  --> 
        <string>tmall</string>  <!--  天猫  --> 
        <string>wireless1688</string> <!--  阿里巴巴1688  --> 
        <string>tbopen</string> <!--  淘宝  --> 
        <string>taobaolite</string> <!-- 淘特   --> 
        <string>taobaoliveshare</string> <!--  淘宝直播  --> 
        <string>koubei</string> <!--  口碑  --> 
        <string>eleme</string> <!--  饿了么  --> 
        <string>alipays</string> <!--  支付宝  --> 
        <string>kfcapplinkurl</string> <!--  KFC  --> 
        <string>pddopen</string> <!--  拼多多  --> 
        <string>pinduoduo</string> <!--  拼多多  --> 
        <string>mogujie</string> <!--  蘑菇街  --> 
        <string>lianjiabeike</string> <!--  链家贝壳  --> 
        <string>lianjia</string> <!--  链家  --> 
        <string>openanjuke</string> <!--  安居客  --> 
        <string>zhuanzhuan</string> <!--  转转  --> 
        <string>farfetchCN</string> <!--  发发奇全球时尚购物  --> 
        <!--  社交社区  --> 
        <string>weibo</string> <!--  微博  --> 
        <string>xhsdiscover</string> <!--  小红书  --> 
        <string>uclink</string> <!--  uc浏览器  --> 
        <string>momochat</string> <!--  陌陌  --> 
        <string>blued</string> <!--  Blued  --> 
        <string>zhihu</string> <!--  知乎  --> 
        <string>baiduboxapp</string> <!--  手机百度  --> 
        <string>yidui</string> <!--  伊对  --> 
        <!--  资讯及阅读  -->    
        <string>sinanews</string> <!--  新浪新闻  --> 
        <string>snssdk141</string> <!--  今日头条  --> 
        <string>newsapp</string> <!--  网易新闻  --> 
        <string>igetApp</string> <!--  得到  -->  
        <string>kuaikan</string> <!--  快看漫画  --> 
        <!--  短视频及音乐  -->         
        <string>youku</string> <!--  优酷  --> 
        <string>snssdk1128</string> <!--  抖音  --> 
        <string>gifshow</string> <!--  快手  --> 
        <string>snssdk1112</string> <!--  火山  --> 
        <string>miguvideo</string> <!--  咪咕视频  --> 
        <string>iqiyi</string> <!--  爱奇艺  --> 
        <string>bilibili</string> <!--  B站  -->   
        <string>tenvideo</string> <!--  腾讯视频  --> 
        <string>baiduhaokan</string> <!--  百度好看  --> 
        <string>yykiwi</string> <!--  虎牙直播  --> 
        <string>qqmusic</string> <!--  qq音乐  -->  
        <string>orpheus</string> <!--  网易云音乐  --> 
        <string>kugouURL</string> <!--  酷狗  -->   
        <string>qmkege</string> <!--  全民K歌  -->  
        <string>changba</string> <!--  唱吧  --> 
        <string>iting</string> <!--  喜马拉雅  --> 
        <!--  出行  -->                 
        <string>ctrip</string> <!--  携程  --> 
        <string>QunarAlipay</string> <!--  去哪儿旅行  --> 
        <string>diditaxi</string> <!--  滴滴出行  --> 
        <string>didicommon</string> <!--  滴滴出行  --> 
        <string>taobaotravel</string> <!--  飞猪  --> 
        <string>OneTravel</string> <!--  海南航空  --> 
        <string>kfhxztravel</string> <!--  花小猪  --> 
        <!--  医美  -->             
        <string>gengmei</string> <!--  更美  --> 
        <string>app.soyoung</string> <!--  新氧医美  --> 
    </array>
```

### 3.1.2 Android 工程环境配置

#### 3.1.2.1 权限配置
```java
<!-- 广告必须的权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

<!-- 广点通广告必须的权限 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<!-- 影响广告填充，强烈建议的权限 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

<!-- 为了提高广告收益，建议设置的权限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<!-- 如果有视频相关的广告播放请务必添加-->
<uses-permission android:name="android.permission.WAKE_LOCK" />
```
#### 3.1.2.2 FileProvider配置
适配Anroid7.0以及以上，请在AndroidManifest中添加如下代码：
如果支持库是support
```java
<provider
	  android:name="android.support.v4.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
    		android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/adsuyi_file_paths" />
</provider>
```
如果支持库为androidx
```java
<provider
	  android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
    		android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/adsuyi_file_paths" />
</provider>
```
在res/xml目录下(如果xml目录不存在需要手动创建)，新建xml文件adsuyi_file_paths，在该文件中加入如下配置，如果存在相同android:authorities的provider，请将paths标签中的路劲配置到自己的xml文件中：
```java
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_path" path="." />
    <external-files-path name="external_files_path" path="." />
</paths>
```
#### 3.1.2.3 网络配置 
需要在 AndroidManifest.xml 添加依赖声明uses-library android:name="org.apache.http.legacy" android:required="false"， 且 application标签中添加 android:usesCleartextTraffic="true"，适配网络http请求，否则 SDK可能无法正常工作，接入代码示例如下：
```java
<application
    android:name=".MyApplication"
        ... ...
    android:usesCleartextTraffic="true">

    <uses-library
        android:name="org.apache.http.legacy"
        android:required="false" />
    ... ...
</application>
```
#### 3.1.2.4 混淆配置
可以参考demo中proguard-rules.pro相关配置

#### 3.1.2.5 注册java与flutter交互代码

[参考资料](https://developers.google.cn/ad-manager/mobile-ads-sdk/flutter/native/get-started?hl=zh-cn)

```java
public class MainActivity extends FlutterActivity {
    ...
    @Override
    public void configureFlutterEngine(FlutterEngine flutterEngine) {
        super.configureFlutterEngine(flutterEngine);
        ...
        Log.d("configureFlutterEngine", "init");
        flutterEngine.getPlugins().add(new ADSuyiMobileAdsPlugin());
        ADSuyiMobileAdsPlugin.registerNativeAdFactory(flutterEngine, "adFactoryExample");
        ...
    }

    @Override
    public void cleanUpFlutterEngine(FlutterEngine flutterEngine) {

    }
    ...
}
```

#### 3.1.2.6 导入插件后的异常处理

Q：Plugin project :ad_suyi_flutter_sdk not found. Please update settings.gradle
A：可参考demo中的settings.gradle，将配置拷贝到项目中

## 3.2 iOS14适配

由于iOS14中对于权限和隐私内容有一定程度的修改，而且和广告业务关系较大，请按照如下步骤适配，如果未适配。不会导致运行异常或者崩溃等情况，但是会一定程度上影响广告收入。敬请知悉。

1. 应用编译环境升级至 Xcode 12.0 及以上版本；
2. 升级ADSuyiSDK 3.1.5及以上版本；
3. 设置SKAdNetwork和IDFA权限；

### 3.2.1 获取App Tracking Transparency授权（弹窗授权获取IDFA）

从 iOS 14 开始，在应用程序调用 App Tracking Transparency 向用户提跟踪授权请求之前，IDFA 将不可用。

1. 更新 Info.plist，添加 NSUserTrackingUsageDescription 字段和自定义文案描述。

   弹窗小字文案建议：

   - `获取标记权限向您提供更优质、安全的个性化服务及内容，未经同意我们不会用于其他目的；开启后，您也可以前往系统“设置-隐私 ”中随时关闭。`
   - `获取IDFA标记权限向您提供更优质、安全的个性化服务及内容；开启后，您也可以前往系统“设置-隐私 ”中随时关闭。`

```
<key>NSUserTrackingUsageDescription</key>
<string>获取标记权限向您提供更优质、安全的个性化服务及内容，未经同意我们不会用于其他目的；开启后，您也可以前往系统“设置-隐私 ”中随时关闭</string>
```

1. 向用户申请权限。

```
#import <AppTrackingTransparency/AppTrackingTransparency.h>
#import <AdSupport/AdSupport.h>
...
- (void)requestIDFA {
  [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
    // 无需对授权状态进行处理
  }];
}
// 建议启动App用户同意协议后就获取权限或者请求广告前获取
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
         // 针对iOS15中不弹窗被拒解决方案，方案1：经测试可能无效
    //dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)),             dispatch_get_main_queue(), ^{
            // 用户同意协议后获取
                      //[self requestIDFA];
        //});
}
// 方案2：根据官方文档调整权限申请时机
// 根据官方开发文档选择在此方法中进行权限申请
- (void)applicationDidBecomeActive:(UIApplication *)application {
    // 用户同意协议后获取
      [self requestIDFA];
}
// 建议方案1与2一起使用，可正常通过审核。
```

### 3.2.2 SKAdNetwork

SKAdNetwork 是接收iOS端营销推广活动归因数据的一种方法。

1. 将下列SKAdNetwork ID（供参考，需以穿山甲官网为准）添加到 info.plist 中，以保证 SKAdNetwork 的正确运行。根据对接平台添加相应SKAdNetworkID，若无对接平台SKNetworkID则无需添加。

```
<key>SKAdNetworkItems</key>
  <array>
    // 穿山甲广告（ADSuyiBU）
    <dict>
      <key>SKAdNetworkIdentifier</key>
      <string>238da6jt44.skadnetwork</string>
    </dict>
    <dict>
      <key>SKAdNetworkIdentifier</key>
      <string>22mmun2rn5.skadnetwork</string>
    </dict>
    <dict>
      <key>SKAdNetworkIdentifier</key>
      <string>x2jnk7ly8j.skadnetwork</string>
    </dict>
  </array>
```

## 4.1 主SDK初始化

```dart
AdSuyiFlutterSdk.initSdk(appid: "appid");
```

## 4.2 开屏广告

### 4.2.1 开屏广告（跳转至原生页面加载并展示）
```dart
class SplashPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _SplashState(); 
}
class _SplashState extends State<SplashPage> {
  ADSuyiSplashAd _splashAd;
  @override
  Widget build(BuildContext context) {
    showSplashAd();
    return Scaffold(
      appBar: AppBar(
        title: Text("Splash"),
      ),
      body: Center(
      ),
    );
  }
  // 开屏
  // 显示开屏广告请保证当时app内没有其他地方显示开屏广告，否则会有冲突
  void showSplashAd() {
    if(_splashAd != null) {
      return;
    }
    // posId：广告位id，
    // imgName：背景名称，
    // imgLogoName：底部logo名称，
    // isRepeatApplyPermission：用户拒绝权限后是否允许重复读取：true允许，false禁止，
    // isApplyPermission：动态申请权限：true允许，false禁止，
    _splashAd = ADSuyiSplashAd(posId: "posid", imgName: "splash_placeholder", imgLogoName: "splash_bottom_icon", isRepeatApplyPermission: false, isApplyPermission: false);
    _splashAd.onClosed = (AdInfo adInfo) {
      print("开屏广告关闭了");
      // 在加载失败和关闭回调后关闭广告
      releaseSplashAd();
    };
    _splashAd.onFailed = (AdErrorInfo adErrorInfo) {
      print("开屏广告失败了");
      // 在加载失败和关闭回调后关闭广告
      releaseSplashAd();
    };
    _splashAd.onExposed = (AdInfo adInfo) { print("开屏广告曝光了"); };
    _splashAd.onSucced = (AdInfo adInfo) { print("开屏广告成功了"); };
    _splashAd.onClicked = (AdInfo adInfo) { print("开屏广告点击了"); };
    _splashAd.loadAndShow();
  }

  void releaseSplashAd() {
    _splashAd?.release();
    _splashAd = null;
  }

  @override
  void dispose() {
    releaseSplashAd();
    super.dispose();
  }
}
```

### 4.2.2 开屏广告（flutter原生加载展示分离）
```dart
class SplashLoadShowSeparatePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _SplashLoadShowSeparateState();
}

class _SplashLoadShowSeparateState extends State<SplashLoadShowSeparatePage> {
  ADSuyiSplashAdLoadShowSeparate? _adSuyiFlutterSplashAd;
  bool _hasInitBanner = false;

  @override
  Widget build(BuildContext context) {
    if (_adSuyiFlutterSplashAd == null && _hasInitBanner == false) {
      MediaQueryData queryData = MediaQuery.of(context);
      _hasInitBanner = true;
      var width = queryData.size.width;
      var height = queryData.size.height;
      _adSuyiFlutterSplashAd = ADSuyiSplashAdLoadShowSeparate(
          posId: KeyManager.splashPosid(), width: width, height: height);
      // 加载广告
      _adSuyiFlutterSplashAd!.load();
      _adSuyiFlutterSplashAd!.onSucced = (AdInfo adInfo) {
        print("开屏广告加载成功");
        // 展示广告
        _adSuyiFlutterSplashAd!.show();
      };
      _adSuyiFlutterSplashAd!.onFailed = (AdErrorInfo adErrorInfo) {
        releaseSplashAd();
        print("开屏广告加载失败");
        toHome();
      };
      _adSuyiFlutterSplashAd!.onClicked = (AdInfo adInfo) {
        print("开屏广告点击");
      };
      _adSuyiFlutterSplashAd!.onExposed = (AdInfo adInfo) {
        print("开屏广告渲染成功");
      };
      _adSuyiFlutterSplashAd!.onClosed = (AdInfo adInfo) {
        releaseSplashAd();
        print("开屏广告关闭成功");
        toHome();
      };
    }

    return Scaffold(
        body: Center(
            child: (_adSuyiFlutterSplashAd == null
                ? Text("开屏广告已关闭")
                : ADSuyiWidget(adView: _adSuyiFlutterSplashAd!))));
  }

  void toHome() {
    Navigator.pushAndRemoveUntil(
      context,
      new MaterialPageRoute(builder: (context) => new MyApp()),
          (route) => route == null,
    );
  }

  void removeSplashAd() {
    setState(() {
      releaseSplashAd();
    });
  }

  void releaseSplashAd() {
    _adSuyiFlutterSplashAd?.release();
    _adSuyiFlutterSplashAd = null;
  }

  @override
  void dispose() {
    releaseSplashAd();
    super.dispose();
  }
}
```

```dart
// 加载广告
_adSuyiFlutterSplashAd!.load();
```

```dart
// 展示广告（请在onSucced回调后使用，目前安卓端快手存在异常，请勿添加快手广告源）
_adSuyiFlutterSplashAd!.show();
```

## 4.3 横幅广告（banner）

```dart
class BannerPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _BannerState();
}

class _BannerState extends State<BannerPage> {
  ADSuyiFlutterBannerAd _adSuyiFlutterBannerAd;
  bool _hasInitBanner = false;

  @override
  Widget build(BuildContext context) {
    if (_adSuyiFlutterBannerAd == null && _hasInitBanner == false) {
      MediaQueryData queryData = MediaQuery.of(context);
      _hasInitBanner = true;
      var width = queryData.size.width;
      var height = queryData.size.width / 320.0 * 50.0;
      _adSuyiFlutterBannerAd = ADSuyiFlutterBannerAd(posId: KeyManager.bannerPosid(), width: width, height: height);
      _adSuyiFlutterBannerAd.loadAndShow();
      _adSuyiFlutterBannerAd.onSucced = (AdInfo adInfo) {
        print("横幅广告加载成功");
      };
      _adSuyiFlutterBannerAd.onFailed = (AdErrorInfo adErrorInfo) {
        removeBannerAd();
        print("横幅广告加载失败");
      };
      _adSuyiFlutterBannerAd.onClicked = (AdInfo adInfo) {
        print("横幅广告点击");
      };
      _adSuyiFlutterBannerAd.onExposed = (AdInfo adInfo) {
        print("横幅广告渲染成功");
      };
      _adSuyiFlutterBannerAd.onClosed = (AdInfo adInfo) {
        removeBannerAd();
        print("横幅广告关闭成功");
      };
    }

    return Scaffold(
        appBar: AppBar(
          title: Text("BannerPage"),
        ),
        body: Center(child: (_adSuyiFlutterBannerAd == null ? Text("banner广告已关闭") : ADSuyiWidget(adView: _adSuyiFlutterBannerAd) ))
    );
  }

  void removeBannerAd() {
    setState(() {
      releaseBannerAd();
    });
  }

  void releaseBannerAd() {
    _adSuyiFlutterBannerAd?.release();
    _adSuyiFlutterBannerAd = null;
  }

  @override
  void dispose() {
    releaseBannerAd();
    super.dispose();
  }
}
```

## 4.4 全屏视频广告

```dart
class FullScreenPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => FullScreenState();
}
class FullScreenState extends State<FullScreenPage> {
  ADSuyiFullScreenVodAd _fullScreenVodAd;
  @override
  Widget build(BuildContext context) {
    showFullScreenVodAd();
    return Scaffold(
      appBar: AppBar(
        title: Text("FullScreenVodAd"),
      ),
      body: Center(
      ),
    );
  }
  // 全屏视频
  // 显示全屏视频广告请保证当时app内没有其他地方显示全屏视频广告，否则会有冲突
  void showFullScreenVodAd() {
    if(_fullScreenVodAd != null) {
      return;
    }
    _fullScreenVodAd = ADSuyiFullScreenVodAd(posId: KeyManager.fullScreenPosid());
    _fullScreenVodAd.onClicked = (AdInfo adInfo) {
      print("全屏视频广告关闭了");
    };
    _fullScreenVodAd.onFailed = (AdErrorInfo adErrorInfo) {
      print("全屏视频广告失败了");
      releaseFullScreenVodAd();
    };
    _fullScreenVodAd.onExposed = (AdInfo adInfo) {
      print("全屏视频广告曝光了");
    };
    _fullScreenVodAd.onSucced = (AdInfo adInfo) {
      print("全屏视频广告成功了");
      playFullScreenVodAd();
    };
    _fullScreenVodAd.onClicked = (AdInfo adInfo) {
      print("全屏视频广告点击了");
    };
    _fullScreenVodAd.onRewarded = () {
      print("全屏视频广告激励达成");
    };
    _fullScreenVodAd.onClosed = (AdInfo adInfo) {
      print("全屏视频广告关闭");
      releaseFullScreenVodAd();
    };
    _fullScreenVodAd.load();
  }
  void releaseFullScreenVodAd() {
    _fullScreenVodAd?.release();
    _fullScreenVodAd = null;
  }
  void playFullScreenVodAd() {
    _fullScreenVodAd.show();
  }
  @override
  void dispose() {
    releaseFullScreenVodAd();
    super.dispose();
  }
}
```

## 4.5 插屏广告

```dart
class InterPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _InterState();
}
class _InterState extends State<InterPage> {
  ADSuyiIntertitialAd _interAd;
  @override
  Widget build(BuildContext context) {
    showInterAd();
    return Scaffold(
      appBar: AppBar(
        title: Text("Intertitial"),
      ),
      body: Center(
      ),
    );
  }
  // 插屏
  // 显示插屏广告请保证当时app内没有其他地方显示插屏广告，否则会有冲突
  void showInterAd() {
    if(_interAd != null) {
      return;
    }
    _interAd = ADSuyiIntertitialAd(posId: KeyManager.interPosid());
    _interAd.onClicked = (AdInfo adInfo) {
      print("插屏广告关闭了");
    };
    _interAd.onFailed = (AdErrorInfo adErrorInfo) {
      print("插屏广告失败了");
      releaseInterAd();
    };
    _interAd.onExposed = (AdInfo adInfo) {
      print("插屏广告曝光了");
    };
    _interAd.onSucced = (AdInfo adInfo) {
      print("插屏广告成功了");
      playInterAd();
    };
    _interAd.onClicked = (AdInfo adInfo) {
      print("插屏广告点击了");
    };
    _interAd.onRewarded = () {
      print("插屏广告激励达成");
    };
    _interAd.onClosed = (AdInfo adInfo) {
      print("插屏广告关闭");
      releaseInterAd();
    };
    _interAd.load();
  }
  void releaseInterAd() {
    _interAd?.release();
    _interAd = null;
  }
  void playInterAd() {
    _interAd.show();
  }
  @override
  void dispose() {
    releaseInterAd();
    super.dispose();
  }
}
```

## 4.6 信息流广告

```dart
class NativePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => NativeState();
}

class NativeState extends State<NativePage> {

  ADSuyiFlutterNativeAd _nativeAd;

  List<dynamic> _items = List.generate(10, (i) => i);
  ScrollController _scrollController = ScrollController();

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(() {
      if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
        _getAdData();
      }
    });
  }

  _getAdData() async {
    _nativeAd.load();
  }

  void createNativeAd(BuildContext context) {
    if(_nativeAd == null) {
      MediaQueryData queryData = MediaQuery.of(context);
      var width = queryData.size.width;
		/**
		 * 由于flutter对接安卓原生广告无法提前获取到部分渠道广告视图的高度，开发者需要做一下处理。
		 * 例如每个渠道都申请大小相近的广告样式，可在此设置固定比例。（优量汇：选横版纯图片；穿山甲：文字浮层；百度：三图；快手：横版大图）
		 * 由于demo上用的广告位素材样式不固定，我们直接写16：12进行测试。
		 * 设置比例的作用，主要是防止广告素材被遮挡，导致收益降低和合规问题。
		 */
		var height = width / 16 * 12;
      _nativeAd = ADSuyiFlutterNativeAd(posId: KeyManager.nativePosid(), width: width, height: height);
      _nativeAd!.onFailed = (AdErrorInfo adErrorInfo) {
      };
      _nativeAd.onReceived = (ADSuyiFlutterNativeAdView adView) {
        setState(() {
          var adWidget = ADSuyiWidget(adView: adView);
          adView.onClosed = (AdInfo adInfo) {
            // 广告关闭回调
            setState(() {
              _items.remove(adWidget);
              adView.release();
            });
          };
          adView.onExposed = (AdInfo adInfo) {
            // 广告曝光回调
          };
          adView.onClicked = (AdInfo adInfo) {
            // 广告点击回调
          };
          _items.add(adWidget);
          _items.addAll(List.generate(1, (i) => i));
        });
      };
    }
  }

  @override
  Widget build(BuildContext context) {
    createNativeAd(context);
    return Scaffold(
      appBar: AppBar(
        title: Text("Native"),
      ),
      body: Center(
        child: ListView.builder(
          itemCount: _items.length,
          controller: _scrollController,
          itemBuilder: (BuildContext context, int index) {
            final item = _items[index];
            if (item is Widget) {
              return item;
            } else {
              return Container(
                width: 300,
                height: 150,
                child: Text("Cell", style: TextStyle(fontSize: 75))
              );
            }
          }
        ),
      ),
    );
  }
  
  @override
  void dispose() {
    for (var item in _items) {
      if (item is ADSuyiFlutterNativeAdView) {
        item.release();
      }
    }
    _nativeAd.release();
    _nativeAd = null;
    super.dispose();
  }
}
```

## 4.7 激励视频广告

```dart
class RewardPage extends StatefulWidget {
  
  @override
  State<StatefulWidget> createState() => _RewardState();

}
class _RewardState extends State<RewardPage> {

  ADSuyiRewardAd _rewardAd;

  @override
  Widget build(BuildContext context) {
    showRewardAd();
    return Scaffold(
      appBar: AppBar(
        title: Text("Reward"),
      ),
      body: Center(
      ),
    );
  }

  void showRewardAd() {
    if(_rewardAd != null) {
      return;
    }
    _rewardAd = ADSuyiRewardAd(posId: KeyManager.rewardPosid());
    _rewardAd.onFailed = (AdErrorInfo adErrorInfo) {
      print("激励视频广告失败了");
      releaseRewardAd();
    };
    _rewardAd.onExposed = (AdInfo adInfo) {
      print("激励视频广告曝光了");
    };
    _rewardAd.onSucced = (AdInfo adInfo) {
      print("激励视频广告成功了");
      playRewardAd();
    };
    _rewardAd.onClicked = (AdInfo adInfo) {
      print("激励视频广告点击了");
    };
    _rewardAd.onRewarded = (AdInfo adInfo) {
      print("激励视频广告激励达成");
    };
    _rewardAd.onClosed = (AdInfo adInfo) {
      print("激励视频广告关闭");
      releaseRewardAd();
    };
    _rewardAd.load();
  }

  void releaseRewardAd() {
    _rewardAd?.release();
    _rewardAd = null;
  }

  void playRewardAd() {
    _rewardAd.show();
  }

  @override
  void dispose() {
    releaseRewardAd();
    super.dispose();
  }
}
```

## 4.8 个性化化开关

```dart
AdSuyiFlutterSdk.setPersonalizedEnabled(personalized: true);
```


## 4.9 获取广告信息

目前onSucced、onClosed、onClicked、onExposed、onRewarded回调增加adInfo回传，开发者可通过以下方法获取广告信息

| 方法名         | 介绍 |
| ------------ | ---- |
| platformName | 获取三方广告平台名称，返回String类型。|
| ecpm | 获取ECPM，返回Double类型（单位：元）。|
| ecpmPrecision | ECPM类型，返回String类型（accurate：精准、platform_assignment：平台指定、estimate：估算）。|


## 5.0 获取广告错误信息

目前仅onFailed方法会回调，开发者可通过以下方法获取广告错误信息

| 方法名         | 介绍 |
| ------------ | ---- |
| errorCode | 错误码，返回int类型。|
| errorDescription | 获取错误信息，返回String类型。|