├── .github └── workflows │ ├── flutter.yml │ └── release.yml ├── .gitignore ├── .pubignore ├── .vscode └── launch.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── android ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── zero │ │ └── flutter_pangle_ads │ │ ├── FlutterPangleAdsPlugin.java │ │ ├── PluginDelegate.java │ │ ├── event │ │ ├── AdErrorEvent.java │ │ ├── AdEvent.java │ │ ├── AdEventAction.java │ │ ├── AdEventHandler.java │ │ └── AdRewardEvent.java │ │ ├── load │ │ ├── FeedAdLoad.java │ │ └── FeedAdManager.java │ │ ├── page │ │ ├── AdBannerView.java │ │ ├── AdFeedView.java │ │ ├── AdSplashActivity.java │ │ ├── BaseAdPage.java │ │ ├── FullScreenVideoPage.java │ │ ├── NativeViewFactory.java │ │ └── RewardVideoPage.java │ │ └── utils │ │ ├── DataUtils.java │ │ ├── RewardBundleModel.java │ │ ├── StatusBarUtils.java │ │ └── UIUtils.java │ └── res │ ├── layout │ └── activity_ad_splash.xml │ ├── mipmap-xxhdpi │ └── flutterads_logo.png │ ├── values │ └── themes.xml │ └── xml │ ├── network_config.xml │ └── tt_file_paths.xml ├── example ├── .gitignore ├── README.md ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── zero │ │ │ │ │ └── flutter_pangle_ads_example │ │ │ │ │ └── MainActivity.java │ │ │ └── res │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── flutterads_logo.png │ │ │ │ ├── flutterads_logo2.png │ │ │ │ └── ic_launcher.png │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── images │ ├── gromore_1.png │ ├── gromore_2.png │ ├── gromore_pro.png │ └── pro.png ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── icon-1024.png │ │ │ ├── icon-20-ipad.png │ │ │ ├── icon-20@2x-ipad.png │ │ │ ├── icon-20@2x.png │ │ │ ├── icon-20@3x.png │ │ │ ├── icon-29-ipad.png │ │ │ ├── icon-29.png │ │ │ ├── icon-29@2x-ipad.png │ │ │ ├── icon-29@2x.png │ │ │ ├── icon-29@3x.png │ │ │ ├── icon-40.png │ │ │ ├── icon-40@2x.png │ │ │ ├── icon-40@3x.png │ │ │ ├── icon-60@2x.png │ │ │ ├── icon-60@3x.png │ │ │ ├── icon-76.png │ │ │ ├── icon-76@2x.png │ │ │ └── icon-83.5@2x.png │ │ ├── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage@1x.png │ │ │ ├── LaunchImage@2x.png │ │ │ └── LaunchImage@3x.png │ │ └── LaunchImage2.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage2@1x.png │ │ │ ├── LaunchImage2@2x.png │ │ │ └── LaunchImage2@3x.png │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── main.m ├── lib │ ├── ads_config.dart │ ├── main.dart │ ├── pages │ │ ├── banner_page.dart │ │ ├── feed_page.dart │ │ ├── fullscreen_video_page.dart │ │ ├── home_page.dart │ │ ├── pro_page.dart │ │ ├── reward_video_page.dart │ │ └── splash_page.dart │ ├── router │ │ └── router.dart │ ├── theme │ │ └── style.dart │ └── widgets │ │ └── widgets.dart ├── pubspec.yaml └── test │ └── widget_test.dart ├── go.mod ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── Event │ │ ├── AdErrorEvent.h │ │ ├── AdErrorEvent.m │ │ ├── AdEvent.h │ │ ├── AdEvent.m │ │ ├── AdEventAction.h │ │ ├── AdEventAction.m │ │ ├── AdRewardEvent.h │ │ └── AdRewardEvent.m │ ├── FlutterPangleAdsPlugin.h │ ├── FlutterPangleAdsPlugin.m │ ├── Load │ │ ├── FeedAdLoad.h │ │ ├── FeedAdLoad.m │ │ ├── FeedAdManager.h │ │ └── FeedAdManager.m │ └── Page │ │ ├── AdBannerView.h │ │ ├── AdBannerView.m │ │ ├── AdFeedView.h │ │ ├── AdFeedView.m │ │ ├── BaseAdPage.h │ │ ├── BaseAdPage.m │ │ ├── FullScreenVideoPage.h │ │ ├── FullScreenVideoPage.m │ │ ├── NativeViewFactory.h │ │ ├── NativeViewFactory.m │ │ ├── RewardVideoPage.h │ │ ├── RewardVideoPage.m │ │ ├── SplashPage.h │ │ ├── SplashPage.m │ │ ├── SplashViewController.h │ │ └── SplashViewController.m └── flutter_pangle_ads.podspec ├── lib ├── event │ ├── ad_error_event.dart │ ├── ad_event.dart │ ├── ad_event_action.dart │ ├── ad_event_handler.dart │ └── ad_reward_event.dart ├── flutter_pangle_ads.dart ├── flutter_pangle_ads_method_channel.dart ├── flutter_pangle_ads_platform_interface.dart ├── options │ └── network_type.dart └── view │ ├── ad_banner_widget.dart │ └── ad_feed_widget.dart ├── pubspec.yaml └── releaselog.sh /.github/workflows/flutter.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | # This workflow is triggered on pushes to the repository. 4 | 5 | on: 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | build: 12 | # This job will run on macos virtual machine 13 | runs-on: macos-13 14 | steps: 15 | 16 | # Setup Java environment in order to build the Android app. 17 | - uses: actions/checkout@v3 18 | - uses: actions/setup-java@v3 19 | with: 20 | distribution: 'temurin' 21 | java-version: '17' 22 | cache: 'gradle' 23 | # Setup the flutter environment. 24 | - uses: subosito/flutter-action@v2 25 | with: 26 | channel: 'stable' # 'dev', 'alpha', default to: 'stable' 27 | # flutter-version: '1.22.x' # you can also specify exact version of flutter 28 | 29 | # Get flutter dependencies. 30 | - run: flutter pub get 31 | 32 | # Check for any formatting issues in the code. 33 | - run: dart format --set-exit-if-changed . 34 | 35 | # Statically analyze the Dart code for any errors. 36 | - run: dart analyze . 37 | 38 | ##### 39 | # Build app 40 | ##### 41 | # Build apk. 42 | - run: cd example && flutter build apk 43 | # Build ios 44 | - run: cd example && flutter build ios --no-codesign 45 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*.*.*' 7 | 8 | jobs: 9 | create_release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout repository 13 | uses: actions/checkout@v4 14 | 15 | - name: Extract version from tag 16 | id: extract_version 17 | run: | 18 | VERSION=${GITHUB_REF#refs/tags/v} 19 | echo "VERSION=$VERSION" >> $GITHUB_ENV 20 | 21 | - name: Read release notes 22 | id: release_notes 23 | run: | 24 | VERSION=${{ env.VERSION }} 25 | CHANGELOG=$(sed -n "/## $VERSION/,/^## /p" CHANGELOG.md | sed '$d' | tail -n +2) 26 | if [ -z "$CHANGELOG" ]; then 27 | echo "Release notes not found for version $VERSION" 28 | exit 1 29 | fi 30 | echo "RELEASE_NOTES<> $GITHUB_ENV 31 | echo "$CHANGELOG" >> $GITHUB_ENV 32 | echo "EOF" >> $GITHUB_ENV 33 | 34 | - name: Create GitHub release 35 | uses: softprops/action-gh-release@v2 36 | with: 37 | body: ${{ env.RELEASE_NOTES }} 38 | env: 39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | *.lock 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.pubignore: -------------------------------------------------------------------------------- 1 | example/images 2 | example/android 3 | example/ios -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "flutter_pangle_ads", 9 | "request": "launch", 10 | "type": "dart" 11 | }, 12 | { 13 | "name": "flutter_pangle_ads (profile mode)", 14 | "request": "launch", 15 | "type": "dart", 16 | "flutterMode": "profile" 17 | }, 18 | { 19 | "name": "flutter_pangle_ads (release mode)", 20 | "request": "launch", 21 | "type": "dart", 22 | "flutterMode": "release" 23 | }, 24 | { 25 | "name": "example", 26 | "cwd": "example", 27 | "request": "launch", 28 | "type": "dart" 29 | }, 30 | { 31 | "name": "example (profile mode)", 32 | "cwd": "example", 33 | "request": "launch", 34 | "type": "dart", 35 | "flutterMode": "profile" 36 | }, 37 | { 38 | "name": "example (release mode)", 39 | "cwd": "example", 40 | "request": "launch", 41 | "type": "dart", 42 | "flutterMode": "release" 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 3.1.0 2 | * 🚀 推荐使用【[Gromore Pro](https://flutterads.top/)】 让您的广告收益翻倍 3 | * 新增 `directDownloadNetworkType` 字段,允许直接下载的网络类型 4 | * 升级 Android 和 iOS SDK 到最新版本 5 | * 修复提示【广告 SDK 未 Ready】 的问题 6 | * 迁移升级到 `plugin_platform_interface` 7 | 8 | ## 3.0.0 9 | * 🚀 推荐使用【[Gromore Pro](https://flutterads.top/)】 让您的广告收益翻倍 10 | * 升级 iOS SDK 到 `v6.2.1.6` 11 | * 升级 Android SDK 到 `v6.1.0.7`,感谢 @daixianceng 12 | * 适配 iOS 17 隐私清单政策,防止上架被拒 13 | * 适配新的广告初始化逻辑 14 | * 优化帮助文档说明到 [wiki](https://github.com/FlutterAds/flutter_pangle_ads/wiki) 15 | 16 | ## 2.9.0 17 | * 🚀 推荐使用【[Gromore Pro](https://flutterads.top/)】 让您的广告收益翻倍 18 | * 升级 iOS SDK 到 `v5.7.0.6` 19 | * 升级 Android SDK 到 `v5.5.1.7` 20 | 21 | ## 2.8.0 22 | * 升级 iOS SDK 到 `v5.4.1.1` 23 | * 升级 Android SDK 到 `v5.4.1.6` 24 | 25 | ## 2.7.0 26 | * [修复] iOS SDK 版本升级后不可用问题 27 | * 升级 iOS SDK 到 `v5.0.0.5` 28 | * 升级 Android SDK 到 Pro 版 `v5.1.0.2` 29 | ## 2.6.1 30 | * [修复] iOS 开屏广告点击后不跳转的问题 31 | * [修复] Android 激励视频通知两次激励的问题 32 | 33 | ## 2.6.0 34 | * [升级] 适配 4700 SDK 版本[#30](https://github.com/FlutterAds/flutter_pangle_ads/issues/30) 35 | * [新增] 新增进阶激励视频支持[#31](https://github.com/FlutterAds/flutter_pangle_ads/issues/31) 36 | * [升级] iOS SDK 到 `v4.7.0.8` 37 | * [升级] Android SDK 到 Pro 版 `v4.7.1.2` 38 | 39 | ## 2.5.0 40 | * [修复] `splashButtonType` 新版 SKD 不存在的问题 [#20](https://github.com/FlutterAds/flutter_pangle_ads/issues/20) 41 | * 升级 iOS SDK 到 `v4.3.0.4` 42 | * 升级 Android SDK 到 Pro 版 `v4.3.0.8` 43 | 44 | ## 2.4.0 45 | * [新增] 信息流广告类型 46 | * 升级 Android SDK 到 Pro 版 `v4.0.2.2` 47 | 48 | ## 2.3.1 49 | * 新增配置开屏广告点击热区样式 50 | * 升级 Android SDK 到 Pro 版 `v4.0.1.1` 51 | 52 | ## 2.3.0 53 | * 修复 iOS 点击开屏广告后 Flutter 页面点击事件无响应的问题 #8(全网唯一修复💪🏻) 54 | * 优化 Android 开屏加载素材尺寸 55 | * 增加 Android 下载确认弹窗配置 56 | * `Banner` 增加 `autoClose` 字段决定是否可以关闭广告,以适配会员才可以关闭的场景 57 | * 优化基础架构 58 | * 升级 Android SDK 到 Pro 版 `v4.0.0.1` 59 | 60 | ## 2.2.0 61 | * 增加 Banner 广告支持 62 | * 优化开屏广告支持超时时间设置 #3 63 | * 开屏 API 变更,logo 参数由位置参数变为可选命名参数,注意修改 #3 64 | * 升级 Android SDK 到 `v3.9.0.5` 65 | 66 | ## 2.1.0 67 | * 增加全屏视屏 68 | * 增加新插屏 69 | * 优化事件发送 70 | 71 | ## 2.0.0 72 | * 1.x.x 是非 Null Safety 版本 73 | * 2.x.x 是 Null Safety 版本 74 | * 现在阶段会同时维护这 2 个版本,再往后可能仅维护一个版本 75 | 76 | ## 1.4.0 77 | * [新增] 信息流广告类型 78 | * 升级 Android SDK 到 Pro 版 `v4.0.2.2` 79 | 80 | ## 1.3.1 81 | * 新增配置开屏广告点击热区样式 82 | * 升级 Android SDK 到 Pro 版 `v4.0.1.1` 83 | 84 | ## 1.2.0 85 | * 增加 Banner 广告支持 86 | * 优化开屏广告支持超时时间设置 #3 87 | * 开屏 API 变更,logo 参数由位置参数变为可选命名参数,注意修改 #3 88 | * 升级 Android SDK 到 `v3.9.0.5` 89 | 90 | ## 1.1.0 91 | * 增加全屏视屏 92 | * 增加新插屏 93 | * 优化事件发送 94 | 95 | ## 1.0.0 96 | * 优化初始化配置 97 | * 更新说明 98 | * Android SDK `v3.9.0.2` 99 | * iOS SDK 可自行更新至最新 100 | 101 | ## 0.0.1 [2021-08-23] 102 | * 支持开屏广告 103 | * 支持插屏广告 104 | * 支持激励视频 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 FlutterAds 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

📱 Flutter Pangle Ads

2 | 3 |

一款优质的穿山甲(字节跳动、巨量引擎、GroMore)Flutter 广告变现插件

4 | 5 |

♻️ 持续更新 ♻️

6 | 7 |

8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |

22 | 23 | 24 | 25 |

🏆 FlutterAds ~ 致力于构建优质的 Flutter 变现插件

26 |
27 | 28 | ## 🚀 核心功能 29 | 30 | - ✅ 开屏广告 31 | - ✅ 插屏广告 32 | - ✅ 激励视频 33 | - ✅ Banner 34 | - ✅ 信息流 35 | - 🏆 二次激励(可有效提升收益)[🚀 Pro 版](https://flutterads.top/) 36 | - 💰 实时价格 eCPM(收益统计、实时分佣)[🚀 Pro 版](https://flutterads.top/) 37 | - 🦥 预缓存(预加载,极速展示,高填充率) [🚀 Pro 版](https://flutterads.top/) 38 | - 🧪 测试助手(精准测试,稳定上线)[🚀 Pro 版](https://flutterads.top/) 39 | - ♻️ 适配最新(紧跟官方最新 SDK)[🚀 Pro 版](https://flutterads.top/) 40 | 41 | ## 📱 下载体验 42 | 43 | 44 | > 回复 `Pro` 进行体验 45 | 46 | 47 | ## 📃 接入文档 48 | 49 | - [ 🎯 极速接入、快速体验、持续更新](https://flutterads.top/) 50 | 51 | - [ 💰 变现套件 = 【GroMore】+【AdSpark】+【AdContent】](https://flutterads.top/) 52 | 53 | 54 | ## 📌 FlutterAds 广告系列插件 — 打造 Flutter 应用的变现新机会 55 | 56 | 致力于构建优质的 Flutter 变现插件,选择我们提供的高效广告插件,专为 Flutter 开发者量身定制!我们不仅涵盖国内各大广告平台,还支持国际市场,助你快速打开全球创收之门。 57 | 58 | |插件|描述| 59 | |-|-| 60 | |[🏆 FlutterAds](https://flutterads.top/)| 🎉 提供全方位的广告解决方案,轻松集成,变现效益翻倍 🚀,助力你的 Flutter 应用赢得更多收益!| 61 | |[🌐 flutter_gromore_ads](https://github.com/FlutterAds/flutter_gromore_ads)|字节跳动、穿山甲、GroMore 广告平台一站式聚合,助力你在 Flutter 中轻松集成多种广告源!| 62 | |[🌐 flutter_pangle_ads](https://github.com/FlutterAds/flutter_pangle_ads)|专为 Flutter 打造的字节跳动、穿山甲广告插件,让你畅享流量和收益!| 63 | |[🚢 flutter_pangle_global_ads](https://github.com/FlutterAds/flutter_pangle_global_ads)|面向全球用户的字节跳动、穿山甲国际版广告插件,全面支持海外市场的广告变现!| 64 | |[🌐 flutter_qq_ads](https://github.com/FlutterAds/flutter_qq_ads)| 集成腾讯广告、广点通、优量汇,快速为 Flutter 应用打开更多广告渠道!| 65 | |[Ⓜ flutter_ohos_ads](https://github.com/FlutterAds/flutter_ohos_ads)|专为 HarmonyOS(鸿蒙系统)优化的广告变现插件,提供专属 Flutter 广告支持!| 66 | |[📡 flutter_adspark](https://github.com/FlutterAds/flutter_adspark)|强大的广告监测、增长分析与事件管理功能,助力你提升广告投放效果,精准归因!| 67 | |[🎬 flutter_adcontent_pro](https://github.com/FlutterAds/flutter_adcontent)|支持短剧、小视频内容的穿山甲内容输出插件,助力你轻松打造短剧类应用!| 68 | |[📚 flutter_novel_story_pro](https://github.com/FlutterAds/flutter_novel_story)|支持短故事、小说等内容输出,带有内置阅读器和聚合首页,助力你轻松打造小说类应用!| 69 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'com.zero.flutter_pangle_ads' 2 | version '1.0' 3 | 4 | buildscript { 5 | repositories { 6 | google() 7 | jcenter() 8 | maven {url 'https://artifact.bytedance.com/repository/pangle'} 9 | } 10 | 11 | dependencies { 12 | classpath 'com.android.tools.build:gradle:4.2.1' 13 | } 14 | } 15 | 16 | rootProject.allprojects { 17 | repositories { 18 | google() 19 | jcenter() 20 | maven {url 'https://artifact.bytedance.com/repository/pangle'} 21 | } 22 | } 23 | 24 | apply plugin: 'com.android.library' 25 | 26 | android { 27 | if (project.android.hasProperty("namespace")) { 28 | namespace = "com.zero.flutter_pangle_ads" 29 | } 30 | compileSdk 31 31 | defaultConfig { 32 | minSdkVersion 19 33 | } 34 | lintOptions { 35 | disable 'InvalidPackage' 36 | } 37 | } 38 | dependencies { 39 | implementation 'androidx.appcompat:appcompat:1.3.0' 40 | implementation 'com.google.android.material:material:1.4.0' 41 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 42 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 43 | // 广告 SDK 依赖: https://artifact.bytedance.com/repository/pangle/com/pangle/cn/ads-sdk-pro/ 44 | implementation 'com.pangle.cn:ads-sdk-pro:6.1.0.7' 45 | } 46 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'flutter_pangle_ads' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 35 | 36 | 41 | 44 | 45 | 46 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /android/src/main/java/com/zero/flutter_pangle_ads/FlutterPangleAdsPlugin.java: -------------------------------------------------------------------------------- 1 | package com.zero.flutter_pangle_ads; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import io.flutter.embedding.engine.plugins.FlutterPlugin; 6 | import io.flutter.embedding.engine.plugins.activity.ActivityAware; 7 | import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; 8 | import io.flutter.plugin.common.EventChannel; 9 | import io.flutter.plugin.common.MethodChannel; 10 | 11 | /** 12 | * FlutterPangleAdsPlugin 13 | */ 14 | public class FlutterPangleAdsPlugin implements FlutterPlugin, ActivityAware { 15 | 16 | // 方法通道 17 | private MethodChannel methodChannel; 18 | // 事件通道 19 | private EventChannel eventChannel; 20 | // 插件代理 21 | private PluginDelegate delegate; 22 | // 插件连接器 23 | private FlutterPluginBinding bind; 24 | 25 | @Override 26 | public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { 27 | bind = flutterPluginBinding; 28 | // 初始化方法通道和事件通道 29 | methodChannel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "flutter_pangle_ads"); 30 | eventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "flutter_pangle_ads_event"); 31 | } 32 | 33 | 34 | @Override 35 | public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { 36 | // 解除方法通道和事件通道 37 | methodChannel.setMethodCallHandler(null); 38 | eventChannel.setStreamHandler(null); 39 | } 40 | 41 | 42 | @Override 43 | public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { 44 | this.delegate = new PluginDelegate(binding.getActivity(), bind); 45 | methodChannel.setMethodCallHandler(delegate); 46 | eventChannel.setStreamHandler(delegate); 47 | this.delegate.registerBannerView(); 48 | this.delegate.registerFeedView(); 49 | } 50 | 51 | @Override 52 | public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { 53 | onAttachedToActivity(binding); 54 | } 55 | 56 | 57 | @Override 58 | public void onDetachedFromActivityForConfigChanges() { 59 | onDetachedFromActivity(); 60 | } 61 | 62 | 63 | @Override 64 | public void onDetachedFromActivity() { 65 | this.delegate = null; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /android/src/main/java/com/zero/flutter_pangle_ads/event/AdErrorEvent.java: -------------------------------------------------------------------------------- 1 | package com.zero.flutter_pangle_ads.event; 2 | 3 | import java.util.HashMap; 4 | 5 | /** 6 | * 广告错误事件 7 | */ 8 | public class AdErrorEvent extends AdEvent { 9 | // 错误码 10 | private final int errCode; 11 | // 错误信息 12 | private final String errMsg; 13 | 14 | // 错误事件实体 15 | public AdErrorEvent(String adId, int errCode, String errMsg) { 16 | super(adId, AdEventAction.onAdError); 17 | this.errCode = errCode; 18 | this.errMsg = errMsg; 19 | } 20 | 21 | /** 22 | * 重写 toMap 方法 23 | * 24 | * @return 返回错误事件的map 25 | */ 26 | @Override 27 | public HashMap toMap() { 28 | HashMap newMap = super.toMap(); 29 | newMap.put("errCode", errCode); 30 | newMap.put("errMsg", errMsg); 31 | return newMap; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /android/src/main/java/com/zero/flutter_pangle_ads/event/AdEvent.java: -------------------------------------------------------------------------------- 1 | package com.zero.flutter_pangle_ads.event; 2 | 3 | import java.util.HashMap; 4 | 5 | /** 6 | * 广告事件 7 | */ 8 | public class AdEvent { 9 | // 广告 id 10 | private final String adId; 11 | // 操作 12 | private final String action; 13 | 14 | public AdEvent(String adId, String action) { 15 | this.adId = adId; 16 | this.action = action; 17 | } 18 | 19 | /** 20 | * 转换为 Map 方面传输 21 | * 22 | * @return 转换后的 Map 对象 23 | */ 24 | public HashMap toMap() { 25 | HashMap map = new HashMap(); 26 | map.put("adId", adId); 27 | map.put("action", action); 28 | return map; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /android/src/main/java/com/zero/flutter_pangle_ads/event/AdEventAction.java: -------------------------------------------------------------------------------- 1 | package com.zero.flutter_pangle_ads.event; 2 | 3 | /** 4 | * 广告事件操作 5 | */ 6 | public class AdEventAction { 7 | // 广告错误 8 | public static final String onAdError = "onAdError"; 9 | // 广告加载成功 10 | public static final String onAdLoaded = "onAdLoaded"; 11 | // 广告填充 12 | public static final String onAdPresent = "onAdPresent"; 13 | // 广告曝光 14 | public static final String onAdExposure = "onAdExposure"; 15 | // 广告关闭(计时结束或者用户点击关闭) 16 | public static final String onAdClosed = "onAdClosed"; 17 | // 广告点击 18 | public static final String onAdClicked = "onAdClicked"; 19 | // 广告跳过 20 | public static final String onAdSkip = "onAdSkip"; 21 | // 广告播放或计时完毕 22 | public static final String onAdComplete = "onAdComplete"; 23 | // 获得广告激励 24 | public static final String onAdReward = "onAdReward"; 25 | } 26 | -------------------------------------------------------------------------------- /android/src/main/java/com/zero/flutter_pangle_ads/event/AdEventHandler.java: -------------------------------------------------------------------------------- 1 | package com.zero.flutter_pangle_ads.event; 2 | 3 | import com.zero.flutter_pangle_ads.PluginDelegate; 4 | 5 | /** 6 | * 广告事件处理类 7 | */ 8 | public class AdEventHandler { 9 | // 广告事件处理对象 10 | private static volatile AdEventHandler _instance; 11 | 12 | /** 13 | * 获取广告事件处理类 14 | * 15 | * @return 广告事件处理对象 16 | */ 17 | public static AdEventHandler getInstance() { 18 | if (_instance == null) { 19 | synchronized (AdEventHandler.class) { 20 | _instance = new AdEventHandler(); 21 | } 22 | } 23 | return _instance; 24 | } 25 | 26 | /** 27 | * 添加广告事件 28 | * 29 | * @param event 广告事件 30 | */ 31 | public void sendEvent(AdEvent event) { 32 | if (event != null) { 33 | PluginDelegate.getInstance().addEvent(event.toMap()); 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /android/src/main/java/com/zero/flutter_pangle_ads/event/AdRewardEvent.java: -------------------------------------------------------------------------------- 1 | package com.zero.flutter_pangle_ads.event; 2 | 3 | import java.util.HashMap; 4 | 5 | /** 6 | * 广告激励事件 7 | */ 8 | public class AdRewardEvent extends AdEvent { 9 | 10 | // 奖励类型 11 | private final int rewardType; 12 | // 奖励是否有效 13 | private final boolean rewardVerify; 14 | // 奖励数量 15 | private final int rewardAmount; 16 | // 奖励名称 17 | private final String rewardName; 18 | // 错误码 19 | private final int errCode; 20 | // 错误信息 21 | private final String errMsg; 22 | // 自定义信息 23 | private final String customData; 24 | // 用户信息 25 | private final String userId; 26 | 27 | public AdRewardEvent(String adId, int rewardType,boolean rewardVerify, int rewardAmount, String rewardName, int errCode, String errMsg, String customData, String userId) { 28 | super(adId, AdEventAction.onAdReward); 29 | this.rewardType=rewardType; 30 | this.rewardVerify = rewardVerify; 31 | this.rewardAmount = rewardAmount; 32 | this.rewardName = rewardName; 33 | this.errCode = errCode; 34 | this.errMsg = errMsg; 35 | this.customData = customData; 36 | this.userId = userId; 37 | } 38 | 39 | @Override 40 | public HashMap toMap() { 41 | HashMap newMap = super.toMap(); 42 | newMap.put("rewardType", rewardType); 43 | newMap.put("rewardVerify", rewardVerify); 44 | newMap.put("rewardAmount", rewardAmount); 45 | newMap.put("rewardName", rewardName); 46 | newMap.put("errCode", errCode); 47 | newMap.put("errMsg", errMsg); 48 | newMap.put("customData", customData); 49 | newMap.put("userId", userId); 50 | return newMap; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /android/src/main/java/com/zero/flutter_pangle_ads/load/FeedAdLoad.java: -------------------------------------------------------------------------------- 1 | package com.zero.flutter_pangle_ads.load; 2 | 3 | import android.app.Activity; 4 | import android.util.Log; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import com.bytedance.sdk.openadsdk.AdSlot; 9 | import com.bytedance.sdk.openadsdk.TTAdNative; 10 | import com.bytedance.sdk.openadsdk.TTNativeExpressAd; 11 | import com.zero.flutter_pangle_ads.event.AdEventAction; 12 | import com.zero.flutter_pangle_ads.page.BaseAdPage; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | import io.flutter.plugin.common.MethodCall; 18 | import io.flutter.plugin.common.MethodChannel; 19 | 20 | /** 21 | * 信息流加载对象 22 | */ 23 | public class FeedAdLoad extends BaseAdPage implements TTAdNative.NativeExpressAdListener { 24 | private final String TAG = FeedAdManager.class.getSimpleName(); 25 | private MethodChannel.Result result; 26 | 27 | /** 28 | * 加载信息流广告列表 29 | * @param call 30 | * @param result 31 | */ 32 | public void loadFeedAdList(Activity activity, @NonNull MethodCall call, @NonNull MethodChannel.Result result) { 33 | this.result=result; 34 | showAd(activity,call); 35 | } 36 | 37 | @Override 38 | public void loadAd(@NonNull MethodCall call) { 39 | // 获取请求模板广告素材的尺寸 40 | int expressViewWidth = call.argument("width"); 41 | int expressViewHeight = call.argument("height"); 42 | int count = call.argument("count"); 43 | adSlot = new AdSlot.Builder() 44 | .setCodeId(posId) 45 | .setAdCount(count) 46 | .setSupportDeepLink(true) 47 | .setExpressViewAcceptedSize(expressViewWidth, expressViewHeight) 48 | .build(); 49 | ad.loadNativeExpressAd(adSlot,this); 50 | } 51 | 52 | @Override 53 | public void onError(int i, String s) { 54 | Log.e(TAG, "onError code:" + i + " msg:" + s); 55 | sendErrorEvent(i, s); 56 | this.result.error(""+i,s,s); 57 | } 58 | 59 | 60 | @Override 61 | public void onNativeExpressAdLoad(List list) { 62 | List adResultList=new ArrayList<>(); 63 | Log.i(TAG, "onNativeExpressAdLoad"); 64 | if (list == null || list.size() == 0) { 65 | this.result.success(adResultList); 66 | return; 67 | } 68 | for (TTNativeExpressAd adItem : list) { 69 | int key=adItem.hashCode(); 70 | adResultList.add(key); 71 | FeedAdManager.getInstance().putAd(key,adItem); 72 | } 73 | // 添加广告事件 74 | sendEvent(AdEventAction.onAdLoaded); 75 | this.result.success(adResultList); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /android/src/main/java/com/zero/flutter_pangle_ads/load/FeedAdManager.java: -------------------------------------------------------------------------------- 1 | package com.zero.flutter_pangle_ads.load; 2 | 3 | import com.bytedance.sdk.openadsdk.TTNativeExpressAd; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * 信息流广告管理 10 | */ 11 | public class FeedAdManager { 12 | private final String TAG = FeedAdManager.class.getSimpleName(); 13 | // 信息流广告管理类对象 14 | private static FeedAdManager _instance; 15 | 16 | public static synchronized FeedAdManager getInstance() { 17 | if (_instance == null) { 18 | synchronized (FeedAdManager.class) { 19 | _instance = new FeedAdManager(); 20 | } 21 | } 22 | return _instance; 23 | } 24 | 25 | // 信息流广告列表 26 | private final Map feedAdList = new HashMap(); 27 | 28 | /** 29 | * 添加广告渲染对象 30 | * 31 | * @param key 广告缓存id 32 | * @param ad 广告渲染对象 33 | */ 34 | public void putAd(int key, TTNativeExpressAd ad) { 35 | feedAdList.put(key, ad); 36 | } 37 | 38 | /** 39 | * 获取广告渲染对象 40 | * 41 | * @param key 广告缓存id 42 | * @return 广告渲染对象 43 | */ 44 | public TTNativeExpressAd getAd(int key) { 45 | return feedAdList.get(key); 46 | } 47 | 48 | /** 49 | * 删除广告渲染对象 50 | * 51 | * @param key 广告缓存id 52 | * @return 广告渲染对象 53 | */ 54 | public TTNativeExpressAd removeAd(int key) { 55 | return feedAdList.remove(key); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /android/src/main/java/com/zero/flutter_pangle_ads/page/AdBannerView.java: -------------------------------------------------------------------------------- 1 | package com.zero.flutter_pangle_ads.page; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import android.view.View; 6 | import android.widget.FrameLayout; 7 | 8 | import androidx.annotation.NonNull; 9 | import androidx.annotation.Nullable; 10 | 11 | import com.bytedance.sdk.openadsdk.AdSlot; 12 | import com.bytedance.sdk.openadsdk.TTAdDislike; 13 | import com.bytedance.sdk.openadsdk.TTAdNative; 14 | import com.bytedance.sdk.openadsdk.TTNativeExpressAd; 15 | import com.zero.flutter_pangle_ads.PluginDelegate; 16 | import com.zero.flutter_pangle_ads.event.AdEventAction; 17 | 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | import io.flutter.plugin.common.MethodCall; 22 | import io.flutter.plugin.platform.PlatformView; 23 | 24 | /** 25 | * Banner 广告 View 26 | */ 27 | class AdBannerView extends BaseAdPage implements PlatformView, TTAdNative.NativeExpressAdListener, TTNativeExpressAd.AdInteractionListener { 28 | private final String TAG = AdBannerView.class.getSimpleName(); 29 | @NonNull 30 | private final FrameLayout frameLayout; 31 | private final PluginDelegate pluginDelegate; 32 | private int id; 33 | private TTNativeExpressAd bad; 34 | private int interval; 35 | private boolean autoClose; 36 | 37 | 38 | AdBannerView(@NonNull Context context, int id, @Nullable Map creationParams, PluginDelegate pluginDelegate) { 39 | this.id = id; 40 | this.pluginDelegate = pluginDelegate; 41 | frameLayout = new FrameLayout(context); 42 | MethodCall call = new MethodCall("AdBannerView", creationParams); 43 | showAd(this.pluginDelegate.activity, call); 44 | } 45 | 46 | @NonNull 47 | @Override 48 | public View getView() { 49 | return frameLayout; 50 | } 51 | 52 | @Override 53 | public void dispose() { 54 | disposeAd(); 55 | } 56 | 57 | @Override 58 | public void loadAd(@NonNull MethodCall call) { 59 | // 获取轮播时间间隔参数 60 | interval = call.argument("interval"); 61 | // 获取请求模板广告素材的尺寸 62 | int expressViewWidth = call.argument("width"); 63 | int expressViewHeight = call.argument("height"); 64 | // 是否自动关闭 65 | autoClose = call.argument("autoClose"); 66 | adSlot = new AdSlot.Builder() 67 | .setCodeId(posId) 68 | .setAdCount(1) 69 | .setSupportDeepLink(true) 70 | .setExpressViewAcceptedSize(expressViewWidth, expressViewHeight) 71 | .build(); 72 | ad.loadBannerExpressAd(adSlot, this); 73 | } 74 | 75 | /** 76 | * 销毁广告 77 | */ 78 | private void disposeAd() { 79 | frameLayout.removeAllViews(); 80 | if (bad != null) { 81 | bad.destroy(); 82 | } 83 | } 84 | 85 | 86 | @Override 87 | public void onError(int i, String s) { 88 | Log.e(TAG, "onError code:" + i + " msg:" + s); 89 | sendErrorEvent(i, s); 90 | disposeAd(); 91 | } 92 | 93 | @Override 94 | public void onNativeExpressAdLoad(List list) { 95 | Log.i(TAG, "onRenderSuccess"); 96 | if (list == null || list.size() == 0) { 97 | return; 98 | } 99 | bad = list.get(0); 100 | bad.setExpressInteractionListener(this); 101 | bindDislikeAction(bad); 102 | // 设置广告轮播间隔 103 | if (interval > 0) { 104 | bad.setSlideIntervalTime(interval * 1000); 105 | } 106 | 107 | bad.render(); 108 | // 添加广告事件 109 | sendEvent(AdEventAction.onAdLoaded); 110 | } 111 | 112 | @Override 113 | public void onAdDismiss() { 114 | Log.i(TAG, "onAdDismiss"); 115 | // 添加广告事件 116 | sendEvent(AdEventAction.onAdClosed); 117 | // 点击如不感兴趣后,自动关闭 118 | if (autoClose) { 119 | disposeAd(); 120 | } 121 | } 122 | 123 | @Override 124 | public void onAdClicked(View view, int i) { 125 | Log.i(TAG, "onAdClicked"); 126 | // 添加广告事件 127 | sendEvent(AdEventAction.onAdClicked); 128 | } 129 | 130 | @Override 131 | public void onAdShow(View view, int i) { 132 | Log.i(TAG, "onAdShow"); 133 | // 添加广告事件 134 | sendEvent(AdEventAction.onAdExposure); 135 | } 136 | 137 | @Override 138 | public void onRenderFail(View view, String s, int i) { 139 | Log.e(TAG, "onRenderFail code:" + i + " msg:" + s); 140 | // 添加广告错误事件 141 | sendErrorEvent(i, s); 142 | } 143 | 144 | @Override 145 | public void onRenderSuccess(View view, float v, float v1) { 146 | Log.i(TAG, "onRenderSuccess"); 147 | if (bad != null && activity != null) { 148 | frameLayout.addView(view); 149 | // 添加广告事件 150 | sendEvent(AdEventAction.onAdPresent); 151 | } 152 | } 153 | 154 | /** 155 | * 接入dislike 逻辑,有助于提示广告精准投放度 156 | * 和后续广告关闭逻辑处理 157 | * 158 | * @param ad 广告 View 159 | */ 160 | private void bindDislikeAction(TTNativeExpressAd ad) { 161 | // 使用默认Dislike 162 | final TTAdDislike ttAdDislike = ad.getDislikeDialog(activity); 163 | if (ttAdDislike != null) { 164 | ttAdDislike.setDislikeInteractionCallback(new TTAdDislike.DislikeInteractionCallback() { 165 | @Override 166 | public void onShow() { 167 | 168 | } 169 | 170 | @Override 171 | public void onSelected(int position, String value, boolean enforce) { 172 | onAdDismiss(); 173 | } 174 | 175 | @Override 176 | public void onCancel() { 177 | 178 | } 179 | }); 180 | } 181 | } 182 | 183 | 184 | } -------------------------------------------------------------------------------- /android/src/main/java/com/zero/flutter_pangle_ads/page/AdFeedView.java: -------------------------------------------------------------------------------- 1 | package com.zero.flutter_pangle_ads.page; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.FrameLayout; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.annotation.Nullable; 11 | 12 | import com.bytedance.sdk.openadsdk.TTAdDislike; 13 | import com.bytedance.sdk.openadsdk.TTNativeExpressAd; 14 | import com.zero.flutter_pangle_ads.PluginDelegate; 15 | import com.zero.flutter_pangle_ads.event.AdEventAction; 16 | import com.zero.flutter_pangle_ads.load.FeedAdManager; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import io.flutter.plugin.common.MethodCall; 22 | import io.flutter.plugin.common.MethodChannel; 23 | import io.flutter.plugin.platform.PlatformView; 24 | 25 | /** 26 | * Feed 信息流广告 View 27 | */ 28 | class AdFeedView extends BaseAdPage implements PlatformView, TTNativeExpressAd.AdInteractionListener { 29 | private final String TAG = AdFeedView.class.getSimpleName(); 30 | @NonNull 31 | private final FrameLayout frameLayout; 32 | private final PluginDelegate pluginDelegate; 33 | private int id; 34 | private TTNativeExpressAd fad; 35 | private MethodChannel methodChannel; 36 | 37 | 38 | AdFeedView(@NonNull Context context, int id, @Nullable Map creationParams, PluginDelegate pluginDelegate) { 39 | this.id = id; 40 | this.pluginDelegate = pluginDelegate; 41 | methodChannel = new MethodChannel(this.pluginDelegate.bind.getBinaryMessenger(), PluginDelegate.KEY_FEED_VIEW + "/" + id); 42 | frameLayout = new FrameLayout(context); 43 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 44 | frameLayout.setLayoutParams(params); 45 | MethodCall call = new MethodCall("AdFeedView", creationParams); 46 | showAd(this.pluginDelegate.activity, call); 47 | } 48 | 49 | @NonNull 50 | @Override 51 | public View getView() { 52 | return frameLayout; 53 | } 54 | 55 | @Override 56 | public void dispose() { 57 | removeAd(); 58 | } 59 | 60 | @Override 61 | public void loadAd(@NonNull MethodCall call) { 62 | fad = FeedAdManager.getInstance().getAd(Integer.parseInt(this.posId)); 63 | if (fad != null) { 64 | View adView = fad.getExpressAdView(); 65 | if (adView.getParent() != null) { 66 | ((ViewGroup) adView.getParent()).removeAllViews(); 67 | } 68 | frameLayout.removeAllViews(); 69 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 70 | frameLayout.addView(adView, params); 71 | fad.setExpressInteractionListener(this); 72 | bindDislikeAction(fad); 73 | fad.render(); 74 | } 75 | } 76 | 77 | /** 78 | * 移除广告 79 | */ 80 | private void removeAd() { 81 | frameLayout.removeAllViews(); 82 | } 83 | 84 | /** 85 | * 销毁广告 86 | */ 87 | private void disposeAd() { 88 | removeAd(); 89 | FeedAdManager.getInstance().removeAd(Integer.parseInt(this.posId)); 90 | if (fad != null) { 91 | fad.destroy(); 92 | } 93 | // 更新宽高 94 | setFlutterViewSize(0f, 0f); 95 | } 96 | 97 | @Override 98 | public void onAdDismiss() { 99 | Log.i(TAG, "onAdDismiss"); 100 | // 添加广告事件 101 | sendEvent(AdEventAction.onAdClosed); 102 | disposeAd(); 103 | } 104 | 105 | @Override 106 | public void onAdClicked(View view, int i) { 107 | Log.i(TAG, "onAdClicked"); 108 | // 添加广告事件 109 | sendEvent(AdEventAction.onAdClicked); 110 | } 111 | 112 | @Override 113 | public void onAdShow(View view, int i) { 114 | Log.i(TAG, "onAdShow"); 115 | // 添加广告事件 116 | sendEvent(AdEventAction.onAdExposure); 117 | } 118 | 119 | @Override 120 | public void onRenderFail(View view, String s, int i) { 121 | Log.e(TAG, "onRenderFail code:" + i + " msg:" + s); 122 | // 添加广告错误事件 123 | sendErrorEvent(i, s); 124 | // 更新宽高 125 | setFlutterViewSize(0f, 0f); 126 | } 127 | 128 | @Override 129 | public void onRenderSuccess(View view, float width, float height) { 130 | Log.i(TAG, "onRenderSuccess v:" + width + " v1:" + height); 131 | // 添加广告事件 132 | sendEvent(AdEventAction.onAdPresent); 133 | setFlutterViewSize(width, height); 134 | } 135 | 136 | /** 137 | * 设置 FlutterAds 视图宽高 138 | * 139 | * @param width 宽度 140 | * @param height 高度 141 | */ 142 | private void setFlutterViewSize(float width, float height) { 143 | // 更新宽高 144 | Map sizeMap = new HashMap<>(); 145 | sizeMap.put("width", (double) width); 146 | sizeMap.put("height", (double) height); 147 | if (methodChannel != null) { 148 | methodChannel.invokeMethod("setSize", sizeMap); 149 | } 150 | } 151 | 152 | /** 153 | * 接入dislike 逻辑,有助于提示广告精准投放度 154 | * 和后续广告关闭逻辑处理 155 | * 156 | * @param ad 广告 View 157 | */ 158 | private void bindDislikeAction(TTNativeExpressAd ad) { 159 | // 使用默认Dislike 160 | final TTAdDislike ttAdDislike = ad.getDislikeDialog(activity); 161 | if (ttAdDislike != null) { 162 | ttAdDislike.setDislikeInteractionCallback(new TTAdDislike.DislikeInteractionCallback() { 163 | @Override 164 | public void onShow() { 165 | 166 | } 167 | 168 | @Override 169 | public void onSelected(int position, String value, boolean enforce) { 170 | onAdDismiss(); 171 | } 172 | 173 | @Override 174 | public void onCancel() { 175 | 176 | } 177 | }); 178 | } 179 | } 180 | 181 | 182 | } -------------------------------------------------------------------------------- /android/src/main/java/com/zero/flutter_pangle_ads/page/AdSplashActivity.java: -------------------------------------------------------------------------------- 1 | package com.zero.flutter_pangle_ads.page; 2 | 3 | import android.os.Bundle; 4 | import android.text.TextUtils; 5 | import android.util.Log; 6 | import android.view.KeyEvent; 7 | import android.view.View; 8 | import android.widget.FrameLayout; 9 | 10 | import androidx.appcompat.app.AppCompatActivity; 11 | import androidx.appcompat.widget.AppCompatImageView; 12 | 13 | import com.bytedance.sdk.openadsdk.AdSlot; 14 | import com.bytedance.sdk.openadsdk.CSJAdError; 15 | import com.bytedance.sdk.openadsdk.CSJSplashAd; 16 | import com.bytedance.sdk.openadsdk.TTAdLoadType; 17 | import com.bytedance.sdk.openadsdk.TTAdNative; 18 | import com.bytedance.sdk.openadsdk.TTAdSdk; 19 | import com.zero.flutter_pangle_ads.PluginDelegate; 20 | import com.zero.flutter_pangle_ads.R; 21 | import com.zero.flutter_pangle_ads.event.AdErrorEvent; 22 | import com.zero.flutter_pangle_ads.event.AdEvent; 23 | import com.zero.flutter_pangle_ads.event.AdEventAction; 24 | import com.zero.flutter_pangle_ads.event.AdEventHandler; 25 | import com.zero.flutter_pangle_ads.utils.StatusBarUtils; 26 | import com.zero.flutter_pangle_ads.utils.UIUtils; 27 | 28 | /** 29 | * 开屏广告 30 | */ 31 | public class AdSplashActivity extends AppCompatActivity implements TTAdNative.CSJSplashAdListener, CSJSplashAd.SplashAdListener { 32 | private final String TAG = AdSplashActivity.class.getSimpleName(); 33 | // 广告容器 34 | private FrameLayout ad_container; 35 | // 自定义品牌 logo 36 | private AppCompatImageView ad_logo; 37 | // 广告位 id 38 | private String posId; 39 | 40 | @Override 41 | protected void onCreate(Bundle savedInstanceState) { 42 | super.onCreate(savedInstanceState); 43 | UIUtils.hideBottomUIMenu(this); 44 | StatusBarUtils.setTranslucent(this); 45 | setContentView(R.layout.activity_ad_splash); 46 | initView(); 47 | initData(); 48 | } 49 | 50 | /** 51 | * 初始化View 52 | */ 53 | private void initView() { 54 | ad_container = findViewById(R.id.splash_ad_container); 55 | ad_logo = findViewById(R.id.splash_ad_logo); 56 | } 57 | 58 | /** 59 | * 初始化数据 60 | */ 61 | private void initData() { 62 | // 获取参数 63 | posId = getIntent().getStringExtra(PluginDelegate.KEY_POSID); 64 | String logo = getIntent().getStringExtra(PluginDelegate.KEY_LOGO); 65 | double timeout = getIntent().getDoubleExtra(PluginDelegate.KEY_TIMEOUT, 3.5); 66 | int absTimeout = (int) (timeout * 1000); 67 | // 判断是否有 Logo 68 | boolean hasLogo = !TextUtils.isEmpty(logo); 69 | if (hasLogo) { 70 | // 加载 logo 71 | int resId = getMipmapId(logo); 72 | hasLogo = resId > 0; 73 | if (hasLogo) { 74 | ad_logo.setVisibility(View.VISIBLE); 75 | ad_logo.setImageResource(resId); 76 | } else { 77 | Log.e(TAG, "Logo 名称不匹配或不在 mipmap 文件夹下,展示全屏"); 78 | } 79 | } 80 | int width = UIUtils.getScreenWidthInPx(this); 81 | int widthDp = (int) UIUtils.getScreenWidthDp(this); 82 | int height = UIUtils.getRealHeight(this); 83 | // 判断最终的 Logo 是否显示 84 | if (!hasLogo) { 85 | ad_logo.setVisibility(View.GONE); 86 | } else { 87 | // 显示 Logo 高度去掉 Logo 的高度 88 | height = height - ad_logo.getLayoutParams().height; 89 | } 90 | // 创建开屏广告 91 | TTAdNative splashAD = TTAdSdk.getAdManager().createAdNative(this); 92 | AdSlot adSlot = new AdSlot.Builder() 93 | .setCodeId(posId) 94 | .setSupportDeepLink(true) 95 | .setImageAcceptedSize(width, height) // 单位是px 96 | .setExpressViewAcceptedSize(widthDp, UIUtils.px2dip(this,height)) // 单位是dp 97 | .setAdLoadType(TTAdLoadType.LOAD) 98 | .build(); 99 | // 加载广告 100 | splashAD.loadSplashAd(adSlot,this,absTimeout); 101 | } 102 | 103 | /** 104 | * 完成广告,退出开屏页面 105 | */ 106 | private void finishPage() { 107 | finish(); 108 | // 设置退出动画 109 | overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); 110 | } 111 | 112 | /** 113 | * 开屏页一定要禁止用户对返回按钮的控制,否则将可能导致用户手动退出了App而广告无法正常曝光和计费 114 | */ 115 | @Override 116 | public boolean onKeyDown(int keyCode, KeyEvent event) { 117 | if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_HOME) { 118 | return true; 119 | } 120 | return super.onKeyDown(keyCode, event); 121 | } 122 | 123 | /** 124 | * 获取图片资源的id 125 | * 126 | * @param resName 资源名称,不带后缀 127 | * @return 返回资源id 128 | */ 129 | private int getMipmapId(String resName) { 130 | return getResources().getIdentifier(resName, "mipmap", getPackageName()); 131 | } 132 | 133 | @Override 134 | public void onSplashLoadSuccess(CSJSplashAd csjSplashAd) { 135 | Log.d(TAG, "onSplashLoadSuccess"); 136 | // 加载事件 137 | AdEventHandler.getInstance().sendEvent(new AdEvent(this.posId, AdEventAction.onAdLoaded)); 138 | } 139 | 140 | @Override 141 | public void onSplashLoadFail(CSJAdError error) { 142 | AdEventHandler.getInstance().sendEvent(new AdErrorEvent(this.posId, error.getCode(), error.getMsg())); 143 | finishPage(); 144 | } 145 | 146 | @Override 147 | public void onSplashRenderSuccess(CSJSplashAd csjSplashAd) { 148 | Log.d(TAG, "onSplashAdLoad"); 149 | if (this.isFinishing()) { 150 | AdEventHandler.getInstance().sendEvent(new AdEvent(this.posId, AdEventAction.onAdClosed)); 151 | finishPage(); 152 | } else { 153 | csjSplashAd.showSplashView(ad_container); 154 | // 设置交互监听 155 | csjSplashAd.setSplashAdListener(this); 156 | // 加载事件 157 | AdEventHandler.getInstance().sendEvent(new AdEvent(this.posId, AdEventAction.onAdPresent)); 158 | } 159 | 160 | } 161 | 162 | @Override 163 | public void onSplashRenderFail(CSJSplashAd csjSplashAd, CSJAdError error) { 164 | AdEventHandler.getInstance().sendEvent(new AdErrorEvent(this.posId, error.getCode(), error.getMsg())); 165 | finishPage(); 166 | } 167 | 168 | @Override 169 | public void onSplashAdShow(CSJSplashAd csjSplashAd) { 170 | Log.d(TAG, "onSplashLoadSuccess"); 171 | AdEventHandler.getInstance().sendEvent(new AdEvent(this.posId, AdEventAction.onAdExposure)); 172 | } 173 | 174 | @Override 175 | public void onSplashAdClick(CSJSplashAd csjSplashAd) { 176 | Log.d(TAG, "onSplashAdClick"); 177 | AdEventHandler.getInstance().sendEvent(new AdEvent(this.posId, AdEventAction.onAdClicked)); 178 | finishPage(); 179 | } 180 | 181 | @Override 182 | public void onSplashAdClose(CSJSplashAd csjSplashAd, int i) { 183 | Log.d(TAG, "onSplashAdClose"); 184 | AdEventHandler.getInstance().sendEvent(new AdEvent(this.posId, AdEventAction.onAdClosed)); 185 | finishPage(); 186 | } 187 | } -------------------------------------------------------------------------------- /android/src/main/java/com/zero/flutter_pangle_ads/page/BaseAdPage.java: -------------------------------------------------------------------------------- 1 | package com.zero.flutter_pangle_ads.page; 2 | 3 | import static com.zero.flutter_pangle_ads.PluginDelegate.KEY_POSID; 4 | 5 | import android.app.Activity; 6 | 7 | import com.bytedance.sdk.openadsdk.AdSlot; 8 | import com.bytedance.sdk.openadsdk.TTAdNative; 9 | import com.bytedance.sdk.openadsdk.TTAdSdk; 10 | import com.zero.flutter_pangle_ads.event.AdErrorEvent; 11 | import com.zero.flutter_pangle_ads.event.AdEvent; 12 | import com.zero.flutter_pangle_ads.event.AdEventHandler; 13 | 14 | import io.flutter.plugin.common.MethodCall; 15 | 16 | /** 17 | * 基础广告页面 18 | */ 19 | public abstract class BaseAdPage { 20 | // 上下文 21 | protected Activity activity; 22 | // 广告位 id 23 | protected String posId; 24 | // 广告管理对象 25 | protected TTAdNative ad; 26 | // 广告配置对象 27 | protected AdSlot adSlot; 28 | 29 | 30 | /** 31 | * 显示广告 32 | * 33 | * @param activity 上下文 34 | * @param call 方法调用 35 | */ 36 | public void showAd(Activity activity, MethodCall call) { 37 | this.activity = activity; 38 | this.posId = call.argument(KEY_POSID); 39 | ad = TTAdSdk.getAdManager().createAdNative(activity); 40 | loadAd(call); 41 | } 42 | 43 | /** 44 | * 加载广告 45 | * 46 | * @param call 方法调用 47 | */ 48 | public abstract void loadAd(MethodCall call); 49 | 50 | /** 51 | * 发送广告事件 52 | * 53 | * @param event 广告事件 54 | */ 55 | protected void sendEvent(AdEvent event) { 56 | AdEventHandler.getInstance().sendEvent(event); 57 | } 58 | 59 | /** 60 | * 发送广告事件 61 | * 62 | * @param action 操作 63 | */ 64 | protected void sendEvent(String action) { 65 | sendEvent(new AdEvent(posId, action)); 66 | } 67 | 68 | /** 69 | * 发送错误事件 70 | * 71 | * @param errCode 错误码 72 | * @param errMsg 错误事件 73 | */ 74 | protected void sendErrorEvent(int errCode, String errMsg) { 75 | sendEvent(new AdErrorEvent(posId, errCode, errMsg)); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /android/src/main/java/com/zero/flutter_pangle_ads/page/FullScreenVideoPage.java: -------------------------------------------------------------------------------- 1 | package com.zero.flutter_pangle_ads.page; 2 | 3 | import android.util.Log; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | import com.bytedance.sdk.openadsdk.AdSlot; 8 | import com.bytedance.sdk.openadsdk.TTAdNative; 9 | import com.bytedance.sdk.openadsdk.TTFullScreenVideoAd; 10 | import com.zero.flutter_pangle_ads.event.AdEventAction; 11 | 12 | import io.flutter.plugin.common.MethodCall; 13 | 14 | /** 15 | * 全屏视频页面 16 | */ 17 | public class FullScreenVideoPage extends BaseAdPage implements TTAdNative.FullScreenVideoAdListener, TTFullScreenVideoAd.FullScreenVideoAdInteractionListener { 18 | private static final String TAG = FullScreenVideoPage.class.getSimpleName(); 19 | 20 | @Override 21 | public void loadAd(@NonNull MethodCall call) { 22 | // 配置广告 23 | adSlot = new AdSlot.Builder() 24 | .setCodeId(posId) 25 | .setExpressViewAcceptedSize(500, 500) 26 | .setSupportDeepLink(true) 27 | .build(); 28 | // 加载广告 29 | ad.loadFullScreenVideoAd(adSlot, this); 30 | } 31 | 32 | @Override 33 | public void onError(int i, String s) { 34 | Log.e(TAG, "onError code:" + i + " msg:" + s); 35 | // 添加广告错误事件 36 | sendErrorEvent(i, s); 37 | } 38 | 39 | @Override 40 | public void onFullScreenVideoAdLoad(TTFullScreenVideoAd ttFullScreenVideoAd) { 41 | Log.i(TAG, "onFullScreenVideoAdLoad"); 42 | ttFullScreenVideoAd.setFullScreenVideoAdInteractionListener(this); 43 | // 添加广告事件 44 | sendEvent(AdEventAction.onAdLoaded); 45 | } 46 | 47 | @Override 48 | public void onFullScreenVideoCached() { 49 | Log.i(TAG, "onFullScreenVideoCached"); 50 | } 51 | 52 | @Override 53 | public void onFullScreenVideoCached(TTFullScreenVideoAd ttFullScreenVideoAd) { 54 | Log.i(TAG, "onFullScreenVideoCached ttFullScreenVideoAd"); 55 | ttFullScreenVideoAd.showFullScreenVideoAd(activity); 56 | // 添加广告事件 57 | sendEvent(AdEventAction.onAdPresent); 58 | } 59 | 60 | @Override 61 | public void onAdShow() { 62 | Log.i(TAG, "onAdShow"); 63 | // 添加广告事件 64 | sendEvent(AdEventAction.onAdExposure); 65 | } 66 | 67 | @Override 68 | public void onAdVideoBarClick() { 69 | Log.i(TAG, "onAdVideoBarClick"); 70 | // 添加广告事件 71 | sendEvent(AdEventAction.onAdClicked); 72 | } 73 | 74 | @Override 75 | public void onAdClose() { 76 | Log.i(TAG, "onAdClose"); 77 | // 添加广告事件 78 | sendEvent(AdEventAction.onAdClosed); 79 | } 80 | 81 | @Override 82 | public void onVideoComplete() { 83 | Log.i(TAG, "onVideoComplete"); 84 | // 添加广告事件 85 | sendEvent(AdEventAction.onAdComplete); 86 | } 87 | 88 | @Override 89 | public void onSkippedVideo() { 90 | Log.i(TAG, "onSkippedVideo"); 91 | // 添加广告事件 92 | sendEvent(AdEventAction.onAdSkip); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /android/src/main/java/com/zero/flutter_pangle_ads/page/NativeViewFactory.java: -------------------------------------------------------------------------------- 1 | package com.zero.flutter_pangle_ads.page; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.annotation.Nullable; 7 | 8 | import com.zero.flutter_pangle_ads.PluginDelegate; 9 | 10 | import java.util.Map; 11 | 12 | import io.flutter.plugin.common.StandardMessageCodec; 13 | import io.flutter.plugin.platform.PlatformView; 14 | import io.flutter.plugin.platform.PlatformViewFactory; 15 | 16 | /** 17 | * 原生平台 View 工厂 18 | */ 19 | public class NativeViewFactory extends PlatformViewFactory { 20 | @NonNull 21 | private final String viewName;// View 名字 22 | private final PluginDelegate pluginDelegate; // 插件代理类 23 | 24 | public NativeViewFactory(String viewName, @NonNull PluginDelegate pluginDelegate) { 25 | super(StandardMessageCodec.INSTANCE); 26 | this.viewName = viewName; 27 | this.pluginDelegate = pluginDelegate; 28 | } 29 | 30 | @NonNull 31 | @Override 32 | public PlatformView create(@NonNull Context context, int id, @Nullable Object args) { 33 | final Map creationParams = (Map) args; 34 | if (this.viewName.equals(PluginDelegate.KEY_BANNER_VIEW)) { 35 | return new AdBannerView(context, id, creationParams, pluginDelegate); 36 | } else if (this.viewName.equals(PluginDelegate.KEY_FEED_VIEW)) { 37 | return new AdFeedView(context, id, creationParams, pluginDelegate); 38 | } 39 | return null; 40 | } 41 | } -------------------------------------------------------------------------------- /android/src/main/java/com/zero/flutter_pangle_ads/page/RewardVideoPage.java: -------------------------------------------------------------------------------- 1 | package com.zero.flutter_pangle_ads.page; 2 | 3 | import android.os.Bundle; 4 | import android.util.Log; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import com.bytedance.sdk.openadsdk.AdSlot; 9 | import com.bytedance.sdk.openadsdk.TTAdNative; 10 | import com.bytedance.sdk.openadsdk.TTRewardVideoAd; 11 | import com.zero.flutter_pangle_ads.event.AdEventAction; 12 | import com.zero.flutter_pangle_ads.event.AdRewardEvent; 13 | import com.zero.flutter_pangle_ads.utils.RewardBundleModel; 14 | 15 | import io.flutter.plugin.common.MethodCall; 16 | 17 | /** 18 | * 激励视频页面 19 | */ 20 | public class RewardVideoPage extends BaseAdPage implements TTAdNative.RewardVideoAdListener, TTRewardVideoAd.RewardAdInteractionListener { 21 | private static final String TAG = RewardVideoPage.class.getSimpleName(); 22 | // 显示广告对象 23 | private TTRewardVideoAd rvad; 24 | // 设置激励视频服务端验证的自定义信息 25 | private String customData; 26 | // 设置服务端验证的用户信息 27 | private String userId; 28 | 29 | @Override 30 | public void loadAd(@NonNull MethodCall call) { 31 | customData = call.argument("customData"); 32 | userId = call.argument("userId"); 33 | // 配置广告 34 | adSlot = new AdSlot.Builder() 35 | .setCodeId(posId) 36 | .setExpressViewAcceptedSize(500, 500) 37 | .setUserID(userId)//tag_id 38 | .setMediaExtra(customData) //附加参数 39 | .build(); 40 | // 加载广告 41 | ad.loadRewardVideoAd(adSlot, this); 42 | } 43 | 44 | @Override 45 | public void onError(int i, String s) { 46 | Log.e(TAG, "onError code:" + i + " msg:" + s); 47 | // 添加广告错误事件 48 | sendErrorEvent(i, s); 49 | } 50 | 51 | @Override 52 | public void onRewardVideoAdLoad(TTRewardVideoAd ttRewardVideoAd) { 53 | Log.i(TAG, "onRewardVideoAdLoad"); 54 | rvad = ttRewardVideoAd; 55 | rvad.setRewardAdInteractionListener(this); 56 | rvad.setRewardPlayAgainInteractionListener(this); 57 | // 添加广告事件 58 | sendEvent(AdEventAction.onAdLoaded); 59 | } 60 | 61 | @Override 62 | public void onRewardVideoCached() { 63 | Log.i(TAG, "onRewardVideoCached"); 64 | } 65 | 66 | @Override 67 | public void onRewardVideoCached(TTRewardVideoAd ttRewardVideoAd) { 68 | Log.i(TAG, "onRewardVideoCached ttRewardVideoAd"); 69 | if (rvad != null) { 70 | rvad.showRewardVideoAd(activity); 71 | } 72 | // 添加广告事件 73 | sendEvent(AdEventAction.onAdPresent); 74 | } 75 | 76 | @Override 77 | public void onAdShow() { 78 | Log.i(TAG, "onAdShow"); 79 | // 添加广告事件 80 | sendEvent(AdEventAction.onAdExposure); 81 | } 82 | 83 | @Override 84 | public void onAdVideoBarClick() { 85 | Log.i(TAG, "onAdVideoBarClick"); 86 | // 添加广告事件 87 | sendEvent(AdEventAction.onAdClicked); 88 | } 89 | 90 | @Override 91 | public void onAdClose() { 92 | Log.i(TAG, "onAdClose"); 93 | // 添加广告事件 94 | sendEvent(AdEventAction.onAdClosed); 95 | rvad = null; 96 | } 97 | 98 | @Override 99 | public void onVideoComplete() { 100 | Log.i(TAG, "onVideoComplete"); 101 | // 添加广告事件 102 | sendEvent(AdEventAction.onAdComplete); 103 | } 104 | 105 | @Override 106 | public void onVideoError() { 107 | Log.i(TAG, "onVideoError"); 108 | } 109 | 110 | // 视频播放完成后,奖励验证回调,rewardVerify:是否有效,rewardAmount:奖励数量,rewardName:奖励名称,code:错误码,msg:错误信息 111 | @Override 112 | public void onRewardVerify(boolean rewardVerify, int rewardAmount, String rewardName, int code, String msg) { 113 | String logString ="verify:" + rewardVerify + " amount:" + rewardAmount + 114 | " name:" + rewardName + " errorCode:" + code + " errorMsg:" + msg; 115 | Log.e(TAG, "onRewardVerify " + logString); 116 | // sendEvent(new AdRewardEvent(posId,0, rewardVerify, rewardAmount, rewardName, code, msg, customData, userId)); 117 | } 118 | 119 | @Override 120 | public void onRewardArrived(boolean isRewardValid, int rewardType, Bundle extraInfo) { 121 | RewardBundleModel rewardBundleModel = new RewardBundleModel(extraInfo); 122 | String logString = "rewardType:"+rewardType+" verify:" + isRewardValid + " amount:" + rewardBundleModel.getRewardAmount() + 123 | " name:" + rewardBundleModel.getRewardName() + " errorCode:" + rewardBundleModel.getServerErrorCode() + " errorMsg:" + rewardBundleModel.getServerErrorMsg(); 124 | Log.e(TAG, "onRewardArrived " + logString); 125 | sendEvent(new AdRewardEvent(posId,rewardType, isRewardValid, rewardBundleModel.getRewardAmount(), rewardBundleModel.getRewardName(), rewardBundleModel.getServerErrorCode(), rewardBundleModel.getServerErrorMsg(), customData, userId)); 126 | } 127 | 128 | @Override 129 | public void onSkippedVideo() { 130 | Log.i(TAG, "onSkippedVideo"); 131 | // 添加广告事件 132 | sendEvent(AdEventAction.onAdSkip); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /android/src/main/java/com/zero/flutter_pangle_ads/utils/DataUtils.java: -------------------------------------------------------------------------------- 1 | package com.zero.flutter_pangle_ads.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Iterator; 5 | 6 | /** 7 | * 数据工具类 8 | */ 9 | public class DataUtils { 10 | /** 11 | * 转换为 int [] 12 | * 13 | * @param integers List 14 | * @return int [] 15 | */ 16 | public static int[] convertIntegers(ArrayList integers) { 17 | if (integers == null) { 18 | return new int[]{}; 19 | } 20 | int[] ret = new int[integers.size()]; 21 | Iterator iterator = integers.iterator(); 22 | for (int i = 0; i < ret.length; i++) { 23 | ret[i] = iterator.next().intValue(); 24 | } 25 | return ret; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /android/src/main/java/com/zero/flutter_pangle_ads/utils/RewardBundleModel.java: -------------------------------------------------------------------------------- 1 | package com.zero.flutter_pangle_ads.utils; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.bytedance.sdk.openadsdk.TTRewardVideoAd; 6 | 7 | /** 8 | * Created by zero 9 | * Usage: 激励视频奖励回调额外参数 10 | */ 11 | public class RewardBundleModel { 12 | 13 | private int mServerErrorCode; 14 | private String mServerErrorMsg; 15 | private String mRewardName; 16 | private int mRewardAmount; 17 | private float mRewardPropose; 18 | 19 | public RewardBundleModel(Bundle extraInfo) { 20 | 21 | mServerErrorCode = extraInfo.getInt(TTRewardVideoAd.REWARD_EXTRA_KEY_ERROR_CODE); 22 | 23 | mServerErrorMsg = extraInfo.getString(TTRewardVideoAd.REWARD_EXTRA_KEY_ERROR_MSG); 24 | 25 | mRewardName = extraInfo.getString(TTRewardVideoAd.REWARD_EXTRA_KEY_REWARD_NAME); 26 | 27 | mRewardAmount = extraInfo.getInt(TTRewardVideoAd.REWARD_EXTRA_KEY_REWARD_AMOUNT); 28 | 29 | mRewardPropose = extraInfo.getFloat(TTRewardVideoAd.REWARD_EXTRA_KEY_REWARD_PROPOSE); 30 | } 31 | 32 | /** 33 | * 获得服务器验证的错误码 34 | */ 35 | public int getServerErrorCode() { 36 | return mServerErrorCode; 37 | } 38 | /** 39 | * 获得服务器验证的错误信息 40 | */ 41 | public String getServerErrorMsg() { 42 | return mServerErrorMsg; 43 | } 44 | /** 45 | * 获得开发者平台配置的奖励名称 46 | */ 47 | public String getRewardName() { 48 | return mRewardName; 49 | } 50 | /** 51 | * 获得开发者平台配置的奖励数量 52 | */ 53 | public int getRewardAmount() { 54 | return mRewardAmount; 55 | } 56 | 57 | /** 58 | * 获得此次奖励建议发放的奖励比例 59 | */ 60 | public float getRewardPropose() { 61 | return mRewardPropose; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /android/src/main/java/com/zero/flutter_pangle_ads/utils/StatusBarUtils.java: -------------------------------------------------------------------------------- 1 | package com.zero.flutter_pangle_ads.utils; 2 | 3 | import android.app.Activity; 4 | import android.graphics.Color; 5 | import android.os.Build; 6 | import android.view.View; 7 | import android.view.Window; 8 | import android.view.WindowManager; 9 | 10 | /** 11 | * 状态栏工具类 12 | */ 13 | public class StatusBarUtils { 14 | /** 15 | * 设置透明状态栏 16 | * 17 | * @param activity Activity 18 | */ 19 | public static void setTranslucent(Activity activity) { 20 | Window window = activity.getWindow(); 21 | /// 设置透明状态栏 22 | if (window != null) { 23 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 24 | window.setStatusBarColor(Color.TRANSPARENT); 25 | } else { 26 | window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 27 | } 28 | } 29 | } 30 | 31 | /** 32 | * Android 6.0使用原始的主题适配 33 | * 34 | * @param activity Activity 35 | * @param darkMode 是否是黑色模式 36 | */ 37 | public static void setBarDarkMode(Activity activity, boolean darkMode) { 38 | Window window = activity.getWindow(); 39 | if (window == null) { 40 | return; 41 | } 42 | //设置StatusBar模式 43 | if (darkMode) {//黑色模式 44 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 45 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//设置statusBar和navigationBar为黑色 46 | window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); 47 | } else {//设置statusBar为黑色 48 | window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); 49 | } 50 | } 51 | } else {//白色模式 52 | int statusBarFlag = View.SYSTEM_UI_FLAG_VISIBLE; 53 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 54 | statusBarFlag = window.getDecorView().getSystemUiVisibility() 55 | & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; 56 | } 57 | window.getDecorView().setSystemUiVisibility(statusBarFlag); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /android/src/main/res/layout/activity_ad_splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | 19 | 28 | -------------------------------------------------------------------------------- /android/src/main/res/mipmap-xxhdpi/flutterads_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/android/src/main/res/mipmap-xxhdpi/flutterads_logo.png -------------------------------------------------------------------------------- /android/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 14 | -------------------------------------------------------------------------------- /android/src/main/res/xml/network_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/src/main/res/xml/tt_file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # flutter_pangle_ads_example 2 |

3 | logo 4 |

5 |

FlutterAds 致力于构建优质的 Flutter 广告插件

6 | 7 | ## 联系作者 8 | - 微信:ZeroFlutter 9 | - 邮箱:zhengsonglan001@gmail.com 10 | 11 | ## 支持开源 12 | 13 | 支持作者 14 | 15 | > 或许你不知道经过了多少个夜晚,就是为了方便你使用 16 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | android { 8 | namespace = "com.zero.flutter_pangle_ads_example" 9 | compileSdk = 34 10 | ndkVersion = flutter.ndkVersion 11 | 12 | compileOptions { 13 | sourceCompatibility = JavaVersion.VERSION_1_8 14 | targetCompatibility = JavaVersion.VERSION_1_8 15 | } 16 | 17 | kotlinOptions { 18 | jvmTarget = JavaVersion.VERSION_1_8 19 | } 20 | 21 | defaultConfig { 22 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 23 | applicationId = "com.zero.flutter_pangle_ads_example" 24 | // You can update the following values to match your application needs. 25 | // For more information, see: https://flutter.dev/to/review-gradle-config. 26 | minSdk = flutter.minSdkVersion 27 | targetSdk = flutter.targetSdkVersion 28 | versionCode = flutter.versionCode 29 | versionName = flutter.versionName 30 | } 31 | 32 | buildTypes { 33 | release { 34 | // TODO: Add your own signing config for the release build. 35 | // Signing with the debug keys for now, so `flutter run --release` works. 36 | signingConfig = signingConfigs.debug 37 | } 38 | } 39 | } 40 | 41 | flutter { 42 | source = "../.." 43 | } 44 | 45 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 9 | 17 | 21 | 25 | 30 | 34 | 35 | 36 | 37 | 38 | 39 | 41 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/zero/flutter_pangle_ads_example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.zero.flutter_pangle_ads_example; 2 | 3 | import android.os.Bundle; 4 | 5 | import androidx.annotation.Nullable; 6 | 7 | import com.zero.flutter_pangle_ads.utils.StatusBarUtils; 8 | 9 | import io.flutter.embedding.android.FlutterActivity; 10 | 11 | public class MainActivity extends FlutterActivity { 12 | 13 | @Override 14 | protected void onCreate(@Nullable Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | StatusBarUtils.setTranslucent(this); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/flutterads_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/android/app/src/main/res/mipmap-xxhdpi/flutterads_logo.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/flutterads_logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/android/app/src/main/res/mipmap-xxhdpi/flutterads_logo2.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | rootProject.buildDir = "../build" 9 | subprojects { 10 | project.buildDir = "${rootProject.buildDir}/${project.name}" 11 | } 12 | subprojects { 13 | project.evaluationDependsOn(":app") 14 | } 15 | 16 | tasks.register("clean", Delete) { 17 | delete rootProject.buildDir 18 | } 19 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | android.enableR8=true 5 | android.injected.testOnly=false 6 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version "7.3.0" apply false 22 | id "org.jetbrains.kotlin.android" version "1.7.10" apply false 23 | } 24 | 25 | include ":app" 26 | -------------------------------------------------------------------------------- /example/images/gromore_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/images/gromore_1.png -------------------------------------------------------------------------------- /example/images/gromore_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/images/gromore_2.png -------------------------------------------------------------------------------- /example/images/gromore_pro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/images/gromore_pro.png -------------------------------------------------------------------------------- /example/images/pro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/images/pro.png -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '12.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 32 | end 33 | 34 | post_install do |installer| 35 | installer.pods_project.targets.each do |target| 36 | flutter_additional_ios_build_settings(target) 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | #import "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "size": "20x20", 5 | "idiom": "iphone", 6 | "filename": "icon-20@2x.png", 7 | "scale": "2x" 8 | }, 9 | { 10 | "size": "20x20", 11 | "idiom": "iphone", 12 | "filename": "icon-20@3x.png", 13 | "scale": "3x" 14 | }, 15 | { 16 | "size": "29x29", 17 | "idiom": "iphone", 18 | "filename": "icon-29.png", 19 | "scale": "1x" 20 | }, 21 | { 22 | "size": "29x29", 23 | "idiom": "iphone", 24 | "filename": "icon-29@2x.png", 25 | "scale": "2x" 26 | }, 27 | { 28 | "size": "29x29", 29 | "idiom": "iphone", 30 | "filename": "icon-29@3x.png", 31 | "scale": "3x" 32 | }, 33 | { 34 | "size": "40x40", 35 | "idiom": "iphone", 36 | "filename": "icon-40@2x.png", 37 | "scale": "2x" 38 | }, 39 | { 40 | "size": "40x40", 41 | "idiom": "iphone", 42 | "filename": "icon-40@3x.png", 43 | "scale": "3x" 44 | }, 45 | { 46 | "size": "60x60", 47 | "idiom": "iphone", 48 | "filename": "icon-60@2x.png", 49 | "scale": "2x" 50 | }, 51 | { 52 | "size": "60x60", 53 | "idiom": "iphone", 54 | "filename": "icon-60@3x.png", 55 | "scale": "3x" 56 | }, 57 | { 58 | "size": "20x20", 59 | "idiom": "ipad", 60 | "filename": "icon-20-ipad.png", 61 | "scale": "1x" 62 | }, 63 | { 64 | "size": "20x20", 65 | "idiom": "ipad", 66 | "filename": "icon-20@2x-ipad.png", 67 | "scale": "2x" 68 | }, 69 | { 70 | "size": "29x29", 71 | "idiom": "ipad", 72 | "filename": "icon-29-ipad.png", 73 | "scale": "1x" 74 | }, 75 | { 76 | "size": "29x29", 77 | "idiom": "ipad", 78 | "filename": "icon-29@2x-ipad.png", 79 | "scale": "2x" 80 | }, 81 | { 82 | "size": "40x40", 83 | "idiom": "ipad", 84 | "filename": "icon-40.png", 85 | "scale": "1x" 86 | }, 87 | { 88 | "size": "40x40", 89 | "idiom": "ipad", 90 | "filename": "icon-40@2x.png", 91 | "scale": "2x" 92 | }, 93 | { 94 | "size": "76x76", 95 | "idiom": "ipad", 96 | "filename": "icon-76.png", 97 | "scale": "1x" 98 | }, 99 | { 100 | "size": "76x76", 101 | "idiom": "ipad", 102 | "filename": "icon-76@2x.png", 103 | "scale": "2x" 104 | }, 105 | { 106 | "size": "83.5x83.5", 107 | "idiom": "ipad", 108 | "filename": "icon-83.5@2x.png", 109 | "scale": "2x" 110 | }, 111 | { 112 | "size": "1024x1024", 113 | "idiom": "ios-marketing", 114 | "filename": "icon-1024.png", 115 | "scale": "1x" 116 | } 117 | ], 118 | "info": { 119 | "version": 1, 120 | "author": "icon.wuruihong.com" 121 | } 122 | } -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-1024.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20-ipad.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@2x-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@2x-ipad.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29-ipad.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x-ipad.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "LaunchImage@1x.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "LaunchImage@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "LaunchImage@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "LaunchImage2@1x.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "LaunchImage2@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "LaunchImage2@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage2.imageset/LaunchImage2@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/LaunchImage2.imageset/LaunchImage2@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage2.imageset/LaunchImage2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/LaunchImage2.imageset/LaunchImage2@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage2.imageset/LaunchImage2@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/example/ios/Runner/Assets.xcassets/LaunchImage2.imageset/LaunchImage2@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CADisableMinimumFrameDurationOnPhone 6 | 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | flutter_pangle_ads_example 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | 32 | NSUserTrackingUsageDescription 33 | 为了向您提供更优质、安全的个性化服务及内容,需要您允许使用相关权限 34 | UILaunchStoryboardName 35 | LaunchScreen 36 | UIMainStoryboardFile 37 | Main 38 | UISupportedInterfaceOrientations 39 | 40 | UIInterfaceOrientationPortrait 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UISupportedInterfaceOrientations~ipad 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationPortraitUpsideDown 48 | UIInterfaceOrientationLandscapeLeft 49 | UIInterfaceOrientationLandscapeRight 50 | 51 | UIViewControllerBasedStatusBarAppearance 52 | 53 | UIApplicationSupportsIndirectInputEvents 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /example/ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/lib/ads_config.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | /// 广告配置信息 4 | class AdsConfig { 5 | /// 获取 Logo 资源名称 6 | static String get logo { 7 | if (Platform.isAndroid) { 8 | return 'flutterads_logo'; 9 | } else { 10 | return 'LaunchImage'; 11 | } 12 | } 13 | 14 | /// 获取 Logo 资源名称 2 15 | static String get logo2 { 16 | if (Platform.isAndroid) { 17 | return 'flutterads_logo2'; 18 | } else { 19 | return 'LaunchImage2'; 20 | } 21 | } 22 | 23 | /// 获取 App id 24 | static String get appId => '5324024'; 25 | 26 | /// 获取开屏广告位id 27 | static String get splashId => '887870893'; 28 | 29 | /// 获取新插屏广告位id 30 | static String get newInterstitialId => '949641653'; 31 | 32 | /// 获取新插屏(半屏)广告位id 33 | static String get newInterstitialId2 => '949641665'; 34 | 35 | /// 获取激励视频广告位id 36 | static String get rewardVideoId => '949641706'; 37 | 38 | /// 获取进阶激励视频广告位id 39 | static String get rewardInteractVideoId => '949641720'; 40 | 41 | /// 获取全屏视频广告位id 42 | static String get fullScreenVideoId => '946593099'; 43 | 44 | /// 获取 Banner 广告位id 45 | static String get bannerId => '949641731'; 46 | 47 | /// 获取 Banner 广告位id 01 48 | static String get bannerId01 => '949641733'; 49 | 50 | /// 获取 Banner 广告位id 02 51 | static String get bannerId02 => '949641736'; 52 | 53 | /// 获取 Feed 信息流广告位id(左右图文 2.4) 54 | static String get feedId => '949641744'; 55 | } 56 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_pangle_ads/flutter_pangle_ads.dart'; 3 | 4 | import 'ads_config.dart'; 5 | import 'pages/home_page.dart'; 6 | 7 | void main() { 8 | // 绑定引擎 9 | WidgetsFlutterBinding.ensureInitialized(); 10 | setAdEvent(); 11 | init().then((value) { 12 | if (value) { 13 | FlutterPangleAds.showSplashAd( 14 | AdsConfig.splashId, 15 | logo: AdsConfig.logo, 16 | timeout: 3.5, 17 | ); 18 | } 19 | }); 20 | 21 | // 启动 22 | runApp(MyApp()); 23 | } 24 | 25 | class MyApp extends StatefulWidget { 26 | @override 27 | _MyAppState createState() => _MyAppState(); 28 | } 29 | 30 | class _MyAppState extends State { 31 | @override 32 | Widget build(BuildContext context) { 33 | return MaterialApp( 34 | home: HomePage(), 35 | ); 36 | } 37 | } 38 | 39 | /// 初始化广告 SDK 40 | Future init() async { 41 | bool result = await FlutterPangleAds.initAd( 42 | AdsConfig.appId, 43 | directDownloadNetworkType: [ 44 | NetworkType.kNetworkStateMobile, 45 | NetworkType.kNetworkStateWifi, 46 | ], 47 | ); 48 | debugPrint("广告SDK 初始化${result ? '成功' : '失败'}"); 49 | 50 | // 打开个性化广告推荐 51 | FlutterPangleAds.setUserExtData(personalAdsType: '1'); 52 | return result; 53 | } 54 | 55 | /// 设置广告监听 56 | Future setAdEvent() async { 57 | FlutterPangleAds.onEventListener((event) { 58 | debugPrint('adId:${event.adId} action:${event.action}'); 59 | if (event is AdErrorEvent) { 60 | // 错误事件 61 | debugPrint(' errCode:${event.errCode} errMsg:${event.errMsg}'); 62 | } else if (event is AdRewardEvent) { 63 | // 激励事件 64 | debugPrint( 65 | ' rewardType:${event.rewardType} rewardVerify:${event.rewardVerify} rewardAmount:${event.rewardAmount} rewardName:${event.rewardName} errCode:${event.errCode} errMsg:${event.errMsg} customData:${event.customData} userId:${event.userId}'); 66 | } 67 | // 测试关闭 Banner(会员场景) 68 | if (event.action == AdEventAction.onAdClosed && 69 | event.adId == AdsConfig.bannerId02) { 70 | debugPrint('仅会员可以关闭广告'); 71 | } 72 | }); 73 | } 74 | -------------------------------------------------------------------------------- /example/lib/pages/banner_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_pangle_ads/flutter_pangle_ads.dart'; 3 | 4 | import '../ads_config.dart'; 5 | import '../widgets/widgets.dart'; 6 | 7 | class BannerPage extends StatefulWidget { 8 | const BannerPage({Key? key}) : super(key: key); 9 | 10 | @override 11 | _BannerPageState createState() => _BannerPageState(); 12 | } 13 | 14 | class _BannerPageState extends State { 15 | @override 16 | Widget build(BuildContext context) { 17 | return Scaffold( 18 | appBar: buildAppBar(context, 'Banner广告'), 19 | body: Center( 20 | child: Column( 21 | mainAxisSize: MainAxisSize.max, 22 | mainAxisAlignment: MainAxisAlignment.center, 23 | children: [ 24 | SizedBox(height: 20), 25 | AdBannerWidget( 26 | posId: AdsConfig.bannerId, 27 | ), 28 | SizedBox(height: 10), 29 | AdBannerWidget( 30 | posId: AdsConfig.bannerId01, 31 | width: 300, 32 | height: 75, 33 | interval: 30, 34 | show: true, 35 | ), 36 | SizedBox(height: 10), 37 | AdBannerWidget( 38 | posId: AdsConfig.bannerId02, 39 | width: 320, 40 | height: 50, 41 | autoClose: false, 42 | ), 43 | ], 44 | ), 45 | ), 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /example/lib/pages/feed_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_pangle_ads/flutter_pangle_ads.dart'; 3 | import 'package:flutter_pangle_ads_example/ads_config.dart'; 4 | import 'package:loadany/loadany.dart'; 5 | 6 | import '../widgets/widgets.dart'; 7 | 8 | /// 信息流页面 9 | class FeedPage extends StatefulWidget { 10 | FeedPage({Key? key}) : super(key: key); 11 | 12 | @override 13 | _FeedPageState createState() => _FeedPageState(); 14 | } 15 | 16 | class _FeedPageState extends State { 17 | List feedList = []; 18 | List feedAdList = []; 19 | 20 | LoadStatus loadStatus = LoadStatus.normal; 21 | 22 | @override 23 | void initState() { 24 | getFeedList(); 25 | super.initState(); 26 | } 27 | 28 | @override 29 | void dispose() { 30 | clearFeedAd(); 31 | super.dispose(); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | return Scaffold( 37 | appBar: buildAppBar(context, '信息流广告'), 38 | body: LoadAny( 39 | onLoadMore: () async { 40 | getFeedList(); 41 | }, 42 | status: loadStatus, 43 | child: CustomScrollView( 44 | slivers: [ 45 | SliverList( 46 | delegate: SliverChildBuilderDelegate( 47 | (BuildContext context, int index) { 48 | if (index % 5 == 4) { 49 | int adIndex = index ~/ 5; 50 | if (adIndex >= feedAdList.length) { 51 | return Container( 52 | height: 80, 53 | width: double.maxFinite, 54 | color: Colors.blueAccent, 55 | alignment: Alignment.centerLeft, 56 | child: Text('暂无广告 $index'), 57 | ); 58 | } 59 | 60 | int adId = feedAdList[adIndex]; 61 | return AdFeedWidget( 62 | posId: '$adId', 63 | width: 375, 64 | height: 128, 65 | show: true, 66 | ); 67 | } 68 | return LoadingItemWidget(); 69 | }, 70 | childCount: feedList.length, 71 | ), 72 | ) 73 | ], 74 | ), 75 | ), 76 | ); 77 | } 78 | 79 | /// 加载信息流 80 | Future getFeedList() async { 81 | setState(() { 82 | loadStatus = LoadStatus.loading; 83 | }); 84 | await Future.delayed(Duration(seconds: 2)); 85 | for (var i = 0; i < 30; i++) { 86 | feedList.add(i); 87 | } 88 | getFeedAdList(); 89 | } 90 | 91 | // 加载信息流广告 92 | Future getFeedAdList() async { 93 | try { 94 | List adResultList = await FlutterPangleAds.loadFeedAd( 95 | AdsConfig.feedId, 96 | count: 3, 97 | ); 98 | feedAdList.addAll(adResultList); 99 | } catch (e) { 100 | print(e.toString()); 101 | } 102 | setState(() { 103 | loadStatus = LoadStatus.normal; 104 | }); 105 | } 106 | 107 | // 清除信息流广告 108 | Future clearFeedAd() async { 109 | bool result = await FlutterPangleAds.clearFeedAd(feedAdList); 110 | print('clearFeedAd:$result'); 111 | } 112 | } 113 | 114 | /// 加载项组件 115 | class LoadingItemWidget extends StatelessWidget { 116 | const LoadingItemWidget({ 117 | Key? key, 118 | }) : super(key: key); 119 | 120 | @override 121 | Widget build(BuildContext context) { 122 | return Container( 123 | height: 80, 124 | width: double.maxFinite, 125 | alignment: Alignment.centerLeft, 126 | padding: EdgeInsets.all(10), 127 | margin: EdgeInsets.symmetric(vertical: 10), 128 | color: Colors.white, 129 | child: Row( 130 | children: [ 131 | Container( 132 | width: 60, 133 | height: 60, 134 | decoration: BoxDecoration( 135 | shape: BoxShape.circle, 136 | color: Color(0xFFEBEBF4), 137 | ), 138 | ), 139 | SizedBox(width: 20), 140 | Expanded( 141 | child: Padding( 142 | padding: const EdgeInsets.all(4), 143 | child: Column( 144 | crossAxisAlignment: CrossAxisAlignment.start, 145 | children: [ 146 | Container( 147 | width: double.maxFinite, 148 | height: 20, 149 | decoration: BoxDecoration( 150 | color: Color(0xFFEBEBF4), 151 | ), 152 | ), 153 | Spacer(), 154 | Container( 155 | width: 200, 156 | height: 20, 157 | decoration: BoxDecoration( 158 | color: Color(0xFFE4E4F4), 159 | ), 160 | ), 161 | ], 162 | ), 163 | ), 164 | ) 165 | ], 166 | ), 167 | ); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /example/lib/pages/fullscreen_video_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_pangle_ads/flutter_pangle_ads.dart'; 3 | import 'package:flutter_pangle_ads_example/theme/style.dart'; 4 | 5 | import '../ads_config.dart'; 6 | import '../widgets/widgets.dart'; 7 | 8 | // 全屏视频 9 | class FullScreenVideoPage extends StatefulWidget { 10 | const FullScreenVideoPage({Key? key}) : super(key: key); 11 | 12 | @override 13 | _FullScreenVideoPageState createState() => _FullScreenVideoPageState(); 14 | } 15 | 16 | class _FullScreenVideoPageState extends State { 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: buildAppBar(context, '插屏广告'), 21 | body: Column( 22 | children: [ 23 | ListTile( 24 | title: Text('新插屏-半屏'), 25 | onTap: () => showFullScreenVideoAd(AdsConfig.newInterstitialId2), 26 | ), 27 | kDivider, 28 | ListTile( 29 | title: Text('新插屏-全屏'), 30 | onTap: () => showFullScreenVideoAd(AdsConfig.newInterstitialId), 31 | ), 32 | kDivider, 33 | ], 34 | ), 35 | ); 36 | } 37 | 38 | /// 展示全屏视频、新插屏广告 39 | /// [posId] 广告位id 40 | Future showFullScreenVideoAd(String posId) async { 41 | bool result = await FlutterPangleAds.showFullScreenVideoAd(posId); 42 | print("展示插屏广告${result ? '成功' : '失败'}"); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /example/lib/pages/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_pangle_ads/flutter_pangle_ads.dart'; 5 | import 'package:flutter_pangle_ads_example/pages/banner_page.dart'; 6 | import 'package:flutter_pangle_ads_example/pages/splash_page.dart'; 7 | import 'package:flutter_pangle_ads_example/router/router.dart'; 8 | import 'package:flutter_pangle_ads_example/theme/style.dart'; 9 | import 'feed_page.dart'; 10 | 11 | import 'fullscreen_video_page.dart'; 12 | import 'reward_video_page.dart'; 13 | 14 | class HomePage extends StatefulWidget { 15 | @override 16 | _HomePageState createState() => _HomePageState(); 17 | } 18 | 19 | class _HomePageState extends State { 20 | @override 21 | void initState() { 22 | super.initState(); 23 | } 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return Scaffold( 28 | appBar: AppBar( 29 | title: const Text('FlutterAds pangle plugin'), 30 | ), 31 | body: SafeArea( 32 | child: SingleChildScrollView( 33 | child: Center( 34 | child: Column( 35 | children: [ 36 | InkWell( 37 | child: Image.asset( 38 | 'images/gromore_pro.png', 39 | width: double.maxFinite, 40 | ), 41 | onTap: () => pushProPage(context), 42 | ), 43 | kDivider, 44 | ListTile( 45 | dense: true, 46 | title: Text('🚀 GroMore Pro 版'), 47 | onTap: () => pushProPage(context), 48 | ), 49 | kDivider, 50 | ListTile( 51 | dense: true, 52 | title: Text('📢 请求应用跟踪透明授权(iOS)'), 53 | onTap: () => requestIDFA(), 54 | ), 55 | kDivider, 56 | ListTile( 57 | dense: true, 58 | title: Text('📱 请求相关权限(Android)'), 59 | onTap: () => requestPermissionIfNecessary(), 60 | ), 61 | kDivider, 62 | ListTile( 63 | title: Text('开屏广告'), 64 | onTap: () => pushPage(context, SplashPage()), 65 | ), 66 | kDivider, 67 | ListTile( 68 | title: Text('新插屏广告'), 69 | onTap: () => pushPage(context, FullScreenVideoPage()), 70 | ), 71 | kDivider, 72 | ListTile( 73 | title: Text('Banner 广告'), 74 | onTap: () => pushPage(context, BannerPage()), 75 | ), 76 | kDivider, 77 | ListTile( 78 | title: Text('激励视频广告'), 79 | onTap: () => pushPage(context, RewardVideoPage()), 80 | ), 81 | kDivider, 82 | ListTile( 83 | title: Text('信息流'), 84 | onTap: () => pushPage(context, FeedPage()), 85 | ), 86 | kDivider, 87 | ], 88 | ), 89 | ), 90 | ), 91 | ), 92 | ); 93 | } 94 | 95 | /// 请求应用跟踪透明度授权 96 | Future requestIDFA() async { 97 | bool result = await FlutterPangleAds.requestIDFA; 98 | print('请求广告标识符:$result'); 99 | } 100 | 101 | /// 请求应用跟踪透明度授权 102 | Future requestPermissionIfNecessary() async { 103 | bool result = await FlutterPangleAds.requestPermissionIfNecessary; 104 | print('请求相关权限:$result'); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /example/lib/pages/pro_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | 4 | // Pro 版本 5 | class ProPage extends StatefulWidget { 6 | const ProPage({Key? key}) : super(key: key); 7 | 8 | @override 9 | _ProPageState createState() => _ProPageState(); 10 | } 11 | 12 | class _ProPageState extends State { 13 | @override 14 | Widget build(BuildContext context) { 15 | return Scaffold( 16 | appBar: AppBar( 17 | title: const Text('Gromore Pro 版本'), 18 | actions: [ 19 | // 复制链接 20 | IconButton( 21 | icon: Icon(Icons.paste_rounded), 22 | onPressed: () => pasteUrl(), 23 | ) 24 | ], 25 | ), 26 | body: GestureDetector( 27 | onTap: () => pasteUrl(), 28 | child: SingleChildScrollView( 29 | child: Column( 30 | children: [ 31 | Image.asset('images/gromore_1.png'), 32 | Image.asset('images/gromore_2.png'), 33 | ], 34 | ), 35 | ), 36 | ), 37 | ); 38 | } 39 | 40 | /// 复制url 41 | Future pasteUrl() async { 42 | Clipboard.setData(ClipboardData(text: 'https://flutterads.top/')).then( 43 | (value) => ScaffoldMessenger.of(context) 44 | .showSnackBar(SnackBar(content: Text('链接复制成功')))); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /example/lib/pages/reward_video_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_pangle_ads/flutter_pangle_ads.dart'; 3 | 4 | import '../ads_config.dart'; 5 | import '../theme/style.dart'; 6 | import '../widgets/widgets.dart'; 7 | 8 | // 激励视频 9 | class RewardVideoPage extends StatefulWidget { 10 | const RewardVideoPage({Key? key}) : super(key: key); 11 | 12 | @override 13 | _RewardVideoPageState createState() => _RewardVideoPageState(); 14 | } 15 | 16 | class _RewardVideoPageState extends State { 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: buildAppBar(context, '激励视频'), 21 | body: Column( 22 | children: [ 23 | ListTile( 24 | title: Text('激励视频'), 25 | onTap: () => showRewardVideoAd(AdsConfig.rewardVideoId), 26 | ), 27 | kDivider, 28 | ListTile( 29 | title: Text('激励视频-二次进阶'), 30 | onTap: () => showRewardVideoAd(AdsConfig.rewardInteractVideoId), 31 | ), 32 | kDivider, 33 | ], 34 | ), 35 | ); 36 | } 37 | 38 | /// 展示激励视频广告 39 | /// [posId] 广告位id 40 | Future showRewardVideoAd(String posId) async { 41 | bool result = await FlutterPangleAds.showRewardVideoAd( 42 | AdsConfig.rewardVideoId, 43 | customData: 'customData', 44 | userId: 'userId', 45 | ); 46 | print("展示激励视频广告${result ? '成功' : '失败'}"); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /example/lib/pages/splash_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_pangle_ads/flutter_pangle_ads.dart'; 3 | import 'package:flutter_pangle_ads_example/theme/style.dart'; 4 | 5 | import '../ads_config.dart'; 6 | import '../widgets/widgets.dart'; 7 | 8 | // 开屏 9 | class SplashPage extends StatefulWidget { 10 | const SplashPage({Key? key}) : super(key: key); 11 | 12 | @override 13 | _SplashPageState createState() => _SplashPageState(); 14 | } 15 | 16 | class _SplashPageState extends State { 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: buildAppBar(context, '开屏广告'), 21 | body: Column( 22 | children: [ 23 | ListTile( 24 | title: Text('开屏(全屏)'), 25 | onTap: () => showSplashAd(), 26 | ), 27 | kDivider, 28 | ListTile( 29 | title: Text('开屏-Logo1'), 30 | onTap: () => showSplashAd(AdsConfig.logo), 31 | ), 32 | kDivider, 33 | ListTile( 34 | title: Text('开屏-Logo2'), 35 | onTap: () => showSplashAd(AdsConfig.logo2), 36 | ), 37 | kDivider, 38 | ], 39 | ), 40 | ); 41 | } 42 | 43 | /// 展示开屏广告 44 | /// [logo] 展示如果传递则展示logo,不传递不展示 45 | Future showSplashAd([String? logo]) async { 46 | bool result = await FlutterPangleAds.showSplashAd( 47 | AdsConfig.splashId, 48 | logo: logo, 49 | timeout: 3.5, 50 | ); 51 | print("展示开屏广告${result ? '成功' : '失败'}"); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /example/lib/router/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../pages/pro_page.dart'; 4 | 5 | // 去对应的页面 6 | void pushPage(BuildContext context, Widget page) { 7 | Navigator.push(context, MaterialPageRoute(builder: (context) => page)); 8 | } 9 | 10 | // 去 Pro 页面 11 | void pushProPage(BuildContext context) { 12 | pushPage(context, ProPage()); 13 | } 14 | -------------------------------------------------------------------------------- /example/lib/theme/style.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | // 横线 4 | const kDivider = const Divider(height: 0.5); 5 | -------------------------------------------------------------------------------- /example/lib/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../router/router.dart'; 4 | 5 | // 构建 AppBar 6 | AppBar buildAppBar(BuildContext context, String title) { 7 | return AppBar( 8 | title: Text('$title (FlutterAds)'), 9 | actions: [ 10 | // 去 Pro 页面 11 | IconButton( 12 | icon: Image.asset('images/pro.png'), 13 | onPressed: () => pushProPage(context), 14 | ), 15 | ], 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_pangle_ads_example 2 | description: FlutterAds 穿山甲广告 SDK 插件示例 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: "none" # Remove this line if you wish to publish to pub.dev 7 | version: 3.1.0+20 8 | 9 | environment: 10 | sdk: ">=2.12.0 <4.0.0" 11 | 12 | dependencies: 13 | flutter: 14 | sdk: flutter 15 | 16 | flutter_pangle_ads: 17 | # When depending on this package from a real application you should use: 18 | # flutter_pangle_ads: ^x.y.z 19 | # See https://dart.dev/tools/pub/dependencies#version-constraints 20 | # The example app is bundled with the plugin so we use a path dependency on 21 | # the parent directory to use the current plugin's version. 22 | path: ../ 23 | 24 | # The following adds the Cupertino Icons font to your application. 25 | # Use with the CupertinoIcons class for iOS style icons. 26 | cupertino_icons: ^1.0.4 27 | loadany: ^1.0.0 28 | 29 | dev_dependencies: 30 | flutter_test: 31 | sdk: flutter 32 | 33 | # For information on the generic Dart part of this file, see the 34 | # following page: https://dart.dev/tools/pub/pubspec 35 | 36 | # The following section is specific to Flutter. 37 | flutter: 38 | # The following line ensures that the Material Icons font is 39 | # included with your application, so that you can use the icons in 40 | # the material Icons class. 41 | uses-material-design: true 42 | # To add assets to your application, add an assets section, like this: 43 | assets: 44 | - images/ 45 | # - images/a_dot_ham.jpeg 46 | # An image asset can refer to one or more resolution-specific "variants", see 47 | # https://flutter.dev/assets-and-images/#resolution-aware. 48 | # For details regarding adding assets from package dependencies, see 49 | # https://flutter.dev/assets-and-images/#from-packages 50 | # To add custom fonts to your application, add a fonts section here, 51 | # in this "flutter" section. Each entry in this list should have a 52 | # "family" key with the font family name, and a "fonts" key with a 53 | # list giving the asset and other descriptors for the font. For 54 | # example: 55 | # fonts: 56 | # - family: Schyler 57 | # fonts: 58 | # - asset: fonts/Schyler-Regular.ttf 59 | # - asset: fonts/Schyler-Italic.ttf 60 | # style: italic 61 | # - family: Trajan Pro 62 | # fonts: 63 | # - asset: fonts/TrajanPro.ttf 64 | # - asset: fonts/TrajanPro_Bold.ttf 65 | # weight: 700 66 | # 67 | # For details regarding fonts from package dependencies, 68 | # see https://flutter.dev/custom-fonts/#from-packages 69 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:flutter_pangle_ads_example/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Verify Platform version', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that platform version is retrieved. 19 | expect( 20 | find.byWidgetPredicate( 21 | (Widget widget) => 22 | widget is Text && widget.data!.startsWith('Running on:'), 23 | ), 24 | findsOneWidget, 25 | ); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module app_store 2 | 3 | go 1.16 4 | 5 | require github.com/flutter/flutter v2.2.3 // indirect 6 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | /Flutter/flutter_export_environment.sh -------------------------------------------------------------------------------- /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlutterAds/flutter_pangle_ads/d9f52530b4aefeb457c6146037a22b3032bdd65f/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/Event/AdErrorEvent.h: -------------------------------------------------------------------------------- 1 | // 2 | // AdErrorEvent.h 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/13. 6 | // 7 | 8 | #import "AdEvent.h" 9 | // 广告错误事件 10 | @interface AdErrorEvent : AdEvent 11 | // 错误码 12 | @property (assign, nonatomic) NSNumber* errCode; 13 | // 错误信息 14 | @property (copy,nonatomic) NSString *errMsg; 15 | // 构造广告错误事件 16 | -(id) initWithAdId:(NSString *)adId errCode:(NSNumber *)errCode errMsg:(NSString *)errMsg; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ios/Classes/Event/AdErrorEvent.m: -------------------------------------------------------------------------------- 1 | // 2 | // AdErrorEvent.m 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/13. 6 | // 7 | 8 | #import "AdErrorEvent.h" 9 | #import "AdEventAction.h" 10 | 11 | @implementation AdErrorEvent 12 | 13 | - (id)initWithAdId:(NSString *)adId errCode:(NSNumber *)errCode errMsg:(NSString *)errMsg{ 14 | self.adId=adId; 15 | self.action=onAdError; 16 | self.errCode=errCode; 17 | self.errMsg=errMsg; 18 | return self; 19 | } 20 | 21 | - (NSDictionary *)toMap{ 22 | NSDictionary *data=[super toMap]; 23 | NSMutableDictionary *errData=[[NSMutableDictionary alloc]initWithDictionary:data]; 24 | [errData setObject:_errMsg forKey:@"errMsg"]; 25 | [errData setObject:_errCode forKey:@"errCode"]; 26 | 27 | return errData; 28 | } 29 | @end 30 | -------------------------------------------------------------------------------- /ios/Classes/Event/AdEvent.h: -------------------------------------------------------------------------------- 1 | // 2 | // AdEvent.h 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/13. 6 | // 7 | 8 | #import 9 | // 广告事件 10 | @interface AdEvent : NSObject 11 | // 广告 id 12 | @property (copy, nonatomic) NSString *adId; 13 | // 操作 14 | @property (copy, nonatomic) NSString *action; 15 | // 构造广告事件 16 | - (id) initWithAdId:(NSString *) adId andAction:(NSString *)action; 17 | // 转换为 Map 操作 18 | - (NSDictionary*) toMap; 19 | @end 20 | -------------------------------------------------------------------------------- /ios/Classes/Event/AdEvent.m: -------------------------------------------------------------------------------- 1 | // 2 | // AdEvent.m 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/13. 6 | // 7 | 8 | #import "AdEvent.h" 9 | 10 | @implementation AdEvent 11 | 12 | - (id)initWithAdId:(NSString *)adId andAction:(NSString *)action{ 13 | self.adId=adId; 14 | self.action=action; 15 | return self; 16 | } 17 | 18 | - (NSDictionary *)toMap{ 19 | NSDictionary *data=@{@"adId":_adId,@"action":_action}; 20 | return data; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /ios/Classes/Event/AdEventAction.h: -------------------------------------------------------------------------------- 1 | // 2 | // AdEventAction.h 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/14. 6 | // 7 | 8 | #import 9 | // 广告事件操作 10 | @interface AdEventAction : NSObject 11 | // 广告错误 12 | extern NSString * const onAdError; 13 | // 广告加载成功 14 | extern NSString * const onAdLoaded; 15 | // 广告填充 16 | extern NSString * const onAdPresent; 17 | // 广告曝光 18 | extern NSString * const onAdExposure; 19 | // 广告关闭(计时结束或者用户点击关闭) 20 | extern NSString * const onAdClosed; 21 | // 广告点击 22 | extern NSString * const onAdClicked; 23 | // 广告跳过 24 | extern NSString * const onAdSkip; 25 | // 广告播放或计时完毕 26 | extern NSString * const onAdComplete; 27 | // 获得广告激励 28 | extern NSString * const onAdReward; 29 | @end 30 | -------------------------------------------------------------------------------- /ios/Classes/Event/AdEventAction.m: -------------------------------------------------------------------------------- 1 | // 2 | // AdEventAction.m 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/14. 6 | // 7 | 8 | #import "AdEventAction.h" 9 | // 广告事件操作 10 | @implementation AdEventAction 11 | // 广告错误 12 | NSString *const onAdError=@"onAdError"; 13 | // 广告加载成功 14 | NSString *const onAdLoaded=@"onAdLoaded"; 15 | // 广告填充 16 | NSString *const onAdPresent=@"onAdPresent"; 17 | // 广告曝光 18 | NSString *const onAdExposure=@"onAdExposure"; 19 | // 广告关闭 20 | NSString *const onAdClosed=@"onAdClosed"; 21 | // 广告点击 22 | NSString *const onAdClicked=@"onAdClicked"; 23 | // 广告跳过 24 | NSString *const onAdSkip=@"onAdSkip"; 25 | // 广告播放或计时完毕 26 | NSString *const onAdComplete=@"onAdComplete"; 27 | // 获得广告激励 28 | NSString *const onAdReward=@"onAdReward"; 29 | 30 | 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /ios/Classes/Event/AdRewardEvent.h: -------------------------------------------------------------------------------- 1 | // 2 | // AdRewardEvent.h 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/19. 6 | // 7 | 8 | #import "BaseAdPage.h" 9 | // 广告激励事件 10 | @interface AdRewardEvent : AdEvent 11 | // 激励类型 12 | @property (nonatomic,assign) NSInteger rewardType; 13 | // 奖励是否有效 14 | @property BOOL rewardVerify; 15 | // 奖励数量 16 | @property (nonatomic,assign) NSInteger rewardAmount; 17 | // 奖励名称 18 | @property (copy,nonatomic) NSString *rewardName; 19 | // 错误码 20 | @property (nonatomic,assign) NSInteger errCode; 21 | // 错误信息 22 | @property (copy,nonatomic) NSString *errMsg; 23 | // 服务端验证的自定义信息 24 | @property (copy,nonatomic) NSString *customData; 25 | // 服务端验证的用户信息 26 | @property (copy,nonatomic) NSString *userId; 27 | // 构造广告激励事件 28 | -(id) initWithAdId:(NSString *)adId rewardType:(NSInteger) rewardType rewardVerify:(BOOL) rewardVerify rewardAmount:(NSInteger) rewardAmount rewardName:(NSString *)rewardName customData:(NSString *)customData userId:(NSString *)userId errCode:(NSInteger) errCode errMsg:(NSString*) errMsg; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /ios/Classes/Event/AdRewardEvent.m: -------------------------------------------------------------------------------- 1 | // 2 | // AdRewardEvent.m 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/19. 6 | // 7 | 8 | #import "AdRewardEvent.h" 9 | #import "AdEventAction.h" 10 | 11 | @implementation AdRewardEvent 12 | - (id)initWithAdId:(NSString *)adId rewardType:(NSInteger) rewardType rewardVerify:(BOOL) rewardVerify rewardAmount:(NSInteger) rewardAmount rewardName:(NSString *)rewardName customData:(NSString *)customData userId:(NSString *)userId errCode:(NSInteger) errCode errMsg:(NSString*) errMsg{ 13 | self.rewardType=rewardType; 14 | self.adId=adId; 15 | self.action=onAdReward; 16 | self.rewardVerify=rewardVerify; 17 | self.rewardAmount=rewardAmount; 18 | self.rewardName=rewardName; 19 | self.customData=customData; 20 | self.userId=userId; 21 | self.errCode=errCode; 22 | self.errMsg=errMsg; 23 | return self; 24 | } 25 | 26 | - (NSDictionary *)toMap{ 27 | NSDictionary *data=[super toMap]; 28 | NSMutableDictionary *errData=[[NSMutableDictionary alloc]initWithDictionary:data]; 29 | [errData setObject:[NSNumber numberWithInteger:_rewardType] forKey:@"rewardType"]; 30 | [errData setObject:@(_rewardVerify) forKey:@"rewardVerify"]; 31 | [errData setObject:[NSNumber numberWithInteger:_rewardAmount] forKey:@"rewardAmount"]; 32 | [errData setObject:_rewardName forKey:@"rewardName"]; 33 | [errData setObject:[NSNumber numberWithInteger: _errCode] forKey:@"errCode"]; 34 | [errData setObject:_errMsg forKey:@"errMsg"]; 35 | [errData setObject:_customData forKey:@"customData"]; 36 | [errData setObject:_userId forKey:@"userId"]; 37 | 38 | return errData; 39 | } 40 | @end 41 | -------------------------------------------------------------------------------- /ios/Classes/FlutterPangleAdsPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "SplashPage.h" 4 | #import "RewardVideoPage.h" 5 | #import "FullScreenVideoPage.h" 6 | #import "FeedAdLoad.h" 7 | #import "FeedAdManager.h" 8 | 9 | @interface FlutterPangleAdsPlugin : NSObject 10 | @property (strong,nonatomic) FlutterEventSink eventSink; 11 | @property (strong, nonatomic) SplashPage *sad; 12 | @property (strong, nonatomic) RewardVideoPage *rvad; 13 | @property (strong,nonatomic) FullScreenVideoPage *fsad; 14 | @property (strong,nonatomic) FeedAdLoad *fad; 15 | 16 | extern NSString *const kAdBannerViewId; 17 | extern NSString *const kAdFeedViewId; 18 | @end 19 | -------------------------------------------------------------------------------- /ios/Classes/FlutterPangleAdsPlugin.m: -------------------------------------------------------------------------------- 1 | #import "FlutterPangleAdsPlugin.h" 2 | #import "NativeViewFactory.h" 3 | #import 4 | #import 5 | 6 | @implementation FlutterPangleAdsPlugin 7 | // AdBannerView 8 | NSString *const kAdBannerViewId=@"flutter_pangle_ads_banner"; 9 | // AdFeedView 10 | NSString *const kAdFeedViewId=@"flutter_pangle_ads_feed"; 11 | 12 | + (void)registerWithRegistrar:(NSObject*)registrar { 13 | FlutterMethodChannel* methodChannel = [FlutterMethodChannel 14 | methodChannelWithName:@"flutter_pangle_ads" 15 | binaryMessenger:[registrar messenger]]; 16 | FlutterEventChannel* eventChannel=[FlutterEventChannel eventChannelWithName:@"flutter_pangle_ads_event" binaryMessenger:[registrar messenger]]; 17 | FlutterPangleAdsPlugin* instance = [[FlutterPangleAdsPlugin alloc] init]; 18 | [registrar addMethodCallDelegate:instance channel:methodChannel]; 19 | [eventChannel setStreamHandler:instance]; 20 | // 注册平台View 工厂 21 | NativeViewFactory *bannerFactory=[[NativeViewFactory alloc] initWithViewName:kAdBannerViewId withMessenger:registrar.messenger withPlugin:instance]; 22 | NativeViewFactory *feedFactory=[[NativeViewFactory alloc] initWithViewName:kAdFeedViewId withMessenger:registrar.messenger withPlugin:instance]; 23 | // 注册 Banner View 24 | [registrar registerViewFactory:bannerFactory withId:kAdBannerViewId]; 25 | // 注册 Feed View 26 | [registrar registerViewFactory:feedFactory withId:kAdFeedViewId]; 27 | } 28 | 29 | - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { 30 | NSString *methodStr=call.method; 31 | if ([@"getPlatformVersion" isEqualToString:methodStr]) { 32 | result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]); 33 | }else if ([@"requestIDFA" isEqualToString:methodStr]) { 34 | [self requestIDFA:call result:result]; 35 | }else if ([@"initAd" isEqualToString:methodStr]) { 36 | [self initAd:call result:result]; 37 | }else if([@"showSplashAd" isEqualToString:methodStr]) { 38 | [self showSplashAd:call result:result]; 39 | }else if ([@"showRewardVideoAd" isEqualToString:methodStr]){ 40 | [self showRewardVideoAd:call result:result]; 41 | }else if ([@"showFullScreenVideoAd" isEqualToString:methodStr]){ 42 | [self showFullScreenVideoAd:call result:result]; 43 | }else if ([@"loadFeedAd" isEqualToString:methodStr]){ 44 | [self loadFeedAd:call result:result]; 45 | }else if ([@"clearFeedAd" isEqualToString:methodStr]){ 46 | [self clearFeedAd:call result:result]; 47 | }else if ([@"setUserExtData" isEqualToString:methodStr]){ 48 | [self setUserExtData:call]; 49 | }else { 50 | result(FlutterMethodNotImplemented); 51 | } 52 | } 53 | 54 | // 请求 IDFA 55 | - (void) requestIDFA:(FlutterMethodCall*) call result:(FlutterResult) result{ 56 | if (@available(iOS 14, *)) { 57 | [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) { 58 | BOOL requestResult=status == ATTrackingManagerAuthorizationStatusAuthorized; 59 | NSLog(@"requestIDFA:%@",requestResult?@"YES":@"NO"); 60 | result(@(requestResult)); 61 | }]; 62 | } else { 63 | result(@(YES)); 64 | } 65 | } 66 | 67 | // 初始化广告 68 | - (void) initAd:(FlutterMethodCall*) call result:(FlutterResult) result{ 69 | NSString* appId=call.arguments[@"appId"]; 70 | BUAdSDKConfiguration *config = [BUAdSDKConfiguration configuration]; 71 | config.appID=appId; 72 | [BUAdSDKManager startWithSyncCompletionHandler:^(BOOL success, NSError *error) { 73 | NSLog(@"initAd:%@",success?@"YES":@"NO"); 74 | result(@(success)); 75 | }]; 76 | // 升级提示 77 | NSLog(@"🎉🎉🎉 FlutterAds ==> 初始化完成,推荐使用 GroMore Pro 版本,获得更高的收益:https://flutterads.top/"); 78 | } 79 | 80 | // 显示开屏广告 81 | - (void) showSplashAd:(FlutterMethodCall*) call result:(FlutterResult) result{ 82 | self.sad=[[SplashPage alloc] init]; 83 | [self.sad showAd:call eventSink:self.eventSink]; 84 | result(@(YES)); 85 | } 86 | 87 | // 显示激励视频广告 88 | - (void) showRewardVideoAd:(FlutterMethodCall*) call result:(FlutterResult) result{ 89 | self.rvad=[[RewardVideoPage alloc] init]; 90 | [self.rvad showAd:call eventSink:self.eventSink]; 91 | result(@(YES)); 92 | } 93 | 94 | // 显示全屏视频广告 95 | - (void) showFullScreenVideoAd:(FlutterMethodCall*) call result:(FlutterResult) result{ 96 | self.fsad=[[FullScreenVideoPage alloc] init]; 97 | [self.fsad showAd:call eventSink:self.eventSink]; 98 | result(@(YES)); 99 | } 100 | // 加载信息流广告 101 | - (void) loadFeedAd:(FlutterMethodCall*) call result:(FlutterResult) result{ 102 | self.fad=[[FeedAdLoad alloc] init]; 103 | [self.fad loadFeedAdList:call result:result eventSink:self.eventSink]; 104 | } 105 | 106 | // 清除信息流广告 107 | - (void) clearFeedAd:(FlutterMethodCall*) call result:(FlutterResult) result{ 108 | NSArray *list= call.arguments[@"list"]; 109 | for (NSNumber *ad in list) { 110 | [FeedAdManager.share removeAd:ad]; 111 | } 112 | result(@(YES)); 113 | } 114 | 115 | // 设置个性化推荐 116 | // personalAdsType: String 117 | // 不传或传空或传非01值没任何影响,默认不屏蔽 118 | // 0,屏蔽个性化推荐广告; 119 | // 1,不屏蔽个性化推荐广告 120 | - (void) setUserExtData:(FlutterMethodCall*) call{ 121 | NSString *personalAdsType = call.arguments[@"personalAdsType"]; 122 | NSString *data = [NSString stringWithFormat:@"[{\"name\":\"personal_ads_type\",\"value\":\"%@\"}]", personalAdsType]; 123 | [BUAdSDKManager setUserExtData: data]; 124 | } 125 | 126 | #pragma mark - FlutterStreamHandler 127 | - (FlutterError * _Nullable)onCancelWithArguments:(id _Nullable)arguments { 128 | self.eventSink=nil; 129 | return nil; 130 | } 131 | 132 | - (FlutterError * _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(nonnull FlutterEventSink)events { 133 | self.eventSink=events; 134 | return nil; 135 | } 136 | 137 | // 添加事件 138 | -(void) addEvent:(NSObject *) event{ 139 | if(self.eventSink!=nil){ 140 | self.eventSink(event); 141 | } 142 | } 143 | 144 | @end 145 | -------------------------------------------------------------------------------- /ios/Classes/Load/FeedAdLoad.h: -------------------------------------------------------------------------------- 1 | // 2 | // FeedAdLoad.h 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/11/29. 6 | // 7 | 8 | #import 9 | #import "BaseAdPage.h" 10 | 11 | @interface FeedAdLoad : BaseAdPage 12 | @property (strong,nonatomic,nonnull) FlutterResult result; 13 | @property (strong,nonatomic,nullable) BUNativeExpressAdManager *adManager; 14 | // 加载信息流广告列表 15 | -(void) loadFeedAdList:(nonnull FlutterMethodCall *)call result:(nonnull FlutterResult) result eventSink:(nonnull FlutterEventSink )events; 16 | @end 17 | -------------------------------------------------------------------------------- /ios/Classes/Load/FeedAdLoad.m: -------------------------------------------------------------------------------- 1 | // 2 | // FeedAdLoad.m 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/11/29. 6 | // 7 | 8 | #import "FeedAdLoad.h" 9 | #import "FeedAdManager.h" 10 | #import "FlutterPangleAdsPlugin.h" 11 | 12 | @implementation FeedAdLoad 13 | 14 | - (void)loadFeedAdList:(FlutterMethodCall *)call result:(FlutterResult)result eventSink:(FlutterEventSink)events{ 15 | self.result=result; 16 | // 这里复用整个加载流程 17 | [self showAd:call eventSink:events]; 18 | } 19 | 20 | // 加载广告 21 | - (void)loadAd:(FlutterMethodCall *)call{ 22 | int width = [call.arguments[@"width"] intValue]; 23 | int height = [call.arguments[@"height"] intValue]; 24 | int count = [call.arguments[@"count"] intValue]; 25 | // 配置广告加载信息 26 | BUAdSlot *slot= [[BUAdSlot alloc]init]; 27 | slot.ID=self.posId; 28 | slot.AdType=BUAdSlotAdTypeFeed; 29 | slot.position=BUAdSlotPositionFeed; 30 | if(!self.adManager){ 31 | self.adManager= [[BUNativeExpressAdManager alloc] initWithSlot:slot adSize:CGSizeMake(width, height)]; 32 | } 33 | self.adManager.adSize=CGSizeMake(width, height); 34 | self.adManager.delegate=self; 35 | // 加载广告 36 | [self.adManager loadAdDataWithCount:count]; 37 | } 38 | 39 | #pragma mark BUNativeExpressAdViewDelegate 40 | 41 | - (void)nativeExpressAdFailToLoad:(BUNativeExpressAdManager *)nativeExpressAdManager error:(NSError *)error{ 42 | NSLog(@"%s",__FUNCTION__); 43 | // 发送广告错误事件 44 | [self sendErrorEvent:error.code withErrMsg:error.localizedDescription]; 45 | } 46 | 47 | - (void)nativeExpressAdSuccessToLoad:(BUNativeExpressAdManager *)nativeExpressAdManager views:(NSArray<__kindof BUNativeExpressAdView *> *)views{ 48 | NSLog(@"%s",__FUNCTION__); 49 | if (views.count) { 50 | // 广告列表,用于返回 Flutter 层 51 | NSMutableArray *adList= [[NSMutableArray alloc] init]; 52 | [views enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 53 | // 通过hash 来标识不同的原生广告 View 54 | NSNumber *key=[NSNumber numberWithInteger:[obj hash]]; 55 | NSLog(@"FeedAdLoad idx:%lu obj:%p hash:%@",(unsigned long)[obj hash],obj,key); 56 | // 添加到返回列表中 57 | [adList addObject:key]; 58 | // 添加到缓存广告列表中 59 | [FeedAdManager.share putAd:key value:obj]; 60 | }]; 61 | // 返回广告列表 62 | self.result(adList); 63 | } 64 | // 发送广告事件 65 | [self sendEventAction:onAdLoaded]; 66 | } 67 | 68 | - (void)nativeExpressAdViewRenderFail:(BUNativeExpressAdView *)nativeExpressAdView error:(NSError *)error{ 69 | NSLog(@"%s",__FUNCTION__); 70 | // 发送广告错误事件 71 | [self sendErrorEvent:error.code withErrMsg:error.localizedDescription]; 72 | [self postNotificationMsg:nativeExpressAdView userInfo:[NSDictionary dictionaryWithObject:onAdError forKey:@"event"]]; 73 | } 74 | 75 | - (void)nativeExpressAdViewRenderSuccess:(BUNativeExpressAdView *)nativeExpressAdView{ 76 | NSLog(@"%s",__FUNCTION__); 77 | // 发送广告事件 78 | [self sendEventAction:onAdExposure]; 79 | // 渲染成功,发送更新展示通知,来更新尺寸 80 | [self postNotificationMsg:nativeExpressAdView userInfo:[NSDictionary dictionaryWithObject:onAdExposure forKey:@"event"]]; 81 | } 82 | 83 | - (void)nativeExpressAdViewDidClick:(BUNativeExpressAdView *)nativeExpressAdView{ 84 | NSLog(@"%s",__FUNCTION__); 85 | // 发送广告事件 86 | [self sendEventAction:onAdClicked]; 87 | [self postNotificationMsg:nativeExpressAdView userInfo:[NSDictionary dictionaryWithObject:onAdClicked forKey:@"event"]]; 88 | } 89 | 90 | - (void)nativeExpressAdViewWillShow:(BUNativeExpressAdView *)nativeExpressAdView{ 91 | NSLog(@"%s",__FUNCTION__); 92 | // 发送广告事件 93 | [self sendEventAction:onAdExposure]; 94 | [self postNotificationMsg:nativeExpressAdView userInfo:[NSDictionary dictionaryWithObject:onAdExposure forKey:@"event"]]; 95 | } 96 | 97 | - (void)nativeExpressAdView:(BUNativeExpressAdView *)nativeExpressAdView dislikeWithReason:(NSArray *)filterWords{ 98 | NSLog(@"%s",__FUNCTION__); 99 | } 100 | 101 | - (void)nativeExpressAdViewDidRemoved:(BUNativeExpressAdView *)nativeExpressAdView{ 102 | NSLog(@"%s",__FUNCTION__); 103 | NSNumber *key=[NSNumber numberWithInteger:[nativeExpressAdView hash]]; 104 | // 删除广告缓存 105 | [FeedAdManager.share removeAd:key]; 106 | // 发送广告事件 107 | [self sendEventAction:onAdClosed]; 108 | [self postNotificationMsg:nativeExpressAdView userInfo:[NSDictionary dictionaryWithObject:onAdClosed forKey:@"event"]]; 109 | } 110 | 111 | // 发送消息 112 | // 这里发送消息到信息流View,主要是适配信息流 View 的尺寸 113 | - (void) postNotificationMsg:(BUNativeExpressAdView *) adView userInfo:(NSDictionary *) userInfo{ 114 | NSLog(@"%s",__FUNCTION__); 115 | NSNumber *key=[NSNumber numberWithInteger:[adView hash]]; 116 | NSString *name=[NSString stringWithFormat:@"%@/%@", kAdFeedViewId, key.stringValue]; 117 | [[NSNotificationCenter defaultCenter] postNotificationName:name object:adView userInfo:userInfo]; 118 | } 119 | 120 | @end 121 | -------------------------------------------------------------------------------- /ios/Classes/Load/FeedAdManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // FeedAdManager.h 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/11/29. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | @interface FeedAdManager : NSObject 12 | + (instancetype) share; 13 | // 加入到缓存中 14 | - (void) putAd:(NSNumber*) key value:(BUNativeExpressAdView*) value; 15 | // 从缓存中获取 16 | - (BUNativeExpressAdView*) getAd:(NSNumber*) key; 17 | // 从缓存中删除 18 | - (void) removeAd:(NSNumber*) key; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /ios/Classes/Load/FeedAdManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // FeedAdManager.m 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/11/29. 6 | // 7 | 8 | #import "FeedAdManager.h" 9 | 10 | @interface FeedAdManager() 11 | @property BUNativeExpressAdManager *adManager;// 广告管理对象 12 | 13 | @end 14 | 15 | @implementation FeedAdManager 16 | 17 | static FeedAdManager *manager; 18 | NSMutableDictionary *adList;// 已加载信息流广告列表 19 | 20 | 21 | + (instancetype)share{ 22 | static dispatch_once_t once_token; 23 | dispatch_once(&once_token, ^{ 24 | adList= [[NSMutableDictionary alloc]init]; 25 | manager=[[FeedAdManager alloc] init]; 26 | }); 27 | return manager; 28 | } 29 | 30 | - (void)putAd:(NSNumber*)key value:(id)value{ 31 | [adList setObject:value forKey:key]; 32 | } 33 | 34 | - (id)getAd:(NSNumber*)key{ 35 | return adList[key]; 36 | } 37 | 38 | - (void)removeAd:(NSNumber*)key{ 39 | [adList removeObjectForKey:key]; 40 | } 41 | 42 | 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /ios/Classes/Page/AdBannerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // AdBannerView.h 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/31. 6 | // 7 | 8 | #import "BaseAdPage.h" 9 | #import "FlutterPangleAdsPlugin.h" 10 | @interface AdBannerView : BaseAdPage 11 | @property (strong,nonatomic,nullable) FlutterPangleAdsPlugin *plugin; 12 | - (nonnull instancetype)initWithFrame:(CGRect)frame 13 | viewIdentifier:(int64_t)viewId 14 | arguments:(id _Nullable)args 15 | binaryMessenger:(NSObject* _Nullable)messenger plugin:(FlutterPangleAdsPlugin* _Nullable) plugin; 16 | 17 | - (nonnull UIView*)view; 18 | @end 19 | -------------------------------------------------------------------------------- /ios/Classes/Page/AdBannerView.m: -------------------------------------------------------------------------------- 1 | // 2 | // AdBannerView.m 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/31. 6 | // 7 | 8 | #import "AdBannerView.h" 9 | // Banner 广告 View 10 | @interface AdBannerView() 11 | @property (strong,nonatomic) BUNativeExpressBannerView *bannerView; 12 | @property bool autoClose; 13 | @end 14 | // Banner 广告 View 15 | @implementation AdBannerView 16 | - (instancetype)initWithFrame:(CGRect)frame 17 | viewIdentifier:(int64_t)viewId 18 | arguments:(id _Nullable)args 19 | binaryMessenger:(NSObject*)messenger 20 | plugin:(FlutterPangleAdsPlugin*) plugin{ 21 | if (self = [super init]) { 22 | FlutterMethodCall *call=[FlutterMethodCall methodCallWithMethodName:@"AdBannerView" arguments:args]; 23 | [self showAd:call eventSink:plugin.eventSink]; 24 | } 25 | return self; 26 | } 27 | 28 | - (UIView*)view { 29 | return self.bannerView; 30 | } 31 | // 加载广告 32 | - (void)loadAd:(FlutterMethodCall *)call{ 33 | // 刷新间隔 34 | int interval=[call.arguments[@"interval"] intValue]; 35 | int width = [call.arguments[@"width"] intValue]; 36 | int height = [call.arguments[@"height"] intValue]; 37 | self.autoClose = [call.arguments[@"autoClose"] boolValue]; 38 | // 大于 0 说明需要设置刷新间隔 39 | if(interval>0){ 40 | self.bannerView=[[BUNativeExpressBannerView alloc] initWithSlotID:self.posId rootViewController:self.rootController adSize:CGSizeMake(width, height) interval:interval]; 41 | }else{ 42 | self.bannerView=[[BUNativeExpressBannerView alloc] initWithSlotID:self.posId rootViewController:self.rootController adSize:CGSizeMake(width, height)]; 43 | } 44 | self.bannerView.frame=CGRectMake(0, 0, width, height); 45 | self.bannerView.delegate=self; 46 | [self.bannerView loadAdData]; 47 | } 48 | 49 | #pragma mark BUNativeExpressBannerViewDelegate 50 | - (void)nativeExpressBannerAdViewDidLoad:(BUNativeExpressBannerView *)bannerAdView { 51 | NSLog(@"%s",__FUNCTION__); 52 | // 发送广告事件 53 | [self sendEventAction:onAdLoaded]; 54 | } 55 | 56 | - (void)nativeExpressBannerAdView:(BUNativeExpressBannerView *)bannerAdView didLoadFailWithError:(NSError *)error { 57 | NSLog(@"%s",__FUNCTION__); 58 | // 发送广告错误事件 59 | [self sendErrorEvent:error.code withErrMsg:error.localizedDescription]; 60 | self.bannerView=nil; 61 | } 62 | 63 | - (void)nativeExpressBannerAdViewRenderSuccess:(BUNativeExpressBannerView *)bannerAdView { 64 | NSLog(@"%s",__FUNCTION__); 65 | // 发送广告事件 66 | [self sendEventAction:onAdExposure]; 67 | } 68 | 69 | - (void)nativeExpressBannerAdViewRenderFail:(BUNativeExpressBannerView *)bannerAdView error:(NSError *)error { 70 | NSLog(@"%s",__FUNCTION__); 71 | // 发送广告错误事件 72 | [self sendErrorEvent:error.code withErrMsg:error.localizedDescription]; 73 | } 74 | 75 | - (void)nativeExpressBannerAdViewWillBecomVisible:(BUNativeExpressBannerView *)bannerAdView { 76 | NSLog(@"%s",__FUNCTION__); 77 | // 发送广告事件 78 | [self sendEventAction:onAdExposure]; 79 | } 80 | 81 | - (void)nativeExpressBannerAdViewDidClick:(BUNativeExpressBannerView *)bannerAdView { 82 | NSLog(@"%s",__FUNCTION__); 83 | // 发送广告事件 84 | [self sendEventAction:onAdClicked]; 85 | } 86 | 87 | - (void)nativeExpressBannerAdView:(BUNativeExpressBannerView *)bannerAdView dislikeWithReason:(NSArray *)filterwords { 88 | NSLog(@"%s",__FUNCTION__); 89 | } 90 | 91 | - (void)nativeExpressBannerAdViewDidRemoved:(BUNativeExpressBannerView *)bannerAdView { 92 | NSLog(@"%s",__FUNCTION__); 93 | if(self.autoClose){ 94 | [bannerAdView removeFromSuperview]; 95 | } 96 | // 发送广告事件 97 | [self sendEventAction:onAdClosed]; 98 | } 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /ios/Classes/Page/AdFeedView.h: -------------------------------------------------------------------------------- 1 | // 2 | // AdFeedView.h 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/11/29. 6 | // 7 | 8 | #import 9 | #import 10 | #import "FlutterPangleAdsPlugin.h" 11 | 12 | @interface AdFeedView : BaseAdPage 13 | @property (strong,nonatomic,nullable) FlutterPangleAdsPlugin *plugin; 14 | @property int64_t viewId; 15 | - (nonnull instancetype) initWithFrame:(CGRect)frame 16 | viewIdentifier:(int64_t)viewId 17 | arguments:(id _Nullable)args 18 | binaryMessenger:(NSObject* _Nullable)messenger plugin:(FlutterPangleAdsPlugin* _Nullable) plugin; 19 | - (nonnull UIView*) view; 20 | @end 21 | -------------------------------------------------------------------------------- /ios/Classes/Page/AdFeedView.m: -------------------------------------------------------------------------------- 1 | // 2 | // AdFeedView.m 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/11/29. 6 | // 7 | 8 | #import "AdFeedView.h" 9 | #import "FeedAdManager.h" 10 | 11 | @interface AdFeedView() 12 | @property (strong,nonatomic) BUNativeExpressAdManager *adManager; 13 | @property (strong,nonatomic) UIView *feedView; 14 | @property (strong,nonatomic) BUNativeExpressAdView *adView; 15 | @property (strong,nonatomic) FlutterMethodChannel *methodChannel; 16 | 17 | @end 18 | 19 | @implementation AdFeedView 20 | 21 | - (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id)args binaryMessenger:(NSObject *)messenger plugin:(FlutterPangleAdsPlugin *)plugin{ 22 | if(self==[super init]){ 23 | self.viewId=viewId; 24 | self.feedView =[[UIView alloc] init]; 25 | self.methodChannel = [FlutterMethodChannel methodChannelWithName:[NSString stringWithFormat:@"%@/%lli",kAdFeedViewId,viewId] binaryMessenger:messenger]; 26 | FlutterMethodCall *call= [FlutterMethodCall methodCallWithMethodName:@"AdFeedView" arguments:args]; 27 | [self showAd:call eventSink:plugin.eventSink]; 28 | } 29 | NSLog(@"%s %lli",__FUNCTION__,viewId); 30 | return self; 31 | } 32 | 33 | - (UIView *)view{ 34 | return self.feedView; 35 | } 36 | 37 | - (void)dealloc{ 38 | NSLog(@"%s",__FUNCTION__); 39 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 40 | } 41 | 42 | // 处理消息 43 | - (void) postMsghandler:(NSNotification*) notification{ 44 | NSLog(@"%s postMsghandler name:%@ obj:%@",__FUNCTION__,notification.name,notification.object); 45 | // NSString *name=notification.name; 46 | BUNativeExpressAdView *loadAdView=notification.object; 47 | NSDictionary *userInfo=notification.userInfo; 48 | NSString *event=[userInfo objectForKey:@"event"]; 49 | if([event isEqualToString:onAdExposure]){ 50 | // 渲染成功,设置高度 51 | CGSize size= loadAdView.frame.size; 52 | [self setFlutterViewSize:size]; 53 | }else if([event isEqualToString:onAdClosed]){ 54 | // 广告关闭移除广告,并且设置大小为 0,隐藏广告 55 | [self.adView removeFromSuperview]; 56 | [self setFlutterViewSize:CGSizeZero]; 57 | } 58 | } 59 | // 设置 FlutterAds 视图宽高 60 | - (void) setFlutterViewSize:(CGSize) size{ 61 | NSNumber *width=[NSNumber numberWithFloat:size.width]; 62 | NSNumber *height=[NSNumber numberWithFloat:size.height]; 63 | NSDictionary *dicSize=[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:width,height, nil] forKeys:[NSArray arrayWithObjects:@"width",@"height", nil]]; 64 | self.adView.center=self.feedView.center; 65 | [self.methodChannel invokeMethod:@"setSize" arguments:dicSize]; 66 | } 67 | 68 | - (void)loadAd:(FlutterMethodCall *)call{ 69 | NSNumber *key=[NSNumber numberWithInteger:[self.posId integerValue]]; 70 | NSString *name=[NSString stringWithFormat:@"%@/%@", kAdFeedViewId, key.stringValue]; 71 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(postMsghandler:) name:name object:nil]; 72 | self.adView=[FeedAdManager.share getAd:key]; 73 | self.adView.rootViewController=self.rootController; 74 | [self.feedView addSubview:self.adView]; 75 | [self.adView render]; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /ios/Classes/Page/BaseAdPage.h: -------------------------------------------------------------------------------- 1 | // 2 | // BaseAdPage.h 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/18. 6 | // 7 | #import 8 | #import 9 | #import "AdEvent.h" 10 | #import "AdErrorEvent.h" 11 | #import "AdRewardEvent.h" 12 | #import "AdEventAction.h" 13 | #import 14 | 15 | // 基础广告页面 16 | @interface BaseAdPage : NSObject 17 | // 广告位 id key 18 | extern NSString * _Nonnull const kPosId; 19 | // 广告位id 20 | @property (weak,nonatomic,nullable) NSString *posId; 21 | // 事件消息 22 | @property (strong,nonatomic,nullable) FlutterEventSink eventSink; 23 | // Window 24 | @property (strong,nonatomic,nullable) UIWindow *mainWin; 25 | // 根控制器 26 | @property (strong,nonatomic,nullable) UIViewController *rootController; 27 | // 屏幕宽度 28 | @property CGFloat width; 29 | // 屏幕高度 30 | @property CGFloat height; 31 | // 显示广告 32 | - (void) showAd:(nonnull FlutterMethodCall *)call eventSink:(nonnull FlutterEventSink) events; 33 | //// 加载广告 34 | - (void) loadAd:(nonnull FlutterMethodCall *) call; 35 | // 发送广告事件 36 | -(void) sendEvent:(nonnull AdEvent *) event; 37 | // 发送广告事件 38 | -(void) sendEventAction:(nonnull NSString *) action; 39 | // 发送广告错误事件 40 | -(void) sendErrorEvent:(NSInteger) errCode withErrMsg:(nullable NSString*) errMsg; 41 | @end 42 | -------------------------------------------------------------------------------- /ios/Classes/Page/BaseAdPage.m: -------------------------------------------------------------------------------- 1 | // 2 | // BaseAdPage.m 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/18. 6 | // 7 | 8 | #import "BaseAdPage.h" 9 | 10 | @implementation BaseAdPage 11 | // 广告位id 12 | NSString *const kPosId=@"posId"; 13 | // 显示广告 14 | -(void)showAd:(FlutterMethodCall *)call eventSink:(nonnull FlutterEventSink )events{ 15 | 16 | self.posId=call.arguments[kPosId]; 17 | self.eventSink=events; 18 | // 获取主 window 19 | self.mainWin=[[UIApplication sharedApplication] keyWindow]; 20 | self.rootController=self.mainWin.rootViewController; 21 | // 获取宽高 22 | CGSize size=[[UIScreen mainScreen] bounds].size; 23 | self.width=size.width; 24 | self.height=size.height; 25 | [self loadAd:call]; 26 | } 27 | 28 | - (void)loadAd:(nonnull FlutterMethodCall *)call { 29 | NSLog(@"%s",__FUNCTION__); 30 | } 31 | 32 | 33 | // 发送广告事件 34 | - (void)sendEvent:(AdEvent *)event{ 35 | if(self.eventSink!=nil){ 36 | self.eventSink([event toMap]); 37 | } 38 | } 39 | // 发送广告事件 40 | - (void)sendEventAction:(NSString *)action{ 41 | AdEvent *event=[[AdEvent alloc] initWithAdId:self.posId andAction:action]; 42 | [self sendEvent:event]; 43 | } 44 | // 发送广告错误事件 45 | - (void)sendErrorEvent:(NSInteger)errCode withErrMsg:(NSString *)errMsg{ 46 | AdErrorEvent *event=[[AdErrorEvent alloc] initWithAdId:self.posId errCode:[NSNumber numberWithInteger:errCode] errMsg:errMsg]; 47 | [self sendEvent:event]; 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /ios/Classes/Page/FullScreenVideoPage.h: -------------------------------------------------------------------------------- 1 | // 2 | // FullScreenVideoPage.h 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/24. 6 | // 7 | 8 | #import "BaseAdPage.h" 9 | // 全屏视频广告 10 | @interface FullScreenVideoPage : BaseAdPage 11 | @property (nonatomic,strong) BUNativeExpressFullscreenVideoAd *fsad; 12 | @end 13 | -------------------------------------------------------------------------------- /ios/Classes/Page/FullScreenVideoPage.m: -------------------------------------------------------------------------------- 1 | // 2 | // FullScreenVideoPage.m 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/24. 6 | // 7 | 8 | #import "FullScreenVideoPage.h" 9 | 10 | @implementation FullScreenVideoPage 11 | // 加载广告 12 | - (void)loadAd:(FlutterMethodCall *)call{ 13 | self.fsad=[[BUNativeExpressFullscreenVideoAd alloc] initWithSlotID:self.posId]; 14 | self.fsad.delegate=self; 15 | [self.fsad loadAdData]; 16 | } 17 | 18 | #pragma mark - BUNativeExpressFullscreenVideoAdDelegate 19 | - (void)nativeExpressFullscreenVideoAdDidLoad:(BUNativeExpressFullscreenVideoAd *)fullscreenVideoAd { 20 | NSLog(@"%s",__FUNCTION__); 21 | if(self.fsad){ 22 | [self.fsad showAdFromRootViewController:self.rootController]; 23 | } 24 | // 发送广告事件 25 | [self sendEventAction:onAdLoaded]; 26 | } 27 | 28 | - (void)nativeExpressFullscreenVideoAd:(BUNativeExpressFullscreenVideoAd *)fullscreenVideoAd didFailWithError:(NSError *_Nullable)error { 29 | NSLog(@"%s",__FUNCTION__); 30 | // 发送广告错误事件 31 | [self sendErrorEvent:error.code withErrMsg:error.localizedDescription]; 32 | } 33 | 34 | - (void)nativeExpressFullscreenVideoAdViewRenderSuccess:(BUNativeExpressFullscreenVideoAd *)rewardedVideoAd { 35 | NSLog(@"%s",__FUNCTION__); 36 | // 发送广告事件 37 | [self sendEventAction:onAdPresent]; 38 | } 39 | 40 | - (void)nativeExpressFullscreenVideoAdViewRenderFail:(BUNativeExpressFullscreenVideoAd *)rewardedVideoAd error:(NSError *_Nullable)error { 41 | NSLog(@"%s",__FUNCTION__); 42 | // 发送广告错误事件 43 | [self sendErrorEvent:error.code withErrMsg:error.localizedDescription]; 44 | } 45 | 46 | - (void)nativeExpressFullscreenVideoAdDidDownLoadVideo:(BUNativeExpressFullscreenVideoAd *)fullscreenVideoAd { 47 | NSLog(@"%s",__FUNCTION__); 48 | } 49 | 50 | - (void)nativeExpressFullscreenVideoAdWillVisible:(BUNativeExpressFullscreenVideoAd *)fullscreenVideoAd { 51 | NSLog(@"%s",__FUNCTION__); 52 | } 53 | 54 | - (void)nativeExpressFullscreenVideoAdDidVisible:(BUNativeExpressFullscreenVideoAd *)fullscreenVideoAd { 55 | NSLog(@"%s",__FUNCTION__); 56 | // 发送广告事件 57 | [self sendEventAction:onAdExposure]; 58 | } 59 | 60 | - (void)nativeExpressFullscreenVideoAdDidClick:(BUNativeExpressFullscreenVideoAd *)fullscreenVideoAd { 61 | NSLog(@"%s",__FUNCTION__); 62 | // 发送广告事件 63 | [self sendEventAction:onAdClicked]; 64 | } 65 | 66 | - (void)nativeExpressFullscreenVideoAdDidClickSkip:(BUNativeExpressFullscreenVideoAd *)fullscreenVideoAd { 67 | NSLog(@"%s",__FUNCTION__); 68 | // 发送广告事件 69 | [self sendEventAction:onAdSkip]; 70 | } 71 | 72 | - (void)nativeExpressFullscreenVideoAdWillClose:(BUNativeExpressFullscreenVideoAd *)fullscreenVideoAd { 73 | NSLog(@"%s",__FUNCTION__); 74 | // 发送广告事件 75 | [self sendEventAction:onAdClosed]; 76 | } 77 | 78 | - (void)nativeExpressFullscreenVideoAdDidClose:(BUNativeExpressFullscreenVideoAd *)fullscreenVideoAd { 79 | NSLog(@"%s",__FUNCTION__); 80 | self.fsad=nil; 81 | } 82 | 83 | - (void)nativeExpressFullscreenVideoAdDidPlayFinish:(BUNativeExpressFullscreenVideoAd *)fullscreenVideoAd didFailWithError:(NSError *_Nullable)error { 84 | NSLog(@"%s",__FUNCTION__); 85 | if(error){ 86 | // 发送广告错误事件 87 | [self sendErrorEvent:error.code withErrMsg:error.localizedDescription]; 88 | }else{ 89 | // 发送广告事件 90 | [self sendEventAction:onAdComplete]; 91 | } 92 | } 93 | 94 | - (void)nativeExpressFullscreenVideoAdCallback:(BUNativeExpressFullscreenVideoAd *)fullscreenVideoAd withType:(BUNativeExpressFullScreenAdType) nativeExpressVideoAdType{ 95 | NSLog(@"%s",__FUNCTION__); 96 | } 97 | 98 | - (void)nativeExpressFullscreenVideoAdDidCloseOtherController:(BUNativeExpressFullscreenVideoAd *)fullscreenVideoAd interactionType:(BUInteractionType)interactionType { 99 | NSLog(@"%s",__FUNCTION__); 100 | } 101 | @end 102 | -------------------------------------------------------------------------------- /ios/Classes/Page/NativeViewFactory.h: -------------------------------------------------------------------------------- 1 | // 2 | // NativeViewFactory.h 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/31. 6 | // 7 | #import 8 | #import 9 | #import "AdBannerView.h" 10 | #import "AdFeedView.h" 11 | 12 | // 原生平台 View 工厂 13 | @interface NativeViewFactory : NSObject 14 | @property (strong,nonatomic) NSObject *messenger; 15 | @property (strong,nonatomic) FlutterPangleAdsPlugin *plugin; 16 | @property (strong,nonatomic) NSString *viewName; 17 | - (instancetype)initWithViewName:(NSString*) viewName withMessenger:(NSObject*)messenger withPlugin:(FlutterPangleAdsPlugin*) plugin; 18 | @end 19 | -------------------------------------------------------------------------------- /ios/Classes/Page/NativeViewFactory.m: -------------------------------------------------------------------------------- 1 | // 2 | // NativeViewFactory.m 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/31. 6 | // 7 | 8 | #import "NativeViewFactory.h" 9 | // 原生平台 View 工厂 10 | @implementation NativeViewFactory 11 | 12 | - (instancetype)initWithViewName:(NSString *)viewName withMessenger:(NSObject *)messenger withPlugin:(FlutterPangleAdsPlugin *)plugin{ 13 | self = [super init]; 14 | if (self) { 15 | self.viewName = viewName; 16 | self.messenger = messenger; 17 | self.plugin = plugin; 18 | } 19 | return self; 20 | } 21 | 22 | - (NSObject*)createArgsCodec { 23 | return [FlutterStandardMessageCodec sharedInstance]; 24 | } 25 | 26 | - (NSObject*)createWithFrame:(CGRect)frame 27 | viewIdentifier:(int64_t)viewId 28 | arguments:(id _Nullable)args { 29 | if (self.viewName==kAdBannerViewId) { 30 | return [[AdBannerView alloc] initWithFrame:frame 31 | viewIdentifier:viewId 32 | arguments:args 33 | binaryMessenger:self.messenger 34 | plugin:self.plugin]; 35 | }else{ 36 | return [[AdFeedView alloc] initWithFrame:frame 37 | viewIdentifier:viewId 38 | arguments:args 39 | binaryMessenger:self.messenger 40 | plugin:self.plugin]; 41 | } 42 | 43 | } 44 | @end 45 | -------------------------------------------------------------------------------- /ios/Classes/Page/RewardVideoPage.h: -------------------------------------------------------------------------------- 1 | // 2 | // RewardVideoPage.h 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/19. 6 | // 7 | 8 | #import "BaseAdPage.h" 9 | // 激励视频页面 10 | @interface RewardVideoPage : BaseAdPage 11 | @property (nonatomic, strong) BUNativeExpressRewardedVideoAd *rvad; 12 | // 服务端验证的自定义信息 13 | @property (copy,nonatomic) NSString *customData; 14 | // 服务端验证的用户信息 15 | @property (copy,nonatomic) NSString *userId; 16 | @end 17 | -------------------------------------------------------------------------------- /ios/Classes/Page/RewardVideoPage.m: -------------------------------------------------------------------------------- 1 | // 2 | // RewardVideoPage.m 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/19. 6 | // 7 | 8 | #import "RewardVideoPage.h" 9 | 10 | @implementation RewardVideoPage 11 | // 加载广告 12 | - (void)loadAd:(FlutterMethodCall *)call{ 13 | self.customData = call.arguments[@"customData"] ; 14 | self.userId = call.arguments[@"userId"]; 15 | // 初始化激励视频广告 16 | BURewardedVideoModel *model = [[BURewardedVideoModel alloc] init]; 17 | model.extra=self.customData; 18 | model.userId=self.userId; 19 | self.rvad=[[BUNativeExpressRewardedVideoAd alloc] initWithSlotID:self.posId rewardedVideoModel:model]; 20 | self.rvad.delegate=self; 21 | self.rvad.rewardPlayAgainInteractionDelegate=self; 22 | [self.rvad loadAdData]; 23 | } 24 | 25 | 26 | #pragma mark - BUNativeExpressRewardedVideoAdDelegate 27 | - (void)nativeExpressRewardedVideoAdDidLoad:(BUNativeExpressRewardedVideoAd *)rewardedVideoAd { 28 | NSLog(@"%s",__FUNCTION__); 29 | if(self.rvad){ 30 | [self.rvad showAdFromRootViewController:self.rootController]; 31 | } 32 | // 发送广告事件 33 | [self sendEventAction:onAdLoaded]; 34 | } 35 | 36 | - (void)nativeExpressRewardedVideoAd:(BUNativeExpressRewardedVideoAd *)rewardedVideoAd didFailWithError:(NSError *_Nullable)error { 37 | NSLog(@"%s",__FUNCTION__); 38 | // 发送广告错误事件 39 | [self sendErrorEvent:error.code withErrMsg:error.localizedDescription]; 40 | } 41 | 42 | - (void)nativeExpressRewardedVideoAdCallback:(BUNativeExpressRewardedVideoAd *)rewardedVideoAd withType:(BUNativeExpressRewardedVideoAdType)nativeExpressVideoType{ 43 | NSLog(@"%s",__FUNCTION__); 44 | } 45 | 46 | - (void)nativeExpressRewardedVideoAdDidDownLoadVideo:(BUNativeExpressRewardedVideoAd *)rewardedVideoAd { 47 | NSLog(@"%s",__FUNCTION__); 48 | } 49 | 50 | - (void)nativeExpressRewardedVideoAdViewRenderSuccess:(BUNativeExpressRewardedVideoAd *)rewardedVideoAd { 51 | NSLog(@"%s",__FUNCTION__); 52 | // 发送广告事件 53 | [self sendEventAction:onAdPresent]; 54 | 55 | } 56 | 57 | - (void)nativeExpressRewardedVideoAdViewRenderFail:(BUNativeExpressRewardedVideoAd *)rewardedVideoAd error:(NSError *_Nullable)error { 58 | NSLog(@"%s",__FUNCTION__); 59 | // 发送广告错误事件 60 | [self sendErrorEvent:error.code withErrMsg:error.localizedDescription]; 61 | } 62 | 63 | - (void)nativeExpressRewardedVideoAdWillVisible:(BUNativeExpressRewardedVideoAd *)rewardedVideoAd { 64 | NSLog(@"%s",__FUNCTION__); 65 | } 66 | 67 | - (void)nativeExpressRewardedVideoAdDidVisible:(BUNativeExpressRewardedVideoAd *)rewardedVideoAd { 68 | NSLog(@"%s",__FUNCTION__); 69 | // 发送广告事件 70 | [self sendEventAction:onAdExposure]; 71 | } 72 | 73 | - (void)nativeExpressRewardedVideoAdWillClose:(BUNativeExpressRewardedVideoAd *)rewardedVideoAd { 74 | NSLog(@"%s",__FUNCTION__); 75 | // 发送广告事件 76 | [self sendEventAction:onAdClosed]; 77 | } 78 | 79 | - (void)nativeExpressRewardedVideoAdDidClose:(BUNativeExpressRewardedVideoAd *)rewardedVideoAd { 80 | NSLog(@"%s",__FUNCTION__); 81 | self.rvad=nil; 82 | } 83 | 84 | - (void)nativeExpressRewardedVideoAdDidClick:(BUNativeExpressRewardedVideoAd *)rewardedVideoAd { 85 | NSLog(@"%s",__FUNCTION__); 86 | // 发送广告事件 87 | [self sendEventAction:onAdClicked]; 88 | } 89 | 90 | - (void)nativeExpressRewardedVideoAdDidClickSkip:(BUNativeExpressRewardedVideoAd *)rewardedVideoAd { 91 | NSLog(@"%s",__FUNCTION__); 92 | // 发送广告事件 93 | [self sendEventAction:onAdSkip]; 94 | } 95 | 96 | - (void)nativeExpressRewardedVideoAdDidPlayFinish:(BUNativeExpressRewardedVideoAd *)rewardedVideoAd didFailWithError:(NSError *_Nullable)error { 97 | NSLog(@"%s",__FUNCTION__); 98 | if(error){ 99 | // 发送广告错误事件 100 | [self sendErrorEvent:error.code withErrMsg:error.localizedDescription]; 101 | }else{ 102 | // 发送广告事件 103 | [self sendEventAction:onAdComplete]; 104 | } 105 | } 106 | 107 | - (void)nativeExpressRewardedVideoAdServerRewardDidSucceed:(BUNativeExpressRewardedVideoAd *)rewardedVideoAd verify:(BOOL)verify { 108 | NSLog(@"%s",__FUNCTION__); 109 | NSLog(@"%@",[NSString stringWithFormat:@"verify:%@ rewardType:%@ rewardName:%ld rewardMount:%ld",verify?@"true":@"false",rewardedVideoAd.rewardedVideoModel.rewardName,(long)rewardedVideoAd.rewardedVideoModel.rewardType,(long)rewardedVideoAd.rewardedVideoModel.rewardAmount]); 110 | BURewardedVideoModel *model=rewardedVideoAd.rewardedVideoModel; 111 | // 发送激励事件 112 | AdRewardEvent *rewardEvent=[[AdRewardEvent alloc] initWithAdId:self.posId rewardType:model.rewardType rewardVerify:verify rewardAmount:model.rewardAmount rewardName:model.rewardName customData:self.customData userId:self.userId errCode:0 errMsg:@""]; 113 | [self sendEvent:rewardEvent]; 114 | 115 | 116 | } 117 | 118 | - (void)nativeExpressRewardedVideoAdServerRewardDidFail:(BUNativeExpressRewardedVideoAd *)rewardedVideoAd error:(NSError * _Nullable)error { 119 | NSLog(@"%s",__FUNCTION__); 120 | 121 | NSLog(@"%@",[NSString stringWithFormat:@"rewardName:%@ rewardMount:%ld error:%@",rewardedVideoAd.rewardedVideoModel.rewardName,(long)rewardedVideoAd.rewardedVideoModel.rewardAmount,error]); 122 | // 发送激励事件 123 | BURewardedVideoModel *model=rewardedVideoAd.rewardedVideoModel; 124 | AdRewardEvent *rewardEvent=[[AdRewardEvent alloc] initWithAdId:self.posId rewardType:model.rewardType rewardVerify:NO rewardAmount:model.rewardAmount rewardName:model.rewardName customData:self.customData userId:self.userId errCode:error.code errMsg:error.localizedDescription]; 125 | [self sendEvent:rewardEvent]; 126 | } 127 | 128 | - (void)nativeExpressRewardedVideoAdDidCloseOtherController:(BUNativeExpressRewardedVideoAd *)rewardedVideoAd interactionType:(BUInteractionType)interactionType { 129 | NSLog(@"%s",__FUNCTION__); 130 | } 131 | 132 | @end 133 | -------------------------------------------------------------------------------- /ios/Classes/Page/SplashPage.h: -------------------------------------------------------------------------------- 1 | // 2 | // SplashPage.h 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/18. 6 | // 7 | 8 | #import "BaseAdPage.h" 9 | // 开屏广告 10 | @interface SplashPage : BaseAdPage 11 | @end 12 | -------------------------------------------------------------------------------- /ios/Classes/Page/SplashPage.m: -------------------------------------------------------------------------------- 1 | // 2 | // SplashPage.m 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/8/18. 6 | // 7 | 8 | #import "SplashPage.h" 9 | #import "SplashViewController.h" 10 | 11 | @implementation SplashPage 12 | // 加载广告 13 | -(void)loadAd:(FlutterMethodCall *)call{ 14 | NSString* logo=call.arguments[@"logo"]; 15 | double timeout=[call.arguments[@"timeout"] doubleValue]; 16 | // 开屏页面 17 | SplashViewController *svc=[[SplashViewController alloc] init]; 18 | svc.posId=self.posId; 19 | svc.logo=logo; 20 | svc.timeout=timeout; 21 | svc.sp=self; 22 | //设置全屏 23 | svc.modalPresentationStyle = UIModalPresentationFullScreen; 24 | // 跳转页面 25 | [self.rootController presentViewController:svc animated:NO completion:^{ 26 | 27 | }]; 28 | 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /ios/Classes/Page/SplashViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SplashViewController.h 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/9/12. 6 | // 7 | 8 | #import 9 | #import "BaseAdPage.h" 10 | #import "SplashPage.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface SplashViewController : UIViewController 15 | @property (strong, nonatomic) SplashPage *sp; 16 | @property (strong, nonatomic) BUSplashAd *splashAd; 17 | @property (strong, nonatomic) UIView *splashView; 18 | @property (nonatomic,copy) NSString *posId; 19 | @property (nonatomic,copy) NSString *logo; 20 | @property double timeout; 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /ios/Classes/Page/SplashViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SplashViewController.m 3 | // flutter_pangle_ads 4 | // 5 | // Created by zero on 2021/9/12. 6 | // 7 | 8 | #import "SplashViewController.h" 9 | 10 | @implementation SplashViewController 11 | 12 | - (void)viewDidLoad { 13 | [super viewDidLoad]; 14 | self.view.backgroundColor=[UIColor whiteColor]; 15 | 16 | // logo 判断为空,则全屏展示 17 | bool fullScreenAd=[self.logo isKindOfClass:[NSNull class]]||[self.logo length]==0; 18 | // 计算大小 19 | CGSize size=[[UIScreen mainScreen] bounds].size; 20 | CGFloat width=size.width; 21 | CGFloat height=size.height; 22 | CGFloat adHeight=size.height;// 广告区域的高度 23 | self.splashView=[[UIView alloc]initWithFrame:CGRectMake(0, 0,width,height)]; 24 | // 非全屏设置 Logo 25 | if(!fullScreenAd){ 26 | CGFloat logoHeight=112.5;// 这里按照 15% 进行logo 的展示,防止尺寸不够的问题,750*15%=112.5 27 | adHeight=height-logoHeight;// 广告区域的高度 28 | // 设置底部 logo 29 | UIImageView *logoView=[[UIImageView alloc]initWithImage:[UIImage imageNamed:self.logo]]; 30 | logoView.frame=CGRectMake(0, adHeight, width, logoHeight); 31 | logoView.contentMode=UIViewContentModeCenter; 32 | // 防止点击 Logo 区域触发 Flutter 层的事件 33 | logoView.userInteractionEnabled=false; 34 | [self.splashView addSubview:logoView]; 35 | } 36 | // 广告区域大小 37 | CGSize adSize = CGSizeMake(width,adHeight); 38 | // 广告数据构建 39 | self.splashAd=[[BUSplashAd alloc] initWithSlotID:self.posId adSize:adSize]; 40 | self.splashAd.tolerateTimeout=self.timeout; 41 | self.splashAd.delegate=self; 42 | // 加载全屏广告 43 | [self.splashAd loadAdData]; 44 | } 45 | 46 | // 销毁页面 47 | - (void) dismissPage{ 48 | [self dismissViewControllerAnimated:YES completion:^{ 49 | 50 | }]; 51 | } 52 | 53 | 54 | #pragma mark - BUSplashAdDelegate 55 | 56 | - (void)splashAdLoadSuccess:(BUSplashAd *)splashAd { 57 | [splashAd showSplashViewInRootViewController:self]; 58 | NSLog(@"%s",__FUNCTION__); 59 | // 发送广告事件 60 | [self.sp sendEventAction:onAdLoaded]; 61 | } 62 | 63 | /// This method is called when material load failed 64 | - (void)splashAdLoadFail:(BUSplashAd *)splashAd error:(BUAdError *_Nullable)error{ 65 | NSLog(@"%s",__FUNCTION__); 66 | [self dismissPage]; 67 | // 发送广告错误事件 68 | [self.sp sendErrorEvent:error.code withErrMsg:error.localizedDescription]; 69 | } 70 | 71 | /// This method is called when splash view render successful 72 | - (void)splashAdRenderSuccess:(BUSplashAd *)splashAd{ 73 | NSLog(@"%s",__FUNCTION__); 74 | // 发送广告事件 75 | [self.sp sendEventAction:onAdExposure]; 76 | // // 设置广告 View 77 | [self.splashView addSubview:splashAd.splashView]; 78 | [self.view addSubview:self.splashView]; 79 | } 80 | 81 | /// This method is called when splash view render failed 82 | - (void)splashAdRenderFail:(BUSplashAd *)splashAd error:(BUAdError *_Nullable)error{ 83 | NSLog(@"%s",__FUNCTION__); 84 | [self dismissPage]; 85 | // 发送广告错误事件 86 | [self.sp sendErrorEvent:error.code withErrMsg:error.localizedDescription]; 87 | } 88 | 89 | - (void)splashAdDidClose:(BUSplashAd *)splashAd closeType:(BUSplashAdCloseType)closeType { 90 | NSLog(@"%s",__FUNCTION__); 91 | // 发送广告事件 92 | [self.sp sendEventAction:onAdClosed]; 93 | [self dismissPage]; 94 | } 95 | 96 | 97 | - (void)splashAdDidClick:(BUSplashAd *)splashAd { 98 | NSLog(@"%s",__FUNCTION__); 99 | // 发送广告事件 100 | [self.sp sendEventAction:onAdClicked]; 101 | } 102 | 103 | @end 104 | -------------------------------------------------------------------------------- /ios/flutter_pangle_ads.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint flutter_pangle_ads.podspec' to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'flutter_pangle_ads' 7 | s.version = '3.1.0' 8 | s.summary = 'FlutterAds 一款优质的 Flutter 广告插件(字节跳动、穿山甲)' 9 | s.description = <<-DESC 10 | FlutterAds 致力于构建优质的 Flutter 广告变现插件 11 | DESC 12 | s.homepage = 'https://flutterads.top' 13 | s.license = { :file => '../LICENSE' } 14 | s.author = { 'ZeroFlutter' => '1300326388@qq.com' } 15 | s.source = { :path => '.' } 16 | s.source_files = 'Classes/**/*' 17 | s.public_header_files = 'Classes/**/*.h' 18 | s.dependency 'Flutter' 19 | # 依赖穿山甲广告:https://github.com/CocoaPods/Specs/tree/master/Specs/d/9/8/Ads-CN/ 20 | s.dependency 'Ads-CN','6.2.1.6' 21 | s.platform = :ios, '11.0' 22 | s.static_framework = true 23 | 24 | # Flutter.framework does not contain a i386 slice. 25 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } 26 | end 27 | -------------------------------------------------------------------------------- /lib/event/ad_error_event.dart: -------------------------------------------------------------------------------- 1 | import 'ad_event.dart'; 2 | 3 | /// 广告错误事件 4 | class AdErrorEvent extends AdEvent { 5 | AdErrorEvent( 6 | {required this.errCode, 7 | this.errMsg, 8 | required String adId, 9 | required String action}) 10 | : super(adId: adId, action: action); 11 | // 错误码 12 | final int errCode; 13 | // 错误信息 14 | final String? errMsg; 15 | // 解析 json 为错误事件对象 16 | factory AdErrorEvent.fromJson(Map json) { 17 | return AdErrorEvent( 18 | errCode: json['errCode'], 19 | errMsg: json['errMsg'], 20 | adId: json['adId'], 21 | action: json['action'], 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/event/ad_event.dart: -------------------------------------------------------------------------------- 1 | import 'ad_error_event.dart'; 2 | import 'ad_event_action.dart'; 3 | import 'ad_reward_event.dart'; 4 | export 'ad_error_event.dart'; 5 | export 'ad_event_action.dart'; 6 | export 'ad_reward_event.dart'; 7 | 8 | /// 广告事件 9 | class AdEvent { 10 | AdEvent({required this.adId, required this.action}); 11 | // 广告 id 12 | final String adId; 13 | // 操作 14 | final String action; 15 | 16 | /// 解析 AdEvent 17 | factory AdEvent.fromJson(Map json) { 18 | String action = json['action']; 19 | if (action == AdEventAction.onAdError) { 20 | return AdErrorEvent.fromJson(json); 21 | } else if (action == AdEventAction.onAdReward) { 22 | return AdRewardEvent.fromJson(json); 23 | } else { 24 | return AdEvent( 25 | adId: json['adId'], 26 | action: action, 27 | ); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/event/ad_event_action.dart: -------------------------------------------------------------------------------- 1 | /// 广告事件操作 2 | class AdEventAction { 3 | // 广告错误 4 | static final String onAdError = "onAdError"; 5 | // 广告加载成功 6 | static final String onAdLoaded = "onAdLoaded"; 7 | // 广告填充 8 | static final String onAdPresent = "onAdPresent"; 9 | // 广告曝光 10 | static final String onAdExposure = "onAdExposure"; 11 | // 广告关闭(计时结束或者用户点击关闭) 12 | static final String onAdClosed = "onAdClosed"; 13 | // 广告点击 14 | static final String onAdClicked = "onAdClicked"; 15 | // 广告跳过 16 | static final String onAdSkip = "onAdSkip"; 17 | // 广告播放或计时完毕 18 | static final String onAdComplete = "onAdComplete"; 19 | // 获得广告激励 20 | static final String onAdReward = "onAdReward"; 21 | } 22 | -------------------------------------------------------------------------------- /lib/event/ad_event_handler.dart: -------------------------------------------------------------------------------- 1 | import 'ad_event.dart'; 2 | export 'ad_event.dart'; 3 | 4 | /// 广告事件回调监听 5 | typedef OnAdEventListener = void Function(AdEvent event); 6 | 7 | /// 处理广告事件 8 | void hanleAdEvent(dynamic data, OnAdEventListener listener) { 9 | if (data != null) { 10 | AdEvent adEvent = AdEvent.fromJson(data); 11 | listener.call(adEvent); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/event/ad_reward_event.dart: -------------------------------------------------------------------------------- 1 | import 'ad_event.dart'; 2 | 3 | /// 广告激励事件 4 | class AdRewardEvent extends AdEvent { 5 | AdRewardEvent( 6 | {required this.rewardType, 7 | required this.rewardVerify, 8 | required this.rewardAmount, 9 | required this.rewardName, 10 | this.errCode, 11 | this.errMsg, 12 | this.customData, 13 | this.userId, 14 | required String adId, 15 | required String action}) 16 | : super(adId: adId, action: action); 17 | // 奖励类型,0:基础奖励 >0:进阶奖励 。4400版本新增 18 | final int rewardType; 19 | // 奖励是否有效 20 | final bool rewardVerify; 21 | // 奖励数量 22 | final int rewardAmount; 23 | // 奖励名称 24 | final String rewardName; 25 | // 错误码 26 | final int? errCode; 27 | // 错误信息 28 | final String? errMsg; 29 | // 服务端验证的自定义信息 30 | final String? customData; 31 | // 服务端验证的用户信息 32 | final String? userId; 33 | // 解析 json 为激励事件对象 34 | factory AdRewardEvent.fromJson(Map json) { 35 | return AdRewardEvent( 36 | adId: json['adId'], 37 | action: json['action'], 38 | rewardType: json['rewardType'], 39 | rewardVerify: json['rewardVerify'], 40 | rewardAmount: json['rewardAmount'], 41 | rewardName: json['rewardName'], 42 | errCode: json['errCode'], 43 | errMsg: json['errMsg'], 44 | customData: json['customData'], 45 | userId: json['userId'], 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/flutter_pangle_ads.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'flutter_pangle_ads_platform_interface.dart'; 4 | import 'event/ad_event_handler.dart'; 5 | 6 | export 'event/ad_event_handler.dart'; 7 | export 'options/network_type.dart'; 8 | export 'view/ad_banner_widget.dart'; 9 | export 'view/ad_feed_widget.dart'; 10 | 11 | /// 穿山甲广告插件 12 | class FlutterPangleAds { 13 | static FlutterPangleAdsPlatform get _platform => 14 | FlutterPangleAdsPlatform.instance; 15 | 16 | /// 请求应用跟踪透明度授权(仅 iOS) 17 | static Future get requestIDFA => _platform.requestIDFA(); 18 | 19 | /// 动态请求相关权限(仅 Android) 20 | static Future get requestPermissionIfNecessary => 21 | _platform.requestPermissionIfNecessary(); 22 | 23 | /// 初始化广告 24 | /// [appId] 应用ID 25 | /// [useTextureView] (Android) 是否使用TextureView控件播放视频 26 | /// [supportMultiProcess] (Android) 是否支持多进程 27 | /// [allowShowNotify] (Android) 是否允许sdk展示通知栏提示 28 | /// [directDownloadNetworkType] 允许直接下载的网络类型,默认是空会有下载确认提示,非空不会有提示 29 | static Future initAd( 30 | String appId, { 31 | bool useTextureView = false, 32 | bool supportMultiProcess = false, 33 | bool allowShowNotify = true, 34 | List directDownloadNetworkType = const [], 35 | }) { 36 | return _platform.initAd( 37 | appId, 38 | useTextureView: useTextureView, 39 | supportMultiProcess: supportMultiProcess, 40 | allowShowNotify: allowShowNotify, 41 | directDownloadNetworkType: directDownloadNetworkType, 42 | ); 43 | } 44 | 45 | /// 展示开屏广告 46 | /// [posId] 广告位 id 47 | /// [logo] 如果传值则展示底部logo,不传不展示,则全屏展示 48 | /// [timeout] 加载超时时间 49 | static Future showSplashAd(String posId, 50 | {String? logo, double timeout = 3.5}) { 51 | return _platform.showSplashAd(posId, logo: logo, timeout: timeout); 52 | } 53 | 54 | /// 展示激励视频广告 55 | /// [posId] 广告位 id 56 | /// [customData] 设置服务端验证的自定义信息 57 | /// [userId] 设置服务端验证的用户信息 58 | static Future showRewardVideoAd( 59 | String posId, { 60 | String? customData, 61 | String? userId, 62 | }) { 63 | return _platform.showRewardVideoAd(posId, 64 | customData: customData, userId: userId); 65 | } 66 | 67 | /// 展示全屏视频、新插屏广告 68 | /// [posId] 广告位 id 69 | static Future showFullScreenVideoAd(String posId) { 70 | return _platform.showFullScreenVideoAd(posId); 71 | } 72 | 73 | /// 加载信息流广告列表 74 | /// [posId] 广告位 id 75 | /// [width] 宽度 76 | /// [height] 高度 77 | /// [count] 获取广告数量,建议 1~3 个 78 | static Future> loadFeedAd(String posId, 79 | {int width = 375, int height = 0, int count = 1}) { 80 | return _platform.loadFeedAd(posId, 81 | width: width, height: height, count: count); 82 | } 83 | 84 | /// 清除信息流广告列表 85 | /// [list] 信息流广告 id 列表 86 | static Future clearFeedAd(List list) { 87 | return _platform.clearFeedAd(list); 88 | } 89 | 90 | ///事件回调 91 | ///@params onData 事件回调 92 | static Future onEventListener(OnAdEventListener onAdEventListener) { 93 | return _platform.onEventListener(onAdEventListener); 94 | } 95 | 96 | /// 设置个性化推荐 97 | /// @params personalAdsType,不传或传空或传非01值没任何影响,默认不屏蔽, 0屏蔽个性化推荐广告, 1不屏蔽个性化推荐广告 98 | static Future setUserExtData({required String personalAdsType}) { 99 | return _platform.setUserExtData(personalAdsType: personalAdsType); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /lib/flutter_pangle_ads_method_channel.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:flutter/services.dart'; 6 | 7 | import 'event/ad_event_handler.dart'; 8 | import 'flutter_pangle_ads_platform_interface.dart'; 9 | 10 | /// 基于方法通道的实现 11 | class MethodChannelFlutterPangleAds extends FlutterPangleAdsPlatform { 12 | @visibleForTesting 13 | final methodChannel = const MethodChannel('flutter_pangle_ads'); 14 | 15 | @visibleForTesting 16 | final eventChannel = const EventChannel('flutter_pangle_ads_event'); 17 | 18 | @override 19 | Future requestIDFA() async { 20 | if (Platform.isIOS) { 21 | final bool result = await methodChannel.invokeMethod('requestIDFA'); 22 | return result; 23 | } 24 | return true; 25 | } 26 | 27 | @override 28 | Future requestPermissionIfNecessary() async { 29 | if (Platform.isAndroid) { 30 | final bool result = 31 | await methodChannel.invokeMethod('requestPermissionIfNecessary'); 32 | return result; 33 | } 34 | return true; 35 | } 36 | 37 | @override 38 | Future initAd( 39 | String appId, { 40 | bool useTextureView = false, 41 | bool supportMultiProcess = false, 42 | bool allowShowNotify = true, 43 | List directDownloadNetworkType = const [], 44 | }) async { 45 | final bool result = await methodChannel.invokeMethod( 46 | 'initAd', 47 | { 48 | 'appId': appId, 49 | 'useTextureView': useTextureView, 50 | 'supportMultiProcess': supportMultiProcess, 51 | 'allowShowNotify': allowShowNotify, 52 | 'directDownloadNetworkType': directDownloadNetworkType, 53 | }, 54 | ); 55 | print( 56 | "🎉🎉🎉 FlutterAds ==> 初始化完成,推荐使用 GroMore Pro 版本,获得更高的收益:https://flutterads.top/"); 57 | return result; 58 | } 59 | 60 | @override 61 | Future showSplashAd(String posId, 62 | {String? logo, double timeout = 3.5}) async { 63 | final bool result = await methodChannel.invokeMethod( 64 | 'showSplashAd', 65 | { 66 | 'posId': posId, 67 | 'logo': logo, 68 | 'timeout': timeout, 69 | }, 70 | ); 71 | return result; 72 | } 73 | 74 | @override 75 | Future showRewardVideoAd(String posId, 76 | {String? customData, String? userId}) async { 77 | final bool result = await methodChannel.invokeMethod( 78 | 'showRewardVideoAd', 79 | { 80 | 'posId': posId, 81 | 'customData': customData, 82 | 'userId': userId, 83 | }, 84 | ); 85 | return result; 86 | } 87 | 88 | @override 89 | Future showFullScreenVideoAd(String posId) async { 90 | final bool result = await methodChannel.invokeMethod( 91 | 'showFullScreenVideoAd', 92 | { 93 | 'posId': posId, 94 | }, 95 | ); 96 | return result; 97 | } 98 | 99 | @override 100 | Future> loadFeedAd(String posId, 101 | {int width = 375, int height = 0, int count = 1}) async { 102 | final List result = await methodChannel.invokeMethod( 103 | 'loadFeedAd', 104 | { 105 | 'posId': posId, 106 | 'width': width, 107 | 'height': height, 108 | 'count': count, 109 | }, 110 | ); 111 | return List.from(result); 112 | } 113 | 114 | @override 115 | Future clearFeedAd(List list) async { 116 | final bool result = await methodChannel.invokeMethod( 117 | 'clearFeedAd', 118 | { 119 | 'list': list, 120 | }, 121 | ); 122 | return result; 123 | } 124 | 125 | @override 126 | Future onEventListener(OnAdEventListener onAdEventListener) async { 127 | eventChannel.receiveBroadcastStream().listen((data) { 128 | hanleAdEvent(data, onAdEventListener); 129 | }); 130 | } 131 | 132 | @override 133 | Future setUserExtData({required String personalAdsType}) async { 134 | await methodChannel.invokeMethod( 135 | 'setUserExtData', 136 | { 137 | 'personalAdsType': personalAdsType, 138 | }, 139 | ); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /lib/flutter_pangle_ads_platform_interface.dart: -------------------------------------------------------------------------------- 1 | import 'package:plugin_platform_interface/plugin_platform_interface.dart'; 2 | 3 | import 'flutter_pangle_ads_method_channel.dart'; 4 | import 'event/ad_event_handler.dart'; 5 | 6 | abstract class FlutterPangleAdsPlatform extends PlatformInterface { 7 | FlutterPangleAdsPlatform() : super(token: _token); 8 | 9 | static final Object _token = Object(); 10 | 11 | static FlutterPangleAdsPlatform _instance = MethodChannelFlutterPangleAds(); 12 | 13 | static FlutterPangleAdsPlatform get instance => _instance; 14 | 15 | static set instance(FlutterPangleAdsPlatform instance) { 16 | PlatformInterface.verifyToken(instance, _token); 17 | _instance = instance; 18 | } 19 | 20 | /// 请求应用跟踪透明度授权(仅 iOS) 21 | Future requestIDFA() { 22 | throw UnimplementedError('requestIDFA() has not been implemented.'); 23 | } 24 | 25 | /// 动态请求相关权限(仅 Android) 26 | Future requestPermissionIfNecessary() { 27 | throw UnimplementedError( 28 | 'requestPermissionIfNecessary() has not been implemented.'); 29 | } 30 | 31 | /// 初始化广告 32 | /// [appId] 应用ID 33 | /// [useTextureView] (Android) 是否使用TextureView控件播放视频 34 | /// [supportMultiProcess] (Android) 是否支持多进程 35 | /// [allowShowNotify] (Android) 是否允许sdk展示通知栏提示 36 | /// [directDownloadNetworkType] 允许直接下载的网络类型,默认是空会有下载确认提示,非空不会有提示 37 | Future initAd( 38 | String appId, { 39 | bool useTextureView = false, 40 | bool supportMultiProcess = false, 41 | bool allowShowNotify = true, 42 | List directDownloadNetworkType = const [], 43 | }) { 44 | throw UnimplementedError('initAd() has not been implemented.'); 45 | } 46 | 47 | /// 展示开屏广告 48 | /// [posId] 广告位 id 49 | /// [logo] 如果传值则展示底部logo,不传不展示,则全屏展示 50 | /// [timeout] 加载超时时间 51 | Future showSplashAd(String posId, 52 | {String? logo, double timeout = 3.5}) { 53 | throw UnimplementedError('showSplashAd() has not been implemented.'); 54 | } 55 | 56 | /// 展示激励视频广告 57 | /// [posId] 广告位 id 58 | /// [customData] 设置服务端验证的自定义信息 59 | /// [userId] 设置服务端验证的用户信息 60 | Future showRewardVideoAd(String posId, 61 | {String? customData, String? userId}) { 62 | throw UnimplementedError('showRewardVideoAd() has not been implemented.'); 63 | } 64 | 65 | /// 展示全屏视频、新插屏广告 66 | /// [posId] 广告位 id 67 | Future showFullScreenVideoAd(String posId) { 68 | throw UnimplementedError( 69 | 'showFullScreenVideoAd() has not been implemented.'); 70 | } 71 | 72 | /// 加载信息流广告列表 73 | /// [posId] 广告位 id 74 | /// [width] 宽度 75 | /// [height] 高度 76 | /// [count] 获取广告数量,建议 1~3 个 77 | Future> loadFeedAd(String posId, 78 | {int width = 375, int height = 0, int count = 1}) { 79 | throw UnimplementedError('loadFeedAd() has not been implemented.'); 80 | } 81 | 82 | /// 清除信息流广告列表 83 | /// [list] 信息流广告 id 列表 84 | Future clearFeedAd(List list) { 85 | throw UnimplementedError('clearFeedAd() has not been implemented.'); 86 | } 87 | 88 | /// 事件回调 89 | Future onEventListener(OnAdEventListener onAdEventListener) { 90 | throw UnimplementedError('onEventListener() has not been implemented.'); 91 | } 92 | 93 | /// 设置个性化推荐 94 | /// @params personalAdsType,不传或传空或传非01值没任何影响,默认不屏蔽, 0屏蔽个性化推荐广告, 1不屏蔽个性化推荐广告 95 | Future setUserExtData({required String personalAdsType}) { 96 | throw UnimplementedError('setUserExtData() has not been implemented.'); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/options/network_type.dart: -------------------------------------------------------------------------------- 1 | /// 网络类型 2 | class NetworkType { 3 | static final int kNetworkStateMobile = 1; 4 | static final int kNetworkState2g = 2; 5 | static final int kNetworkState3g = 3; 6 | static final int kNetworkStateWifi = 4; 7 | static final int kNetworkState4g = 5; 8 | static final int kNetworkState5g = 6; 9 | } 10 | -------------------------------------------------------------------------------- /lib/view/ad_banner_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/widgets.dart'; 4 | import 'package:flutter/services.dart'; 5 | 6 | /// Banner 广告组件 7 | class AdBannerWidget extends StatefulWidget { 8 | AdBannerWidget({ 9 | Key? key, 10 | required this.posId, 11 | this.width = 300, 12 | this.height = 150, 13 | this.interval = 0, 14 | this.show = true, 15 | this.autoClose = true, 16 | }) : super(key: key); 17 | // 广告 id 18 | final String posId; 19 | // 创建 Banner 广告位时选择的宽度,默认值是 300 20 | final int width; 21 | // 创建 Banner 广告位时选择的高度,默认值是 150 22 | final int height; 23 | // 广告轮播间隔,0 或[30~120]之间的数字,单位为 s,默认为 0 不轮播 24 | final int interval; 25 | // 是否显示广告 26 | final bool show; 27 | // 是否自动关闭,一般是在用户点击不感兴趣之后的操作,可以在事件回调[AdEventAction.onAdClosed]中判断 28 | final bool autoClose; 29 | 30 | @override 31 | _AdBannerWidgetState createState() => _AdBannerWidgetState(); 32 | } 33 | 34 | class _AdBannerWidgetState extends State 35 | with AutomaticKeepAliveClientMixin { 36 | // View 类型 37 | final String viewType = 'flutter_pangle_ads_banner'; 38 | // 创建参数 39 | late Map creationParams; 40 | 41 | @override 42 | void initState() { 43 | creationParams = { 44 | "posId": widget.posId, 45 | "width": widget.width, 46 | "height": widget.height, 47 | "interval": widget.interval, 48 | "autoClose": widget.autoClose 49 | }; 50 | super.initState(); 51 | } 52 | 53 | @override 54 | Widget build(BuildContext context) { 55 | super.build(context); 56 | if (!widget.show) { 57 | return SizedBox.shrink(); 58 | } 59 | if (Platform.isIOS) { 60 | return SizedBox.fromSize( 61 | size: Size(widget.width.toDouble(), widget.height.toDouble()), 62 | child: UiKitView( 63 | viewType: viewType, 64 | creationParams: creationParams, 65 | creationParamsCodec: const StandardMessageCodec(), 66 | ), 67 | ); 68 | } else { 69 | return SizedBox.fromSize( 70 | size: Size(widget.width.toDouble(), widget.height.toDouble()), 71 | child: AndroidView( 72 | viewType: viewType, 73 | creationParams: creationParams, 74 | creationParamsCodec: const StandardMessageCodec(), 75 | ), 76 | ); 77 | } 78 | } 79 | 80 | @override 81 | bool get wantKeepAlive => true; 82 | } 83 | -------------------------------------------------------------------------------- /lib/view/ad_feed_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/widgets.dart'; 4 | import 'package:flutter/services.dart'; 5 | 6 | /// Feed 信息流广告组件 7 | /// 建议在个性化模板的广告view中,宽度自动铺满整个view,期望模板尺寸的参数设置中,高度可以设置为0,高度会自适应,达到最佳的展示比例 8 | class AdFeedWidget extends StatefulWidget { 9 | AdFeedWidget({ 10 | Key? key, 11 | required this.posId, 12 | this.show = true, 13 | this.width = 375, 14 | this.height = 128, 15 | }) : super(key: key); 16 | // 返回的广告 id,这里不是广告位id 17 | final String posId; 18 | // 是否显示广告 19 | final bool show; 20 | // 宽高 21 | final double width, height; 22 | 23 | @override 24 | _AdFeedWidgetState createState() => _AdFeedWidgetState(); 25 | } 26 | 27 | class _AdFeedWidgetState extends State 28 | with AutomaticKeepAliveClientMixin { 29 | // View 类型 30 | final String viewType = 'flutter_pangle_ads_feed'; 31 | // 创建参数 32 | late Map creationParams; 33 | // 通道 34 | late MethodChannel _channel; 35 | // 宽高 36 | double width = 375, height = 128; 37 | 38 | @override 39 | void initState() { 40 | this.width = widget.width; 41 | this.height = widget.height; 42 | creationParams = { 43 | "posId": widget.posId, 44 | }; 45 | super.initState(); 46 | } 47 | 48 | @override 49 | Widget build(BuildContext context) { 50 | super.build(context); 51 | if (!widget.show || width <= 0 || height <= 0) { 52 | return SizedBox.shrink(); 53 | } 54 | Widget view; 55 | if (Platform.isIOS) { 56 | view = UiKitView( 57 | viewType: viewType, 58 | creationParams: creationParams, 59 | creationParamsCodec: const StandardMessageCodec(), 60 | onPlatformViewCreated: (id) { 61 | _channel = MethodChannel('$viewType/$id'); 62 | _channel.setMethodCallHandler(onMethodCallHandler); 63 | }, 64 | ); 65 | } else { 66 | view = AndroidView( 67 | viewType: viewType, 68 | creationParams: creationParams, 69 | creationParamsCodec: const StandardMessageCodec(), 70 | onPlatformViewCreated: (id) { 71 | _channel = MethodChannel('$viewType/$id'); 72 | _channel.setMethodCallHandler(onMethodCallHandler); 73 | }, 74 | ); 75 | } 76 | // 有宽高信息了(渲染成功了)设置对应宽高 77 | return SizedBox.fromSize( 78 | size: Size(width, height), 79 | child: view, 80 | ); 81 | } 82 | 83 | @override 84 | bool get wantKeepAlive => true; 85 | 86 | Future onMethodCallHandler(MethodCall call) async { 87 | String method = call.method; 88 | // 设置大小 89 | if (method == 'setSize') { 90 | width = call.arguments['width'] ?? 0; 91 | height = call.arguments['height'] ?? 0; 92 | setState(() {}); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_pangle_ads 2 | description: 【持续更新】一款优质的穿山甲 Flutter 广告变现插件,支持 Android、iOS 平台,可快速接入进行广告变现。 3 | version: 3.1.0 4 | homepage: https://flutterads.top 5 | repository: https://github.com/FlutterAds/flutter_pangle_ads 6 | issue_tracker: https://github.com/FlutterAds/flutter_pangle_ads/issues 7 | documentation: https://github.com/FlutterAds/flutter_pangle_ads/wiki 8 | topics: [flutterads, ads, pangle, bytedance, income] 9 | funding: [https://github.com/FlutterAds/.github] 10 | 11 | environment: 12 | sdk: ">=2.12.0 <4.0.0" 13 | flutter: ">=2.5.0" 14 | 15 | dependencies: 16 | flutter: 17 | sdk: flutter 18 | plugin_platform_interface: ^2.0.2 19 | 20 | dev_dependencies: 21 | flutter_test: 22 | sdk: flutter 23 | # The following section is specific to Flutter. 24 | flutter: 25 | plugin: 26 | platforms: 27 | android: 28 | package: com.zero.flutter_pangle_ads 29 | pluginClass: FlutterPangleAdsPlugin 30 | ios: 31 | pluginClass: FlutterPangleAdsPlugin 32 | -------------------------------------------------------------------------------- /releaselog.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 确定要提取的版本号 4 | VERSION=$1 5 | 6 | if [ -z "$VERSION" ]; then 7 | echo "Usage: $0 " 8 | exit 1 9 | fi 10 | 11 | echo "Extracting release notes for version $VERSION" 12 | 13 | # 调试输出:显示 CHANGELOG.md 的内容 14 | echo "=== CHANGELOG.md content ===" 15 | cat CHANGELOG.md 16 | echo "=== End of CHANGELOG.md content ===" 17 | 18 | # 从 CHANGELOG.md 中提取对应版本的变更日志 19 | CHANGELOG=$(sed -n "/## $VERSION/,/^## /p" CHANGELOG.md | sed '$d' | tail -n +2) 20 | 21 | if [ -z "$CHANGELOG" ]; then 22 | echo "Version $VERSION not found in CHANGELOG.md" 23 | exit 1 24 | fi 25 | 26 | echo "Release notes for version $VERSION:" 27 | echo "$CHANGELOG" 28 | --------------------------------------------------------------------------------