├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── code_quality.yml ├── .gitignore ├── .metadata ├── .pubignore ├── .travis.yml ├── CHANGELOG-CN.md ├── CHANGELOG.md ├── DOC-PROPERTY.md ├── ISSUE.md ├── LICENSE ├── README.md ├── SETUP.md ├── TODO.md ├── analysis_options.yaml ├── android ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── keep │ ├── convert │ ├── keep.zip │ ├── pangle_flutter_keep.xml │ └── whiteList.txt ├── proguard-rules.pro ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── kotlin │ └── io │ │ └── github │ │ └── nullptrx │ │ └── pangleflutter │ │ ├── PangleAdManager.kt │ │ ├── PangleAdSlotManager.kt │ │ ├── PangleFlutterPlugin.kt │ │ ├── common │ │ ├── Data.kt │ │ ├── PangleEventStreamHandler.kt │ │ ├── PangleEventType.java │ │ ├── PangleImgSize.kt │ │ ├── PangleLoadingType.kt │ │ ├── PangleOrientation.kt │ │ └── PangleTitleBarTheme.kt │ │ ├── delegate │ │ ├── FLTBannerExpressAd.kt │ │ ├── FLTFeedExpressAd.kt │ │ ├── FLTFullScreenVideoAd.kt │ │ ├── FLTInterstitialAd.kt │ │ ├── FLTNativeExpressAd.kt │ │ ├── FLTRewardedVideoAd.kt │ │ └── FLTSplashAd.kt │ │ ├── dialog │ │ ├── AdDialog.kt │ │ ├── NativeSplashDialog.kt │ │ └── SupportSplashDialog.kt │ │ ├── util │ │ ├── DialogUtil.kt │ │ ├── KotlinExtensions.kt │ │ ├── OKHttpStack.kt │ │ └── ScreenUtil.kt │ │ └── view │ │ ├── BannerViewFactory.kt │ │ ├── FeedViewFactory.kt │ │ ├── FlutterBannerView.kt │ │ ├── FlutterFeedView.kt │ │ ├── FlutterNativeBannerView.kt │ │ ├── FlutterSplashView.kt │ │ ├── NativeBannerViewFactory.kt │ │ └── SplashViewFactory.kt │ └── res │ ├── anim │ ├── pangle_flutter_push_anim_in.xml │ └── pangle_flutter_push_anim_out.xml │ ├── raw │ └── pangle_flutter_keep.xml │ ├── values │ ├── colors.xml │ └── styles.xml │ └── xml │ ├── pangle_flutter_file_paths.xml │ └── pangle_network_config.xml ├── coverage └── lcov.info ├── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── io │ │ │ │ │ └── github │ │ │ │ │ └── nullptrx │ │ │ │ │ └── pangleflutterexample │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-xxhdpi │ │ │ │ └── flutter.png │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ └── values │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── .last_build_id │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ ├── incon_120-1.png │ │ │ ├── incon_120.png │ │ │ ├── incon_180.png │ │ │ ├── incon_40.png │ │ │ ├── incon_58.png │ │ │ ├── incon_60.png │ │ │ ├── incon_80.png │ │ │ └── incon_87.png │ │ ├── Contents.json │ │ ├── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ └── flutter.imageset │ │ │ ├── Contents.json │ │ │ └── flutter.png │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info-Debug.plist │ │ ├── Info-Release.plist │ │ └── Runner-Bridging-Header.h ├── lib │ ├── common │ │ ├── common.dart │ │ ├── ext.dart │ │ └── version.dart │ ├── main.dart │ ├── page │ │ ├── constant.dart │ │ ├── empty_page.dart │ │ ├── express │ │ │ ├── banner_page.dart │ │ │ ├── custom_splash_page.dart │ │ │ ├── feed_page.dart │ │ │ ├── fullscreen_video_page.dart │ │ │ ├── interstitial_page.dart │ │ │ ├── rewarded_video_page.dart │ │ │ └── splash_page.dart │ │ ├── home_page.dart │ │ ├── home_page_provider.dart │ │ └── pages.dart │ └── widget │ │ └── loading.dart ├── pubspec.yaml └── test │ └── widget_test.dart ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── BannerViewFactory.swift │ ├── Constant.swift │ ├── FLTBannerView.swift │ ├── FLTFeedView.swift │ ├── FLTSplashView.swift │ ├── FLTView.swift │ ├── FeedViewFactory.swift │ ├── PangleAdManager.swift │ ├── PangleEventStreamHandler.swift │ ├── PangleEventType.swift │ ├── PangleFlutterPlugin.h │ ├── PangleFlutterPlugin.m │ ├── SplashViewFactory.swift │ ├── SwiftPangleFlutterPlugin.swift │ ├── delegate │ │ ├── FLTFullscreenVideoExpressAd.swift │ │ ├── FLTInterstitialExpressAd.swift │ │ ├── FLTNativeExpressAd.swift │ │ ├── FLTRewardedVideoExpressAd.swift │ │ └── FLTSplashAd.swift │ ├── task │ │ ├── FLTFullscreenVideoExpressAdTask.swift │ │ ├── FLTInterstitialExpressAdTask.swift │ │ ├── FLTNativeExpressAdTask.swift │ │ ├── FLTRewardedVideoExpressAdTask.swift │ │ ├── FLTSplashAdTask.swift │ │ └── FLTTaskProtocol.swift │ └── util │ │ ├── AppUtil.swift │ │ └── UIUtil.swift └── pangle_flutter.podspec ├── lib ├── pangle_flutter.dart └── src │ ├── build.dart │ ├── config.dart │ ├── config_android.dart │ ├── config_ios.dart │ ├── constant.dart │ ├── extension.dart │ ├── model.dart │ ├── pangle_event_type.dart │ ├── pangle_plugin.dart │ ├── size.dart │ ├── util.dart │ └── view │ ├── banner │ ├── bannerview_android.dart │ ├── bannerview_android_legacy.dart │ ├── bannerview_ios.dart │ ├── bannerview_method_channel.dart │ └── platform_interface.dart │ ├── bannerview.dart │ ├── feed │ ├── feedview_android.dart │ ├── feedview_android_legacy.dart │ ├── feedview_ios.dart │ ├── feedview_method_channel.dart │ └── platform_interface.dart │ ├── feedview.dart │ ├── platform_controller.dart │ ├── splash │ ├── platform_interface.dart │ ├── splashview_android.dart │ ├── splashview_android_legacy.dart │ ├── splashview_ios.dart │ └── splashview_method_channel.dart │ └── splashview.dart ├── pubspec.yaml └── test └── pangle_flutter_test.dart /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **1.简述问题** 8 | 9 | 10 | **2.重现步骤** 11 | 12 | 13 | **3.期望结果** 14 | 15 | 16 | **4.SDK版本:** 17 | - flutter: 18 | - dart: 19 | - pangle_flutter: 20 | - android: 21 | - iOS: 22 | 23 | **额外信息** 24 | 无 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **1.简述需求** 8 | 9 | 10 | **2.解决方案** 11 | 12 | -------------------------------------------------------------------------------- /.github/workflows/code_quality.yml: -------------------------------------------------------------------------------- 1 | name: Qodana 2 | on: 3 | workflow_dispatch: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | - 'releases/*' 9 | 10 | jobs: 11 | qodana: 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: write 15 | pull-requests: write 16 | checks: write 17 | steps: 18 | - uses: actions/checkout@v3 19 | with: 20 | ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit 21 | fetch-depth: 0 # a full history is required for pull request analysis 22 | - name: 'Qodana Scan' 23 | uses: JetBrains/qodana-action@v2023.2 24 | env: 25 | QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .atom/ 3 | .idea/ 4 | .vscode/ 5 | 6 | .packages 7 | .pub/ 8 | .dart_tool/ 9 | pubspec.lock 10 | flutter_export_environment.sh 11 | 12 | examples/all_plugins/pubspec.yaml 13 | 14 | Podfile 15 | Podfile.lock 16 | Pods/ 17 | .symlinks/ 18 | **/Flutter/App.framework/ 19 | **/Flutter/ephemeral/ 20 | **/Flutter/Flutter.framework/ 21 | **/Flutter/Generated.xcconfig 22 | **/Flutter/flutter_assets/ 23 | 24 | ServiceDefinitions.json 25 | xcuserdata/ 26 | **/DerivedData/ 27 | 28 | local.properties 29 | keystore.properties 30 | .gradle/ 31 | gradlew 32 | gradlew.bat 33 | gradle-wrapper.jar 34 | .flutter-plugins-dependencies 35 | *.iml 36 | 37 | generated_plugin_registrant.dart 38 | GeneratedPluginRegistrant.h 39 | GeneratedPluginRegistrant.m 40 | GeneratedPluginRegistrant.java 41 | GeneratedPluginRegistrant.swift 42 | build/ 43 | .flutter-plugins 44 | 45 | .project 46 | .classpath 47 | .settings -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 8af6b2f038c1172e61d418869363a28dffec3cb4 8 | channel: stable 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /.pubignore: -------------------------------------------------------------------------------- 1 | build 2 | android/keep 3 | TODO.md 4 | coverage 5 | test -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: bash 2 | os: 3 | - osx 4 | sudo: false 5 | git: 6 | depth: 3 7 | cache: 8 | directories: 9 | - $HOME/.pub-cache 10 | env: 11 | - FLUTTER_VERSION=beta 12 | - FLUTTER_VERSION=dev 13 | - FLUTTER_VERSION=stable 14 | matrix: 15 | allow_failures: 16 | - env: FLUTTER_VERSION=dev 17 | - env: FLUTTER_VERSION=beta 18 | before_script: 19 | - cd .. 20 | - git clone https://github.com/flutter/flutter.git -b $FLUTTER_VERSION 21 | - export PATH=$PATH:$PWD/flutter/bin 22 | - export PATH=$PATH:$PWD/flutter/bin/cache/dart-sdk/bin 23 | - flutter doctor 24 | - cd - 25 | script: 26 | # abort on error 27 | - set -e 28 | - flutter packages get 29 | - flutter format --set-exit-if-changed lib example 30 | - flutter analyze lib example 31 | - flutter test --no-pub --coverage 32 | # export coverage 33 | - bash <(curl -s https://codecov.io/bash) 34 | -------------------------------------------------------------------------------- /ISSUE.md: -------------------------------------------------------------------------------- 1 | # 常见问题解决方案 2 | 3 | ## 1. Pod install 失败 4 | 5 | ```shell 6 | ### Error 7 | 8 | ​``` 9 | NoMethodError - undefined method `size' for nil:NilClass 10 | /Users/su/.rvm/rubies/ruby-2.4.1/lib/ruby/gems/2.4.0/gems/ruby-macho-1.4.0/lib/macho/macho_file.rb:455:in `populate_mach_header' 11 | /Users/su/.rvm/rubies/ruby-2.4.1/lib/ruby/gems/2.4.0/gems/ruby-macho-1.4.0/lib/macho/macho_file.rb:233:in `populate_fields' 12 | /Users/su/.rvm/rubies/ruby-2.4.1/lib/ruby/gems/2.4.0/gems/ruby-macho-1.4.0/lib/macho/macho_file.rb:55:in `initialize_from_bin' 13 | /Users/su/.rvm/rubies/ruby-2.4.1/lib/ruby/gems/2.4.0/gems/ruby-macho-1.4.0/lib/macho/macho_file.rb:33:in `new_from_bin' 14 | /Users/su/.rvm/rubies/ruby-2.4.1/lib/ruby/gems/2.4.0/gems/ruby-macho-1.4.0/lib/macho/fat_file.rb:365:in `block in populate_machos' 15 | ... 16 | ... 17 | ... 18 | ――― TEMPLATE END ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― 19 | 20 | [!] Oh no, an error occurred. 21 | ... 22 | ... 23 | ``` 24 | 25 | 关键:NoMethodError - undefined method `size' for nil:NilClass 26 | 27 | 解决方案来源:[https://github.com/CocoaPods/CocoaPods/issues/8377](https://github.com/CocoaPods/CocoaPods/issues/8377#issuecomment-554915212) 28 | 29 | ```shell 30 | flutter clean 31 | rm -Rf ios/Pods 32 | rm -Rf ios/.symlinks 33 | rm -Rf ios/Flutter/Flutter.framework 34 | rm -Rf ios/Flutter/Flutter.podspec 35 | ``` 36 | 37 | ## 2. build报错 38 | Command PhaseScriptExecution failed with a nonzero exit code 39 | 40 | `/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh"` 41 | 42 | 如果是flutter构建脚本报错,大概率是Xcode项目与flutter版本不兼容。 43 | 44 | 解决方案:项目根目录下执行如下命令,重新构建即可 45 | 46 | ```shell 47 | rm -rf ios/Runner.xcodeproj 48 | flutter create . 49 | ``` 50 | 51 | Solution: Your Xcode project is incompatible with this version of Flutter. Run `rm -rf ios/Runner.xcodeproj` and `flutter create .` to regenerate. 52 | 53 | 54 | 55 | ## 3. scanning files to index 56 | 57 | 一直卡在扫描文件。 58 | 59 | 解决方案:关闭IDE,然后android项目根目录下执行下面命令行,等待构建成功后重新打开项目。 60 | 61 | ```shell 62 | ./gradlew build 63 | ``` 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 nullptrX 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. -------------------------------------------------------------------------------- /SETUP.md: -------------------------------------------------------------------------------- 1 | ## 集成步骤 2 | 3 | 首先,去官网下载SDK。 4 | 5 | 6 | 7 | ### Android 8 | 9 | 10 | 11 | #### 1. 导入依赖 12 | 13 | - Module方式 14 | 15 | >1. 导入AAR module 16 | >2. 创建一个新module,`File->New->New Module...->Import .JAR/AAR Package`,选择`open_ad_sdk.aar`完成导入。 17 | >3. 注意module名字必须是 **open_ad_sdk**, 因为这是本插件所依赖包。如有什么建议,可在Issues里面讨论。 18 | 19 | 20 | 21 | 22 | #### 2. 处理AndroidManifest错误 23 | 24 | 官方AAR中application节点加了label属性,所以需要覆盖它 25 | 26 | ```xml 27 | 31 | 32 | 39 | ``` 40 | 41 | 42 | 43 | #### 3. 因部分广告请求是http请求,在安卓API 24以上需要手动添加http支持,才能正常请求广告 44 | 45 | ```xml 46 | 56 | 57 | ``` 58 | 59 | 60 | 61 | #### 4. 至此,Android模块一般可以正常使用了。(权限、混淆等配置均已在模块中,无需额外配置) 62 | 63 | 64 | 65 | ### iOS 66 | 67 | 68 | 69 | #### 1. 导入Framework包(无需操作) 70 | 71 | 默认使用pod导入 72 | 73 | 74 | 75 | #### 2. 工程plist文件设置 76 | 77 | - 另官方文档提示:SDK API 已经全部支持HTTPS,但是广告主素材存在非HTTPS情况。所以需要支持http协议正常使用。 78 | 79 | ```xml 80 | 81 | NSAppTransportSecurity 82 | 83 | NSAllowsArbitraryLoads 84 | 85 | 86 | 87 | ``` 88 | 89 | 90 | 91 | #### 3.剩余配置参考官方文档 92 | 93 | [Xcode配置](https://ad.oceanengine.com/union/media/union/download/detail?id=16&docId=5de8d570b1afac00129330c5&osType=ios) 94 | 95 | 文档中提到的`添加依赖库`的相关说明,如果你是用Pod管理依赖库,则不需手动导入;反之,则需手动导入。 96 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | > 1. 自渲染转模板渲染实现 4 | 5 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | # Additional information about this file can be found at 3 | # https://dart.dev/guides/language/analysis-options 4 | analyzer: 5 | strong-mode: 6 | implicit-dynamic: false 7 | errors: 8 | missing_required_param: warning 9 | missing_return: warning 10 | unused_import: error 11 | 12 | linter: 13 | rules: 14 | - avoid_catching_errors 15 | - avoid_function_literals_in_foreach_calls 16 | - avoid_private_typedef_functions 17 | - avoid_renaming_method_parameters 18 | - avoid_returning_null_for_void 19 | - avoid_unused_constructor_parameters 20 | - avoid_void_async 21 | - await_only_futures 22 | - camel_case_types 23 | - cancel_subscriptions 24 | - comment_references 25 | - constant_identifier_names 26 | - control_flow_in_finally 27 | - directives_ordering 28 | - empty_statements 29 | - file_names 30 | - hash_and_equals 31 | - implementation_imports 32 | - iterable_contains_unrelated_type 33 | - join_return_with_assignment 34 | # - lines_longer_than_80_chars 35 | - list_remove_unrelated_type 36 | - missing_whitespace_between_adjacent_strings 37 | - no_runtimeType_toString 38 | - non_constant_identifier_names 39 | - only_throw_errors 40 | - overridden_fields 41 | - package_api_docs 42 | - package_names 43 | - package_prefixed_library_names 44 | - prefer_asserts_in_initializer_lists 45 | - prefer_const_constructors 46 | - prefer_const_declarations 47 | # - prefer_expression_function_bodies 48 | - prefer_final_locals 49 | - prefer_function_declarations_over_variables 50 | - prefer_initializing_formals 51 | - prefer_inlined_adds 52 | - prefer_interpolation_to_compose_strings 53 | - prefer_is_not_operator 54 | - prefer_null_aware_operators 55 | - prefer_relative_imports 56 | - prefer_typing_uninitialized_variables 57 | - prefer_void_to_null 58 | - provide_deprecation_message 59 | - sort_pub_dependencies 60 | - test_types_in_equals 61 | - throw_in_finally 62 | - unnecessary_brace_in_string_interps 63 | - unnecessary_lambdas 64 | - unnecessary_null_aware_assignments 65 | - unnecessary_overrides 66 | - unnecessary_parenthesis 67 | - unnecessary_statements 68 | - unnecessary_string_interpolations 69 | - use_string_buffers 70 | - void_checks -------------------------------------------------------------------------------- /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 'io.github.nullptrx.pangleflutter' 2 | version '2.0.1' 3 | 4 | buildscript { 5 | ext.kotlin_version = '[1.3.40,1.8.10]' 6 | repositories { 7 | google() 8 | mavenCentral() 9 | } 10 | 11 | dependencies { 12 | classpath 'com.android.tools.build:gradle:7.0.0' 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 14 | } 15 | } 16 | 17 | rootProject.allprojects { 18 | repositories { 19 | google() 20 | mavenCentral() 21 | // maven { url 'http://localhost:8081/repository/m2/' } 22 | // maven { url 'https://raw.githubusercontent.com/nullptrX/repo/master/m2' } 23 | // maven { url 'https://cdn.jsdelivr.net/gh/nullptrX/repo/m2/' } 24 | maven { url 'https://artifact.bytedance.com/repository/pangle' } 25 | } 26 | } 27 | 28 | apply plugin: 'com.android.library' 29 | apply plugin: 'kotlin-android' 30 | 31 | def localProperties = new Properties() 32 | def localPropertiesFile = rootProject.file('local.properties') 33 | if (localPropertiesFile.exists()) { 34 | localPropertiesFile.withReader('UTF-8') { reader -> localProperties.load(reader) 35 | } 36 | } 37 | 38 | android { 39 | namespace 'io.github.nullptrx.pangleflutter' 40 | compileSdkVersion 31 41 | // compileOptions { 42 | // sourceCompatibility JavaVersion.VERSION_17 43 | // targetCompatibility JavaVersion.VERSION_17 44 | // } 45 | // 46 | // kotlin { 47 | // jvmToolchain(17) 48 | // } 49 | // 50 | // kotlinOptions { 51 | // jvmTarget = '17' 52 | // } 53 | sourceSets { 54 | main.java.srcDirs += 'src/main/kotlin' 55 | } 56 | defaultConfig { 57 | minSdkVersion 19 58 | } 59 | lintOptions { 60 | disable 'InvalidPackage' 61 | } 62 | buildTypes { 63 | release { 64 | consumerProguardFiles "proguard-rules.pro" 65 | } 66 | } 67 | /*gradle.buildFinished { r -> 68 | System.err.println "pangle_flutter: $kEnv" 69 | }*/ 70 | } 71 | 72 | dependencies { 73 | api 'com.pangle.cn:ads-sdk-pro:[5.4,5.5)' 74 | implementation 'androidx.appcompat:appcompat:[1.3,1.4)' 75 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 76 | } 77 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx2048M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Oct 17 17:39:32 CST 2020 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-6.5-all.zip 7 | -------------------------------------------------------------------------------- /android/keep/convert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/android/keep/convert -------------------------------------------------------------------------------- /android/keep/keep.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/android/keep/keep.zip -------------------------------------------------------------------------------- /android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -keep class com.bytedance.sdk.openadsdk.** { *; } 2 | -keep class com.bytedance.frameworks.** { *; } 3 | 4 | -keep class ms.bd.c.Pgl.**{*;} 5 | -keep class com.bytedance.mobsec.metasec.ml.**{*;} 6 | 7 | -keep class com.ss.android.**{*;} 8 | 9 | -keep class com.bytedance.embedapplog.** {*;} 10 | -keep class com.bytedance.embed_dr.** {*;} 11 | 12 | -keep class com.bykv.vk.** {*;} -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'pangle_flutter' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 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 | 45 | 48 | 49 | 50 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/common/Data.kt: -------------------------------------------------------------------------------- 1 | package io.github.nullptrx.pangleflutter.common 2 | 3 | data class TTSize( 4 | val width: Int = 0, 5 | val height: Int = 0 6 | ) 7 | 8 | data class TTSizeF( 9 | val width: Float = 0f, 10 | val height: Float = 0f 11 | 12 | ) 13 | 14 | val kBlock: (Any) -> Unit = {} -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/common/PangleEventStreamHandler.kt: -------------------------------------------------------------------------------- 1 | package io.github.nullptrx.pangleflutter.common 2 | 3 | import io.flutter.plugin.common.EventChannel 4 | 5 | class PangleEventStreamHandler : EventChannel.StreamHandler { 6 | 7 | companion object { 8 | 9 | private var eventSinks = hashMapOf() 10 | 11 | fun interstitial(event: String = "unknown") { 12 | eventSinks[PangleEventType.interstitial]?.success(event) 13 | } 14 | 15 | fun fullscreen(event: String = "unknown") { 16 | eventSinks[PangleEventType.fullscreen]?.success(event) 17 | } 18 | 19 | fun rewardedVideo(event: String = "unknown") { 20 | eventSinks[PangleEventType.rewarded_video]?.success(event) 21 | } 22 | 23 | fun clear() { 24 | eventSinks.clear() 25 | } 26 | } 27 | 28 | override fun onListen(arguments: Any?, events: EventChannel.EventSink?) { 29 | for (type in PangleEventType.values()) { 30 | if (type.ordinal == arguments) { 31 | eventSinks[type] = events 32 | break 33 | } 34 | } 35 | } 36 | 37 | override fun onCancel(arguments: Any?) { 38 | for (type in PangleEventType.values()) { 39 | if (type.ordinal == arguments) { 40 | eventSinks.remove(type) 41 | break 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/common/PangleEventType.java: -------------------------------------------------------------------------------- 1 | package io.github.nullptrx.pangleflutter.common; 2 | 3 | enum PangleEventType { 4 | interstitial, 5 | fullscreen, 6 | rewarded_video, 7 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/common/PangleImgSize.kt: -------------------------------------------------------------------------------- 1 | package io.github.nullptrx.pangleflutter.common 2 | 3 | import android.content.res.Resources 4 | 5 | enum class PangleImgSize(val width: Int = 0, val height: Int = 0) { 6 | banner640_90(640, 90), 7 | banner640_100(640, 100), 8 | banner600_150(600, 150), 9 | banner600_260(600, 260), 10 | banner600_286(600, 286), 11 | banner600_300(600, 300), 12 | banner690_388(690, 388), 13 | banner600_400(600, 400), 14 | banner600_500(600, 500), 15 | feed228_150(228, 150), 16 | feed690_388(690, 388), 17 | interstitial600_400(600, 400), 18 | interstitial600_600(600, 600), 19 | interstitial600_900(600, 900), 20 | drawFullScreen; 21 | 22 | fun toDeviceSize(): TTSize { 23 | val displayMetrics = Resources.getSystem().displayMetrics 24 | val w = displayMetrics.widthPixels 25 | val h = displayMetrics.widthPixels * height / width.toFloat() 26 | return TTSize(w, h.toInt()) 27 | } 28 | 29 | fun toDeviceSizeF(): TTSizeF { 30 | val displayMetrics = Resources.getSystem().displayMetrics 31 | val w = displayMetrics.widthPixels 32 | val h = displayMetrics.widthPixels * height / width.toFloat() 33 | return TTSizeF(w.toFloat(), h) 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/common/PangleLoadingType.kt: -------------------------------------------------------------------------------- 1 | package io.github.nullptrx.pangleflutter.common 2 | 3 | enum class PangleLoadingType { 4 | normal, 5 | preload, 6 | preload_only, 7 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/common/PangleOrientation.kt: -------------------------------------------------------------------------------- 1 | package io.github.nullptrx.pangleflutter.common 2 | 3 | enum class PangleOrientation { 4 | none, 5 | veritical, 6 | horizontal, 7 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/common/PangleTitleBarTheme.kt: -------------------------------------------------------------------------------- 1 | package io.github.nullptrx.pangleflutter.common 2 | 3 | enum class PangleTitleBarTheme(val value: Int) { 4 | light(0), 5 | dark(1), 6 | no_title_bar(-1), 7 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/delegate/FLTBannerExpressAd.kt: -------------------------------------------------------------------------------- 1 | package io.github.nullptrx.pangleflutter.delegate 2 | 3 | import com.bytedance.sdk.openadsdk.TTAdNative 4 | import com.bytedance.sdk.openadsdk.TTNativeExpressAd 5 | import io.github.nullptrx.pangleflutter.PangleAdManager 6 | import io.github.nullptrx.pangleflutter.common.kBlock 7 | 8 | class FLTBannerExpressAd(var result: (Any) -> Unit) : TTAdNative.NativeExpressAdListener { 9 | 10 | override fun onError(code: Int, message: String?) { 11 | invoke(code, message) 12 | } 13 | 14 | override fun onNativeExpressAdLoad(ttNativeExpressAds: MutableList?) { 15 | if (ttNativeExpressAds == null || ttNativeExpressAds.isEmpty()) { 16 | invoke(-1) 17 | return 18 | } 19 | val data = PangleAdManager.shared.setExpressAd(ttNativeExpressAds) 20 | invoke(count = ttNativeExpressAds.size, data = data) 21 | } 22 | 23 | 24 | private fun invoke( 25 | code: Int = 0, 26 | message: String? = null, 27 | count: Int = 0, 28 | data: List? = null 29 | ) { 30 | if (result == kBlock) { 31 | return 32 | } 33 | result.apply { 34 | val args = mutableMapOf() 35 | args["code"] = code 36 | message?.also { 37 | args["message"] = it 38 | } 39 | args["count"] = count 40 | data?.also { 41 | args["data"] = it 42 | } 43 | invoke(args) 44 | result = kBlock 45 | } 46 | 47 | 48 | } 49 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/delegate/FLTFeedExpressAd.kt: -------------------------------------------------------------------------------- 1 | package io.github.nullptrx.pangleflutter.delegate 2 | 3 | import com.bytedance.sdk.openadsdk.TTAdNative 4 | import com.bytedance.sdk.openadsdk.TTNativeExpressAd 5 | import io.github.nullptrx.pangleflutter.PangleAdManager 6 | import io.github.nullptrx.pangleflutter.common.TTSizeF 7 | import io.github.nullptrx.pangleflutter.common.kBlock 8 | import kotlin.collections.set 9 | 10 | class FLTFeedExpressAd(val size: TTSizeF, var result: (Any) -> Unit = {}) : 11 | TTAdNative.NativeExpressAdListener { 12 | 13 | override fun onError(code: Int, message: String) { 14 | invoke(code, message) 15 | } 16 | 17 | override fun onNativeExpressAdLoad(ttNativeExpressAds: MutableList?) { 18 | if (ttNativeExpressAds == null || ttNativeExpressAds.isEmpty()) { 19 | invoke(-1) 20 | return 21 | } 22 | val data = PangleAdManager.shared.setExpressAd(ttNativeExpressAds) 23 | invoke(0, count = ttNativeExpressAds.size, data = data) 24 | } 25 | 26 | private fun invoke( 27 | code: Int = 0, 28 | message: String? = null, 29 | count: Int = 0, 30 | data: List? = null 31 | ) { 32 | if (result == kBlock) { 33 | return 34 | } 35 | result.apply { 36 | val params = mutableMapOf() 37 | params["code"] = code 38 | message?.also { 39 | params["message"] = it 40 | } 41 | params["count"] = count 42 | data?.also { 43 | params["data"] = it 44 | } 45 | invoke(params) 46 | result = kBlock 47 | } 48 | 49 | 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/delegate/FLTInterstitialAd.kt: -------------------------------------------------------------------------------- 1 | // package io.github.nullptrx.pangleflutter.delegate 2 | // 3 | // import android.app.Activity 4 | // import android.app.Dialog 5 | // import android.content.DialogInterface 6 | // import android.view.View 7 | // import android.view.ViewGroup 8 | // import com.bytedance.sdk.openadsdk.TTAdDislike 9 | // import com.bytedance.sdk.openadsdk.TTAdNative 10 | // import com.bytedance.sdk.openadsdk.TTNativeAd 11 | // import com.bytedance.sdk.openadsdk.TTNativeExpressAd 12 | // import io.github.nullptrx.pangleflutter.common.PangleEventStreamHandler 13 | // import io.github.nullptrx.pangleflutter.common.kBlock 14 | // import io.github.nullptrx.pangleflutter.dialog.AdDialog 15 | // 16 | // class FLTInterstitialAd(var target: Activity?, var result: (Any) -> Unit) : 17 | // TTAdNative.NativeAdListener, TTAdDislike.DislikeInteractionCallback, 18 | // TTNativeAd.AdInteractionListener, TTNativeAd.ExpressRenderListener { 19 | // 20 | // private var ttNativeAd: TTNativeAd? = null 21 | // 22 | // override fun onNativeAdLoad(ads: MutableList?) { 23 | // if (ads.isNullOrEmpty()) { 24 | // return 25 | // } 26 | // target ?: return 27 | // PangleEventStreamHandler.interstitial("load") 28 | // val ad = ads[0] 29 | // ad.setDislikeCallback(target, this) 30 | // ad.setExpressRenderListener(this) 31 | // ad.registerViewForInteraction(ad.adView as ViewGroup, ad.adView, this) 32 | // ad.render() 33 | // this.ttNativeAd = ad 34 | // } 35 | // 36 | // override fun onError(code: Int, message: String?) { 37 | // PangleEventStreamHandler.interstitial("error") 38 | // invoke(code, message) 39 | // } 40 | // 41 | // // ### DISLIKE START ### 42 | // override fun onShow() { 43 | // PangleEventStreamHandler.interstitial("dislike_show") 44 | // } 45 | // 46 | // override fun onSelected(index: Int, selection: String?, fromUser: Boolean) { 47 | // PangleEventStreamHandler.interstitial("dislike_selected") 48 | // } 49 | // 50 | // override fun onCancel() { 51 | // PangleEventStreamHandler.interstitial("dislike_cancel") 52 | // } 53 | // // ### DISLIKE END ### 54 | // 55 | // override fun onAdClicked(view: View?, ad: TTNativeAd?) { 56 | // PangleEventStreamHandler.interstitial("click") 57 | // } 58 | // 59 | // override fun onAdCreativeClick(view: View?, ad: TTNativeAd?) { 60 | // 61 | // } 62 | // 63 | // override fun onAdShow(ad: TTNativeAd?) { 64 | // PangleEventStreamHandler.interstitial("show") 65 | // } 66 | // 67 | // override fun onRenderSuccess(view: View, width: Float, height: Float, isExpress: Boolean) { 68 | // PangleEventStreamHandler.interstitial("render_success") 69 | // target?.also { 70 | // ttNativeAd?.showInteractionExpressAd(it) 71 | // } 72 | // 73 | // } 74 | // 75 | // 76 | // private fun invoke(code: Int = 0, message: String? = null) { 77 | // if (result == kBlock) { 78 | // return 79 | // } 80 | // result.apply { 81 | // val args = mutableMapOf() 82 | // args["code"] = code 83 | // message?.also { 84 | // args["message"] = it 85 | // } 86 | // invoke(args) 87 | // result = kBlock 88 | // } 89 | // target = null 90 | // } 91 | // 92 | // 93 | // } -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/delegate/FLTNativeExpressAd.kt: -------------------------------------------------------------------------------- 1 | package io.github.nullptrx.pangleflutter.delegate 2 | 3 | import com.bytedance.sdk.openadsdk.TTAdNative 4 | import com.bytedance.sdk.openadsdk.TTNativeExpressAd 5 | import io.github.nullptrx.pangleflutter.common.kBlock 6 | 7 | internal class FLTNativeExpressAd(var result: (Any) -> Unit) : TTAdNative.NativeExpressAdListener { 8 | 9 | override fun onNativeExpressAdLoad(ads: MutableList?) { 10 | invoke() 11 | } 12 | 13 | override fun onError(code: Int, message: String?) { 14 | invoke(code, message) 15 | } 16 | 17 | 18 | private fun invoke(code: Int = 0, message: String? = null) { 19 | if (result == kBlock) { 20 | return 21 | } 22 | result.apply { 23 | val args = mutableMapOf() 24 | args["code"] = code 25 | message?.also { 26 | args["message"] = it 27 | } 28 | invoke(args) 29 | result = kBlock 30 | } 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/delegate/FLTSplashAd.kt: -------------------------------------------------------------------------------- 1 | package io.github.nullptrx.pangleflutter.delegate 2 | 3 | import android.app.Activity 4 | import androidx.fragment.app.FragmentActivity 5 | import com.bytedance.sdk.openadsdk.CSJAdError 6 | import com.bytedance.sdk.openadsdk.CSJSplashAd 7 | import com.bytedance.sdk.openadsdk.CSJSplashCloseType 8 | import com.bytedance.sdk.openadsdk.TTAdNative 9 | import io.github.nullptrx.pangleflutter.common.kBlock 10 | import io.github.nullptrx.pangleflutter.dialog.NativeSplashDialog 11 | import io.github.nullptrx.pangleflutter.dialog.SupportSplashDialog 12 | 13 | internal class FLTSplashAd( 14 | val hideSkipButton: Boolean?, 15 | var activity: Activity?, 16 | var result: (Any) -> Unit = {} 17 | ) : TTAdNative.CSJSplashAdListener { 18 | private var supportDialog: SupportSplashDialog? = null 19 | private var nativeDialog: NativeSplashDialog? = null 20 | 21 | 22 | override fun onSplashLoadSuccess() { 23 | 24 | } 25 | 26 | override fun onSplashLoadFail(error: CSJAdError) { 27 | handleSplashEnd() 28 | val msg = error.msg 29 | val code = error.code 30 | invoke(code, message = msg) 31 | } 32 | 33 | override fun onSplashRenderSuccess(ad: CSJSplashAd) { 34 | loadAd(ad) 35 | } 36 | 37 | override fun onSplashRenderFail(ad: CSJSplashAd, error: CSJAdError) { 38 | handleSplashEnd() 39 | invoke(error.code, message = error.msg) 40 | } 41 | 42 | fun loadAd(ad: CSJSplashAd) { 43 | val splashView = ad.splashView 44 | hideSkipButton?.also { 45 | if (it) { 46 | ad.hideSkipButton() 47 | } 48 | } 49 | ad.setSplashAdListener(object : CSJSplashAd.SplashAdListener { 50 | 51 | override fun onSplashAdShow(ad: CSJSplashAd?) { 52 | 53 | } 54 | 55 | override fun onSplashAdClick(ad: CSJSplashAd?) { 56 | } 57 | 58 | override fun onSplashAdClose(ad: CSJSplashAd?, type: Int) { 59 | handleSplashEnd() 60 | invoke(0, type) 61 | } 62 | 63 | }) 64 | activity?.also { 65 | if (it is FragmentActivity) { 66 | val supportSplashDialog = SupportSplashDialog() 67 | supportDialog = supportSplashDialog 68 | supportSplashDialog.show(it.supportFragmentManager, splashView) 69 | } else { 70 | val nativeSplashDialog = NativeSplashDialog() 71 | nativeDialog = nativeSplashDialog 72 | nativeSplashDialog.show(it.fragmentManager, splashView) 73 | } 74 | } 75 | } 76 | 77 | private fun handleSplashEnd() { 78 | supportDialog?.dismissAllowingStateLoss() 79 | nativeDialog?.dismissAllowingStateLoss() 80 | } 81 | 82 | 83 | fun invoke(code: Int = 0, type: Int = 0, message: String = "") { 84 | if (result == kBlock) { 85 | return 86 | } 87 | result.apply { 88 | val params = mutableMapOf() 89 | params["code"] = code 90 | params["type"] = type 91 | params["message"] = message 92 | invoke(params) 93 | result = kBlock 94 | } 95 | } 96 | 97 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/dialog/AdDialog.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package io.github.nullptrx.pangleflutter.dialog 24 | 25 | import android.app.Dialog 26 | import android.content.Context 27 | import android.os.Bundle 28 | import android.view.Gravity 29 | import android.view.View 30 | import android.view.ViewGroup 31 | import android.widget.FrameLayout 32 | 33 | class AdDialog(context: Context, private val view: View) : Dialog(context) { 34 | 35 | val rootView: FrameLayout 36 | 37 | init { 38 | rootView = FrameLayout(context) 39 | } 40 | 41 | override fun onCreate(savedInstanceState: Bundle?) { 42 | super.onCreate(savedInstanceState) 43 | 44 | // 获取自定义的 ViewGroup 45 | 46 | 47 | // 添加另一个 View 到 ViewGroup 中 48 | rootView.addView( 49 | view, 50 | FrameLayout.LayoutParams( 51 | ViewGroup.LayoutParams.WRAP_CONTENT, 52 | ViewGroup.LayoutParams.WRAP_CONTENT 53 | ).apply { gravity = Gravity.CENTER } 54 | ) 55 | 56 | // 设置 Dialog 的 View 57 | setContentView(rootView) 58 | } 59 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/dialog/NativeSplashDialog.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("DEPRECATION", "OVERRIDE_DEPRECATION") 2 | 3 | package io.github.nullptrx.pangleflutter.dialog 4 | 5 | import android.annotation.SuppressLint 6 | import android.app.Dialog 7 | import android.app.DialogFragment 8 | import android.app.FragmentManager 9 | import android.app.FragmentTransaction 10 | import android.content.Context 11 | import android.os.Bundle 12 | import android.view.LayoutInflater 13 | import android.view.View 14 | import android.view.ViewGroup 15 | import io.github.nullptrx.pangleflutter.util.DialogUtil 16 | import java.lang.reflect.Field 17 | 18 | 19 | class NativeSplashDialog : DialogFragment() { 20 | 21 | private lateinit var layoutView: View 22 | private lateinit var ctx: Context 23 | 24 | override fun onAttach(context: Context) { 25 | super.onAttach(context) 26 | ctx = context 27 | } 28 | 29 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { 30 | return DialogUtil.createDialog(ctx) 31 | } 32 | 33 | override fun onCreateView( 34 | inflater: LayoutInflater, 35 | container: ViewGroup?, 36 | savedInstanceState: Bundle? 37 | ): View { 38 | return layoutView 39 | } 40 | 41 | 42 | override fun onSaveInstanceState(outState: Bundle?) { 43 | } 44 | 45 | @SuppressLint("PrivateApi") 46 | fun show(manager: FragmentManager, view: View) { 47 | layoutView = view 48 | try { 49 | val mDismissed: Field = DialogFragment::class.java.getDeclaredField("mDismissed") 50 | mDismissed.isAccessible = true 51 | mDismissed.set(this, false) 52 | val mShownByMe: Field = DialogFragment::class.java.getDeclaredField("mShownByMe") 53 | mShownByMe.isAccessible = true 54 | mShownByMe.set(this, true) 55 | } catch (_: Exception) { 56 | } 57 | val ft: FragmentTransaction = manager.beginTransaction() 58 | ft.add(this, javaClass.simpleName) 59 | ft.commitAllowingStateLoss() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/dialog/SupportSplashDialog.kt: -------------------------------------------------------------------------------- 1 | package io.github.nullptrx.pangleflutter.dialog 2 | 3 | import android.app.Dialog 4 | import android.content.Context 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.fragment.app.DialogFragment 10 | import androidx.fragment.app.FragmentManager 11 | import androidx.fragment.app.FragmentTransaction 12 | import io.github.nullptrx.pangleflutter.util.DialogUtil 13 | import java.lang.reflect.Field 14 | 15 | class SupportSplashDialog : DialogFragment() { 16 | 17 | private lateinit var layoutView: View 18 | private lateinit var ctx: Context 19 | 20 | override fun onAttach(context: Context) { 21 | super.onAttach(context) 22 | ctx = context 23 | } 24 | 25 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { 26 | return DialogUtil.createDialog(ctx) 27 | } 28 | 29 | override fun onCreateView( 30 | inflater: LayoutInflater, 31 | container: ViewGroup?, 32 | savedInstanceState: Bundle? 33 | ): View? { 34 | return layoutView 35 | } 36 | 37 | override fun onSaveInstanceState(outState: Bundle) { 38 | } 39 | 40 | fun show(manager: FragmentManager, view: View) { 41 | layoutView = view 42 | try { 43 | val mDismissed: Field = DialogFragment::class.java.getDeclaredField("mDismissed") 44 | mDismissed.isAccessible = true 45 | mDismissed.set(this, false) 46 | val mShownByMe: Field = DialogFragment::class.java.getDeclaredField("mShownByMe") 47 | mShownByMe.isAccessible = true 48 | mShownByMe.set(this, true) 49 | } catch (_: Exception) { 50 | } 51 | val ft: FragmentTransaction = manager.beginTransaction() 52 | ft.add(this, javaClass.simpleName) 53 | ft.commitAllowingStateLoss() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/util/DialogUtil.kt: -------------------------------------------------------------------------------- 1 | package io.github.nullptrx.pangleflutter.util 2 | 3 | import android.app.Dialog 4 | import android.content.Context 5 | import android.graphics.Color 6 | import android.graphics.drawable.ColorDrawable 7 | import android.os.Build 8 | import android.view.View 9 | import android.view.Window 10 | import android.view.WindowManager 11 | import io.github.nullptrx.pangleflutter.R 12 | 13 | @Suppress("DEPRECATION") 14 | object DialogUtil { 15 | 16 | 17 | fun createDialog(context: Context): Dialog { 18 | // 使用不带Theme的构造器, 获得的dialog边框距离屏幕仍有几毫米的缝隙。 19 | val dialog = Dialog(context, R.style.PangleFlutterAdDialog).apply { 20 | requestWindowFeature(Window.FEATURE_NO_TITLE) // 设置Content前设定 21 | 22 | setCancelable(false) 23 | setCanceledOnTouchOutside(false) // 外部点击取消 24 | window?.apply { 25 | setWindowAnimations(R.style.PangleFlutterAnimNoAnim) 26 | setBackgroundDrawable(ColorDrawable(Color.BLACK)) 27 | // addFlags(FLAG_LAYOUT_NO_LIMITS or FLAG_FULLSCREEN or FLAG_LAYOUT_IN_SCREEN) 28 | 29 | decorView.systemUiVisibility = 30 | View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 31 | setFlags( 32 | WindowManager.LayoutParams.FLAG_FULLSCREEN, 33 | WindowManager.LayoutParams.FLAG_FULLSCREEN 34 | ) 35 | // 设置页面全屏显示 36 | val lp = attributes 37 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { 38 | lp.layoutInDisplayCutoutMode = 39 | WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 40 | } 41 | // 设置页面延伸到刘海区显示 42 | attributes = lp 43 | } 44 | 45 | } 46 | return dialog 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/util/KotlinExtensions.kt: -------------------------------------------------------------------------------- 1 | package io.github.nullptrx.pangleflutter.util 2 | 3 | import android.content.res.Resources 4 | import android.view.View 5 | 6 | /** 7 | * 像素密度计算工具 8 | */ 9 | 10 | val density: Float = Resources.getSystem().displayMetrics.density 11 | 12 | /** 13 | * 根据手机的分辨率从 dp 的单位 转成为 px(像素) 14 | * @param dpValue 虚拟像素 15 | * @return 像素 16 | */ 17 | val Int.dp 18 | get() = (0.5f + this * density).toInt() 19 | 20 | 21 | /** 22 | * 根据手机的分辨率从 dp 的单位 转成为 px(像素) 23 | * @param dpValue 虚拟像素 24 | * @return 像素 25 | */ 26 | val Float.dp 27 | get() = (0.5f + this * density).toInt() 28 | val Double.dp 29 | get() = (0.5f + this * density).toInt() 30 | 31 | /** 32 | * 根据手机的分辨率从 px(像素) 的单位 转成为 dp 33 | * @param pxValue 像素 34 | * @return 虚拟像素 35 | */ 36 | val Number.px 37 | get() = this.toFloat() / Resources.getSystem().displayMetrics.density 38 | 39 | 40 | operator fun View.get(id: Int): T? { 41 | return findViewById(id) 42 | } 43 | 44 | fun View.find(id: Int): T? { 45 | return findViewById(id) 46 | } 47 | 48 | 49 | inline fun Any.asType(): T? = 50 | if (this is T) this 51 | else null 52 | 53 | inline fun List<*>.asList(): List? = 54 | if (all { it is T }) 55 | @Suppress("UNCHECKED_CAST") 56 | this as List 57 | else 58 | null 59 | 60 | inline fun Map<*, *>.asMap(): Map? = 61 | if (all { it.key is T && it.value is E }) 62 | @Suppress("UNCHECKED_CAST") 63 | this as Map 64 | else 65 | null 66 | 67 | 68 | inline fun Any.asMap(): Map? = 69 | if (this is Map<*, *>) this.asMap() 70 | else null -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/view/BannerViewFactory.kt: -------------------------------------------------------------------------------- 1 | package io.github.nullptrx.pangleflutter.view 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import io.flutter.plugin.common.BinaryMessenger 6 | import io.flutter.plugin.common.StandardMessageCodec 7 | import io.flutter.plugin.platform.PlatformView 8 | import io.flutter.plugin.platform.PlatformViewFactory 9 | import io.github.nullptrx.pangleflutter.util.asMap 10 | import java.lang.ref.WeakReference 11 | 12 | class BannerViewFactory(val messenger: BinaryMessenger) : 13 | PlatformViewFactory(StandardMessageCodec.INSTANCE) { 14 | 15 | private var activity: WeakReference? = null 16 | 17 | override fun create(context: Context?, id: Int, args: Any?): PlatformView { 18 | val params: Map = args?.asMap() ?: mutableMapOf() 19 | val act = activity?.get() ?: throw IllegalStateException("Unable to get BannerView instance") 20 | return FlutterBannerView(act, messenger, id, params) 21 | } 22 | 23 | 24 | fun attachActivity(activity: Activity) { 25 | this.activity = WeakReference(activity) 26 | } 27 | 28 | fun detachActivity() { 29 | this.activity?.clear() 30 | this.activity = null 31 | } 32 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/view/FeedViewFactory.kt: -------------------------------------------------------------------------------- 1 | package io.github.nullptrx.pangleflutter.view 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import io.flutter.plugin.common.BinaryMessenger 6 | import io.flutter.plugin.common.StandardMessageCodec 7 | import io.flutter.plugin.platform.PlatformView 8 | import io.flutter.plugin.platform.PlatformViewFactory 9 | import io.github.nullptrx.pangleflutter.util.asMap 10 | import java.lang.ref.WeakReference 11 | 12 | class FeedViewFactory(val messenger: BinaryMessenger) : 13 | PlatformViewFactory(StandardMessageCodec.INSTANCE) { 14 | 15 | private var activity: WeakReference? = null 16 | 17 | override fun create(context: Context?, id: Int, args: Any?): PlatformView { 18 | val params = args?.asMap() ?: mutableMapOf() 19 | val act = activity?.get() ?: throw IllegalStateException("Unable to get FeedView instance") 20 | return FlutterFeedView(act, messenger, id, params) 21 | } 22 | 23 | fun attachActivity(activity: Activity) { 24 | this.activity = WeakReference(activity) 25 | } 26 | 27 | fun detachActivity() { 28 | this.activity?.clear() 29 | this.activity = null 30 | } 31 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/view/NativeBannerViewFactory.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package io.github.nullptrx.pangleflutter.view 24 | 25 | import android.app.Activity 26 | import android.content.Context 27 | import io.flutter.plugin.common.BinaryMessenger 28 | import io.flutter.plugin.common.StandardMessageCodec 29 | import io.flutter.plugin.platform.PlatformView 30 | import io.flutter.plugin.platform.PlatformViewFactory 31 | import io.github.nullptrx.pangleflutter.util.asMap 32 | import java.lang.ref.WeakReference 33 | 34 | class NativeBannerViewFactory(val messenger: BinaryMessenger) : 35 | PlatformViewFactory(StandardMessageCodec.INSTANCE) { 36 | private var activity: WeakReference? = null 37 | 38 | override fun create(context: Context?, id: Int, args: Any?): PlatformView { 39 | val params: Map = args?.asMap() ?: mutableMapOf() 40 | val act = 41 | activity?.get() ?: throw IllegalStateException("Unable to get NativeBannerView instance") 42 | return FlutterNativeBannerView(act, messenger, id, params) 43 | } 44 | 45 | fun attachActivity(activity: Activity) { 46 | this.activity = WeakReference(activity) 47 | } 48 | 49 | fun detachActivity() { 50 | this.activity?.clear() 51 | this.activity = null 52 | } 53 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/io/github/nullptrx/pangleflutter/view/SplashViewFactory.kt: -------------------------------------------------------------------------------- 1 | package io.github.nullptrx.pangleflutter.view 2 | 3 | import android.content.Context 4 | import io.flutter.plugin.common.BinaryMessenger 5 | import io.flutter.plugin.common.StandardMessageCodec 6 | import io.flutter.plugin.platform.PlatformView 7 | import io.flutter.plugin.platform.PlatformViewFactory 8 | import io.github.nullptrx.pangleflutter.util.asMap 9 | 10 | class SplashViewFactory(val messenger: BinaryMessenger) : 11 | PlatformViewFactory(StandardMessageCodec.INSTANCE) { 12 | 13 | override fun create(context: Context?, id: Int, args: Any?): PlatformView { 14 | val params = args?.asMap() ?: mutableMapOf() 15 | context ?: throw IllegalStateException("Unable to get SplashView instance") 16 | return FlutterSplashView(context, messenger, id, params) 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /android/src/main/res/anim/pangle_flutter_push_anim_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /android/src/main/res/anim/pangle_flutter_push_anim_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /android/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #D9D9D9 4 | #555555 5 | #999999 6 | #478FD2 7 | #F5F5F5 8 | -------------------------------------------------------------------------------- /android/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /android/src/main/res/xml/pangle_flutter_file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | 13 | 16 | 19 | -------------------------------------------------------------------------------- /android/src/main/res/xml/pangle_network_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | i.snssdk.com 6 | is.snssdk.com 7 | pangolin.snssdk.com 8 | extlog.snssdk.com 9 | sf3-ttcdn-tos.pstatp.com 10 | bds.snssdk.com 11 | dig.bdurl.net 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /coverage/lcov.info: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/coverage/lcov.info -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | .idea 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 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 | 43 | # Exceptions to above rules. 44 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 45 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 8af6b2f038c1172e61d418869363a28dffec3cb4 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # pangle_flutter_example 2 | 3 | Demonstrates how to use the pangle_flutter plugin. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | 18 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> localProperties.load(reader) 5 | } 6 | } 7 | 8 | def flutterRoot = localProperties.getProperty('flutter.sdk') 9 | if (flutterRoot == null) { 10 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 11 | } 12 | 13 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 14 | if (flutterVersionCode == null) { 15 | flutterVersionCode = '1' 16 | } 17 | 18 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 19 | if (flutterVersionName == null) { 20 | flutterVersionName = '1.0' 21 | } 22 | 23 | apply plugin: 'com.android.application' 24 | apply plugin: 'kotlin-android' 25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | namespace "io.github.nullptrx.pangleflutterexample" 29 | compileSdkVersion flutter.compileSdkVersion 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_17 32 | targetCompatibility JavaVersion.VERSION_17 33 | } 34 | kotlin { 35 | jvmToolchain(17) 36 | } 37 | kotlinOptions { 38 | jvmTarget = '17' 39 | } 40 | sourceSets { 41 | main.java.srcDirs += 'src/main/kotlin' 42 | } 43 | defaultConfig { 44 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 45 | applicationId "io.github.nullptrx.pangleflutterexample" 46 | minSdkVersion 19 47 | // minSdkVersion flutter.minSdkVersion 48 | targetSdkVersion flutter.targetSdkVersion 49 | versionCode flutterVersionCode.toInteger() 50 | versionName flutterVersionName 51 | multiDexEnabled true 52 | } 53 | buildTypes { 54 | release { 55 | // TODO: Add your own signing config for the release build. 56 | // Signing with the debug keys for now, so `flutter run --release` works. 57 | signingConfig signingConfigs.debug 58 | } 59 | } 60 | } 61 | 62 | flutter { 63 | source '../..' 64 | } 65 | 66 | dependencies { 67 | implementation 'androidx.core:core-ktx:1.10.1' 68 | } 69 | 70 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 15 | 23 | 27 | 30 | 35 | 38 | 39 | 40 | 41 | 42 | 43 | 45 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/io/github/nullptrx/pangleflutterexample/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package io.github.nullptrx.pangleflutterexample 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity : FlutterActivity() -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-xxhdpi/flutter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/android/app/src/main/res/drawable-xxhdpi/flutter.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ED6D63 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 18 | 25 | 26 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '[1.3.40,1.8.10]' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:8.0.2' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | // maven { url 'http://localhost:8081/repository/maven-releases/' } 19 | } 20 | } 21 | 22 | rootProject.buildDir = '../build' 23 | subprojects { 24 | project.buildDir = "${rootProject.buildDir}/${project.name}" 25 | } 26 | subprojects { 27 | project.evaluationDependsOn(':app') 28 | } 29 | 30 | tasks.register("clean", Delete) { 31 | delete rootProject.buildDir 32 | } 33 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx2048M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | android.defaults.buildfeatures.buildconfig=true 5 | android.nonTransitiveRClass=false 6 | android.nonFinalResIds=false 7 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Aug 01 09:39:30 CST 2020 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-8.0-all.zip 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Flutter Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | include ':app' 6 | 7 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 8 | def properties = new Properties() 9 | 10 | assert localPropertiesFile.exists() 11 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 12 | 13 | def flutterSdkPath = properties.getProperty("flutter.sdk") 14 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 15 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 16 | 17 | -------------------------------------------------------------------------------- /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/.last_build_id: -------------------------------------------------------------------------------- 1 | 21d111510062939194a688a5d4aaaa64 -------------------------------------------------------------------------------- /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 | 11.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/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 | 37 | 38 | 39 | 40 | 41 | 42 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /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/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "size": "20x20", 5 | "idiom": "iphone", 6 | "filename": "incon_40.png", 7 | "scale": "2x" 8 | }, 9 | { 10 | "size": "20x20", 11 | "idiom": "iphone", 12 | "filename": "incon_60.png", 13 | "scale": "3x" 14 | }, 15 | { 16 | "size": "29x29", 17 | "idiom": "iphone", 18 | "filename": "incon_58.png", 19 | "scale": "2x" 20 | }, 21 | { 22 | "size": "29x29", 23 | "idiom": "iphone", 24 | "filename": "incon_87.png", 25 | "scale": "3x" 26 | }, 27 | { 28 | "size": "40x40", 29 | "idiom": "iphone", 30 | "filename": "incon_80.png", 31 | "scale": "2x" 32 | }, 33 | { 34 | "size": "40x40", 35 | "idiom": "iphone", 36 | "filename": "incon_120.png", 37 | "scale": "3x" 38 | }, 39 | { 40 | "size": "60x60", 41 | "idiom": "iphone", 42 | "filename": "incon_120-1.png", 43 | "scale": "2x" 44 | }, 45 | { 46 | "size": "60x60", 47 | "idiom": "iphone", 48 | "filename": "incon_180.png", 49 | "scale": "3x" 50 | }, 51 | { 52 | "idiom": "ipad", 53 | "size": "20x20", 54 | "scale": "1x" 55 | }, 56 | { 57 | "idiom": "ipad", 58 | "size": "20x20", 59 | "scale": "2x" 60 | }, 61 | { 62 | "idiom": "ipad", 63 | "size": "29x29", 64 | "scale": "1x" 65 | }, 66 | { 67 | "idiom": "ipad", 68 | "size": "29x29", 69 | "scale": "2x" 70 | }, 71 | { 72 | "idiom": "ipad", 73 | "size": "40x40", 74 | "scale": "1x" 75 | }, 76 | { 77 | "idiom": "ipad", 78 | "size": "40x40", 79 | "scale": "2x" 80 | }, 81 | { 82 | "idiom": "ipad", 83 | "size": "76x76", 84 | "scale": "1x" 85 | }, 86 | { 87 | "idiom": "ipad", 88 | "size": "76x76", 89 | "scale": "2x" 90 | }, 91 | { 92 | "idiom": "ipad", 93 | "size": "83.5x83.5", 94 | "scale": "2x" 95 | }, 96 | { 97 | "idiom": "ios-marketing", 98 | "size": "1024x1024", 99 | "scale": "1x" 100 | } 101 | ], 102 | "info": { 103 | "version": 1, 104 | "author": "xcode" 105 | } 106 | } -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/incon_120-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/incon_120-1.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/incon_120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/incon_120.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/incon_180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/incon_180.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/incon_40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/incon_40.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/incon_58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/incon_58.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/incon_60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/incon_60.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/incon_80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/incon_80.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/incon_87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/incon_87.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "author": "xcode", 4 | "version": 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "idiom": "universal", 5 | "filename": "LaunchImage.png", 6 | "scale": "1x" 7 | }, 8 | { 9 | "idiom": "universal", 10 | "filename": "LaunchImage@2x.png", 11 | "scale": "2x" 12 | }, 13 | { 14 | "idiom": "universal", 15 | "filename": "LaunchImage@3x.png", 16 | "scale": "3x" 17 | } 18 | ], 19 | "info": { 20 | "version": 1, 21 | "author": "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/flutter.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "idiom": "universal", 5 | "scale": "1x" 6 | }, 7 | { 8 | "idiom": "universal", 9 | "scale": "2x" 10 | }, 11 | { 12 | "filename": "flutter.png", 13 | "idiom": "universal", 14 | "scale": "3x" 15 | } 16 | ], 17 | "info": { 18 | "author": "xcode", 19 | "version": 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/flutter.imageset/flutter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/example/ios/Runner/Assets.xcassets/flutter.imageset/flutter.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 | -------------------------------------------------------------------------------- /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-Debug.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | Pangle Flutter 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | NSAppTransportSecurity 45 | 46 | NSAllowsArbitraryLoads 47 | 48 | 49 | NSLocationAlwaysUsageDescription 50 | 始终允许 51 | NSLocationWhenInUseUsageDescription 52 | 需要允许 53 | SKAdNetworkItems 54 | 55 | 56 | SKAdNetworkIdentifier 57 | 238da6jt44.skadnetwork 58 | 59 | 60 | SKAdNetworkIdentifier 61 | x2jnk7ly8j.skadnetwork 62 | 63 | 64 | SKAdNetworkIdentifier 65 | 22mmun2rn5.skadnetwork 66 | 67 | 68 | NSUserTrackingUsageDescription 69 | 该标识符将用于向您投放个性化广告 70 | NSBonjourServices 71 | 72 | _dartobservatory._tcp 73 | 74 | NSLocalNetworkUsageDescription 75 | 需要使用您的本地网络用于应用调试 76 | 77 | 78 | -------------------------------------------------------------------------------- /example/ios/Runner/Info-Release.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | Pangle Flutter 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | NSAppTransportSecurity 45 | 46 | NSAllowsArbitraryLoads 47 | 48 | 49 | NSLocationAlwaysUsageDescription 50 | 始终允许 51 | NSLocationWhenInUseUsageDescription 52 | 需要允许 53 | SKAdNetworkItems 54 | 55 | 56 | SKAdNetworkIdentifier 57 | 238da6jt44.skadnetwork 58 | 59 | 60 | SKAdNetworkIdentifier 61 | x2jnk7ly8j.skadnetwork 62 | 63 | 64 | SKAdNetworkIdentifier 65 | 22mmun2rn5.skadnetwork 66 | 67 | 68 | NSUserTrackingUsageDescription 69 | 需要获取您设备的广告标识符,以为您提供更好的广告体验 70 | 71 | 72 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/lib/common/common.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'package:flutter/material.dart'; 24 | import 'package:pangle_flutter/pangle_flutter.dart'; 25 | 26 | var kThemeStatus = PangleTheme.light; 27 | 28 | const _textTheme = TextTheme( 29 | button: TextStyle( 30 | color: Colors.white, 31 | ), 32 | ); 33 | 34 | final kThemeData = ThemeData( 35 | colorScheme: ColorScheme.fromSwatch( 36 | accentColor: const Color(0xFFFF4081), 37 | primaryColorDark: const Color(0xFFFF4081), 38 | ), 39 | appBarTheme: const AppBarTheme( 40 | backgroundColor: Color(0xFFFF4081), 41 | ), 42 | primaryColor: const Color(0xFFFF4081), 43 | primaryTextTheme: _textTheme, 44 | buttonTheme: ButtonThemeData( 45 | highlightColor: Colors.redAccent[400], 46 | buttonColor: const Color(0xFFFF4081), 47 | textTheme: ButtonTextTheme.accent, 48 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(32)), 49 | ), 50 | elevatedButtonTheme: ElevatedButtonThemeData( 51 | style: ElevatedButton.styleFrom( 52 | onPrimary: Colors.white, 53 | primary: const Color(0xFFFF4081), 54 | // minimumSize: Size(88, 36), 55 | // padding: EdgeInsets.symmetric(horizontal: 16), 56 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(32)), 57 | ), 58 | ), 59 | textTheme: const TextTheme(), 60 | ); 61 | -------------------------------------------------------------------------------- /example/lib/common/ext.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'package:flutter/cupertino.dart'; 24 | 25 | extension Navi on BuildContext { 26 | Future navigateTo(Widget? child) { 27 | return Navigator.push( 28 | this, 29 | CupertinoPageRoute(builder: (context) => child!), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /example/lib/common/version.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | import 'dart:io'; 23 | 24 | import 'package:pangle_flutter/pangle_flutter.dart'; 25 | 26 | const kEnv = ''' 27 | Android Studio Arctic Fox 28 | Xcode 12.5.1 29 | 30 | Flutter 3.0.2 31 | Dart 2.17.3 32 | Kotlin 1.5.30 33 | Swift 5.4.2 34 | '''; 35 | const kDependencies = ''' 36 | Pangle SDK %s 37 | '''; 38 | 39 | var isAndroidAbove10 = false; 40 | 41 | Future initVersion() async { 42 | if (Platform.isAndroid) { 43 | var info = await pangle.getAndroidDeviceInfo(); 44 | isAndroidAbove10 = (info.sdkInt ?? 0) >= 29; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'dart:async'; 24 | 25 | import 'package:flutter/material.dart'; 26 | import 'package:pangle_flutter/pangle_flutter.dart'; 27 | import 'package:pangle_flutter_example/common/version.dart'; 28 | 29 | import 'common/common.dart'; 30 | import 'page/constant.dart'; 31 | import 'page/express/custom_splash_page.dart'; 32 | 33 | /// 使用本插件需要知道的几个类,基本覆盖了开始使用时需要用到的入口类 34 | /// 35 | /// [pangle] 加载广告的核心工具类 36 | /// [PangleHelper] 辅助加载广告使用的帮助类 37 | /// [PangleExpressSize] 模板类广告请求宽高设置 38 | /// 39 | /// [PangleResult] 普通加载广告返回的结果 40 | /// 41 | /// [SplashView] 开屏广告Widget 42 | /// 43 | /// [PangleAd] 信息流加载获得的数据源 44 | /// [FeedView] 信息流广告Widget 45 | /// 46 | /// [NativeBannerView] 横幅广告Widget 47 | /// 48 | void main() async { 49 | WidgetsFlutterBinding.ensureInitialized(); 50 | await initPangle(); 51 | await initVersion(); 52 | runApp(const PangleApp()); 53 | } 54 | 55 | /// 范例入口 56 | class PangleApp extends StatelessWidget { 57 | const PangleApp({Key? key}) : super(key: key); 58 | 59 | @override 60 | Widget build(BuildContext context) { 61 | return MaterialApp( 62 | /// 63 | home: const CustomSplashPage(isRoot: true), 64 | theme: kThemeData, 65 | ); 66 | } 67 | } 68 | 69 | /// 初始化广告sdk 70 | /// 71 | /// 工具类根据平台不同会有不同的配置 72 | /// [iOS] iOS平台配置参数 73 | /// [android] android平台配置参数 74 | Future initPangle() async { 75 | PangleResult ret = await pangle.init( 76 | iOS: const IOSConfig( 77 | appId: kAppId, 78 | logLevel: PangleLogLevel.debug, 79 | ), 80 | android: const AndroidConfig( 81 | appId: kAppId, 82 | debug: false, 83 | allowShowNotify: true, 84 | useTextureView: true, 85 | directDownloadNetworkType: [ 86 | AndroidDirectDownloadNetworkType.k2G, 87 | ]), 88 | ); 89 | debugPrint(ret.toString()); 90 | } 91 | -------------------------------------------------------------------------------- /example/lib/page/constant.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | const kAppId = '5001121'; 24 | const kSplashId = '888041257'; 25 | const kRewardedVideoExpressId = '901121365'; 26 | // 600x260 27 | const kBannerExpressId = '901121148'; 28 | // 375x284 29 | const kFeedExpressId = '901121253'; // info 30 | // const kFeedExpressId = '901121134'; // video 31 | // 3x2 32 | // const kInterstitialExpressId = '945940989'; 33 | // 1x1 34 | const kInterstitialExpressId = '947793385'; 35 | // 新模板渲染插屏 36 | const kFullscreenIdFull = '901121375'; 37 | // 全屏视频 38 | const kFullscreenVideoExpressId = '901121073'; 39 | -------------------------------------------------------------------------------- /example/lib/page/empty_page.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'package:flutter/material.dart'; 24 | 25 | class EmptyPage extends StatelessWidget { 26 | const EmptyPage({Key? key}) : super(key: key); 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Scaffold( 31 | appBar: AppBar( 32 | title: const Text('No Examples'), 33 | ), 34 | body: const Center( 35 | child: Text('Unimplemented'), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /example/lib/page/express/interstitial_page.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'dart:convert'; 24 | 25 | import 'package:flutter/material.dart'; 26 | import 'package:pangle_flutter/pangle_flutter.dart'; 27 | 28 | import '../constant.dart'; 29 | 30 | class InterstitialPage extends StatefulWidget { 31 | const InterstitialPage({Key? key}) : super(key: key); 32 | 33 | @override 34 | _InterstitialPageState createState() => _InterstitialPageState(); 35 | } 36 | 37 | class _InterstitialPageState extends State { 38 | @override 39 | Widget build(BuildContext context) { 40 | return Scaffold( 41 | appBar: AppBar( 42 | title: const Text('Interstitial Express AD'), 43 | ), 44 | body: Column( 45 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 46 | children: [ 47 | Center( 48 | child: ElevatedButton( 49 | onPressed: _onTapShow, 50 | child: const Text('Show Ad'), 51 | ), 52 | ), 53 | ], 54 | ), 55 | ); 56 | } 57 | 58 | _onTapShow() async { 59 | final width = kPangleScreenWidth - 30; 60 | final height = width / 1.667; 61 | 62 | final result = await pangle.loadFullscreenVideoAd( 63 | iOS: const IOSFullscreenVideoConfig( 64 | slotId: kInterstitialExpressId, 65 | ), 66 | android: AndroidFullscreenVideoConfig( 67 | slotId: kInterstitialExpressId, 68 | // 该宽高为你申请的广告位宽高,请根据实际情况赋值 69 | expressSize: PangleExpressSize.widthPercent(0.8, aspectRatio: 1.667), 70 | ), 71 | callback: (event) { 72 | debugPrint('interstitial: $event'); 73 | }, 74 | ); 75 | var data = jsonEncode(result); 76 | debugPrint(data); 77 | ScaffoldMessenger.of(context).showSnackBar( 78 | SnackBar(content: Text(data)), 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /example/lib/page/express/splash_page.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'dart:convert'; 24 | 25 | import 'package:flutter/material.dart'; 26 | import 'package:pangle_flutter/pangle_flutter.dart'; 27 | 28 | import '../constant.dart'; 29 | 30 | class SplashPage extends StatefulWidget { 31 | const SplashPage({Key? key}) : super(key: key); 32 | 33 | @override 34 | _SplashPageState createState() => _SplashPageState(); 35 | } 36 | 37 | class _SplashPageState extends State { 38 | @override 39 | Widget build(BuildContext context) { 40 | return Scaffold( 41 | appBar: AppBar( 42 | title: const Text('Splash AD'), 43 | ), 44 | body: Column( 45 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 46 | children: [ 47 | Center( 48 | child: ElevatedButton( 49 | onPressed: _onTapShow, 50 | child: const Text('Show Ad'), 51 | ), 52 | ), 53 | ], 54 | ), 55 | ); 56 | } 57 | 58 | _onTapShow() async { 59 | final result = await pangle.loadSplashAd( 60 | iOS: const IOSSplashConfig( 61 | slotId: kSplashId, 62 | ), 63 | android: const AndroidSplashConfig( 64 | slotId: kSplashId, 65 | ), 66 | ); 67 | var data = jsonEncode(result); 68 | debugPrint(data); 69 | ScaffoldMessenger.of(context).showSnackBar( 70 | SnackBar(content: Text(data)), 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /example/lib/page/home_page.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'dart:io'; 24 | 25 | import 'package:flutter/cupertino.dart'; 26 | import 'package:flutter/material.dart'; 27 | 28 | import '../../common/ext.dart'; 29 | import 'home_page_provider.dart'; 30 | import 'pages.dart'; 31 | 32 | class HomePage extends StatefulWidget { 33 | const HomePage({Key? key}) : super(key: key); 34 | 35 | @override 36 | _HomePageState createState() => _HomePageState(); 37 | } 38 | 39 | class _HomePageState extends State with HomePageProviderStateMixin { 40 | @override 41 | void loadExpressAd() { 42 | context.navigateTo(const Pages()); 43 | } 44 | 45 | @override 46 | void requestPermissions() async { 47 | if (Platform.isIOS) { 48 | requestPermissionsOnIOS(); 49 | } else { 50 | requestPermissionsOnAndroid(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /example/lib/page/pages.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'package:flutter/material.dart'; 24 | 25 | import '../../common/ext.dart'; 26 | import 'express/banner_page.dart'; 27 | import 'express/custom_splash_page.dart'; 28 | import 'express/feed_page.dart'; 29 | import 'express/fullscreen_video_page.dart'; 30 | import 'express/interstitial_page.dart'; 31 | import 'express/rewarded_video_page.dart'; 32 | import 'express/splash_page.dart'; 33 | 34 | class Pages extends StatefulWidget { 35 | const Pages({Key? key}) : super(key: key); 36 | 37 | @override 38 | _PagesState createState() => _PagesState(); 39 | } 40 | 41 | class _PagesState extends State { 42 | final pages = { 43 | 'Custom Splash AD': const CustomSplashPage(isRoot: false), 44 | 'Splash AD': const SplashPage(), 45 | 'Rewarded Video AD': const RewardedVideoPage(), 46 | 'Banner AD': const BannerPage(), 47 | 'Feed AD': const FeedPage(), 48 | 'Interstitial AD': const InterstitialPage(), 49 | 'FullScreenVideo AD': const FullscreenVideoPage(), 50 | }; 51 | 52 | @override 53 | Widget build(BuildContext context) { 54 | return Scaffold( 55 | appBar: AppBar( 56 | title: const Text('Examples'), 57 | ), 58 | body: Padding( 59 | padding: const EdgeInsets.all(16), 60 | child: ListView.separated( 61 | separatorBuilder: (context, index) => const Divider(), 62 | itemCount: pages.length, 63 | itemBuilder: (context, index) { 64 | var titles = pages.keys.toList(); 65 | final title = titles[index]; 66 | return ListTile( 67 | title: Text(title), 68 | trailing: const Icon(Icons.navigate_next), 69 | onTap: () => _onTapItem(title), 70 | ); 71 | }, 72 | ), 73 | ), 74 | ); 75 | } 76 | 77 | _onTapItem(String title) { 78 | context.navigateTo(pages[title]); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /example/lib/widget/loading.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'package:flutter/material.dart'; 24 | 25 | class Loading extends StatelessWidget { 26 | const Loading({Key? key}) : super(key: key); 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Container( 31 | padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0), 32 | child: Padding( 33 | padding: const EdgeInsets.only(bottom: 8.0), 34 | child: Row( 35 | crossAxisAlignment: CrossAxisAlignment.start, 36 | children: [ 37 | Container( 38 | width: 48.0, 39 | height: 48.0, 40 | color: Colors.grey[300], 41 | ), 42 | const Padding( 43 | padding: EdgeInsets.symmetric(horizontal: 8.0), 44 | ), 45 | Expanded( 46 | child: Column( 47 | crossAxisAlignment: CrossAxisAlignment.start, 48 | children: [ 49 | Container( 50 | width: double.infinity, 51 | height: 8.0, 52 | color: Colors.grey[300], 53 | ), 54 | const Padding( 55 | padding: EdgeInsets.symmetric(vertical: 2.0), 56 | ), 57 | Container( 58 | width: double.infinity, 59 | height: 8.0, 60 | color: Colors.grey[300], 61 | ), 62 | const Padding( 63 | padding: EdgeInsets.symmetric(vertical: 2.0), 64 | ), 65 | Container( 66 | width: 40.0, 67 | height: 8.0, 68 | color: Colors.grey[300], 69 | ), 70 | ], 71 | ), 72 | ) 73 | ], 74 | ), 75 | ), 76 | ); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: pangle_flutter_example 2 | description: Demonstrates how to use the pangle_flutter plugin. 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 | 8 | environment: 9 | sdk: '>=3.0.5 <4.0.0' 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | 15 | pangle_flutter: 16 | # When depending on this package from a real application you should use: 17 | # pangle_flutter: ^x.y.z 18 | # See https://dart.dev/tools/pub/dependencies#version-constraints 19 | # The example app is bundled with the plugin so we use a path dependency on 20 | # the parent directory to use the current plugin's version. 21 | path: ../ 22 | 23 | # The following adds the Cupertino Icons font to your application. 24 | # Use with the CupertinoIcons class for iOS style icons. 25 | cupertino_icons: ^1.0.3 26 | # permission_handler: ^5.0.1+1 27 | sprintf: ^7.0.0 28 | 29 | dev_dependencies: 30 | flutter_test: 31 | sdk: flutter 32 | 33 | # The "flutter_lints" package below contains a set of recommended lints to 34 | # encourage good coding practices. The lint set provided by the package is 35 | # activated in the `analysis_options.yaml` file located at the root of your 36 | # package. See that file for information about deactivating specific lint 37 | # rules and activating additional ones. 38 | flutter_lints: ^1.0.0 39 | 40 | # For information on the generic Dart part of this file, see the 41 | # following page: https://dart.dev/tools/pub/pubspec 42 | 43 | # The following section is specific to Flutter. 44 | flutter: 45 | 46 | # The following line ensures that the Material Icons font is 47 | # included with your application, so that you can use the icons in 48 | # the material Icons class. 49 | uses-material-design: true 50 | 51 | # To add assets to your application, add an assets section, like this: 52 | # assets: 53 | # - images/a_dot_burr.jpeg 54 | # - images/a_dot_ham.jpeg 55 | 56 | # An image asset can refer to one or more resolution-specific "variants", see 57 | # https://flutter.dev/assets-and-images/#resolution-aware. 58 | 59 | # For details regarding adding assets from package dependencies, see 60 | # https://flutter.dev/assets-and-images/#from-packages 61 | 62 | # To add custom fonts to your application, add a fonts section here, 63 | # in this "flutter" section. Each entry in this list should have a 64 | # "family" key with the font family name, and a "fonts" key with a 65 | # list giving the asset and other descriptors for the font. For 66 | # example: 67 | # fonts: 68 | # - family: Schyler 69 | # fonts: 70 | # - asset: fonts/Schyler-Regular.ttf 71 | # - asset: fonts/Schyler-Italic.ttf 72 | # style: italic 73 | # - family: Trajan Pro 74 | # fonts: 75 | # - asset: fonts/TrajanPro.ttf 76 | # - asset: fonts/TrajanPro_Bold.ttf 77 | # weight: 700 78 | # 79 | # For details regarding fonts from package dependencies, 80 | # see https://flutter.dev/custom-fonts/#from-packages 81 | -------------------------------------------------------------------------------- /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 | import 'package:pangle_flutter_example/main.dart'; 11 | 12 | void main() { 13 | testWidgets('Verify Platform version', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(const PangleApp()); 16 | 17 | // Verify that platform version is retrieved. 18 | expect( 19 | find.byWidgetPredicate( 20 | (Widget widget) => 21 | widget is Text && widget.data!.startsWith('Running on:'), 22 | ), 23 | findsOneWidget, 24 | ); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /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/nullptrx/pangle_flutter/41d98267da3a8d4d38414ea84e45bf9df6ac5206/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/BannerViewFactory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BannerViewFactory.swift 3 | // Pods-Runner 4 | // 5 | // Created by Jerry on 2020/7/19. 6 | // 7 | 8 | import Flutter 9 | 10 | public class BannerViewFactory: NSObject, FlutterPlatformViewFactory { 11 | private var messenger: FlutterBinaryMessenger 12 | 13 | init(messenger: NSObjectProtocol & FlutterBinaryMessenger) { 14 | self.messenger = messenger 15 | super.init() 16 | } 17 | 18 | public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol { 19 | FlutterStandardMessageCodec.sharedInstance() 20 | } 21 | 22 | public func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView { 23 | FLTBannerView(frame, id: viewId, params: (args as? [String: Any?]) ?? [:], messenger: messenger) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ios/Classes/Constant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constant.swift 3 | // pangle_flutter 4 | // 5 | // Created by Jerry on 2020/8/14. 6 | // 7 | 8 | import Foundation 9 | 10 | internal final class Constant { 11 | static let kBannerView = "nullptrx.github.io/pangle_bannerview" 12 | static let kFeedView = "nullptrx.github.io/pangle_feedview" 13 | 14 | 15 | static let kDefaultFeedAdCount = 3 16 | } 17 | -------------------------------------------------------------------------------- /ios/Classes/FLTFeedView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FLTFeedView.swift 3 | // ttad 4 | // 5 | // Created by Jerry on 2020/7/19. 6 | // 7 | 8 | import BUAdSDK 9 | import Flutter 10 | import WebKit 11 | 12 | public class FLTFeedView: NSObject, FlutterPlatformView { 13 | private let container: FeedView 14 | private var id: String? 15 | 16 | init(_ frame: CGRect, id: Int64, params: [String: Any?], messenger: FlutterBinaryMessenger) { 17 | let channelName = String(format: "nullptrx.github.io/pangle_feedview_%ld", id) 18 | let methodChannel = FlutterMethodChannel(name: channelName, binaryMessenger: messenger) 19 | container = FeedView(frame: frame, params: params, methodChannel: methodChannel) 20 | super.init() 21 | } 22 | 23 | public func view() -> UIView { 24 | container 25 | } 26 | 27 | deinit { 28 | UIUtil.removeAllView(container) 29 | } 30 | } 31 | 32 | 33 | class FeedView: FLTView { 34 | 35 | private var methodChannel: FlutterMethodChannel? = nil 36 | private var params: [String: Any?] = [:] 37 | 38 | 39 | var id: String = "" 40 | 41 | init(frame: CGRect, params: [String: Any?], methodChannel: FlutterMethodChannel) { 42 | self.params = params 43 | self.methodChannel = methodChannel 44 | super.init(frame: frame) 45 | methodChannel.setMethodCallHandler(handle(_:result:)) 46 | loadExpressAd() 47 | } 48 | 49 | required init?(coder: NSCoder) { 50 | super.init(coder: coder) 51 | } 52 | 53 | deinit { 54 | methodChannel?.setMethodCallHandler(nil) 55 | } 56 | 57 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 58 | switch call.method { 59 | case "addTouchableBounds": 60 | let args: [[String: Double?]] = call.arguments as? [[String: Double?]] ?? [[:]] 61 | addTouchableBounds(bounds: args) 62 | case "clearTouchableBounds": 63 | clearTouchableBounds() 64 | default: 65 | result(FlutterMethodNotImplemented) 66 | } 67 | 68 | } 69 | 70 | private func loadExpressAd() { 71 | guard let id = params["id"] as? String else { 72 | return 73 | } 74 | self.id = id 75 | let ad = PangleAdManager.shared.getExpressAd(id) 76 | guard let expressAd: BUNativeExpressAdView = ad else { 77 | return 78 | } 79 | expressAd.rootViewController = AppUtil.getVC() 80 | expressAd.extraChannel = methodChannel 81 | 82 | addSubview(expressAd) 83 | 84 | expressAd.render() 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /ios/Classes/FLTView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FLTUIView.swift 3 | // pangle_flutter 4 | // 5 | // Created by nullptrX on 2022/10/4. 6 | // 7 | 8 | import Foundation 9 | 10 | class FLTView: UIView { 11 | 12 | private var touchableBounds: [CGRect] = [] 13 | 14 | override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 15 | if !self.isUserInteractionEnabled || self.isHidden || self.alpha < 0.01 { 16 | // interaction disable 17 | // hidden 18 | // nearly invisble 19 | return nil 20 | } 21 | let windowPoint = self.convert(point, to: UIApplication.shared.delegate?.window!!) 22 | //找到点击落点所在的Overlay 23 | let targetView = UIUtil.findTargetView(self) 24 | let targetOverlayView = UIUtil.findTargetOverlayView(self, windowPoint) 25 | //判断Overlay符合与广告相交、重合,则返回nil实现事件拦截 26 | if UIUtil.isOverlay(targetView, targetOverlayView) { 27 | // 高级功能:自定义可点击区域(例如:虽然OverlayView覆盖了广告View, 但OverlayView该区域并没有可点击控件,此时可将事件传递下去) 28 | var touchable = false 29 | if touchableBounds.isEmpty { 30 | touchable = false 31 | } 32 | for bound in touchableBounds { 33 | if bound.contains(windowPoint) { 34 | touchable = true 35 | break 36 | } 37 | } 38 | if touchable { 39 | return super.hitTest(point, with: event) 40 | } 41 | 42 | return nil 43 | } else { 44 | return super.hitTest(point, with: event) 45 | } 46 | 47 | } 48 | 49 | 50 | func addTouchableBounds(bounds: [[String: Double?]]) { 51 | 52 | for bound in bounds { 53 | let w = bound["w"] ?? 0 54 | let h = bound["h"] ?? 0 55 | if w == nil || h == nil { 56 | continue 57 | } 58 | let x = bound["x"] ?? 0 59 | let y = bound["y"] ?? 0 60 | 61 | let targetBound = CGRect(x: x!, y: y!, width: w!, height: h!) 62 | var contains = false 63 | for touchableBound in touchableBounds { 64 | if touchableBound.equalTo(targetBound) { 65 | contains = true 66 | break 67 | } 68 | } 69 | if contains { 70 | continue 71 | } 72 | 73 | touchableBounds.append(targetBound) 74 | 75 | } 76 | } 77 | 78 | func clearTouchableBounds() { 79 | touchableBounds.removeAll() 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /ios/Classes/FeedViewFactory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FeedViewFactory.swift 3 | // ttad 4 | // 5 | // Created by Jerry on 2020/7/19. 6 | // 7 | 8 | import Flutter 9 | 10 | public class FeedViewFactory: NSObject, FlutterPlatformViewFactory { 11 | public static func initWithMessenger(with messenger: FlutterBinaryMessenger) -> BannerViewFactory { 12 | let instance = BannerViewFactory(messenger: messenger) 13 | return instance 14 | } 15 | 16 | private var messenger: FlutterBinaryMessenger 17 | 18 | init(messenger: FlutterBinaryMessenger) { 19 | self.messenger = messenger 20 | } 21 | 22 | public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol { 23 | FlutterStandardMessageCodec.sharedInstance() 24 | } 25 | 26 | public func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView { 27 | FLTFeedView(frame, id: viewId, params: (args as? [String: Any?]) ?? [:] as [String: Any?], messenger: messenger) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ios/Classes/PangleEventStreamHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PangleEventStreamHandler.swift 3 | // pangle_flutter 4 | // 5 | // Created by nullptrX on 2021/11/11. 6 | // 7 | 8 | import Foundation 9 | 10 | public final class PangleEventStreamHandler: NSObject, FlutterStreamHandler { 11 | private static var eventSinks: [PangleEventType: FlutterEventSink] = [:] 12 | 13 | public static func interstitial(_ event: String = "unknown") { 14 | guard let eventSink = eventSinks[.fullscreen] else { return } 15 | eventSink(event) 16 | } 17 | 18 | public static func fullscreen(_ event: String = "unknown") { 19 | guard let eventSink = eventSinks[.fullscreen] else { return } 20 | eventSink(event) 21 | } 22 | 23 | public static func rewardedVideo(_ event: String = "unknown") { 24 | guard let eventSink = eventSinks[.rewarded_video] else { return } 25 | eventSink(event) 26 | } 27 | 28 | public static func clear() { 29 | eventSinks.removeAll() 30 | } 31 | 32 | public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { 33 | guard let args: Int = arguments as? Int else { return nil } 34 | for type in PangleEventType.allCases { 35 | if type.rawValue == args { 36 | PangleEventStreamHandler.eventSinks[type] = events 37 | break 38 | } 39 | } 40 | return nil 41 | } 42 | 43 | public func onCancel(withArguments arguments: Any?) -> FlutterError? { 44 | guard let args: Int = arguments as? Int else { return nil } 45 | for type in PangleEventType.allCases { 46 | if type.rawValue == args { 47 | PangleEventStreamHandler.eventSinks.removeValue(forKey: type) 48 | break 49 | } 50 | } 51 | return nil 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ios/Classes/PangleEventType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PangleEventType.swift 3 | // pangle_flutter 4 | // 5 | // Created by nullptrX on 2021/11/11. 6 | // 7 | 8 | enum PangleEventType : Int, CaseIterable { 9 | case interstitial 10 | case fullscreen 11 | case rewarded_video 12 | } 13 | -------------------------------------------------------------------------------- /ios/Classes/PangleFlutterPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface PangleFlutterPlugin : NSObject 4 | @end 5 | -------------------------------------------------------------------------------- /ios/Classes/PangleFlutterPlugin.m: -------------------------------------------------------------------------------- 1 | #import "PangleFlutterPlugin.h" 2 | #if __has_include() 3 | #import 4 | #else 5 | // Support project import fallback if the generated compatibility header 6 | // is not copied when this plugin is created as a library. 7 | // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 8 | #import "pangle_flutter-Swift.h" 9 | #endif 10 | 11 | @implementation PangleFlutterPlugin 12 | + (void)registerWithRegistrar:(NSObject*)registrar { 13 | [SwiftPangleFlutterPlugin registerWithRegistrar:registrar]; 14 | } 15 | @end 16 | -------------------------------------------------------------------------------- /ios/Classes/SplashViewFactory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SplashViewFactory.swift 3 | // pangle_flutter 4 | // 5 | // Created by nullptrX on 2020/11/5. 6 | // 7 | 8 | import Flutter 9 | 10 | public class SplashViewFactory: NSObject, FlutterPlatformViewFactory { 11 | private var messenger: FlutterBinaryMessenger 12 | 13 | init(messenger: NSObjectProtocol & FlutterBinaryMessenger) { 14 | self.messenger = messenger 15 | super.init() 16 | } 17 | 18 | public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol { 19 | return FlutterStandardMessageCodec.sharedInstance() 20 | } 21 | 22 | public func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView { 23 | return FLTSplashView(frame, id: viewId, params: (args as? [String: Any?]) ?? [:], messenger: messenger) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ios/Classes/delegate/FLTInterstitialExpressAd.swift: -------------------------------------------------------------------------------- 1 | //// 2 | //// FLTInterstitialAd.swift 3 | //// pangle_flutter 4 | //// 5 | //// Created by Jerry on 2020/8/12. 6 | //// 7 | // 8 | //import BUAdSDK 9 | //import Flutter 10 | // 11 | //internal final class FLTInterstitialExpressAd: NSObject, BUNativeExpresInterstitialAdDelegate { 12 | // typealias Success = () -> Void 13 | // typealias Fail = (Error?) -> Void 14 | // 15 | // let success: Success? 16 | // let fail: Fail? 17 | // 18 | // init(success: Success?, fail: Fail?) { 19 | // self.success = success 20 | // self.fail = fail 21 | // } 22 | // 23 | // func nativeExpresInterstitialAdDidLoad(_ interstitialAd: BUNativeExpressInterstitialAd) { 24 | // PangleEventStreamHandler.interstitial("load") 25 | // } 26 | // 27 | // func nativeExpresInterstitialAdRenderSuccess(_ interstitialAd: BUNativeExpressInterstitialAd) { 28 | // PangleEventStreamHandler.interstitial("render_success") 29 | // let vc = AppUtil.getVC() 30 | // interstitialAd.show(fromRootViewController: vc) 31 | // } 32 | // 33 | // func nativeExpresInterstitialAd(_ interstitialAd: BUNativeExpressInterstitialAd, didFailWithError error: Error?) { 34 | // PangleEventStreamHandler.interstitial("error") 35 | // self.fail?(error) 36 | // } 37 | // 38 | // func nativeExpresInterstitialAdRenderFail(_ interstitialAd: BUNativeExpressInterstitialAd, error: Error?) { 39 | // PangleEventStreamHandler.interstitial("render_fail") 40 | // self.fail?(error) 41 | // } 42 | // 43 | // func nativeExpresInterstitialAdDidClose(_ interstitialAd: BUNativeExpressInterstitialAd) { 44 | // PangleEventStreamHandler.interstitial("dismiss") 45 | // self.success?() 46 | // } 47 | // 48 | // func nativeExpresInterstitialAdWillVisible(_ interstitialAd: BUNativeExpressInterstitialAd) { 49 | // PangleEventStreamHandler.interstitial("show") 50 | // } 51 | // 52 | // func nativeExpresInterstitialAdDidClick(_ interstitialAd: BUNativeExpressInterstitialAd) { 53 | // PangleEventStreamHandler.interstitial("click") 54 | // } 55 | // 56 | // 57 | // func nativeExpresInterstitialAdDidCloseOtherController(_ interstitialAd: BUNativeExpressInterstitialAd, interactionType: BUInteractionType) { 58 | // 59 | // } 60 | //} 61 | -------------------------------------------------------------------------------- /ios/Classes/delegate/FLTSplashAd.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FLTSplashAd.swift 3 | // Pods-Runner 4 | // 5 | // Created by Jerry on 2020/7/20. 6 | // 7 | 8 | import BUAdSDK 9 | import Foundation 10 | 11 | internal final class FLTSplashAd: NSObject, BUSplashAdDelegate { 12 | 13 | typealias Success = (String, Int) -> Void 14 | typealias Fail = (Error?) -> Void 15 | 16 | let success: Success? 17 | let fail: Fail? 18 | 19 | init(success: Success?, fail: Fail?) { 20 | self.success = success 21 | self.fail = fail 22 | } 23 | 24 | func splashAdDidClick(_ splashAd: BUSplashAd) { 25 | } 26 | 27 | public func splashAdDidClose(_ splashAd: BUSplashAd) { 28 | 29 | } 30 | 31 | func splashAdDidClose(_ splashAd: BUSplashAd, closeType: BUSplashAdCloseType) { 32 | self.success?("close", closeType.rawValue) 33 | splashAd.removeSplashView() 34 | } 35 | 36 | func splashAdLoadSuccess(_ splashAd: BUSplashAd) {} 37 | 38 | func splashAdLoadFail(_ splashAd: BUSplashAd, error: BUAdError?) { 39 | self.fail?(error) 40 | splashAd.removeSplashView() 41 | } 42 | 43 | func splashAdRenderSuccess(_ splashAd: BUSplashAd) {} 44 | 45 | func splashAdRenderFail(_ splashAd: BUSplashAd, error: BUAdError?) { 46 | self.fail?(error) 47 | splashAd.removeSplashView() 48 | } 49 | 50 | func splashAdWillShow(_ splashAd: BUSplashAd) {} 51 | 52 | func splashAdDidShow(_ splashAd: BUSplashAd) { 53 | 54 | } 55 | 56 | func splashAdViewControllerDidClose(_ splashAd: BUSplashAd) { 57 | 58 | } 59 | 60 | func splashDidCloseOtherController(_ splashAd: BUSplashAd, interactionType: BUInteractionType) { 61 | 62 | } 63 | 64 | func splashVideoAdDidPlayFinish(_ splashAd: BUSplashAd, didFailWithError error: Error) { 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /ios/Classes/task/FLTFullscreenVideoExpressAdTask.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FLTFullscreenVideoExpressAdTask.swift 3 | // pangle_flutter 4 | // 5 | // Created by nullptrX on 2020/8/25. 6 | // 7 | 8 | import BUAdSDK 9 | 10 | internal final class FLTFullscreenVideoExpressAdTask: FLTTaskProtocol { 11 | private var manager: BUNativeExpressFullscreenVideoAd 12 | private var delegate: BUNativeExpressFullscreenVideoAdDelegate? 13 | private var slotId: String = "" 14 | 15 | internal init(_ manager: BUNativeExpressFullscreenVideoAd) { 16 | self.manager = manager 17 | } 18 | 19 | init(_ args: [String: Any?]) { 20 | slotId = args["slotId"] as! String 21 | let manager = BUNativeExpressFullscreenVideoAd(slotID: slotId) 22 | self.manager = manager 23 | } 24 | 25 | func execute(_ loadingType: LoadingType) -> (@escaping (FLTTaskProtocol, Any) -> Void) -> Void { 26 | { result in 27 | let delegate = FLTFullscreenVideoExpressAd(self.slotId, loadingType: loadingType, success: { [weak self] () in 28 | guard let self = self else { 29 | return 30 | } 31 | result(self, ["code": 0]) 32 | }, fail: { [weak self] error in 33 | guard let self = self else { 34 | return 35 | } 36 | let e = error as NSError? 37 | result(self, ["code": e?.code ?? -1, "message": error?.localizedDescription ?? ""] as [String:Any]) 38 | }) 39 | 40 | self.manager.delegate = delegate 41 | self.delegate = delegate 42 | 43 | self.manager.loadData() 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ios/Classes/task/FLTInterstitialExpressAdTask.swift: -------------------------------------------------------------------------------- 1 | //// 2 | //// FLTInterstitialExpressAdTask.swift 3 | //// pangle_flutter 4 | //// 5 | //// Created by nullptrX on 2020/8/16. 6 | //// 7 | // 8 | //import BUAdSDK 9 | // 10 | //internal final class FLTInterstitialExpressAdTask: FLTTaskProtocol { 11 | // private var manager: BUNativeExpressInterstitialAd 12 | // private var delegate: BUNativeExpressInterstitialAdDelegate? 13 | // 14 | // internal init(_ manager: BUNativeExpressInterstitialAd) { 15 | // self.manager = manager 16 | // } 17 | // 18 | // convenience init(_ args: [String: Any?]) { 19 | // let slotId: String = args["slotId"] as! String 20 | // let expressArgs = args["expressSize"] as! [String: Double] 21 | // let width = expressArgs["width"]! 22 | // let height = expressArgs["height"]! 23 | //// let width = Double(UIScreen.main.bounds.width) * 0.9 24 | //// let height = width / Double(size.width) * Double(size.height) 25 | // let adSize = CGSize(width: width, height: height) 26 | // let manager = BUNativeExpressInterstitialAd(slotID: slotId, adSize: adSize) 27 | // self.init(manager) 28 | // } 29 | // 30 | // func execute() -> (@escaping (FLTTaskProtocol, Any) -> Void) -> Void { 31 | // { result in 32 | // let delegate = FLTInterstitialExpressAd(success: { [weak self] () in 33 | // guard let self = self else { 34 | // return 35 | // } 36 | // result(self, ["code": 0]) 37 | // }, fail: { [weak self] error in 38 | // guard let self = self else { 39 | // return 40 | // } 41 | // let e = error as NSError? 42 | // result(self, ["code": e?.code ?? -1, "message": error?.localizedDescription ?? ""]) 43 | // }) 44 | // 45 | // self.manager.delegate = delegate 46 | // self.delegate = delegate 47 | // 48 | // self.manager.loadData() 49 | // } 50 | // } 51 | //} 52 | -------------------------------------------------------------------------------- /ios/Classes/task/FLTNativeExpressAdTask.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FLTNativeExpressAdTask.swift 3 | // pangle_flutter 4 | // 5 | // Created by nullptrX on 2020/8/16. 6 | // 7 | 8 | import BUAdSDK 9 | import Foundation 10 | 11 | internal final class FLTNativeExpressAdTask: FLTTaskProtocol { 12 | public let manager: BUNativeExpressAdManager 13 | private var delegate: FLTNativeExpressAdViewDelegate? 14 | private var count: Int 15 | 16 | internal init(manager: BUNativeExpressAdManager, count: Int) { 17 | self.manager = manager 18 | self.count = count 19 | } 20 | 21 | convenience init(_ args: [String: Any?]) { 22 | let slotId: String = args["slotId"] as! String 23 | let count = args["count"] as? Int ?? Constant.kDefaultFeedAdCount 24 | 25 | let expressArgs = args["expressSize"] as! [String: Double] 26 | let width = expressArgs["width"]! 27 | let height = expressArgs["height"]! 28 | 29 | let adSize = CGSize(width: width, height: height) 30 | 31 | let slot = BUAdSlot() 32 | slot.id = slotId 33 | slot.adType = .feed 34 | slot.position = .feed 35 | 36 | let nad = BUNativeExpressAdManager(slot: slot, adSize: adSize) 37 | nad.adSize = adSize 38 | 39 | self.init(manager: nad, count: count) 40 | } 41 | 42 | func execute() -> (@escaping (FLTTaskProtocol, Any) -> Void) -> Void { 43 | return { result in 44 | let delegate = FLTNativeExpressAdViewDelegate(success: { [weak self] data in 45 | guard let self = self else { return } 46 | result(self, ["code": 0, "count": data.count, "data": data] as [String : Any]) 47 | }, fail: { [weak self] error in 48 | guard let self = self else { return } 49 | let e = error as NSError? 50 | result(self, ["code": e?.code ?? -1, "message": error?.localizedDescription ?? "", "count": 0, "data": [] as [Any]] as [String : Any]) 51 | }) 52 | 53 | self.manager.delegate = delegate 54 | self.delegate = delegate 55 | 56 | self.manager.loadAdData(withCount: self.count) 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /ios/Classes/task/FLTRewardedVideoExpressAdTask.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FLTRewardedVideoExpressAdTask.swift 3 | // pangle_flutter 4 | // 5 | // Created by nullptrX on 2020/8/16. 6 | // 7 | 8 | import BUAdSDK 9 | 10 | internal final class FLTRewardedVideoExpressAdTask: FLTTaskProtocol { 11 | private var manager: BUNativeExpressRewardedVideoAd 12 | private var delegate: BUNativeExpressRewardedVideoAdDelegate? 13 | private var slotId: String = "" 14 | 15 | internal init(_ manager: BUNativeExpressRewardedVideoAd) { 16 | self.manager = manager 17 | } 18 | 19 | init(_ args: [String: Any?]) { 20 | slotId = args["slotId"] as! String 21 | let userId: String = args["userId"] as? String ?? "" 22 | let rewardName: String? = args["rewardName"] as? String 23 | let rewardAmount: Int? = args["rewardAmount"] as? Int 24 | let extra: String? = args["extra"] as? String 25 | let model = BURewardedVideoModel() 26 | 27 | model.userId = userId 28 | if rewardName != nil { 29 | model.rewardName = rewardName 30 | } 31 | if rewardAmount != nil { 32 | model.rewardAmount = rewardAmount! 33 | } 34 | if extra != nil { 35 | model.extra = extra 36 | } 37 | let manager = BUNativeExpressRewardedVideoAd(slotID: slotId, rewardedVideoModel: model) 38 | self.manager = manager 39 | } 40 | 41 | func execute(_ loadingType: LoadingType) -> (@escaping (FLTTaskProtocol, Any) -> Void) -> Void { 42 | { [unowned self]result in 43 | let delegate = FLTRewardedVideoExpressAd(slotId, loadingType, success: { [weak self] verify in 44 | guard let self = self else { 45 | return 46 | } 47 | result(self, ["code": 0, "verify": verify] as [String:Any]) 48 | }, fail: { [weak self] error in 49 | guard let self = self else { 50 | return 51 | } 52 | let e = error as NSError? 53 | result(self, ["code": e?.code ?? -1, "message": error?.localizedDescription ?? ""] as [String:Any]) 54 | }) 55 | 56 | manager.delegate = delegate 57 | self.delegate = delegate 58 | 59 | manager.loadData() 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ios/Classes/task/FLTSplashAdTask.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FLTSplashAdTask.swift 3 | // pangle_flutter 4 | // 5 | // Created by nullptrX on 2020/8/16. 6 | // 7 | 8 | import BUAdSDK 9 | 10 | internal final class FLTSplashAdTask: FLTTaskProtocol { 11 | private var manager: BUSplashAd 12 | private var delegate: BUSplashAdDelegate? 13 | 14 | internal init(_ manager: BUSplashAd) { 15 | self.manager = manager 16 | } 17 | 18 | convenience init(_ args: [String: Any?]) { 19 | let slotId: String = args["slotId"] as! String 20 | let tolerateTimeout: Double? = args["tolerateTimeout"] as? Double 21 | let hideSkipButton: Bool? = args["hideSkipButton"] as? Bool 22 | let frame = UIScreen.main.bounds 23 | // BUSplashAdView(slotID: slotId, frame: frame) 24 | let slot = BUAdSlot.init() 25 | slot.id = slotId 26 | let splashAd = BUSplashAd.init(slot: slot, adSize: frame.size) 27 | if tolerateTimeout != nil { 28 | splashAd.tolerateTimeout = tolerateTimeout! 29 | } 30 | if hideSkipButton != nil { 31 | splashAd.hideSkipButton = hideSkipButton! 32 | } 33 | let vc = AppUtil.getVC() 34 | vc.view.addSubview(splashAd.splashView!) 35 | 36 | self.init(splashAd) 37 | } 38 | 39 | func execute() -> (@escaping (FLTTaskProtocol, Any) -> Void) -> Void { 40 | return { result in 41 | let delegate = FLTSplashAd(success: { [weak self] msg, type in 42 | guard let self = self else { return } 43 | result(self, ["code": 0, "message": msg, "type": type] as [String:Any]) 44 | }, fail: { [weak self] error in 45 | guard let self = self else { return } 46 | let e = error as NSError? 47 | result(self, ["code": e?.code ?? -1, "message": error?.localizedDescription ?? "", "type": 0] as [String:Any]) 48 | }) 49 | 50 | self.manager.delegate = delegate 51 | self.delegate = delegate 52 | 53 | self.manager.loadData() 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ios/Classes/task/FLTTaskProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FLTTaskProtocol.swift 3 | // pangle_flutter 4 | // 5 | // Created by nullptrX on 2020/8/16. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol FLTTaskProtocol: AnyObject { 11 | func execute() -> (@escaping (FLTTaskProtocol, Any) -> Void) -> Void 12 | 13 | func execute(_ loadingType: LoadingType) -> (@escaping (FLTTaskProtocol, Any) -> Void) -> Void 14 | } 15 | 16 | extension FLTTaskProtocol { 17 | func execute() -> (@escaping (FLTTaskProtocol, Any) -> Void) -> Void { 18 | { _ in 19 | } 20 | } 21 | 22 | func execute(_ loadingType: LoadingType) -> (@escaping (FLTTaskProtocol, Any) -> Void) -> Void { 23 | { _ in 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ios/Classes/util/AppUtil.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppUtil.swift 3 | // ttad 4 | // 5 | // Created by Jerry on 2020/7/26. 6 | // 7 | 8 | import Foundation 9 | 10 | class AppUtil { 11 | static func getVC() -> UIViewController { 12 | let viewController = UIApplication.shared.windows.filter { (w) -> Bool in 13 | w.isHidden == false 14 | }.first?.rootViewController 15 | // let viewController: UIViewController = (UIApplication.shared.delegate?.window??.rootViewController)! 16 | return viewController! 17 | } 18 | 19 | // static func getKeyWindowVC() -> UIViewController { 20 | // return UIApplication.shared.keyWindow!.rootViewController 21 | // } 22 | 23 | static func getCurrentVC() -> UIViewController? { 24 | let rootViewController = UIApplication.shared.keyWindow?.rootViewController 25 | let currentVC = self.getCurrentVC(from: rootViewController) 26 | return currentVC 27 | } 28 | 29 | static func getCurrentVC(from rootVC: UIViewController?) -> UIViewController? { 30 | var rootVC = rootVC 31 | var currentVC: UIViewController? 32 | if rootVC?.presentedViewController != nil { 33 | // 视图是被presented出来的 34 | rootVC = rootVC?.presentedViewController 35 | } 36 | if rootVC is UITabBarController { 37 | // 根视图为UITabBarController 38 | currentVC = self.getCurrentVC(from: (rootVC as? UITabBarController)?.selectedViewController) 39 | } else if rootVC is UINavigationController { 40 | // 根视图为UINavigationController 41 | currentVC = self.getCurrentVC(from: (rootVC as? UINavigationController)?.visibleViewController) 42 | } else { 43 | // 根视图为非导航类 44 | currentVC = rootVC 45 | } 46 | 47 | return currentVC 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /ios/Classes/util/UIUtil.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIUtil.swift 3 | // pangle_flutter 4 | // 5 | // Created by nullptrX on 2022/10/4. 6 | // 7 | 8 | import Foundation 9 | 10 | // http://jackin.cn/2021/02/01/bytedance-ad-click-penetration-on-flutter.html 11 | class UIUtil { 12 | 13 | static func isOverlay(_ view: UIView?, _ overlayView: UIView?) -> Bool { 14 | guard let v1 = view else { 15 | return false 16 | } 17 | guard let v2 = overlayView else { 18 | return false 19 | } 20 | 21 | if v1.frame.contains(v2.frame) || v2.frame.contains(v1.frame) { 22 | // the view contains overlay, or overlay view contains the view 23 | return true 24 | } 25 | if v1.frame.intersects(v2.frame) || v2.frame.intersects(v1.frame) { 26 | // the view is intersected with overaly 27 | return true 28 | } 29 | 30 | return false 31 | } 32 | 33 | static func findTargetView(_ view: UIView) -> UIView? { 34 | return view.superview?.superview 35 | } 36 | 37 | static func findTargetOverlayView(_ view: UIView, _ point: CGPoint) -> UIView? { 38 | 39 | guard let flutterView: UIView = view.superview?.superview?.superview else { 40 | return nil 41 | } 42 | 43 | if String(describing: flutterView.classForCoder) == "FlutterView" { 44 | for v in flutterView.subviews { 45 | let contains = v.frame.contains(point) 46 | if String(describing: v.classForCoder) == "FlutterOverlayView" && contains { 47 | return v 48 | } 49 | } 50 | } 51 | return nil 52 | } 53 | 54 | 55 | static func removeAllView(_ container: UIView) { 56 | container.subviews.forEach { 57 | $0.subviews.forEach { 58 | $0.removeFromSuperview() 59 | } 60 | $0.removeFromSuperview() 61 | } 62 | container.removeFromSuperview() 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /ios/pangle_flutter.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint pangle_flutter.podspec' to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'pangle_flutter' 7 | s.version = '2.0.1' 8 | s.summary = 'Flutter plugin for Pangle Ad SDK.' 9 | s.description = <<-DESC 10 | Flutter plugin for Pangle Ad SDK. 11 | DESC 12 | s.homepage = 'https://github.com/nullptrx/pangle_flutter' 13 | s.license = { :file => '../LICENSE' } 14 | s.author = { 'nullptrX' => '19757745+nullptrx@users.noreply.github.com' } 15 | s.source = { :path => '.' } 16 | s.source_files = 'Classes/**/*' 17 | s.dependency 'Flutter' 18 | s.platform = :ios, '9.0' 19 | 20 | s.static_framework = true 21 | 22 | # https://cocoapods.org/ 23 | s.ios.dependency 'Ads-CN', '~> 5.1' 24 | 25 | #s.default_subspec = 'cn' 26 | #s.subspec 'cn' do |ss| 27 | # ss.ios.dependency 'Ads-CN', '~> 5.0' 28 | #end 29 | #s.subspec 'global' do |ss| 30 | # ss.ios.dependency 'Ads-Global', '~> 5.0' 31 | #end 32 | 33 | # Flutter.framework does not contain a i386 slice. 34 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } 35 | s.swift_version = '5.0' 36 | end 37 | -------------------------------------------------------------------------------- /lib/pangle_flutter.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | library pangle_flutter; 24 | 25 | export 'src/build.dart'; 26 | export 'src/config_android.dart'; 27 | export 'src/config_ios.dart'; 28 | export 'src/constant.dart'; 29 | export 'src/model.dart'; 30 | export 'src/pangle_plugin.dart'; 31 | export 'src/size.dart'; 32 | export 'src/util.dart'; 33 | export 'src/view/banner/bannerview_android.dart'; 34 | export 'src/view/banner/bannerview_android_legacy.dart'; 35 | export 'src/view/bannerview.dart'; 36 | export 'src/view/feed/feedview_android.dart'; 37 | export 'src/view/feed/feedview_android_legacy.dart'; 38 | export 'src/view/feedview.dart'; 39 | export 'src/view/splash/splashview_android.dart'; 40 | export 'src/view/splash/splashview_android_legacy.dart'; 41 | export 'src/view/splashview.dart'; 42 | -------------------------------------------------------------------------------- /lib/src/build.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | /// Version values of the current Android operating system build derived from 24 | /// `android.os.Build.VERSION`. 25 | /// 26 | /// See: https://developer.android.com/reference/android/os/Build.VERSION.html 27 | class AndroidDeviceInfo { 28 | const AndroidDeviceInfo._({ 29 | this.sdkInt, 30 | }); 31 | 32 | /// The user-visible SDK version of the framework. 33 | /// 34 | /// Possible values are defined in: https://developer.android.com/reference/android/os/Build.VERSION_CODES.html 35 | final int? sdkInt; 36 | 37 | /// Serializes [ AndroidDeviceInfo ] to map. 38 | Map toMap() { 39 | return { 40 | 'sdkInt': sdkInt, 41 | }; 42 | } 43 | 44 | /// Deserializes from the map message. 45 | static AndroidDeviceInfo fromMap(Map map) { 46 | return AndroidDeviceInfo._( 47 | sdkInt: map['sdkInt'], 48 | ); 49 | } 50 | } 51 | 52 | /// Version values of the current IOS operating system build derived from 53 | /// `android.os.Build.VERSION`. 54 | /// 55 | /// See: https://developer.android.com/reference/android/os/Build.VERSION.html 56 | class IOSDeviceInfo { 57 | const IOSDeviceInfo._({ 58 | this.systemVersion, 59 | }); 60 | 61 | /// The current operating system version. 62 | final String? systemVersion; 63 | 64 | /// Serializes [ AndroidDeviceInfo ] to map. 65 | Map toMap() { 66 | return { 67 | 'systemVersion': systemVersion, 68 | }; 69 | } 70 | 71 | /// Deserializes from the map message. 72 | static IOSDeviceInfo fromMap(Map map) { 73 | return IOSDeviceInfo._( 74 | systemVersion: map['systemVersion'], 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/src/config.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | abstract class Config { 24 | /// Convert config to json 25 | Map toJSON() { 26 | return {}; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/src/constant.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | /// log level, only ios works. 24 | enum PangleLogLevel { 25 | none, 26 | error, 27 | debug, 28 | } 29 | 30 | /// Pick image size for ad 31 | /// From [v0.2.0] not works for express ads. 32 | enum PangleImgSize { 33 | banner640_90, 34 | banner640_100, 35 | banner600_150, 36 | banner600_260, 37 | banner600_286, 38 | banner600_300, 39 | banner690_388, 40 | banner600_400, 41 | banner600_500, 42 | feed228_150, 43 | feed690_388, 44 | interstitial600_400, 45 | interstitial600_600, 46 | interstitial600_900, 47 | drawFullScreen, 48 | } 49 | 50 | /// title bar theme for land page, only android works. 51 | enum AndroidTitleBarTheme { 52 | light, 53 | dark, 54 | noTitleBar, 55 | } 56 | 57 | /// available network type for downloading type ad. 58 | class AndroidDirectDownloadNetworkType { 59 | AndroidDirectDownloadNetworkType._(); 60 | 61 | static const int kMobile = 1; 62 | static const int k2G = 2; 63 | static const int k3G = 3; 64 | static const int kWiFi = 4; 65 | static const int k4G = 5; 66 | } 67 | 68 | /// The type of loading ads. 69 | /// 70 | /// [normal] Showing ads up without preloading. 71 | /// [preload] Showing ads up and preloading next ads. 72 | /// [preloadOnly] Preloading ads only, not showing ads up. 73 | enum PangleLoadingType { 74 | normal, 75 | preload, 76 | preloadOnly, 77 | } 78 | 79 | enum PangleOrientation { 80 | // ignore: unused_field 81 | _, 82 | veritical, 83 | horizontal, 84 | } 85 | 86 | enum PangleAuthorizationStatus { 87 | notDetermined, 88 | restricted, 89 | denied, 90 | authorized, 91 | } 92 | 93 | enum PangleTheme { 94 | light, 95 | dark, 96 | } 97 | 98 | enum PangleSplashCloseType { 99 | unknown, 100 | clickSkip, 101 | countDownToZero, 102 | clickAd, 103 | } 104 | -------------------------------------------------------------------------------- /lib/src/extension.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | extension CheckNotNull on String? { 24 | bool get isNotBlank => this?.isNotEmpty ?? false; 25 | } 26 | -------------------------------------------------------------------------------- /lib/src/pangle_event_type.dart: -------------------------------------------------------------------------------- 1 | enum PangleEventType { 2 | interstitial, 3 | fullscreen, 4 | rewardedVideo, 5 | } 6 | -------------------------------------------------------------------------------- /lib/src/size.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | /// default size 24 | const kPangleSize = 1.0; 25 | 26 | /// params contain width, height. 27 | typedef SizeCallback = void Function(Map params); 28 | -------------------------------------------------------------------------------- /lib/src/util.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'package:flutter/widgets.dart'; 24 | 25 | import 'constant.dart'; 26 | 27 | typedef PangleSplashCloseTypeCallback = Function(PangleSplashCloseType type); 28 | 29 | typedef PangleMessageCallback = Function(int code, String message); 30 | 31 | typedef PangleOptionCallback = Function(String message, bool enforce); 32 | 33 | class PangleHelper { 34 | PangleHelper._(); 35 | 36 | static Rect fromRenderBox(RenderBox box) { 37 | final size = box.size; 38 | final offset = box.localToGlobal(Offset.zero); 39 | return Rect.fromLTWH(offset.dx, offset.dy, size.width, size.height); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/src/view/banner/bannerview_android.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'package:flutter/foundation.dart'; 24 | import 'package:flutter/gestures.dart'; 25 | import 'package:flutter/rendering.dart'; 26 | import 'package:flutter/services.dart'; 27 | import 'package:flutter/widgets.dart'; 28 | 29 | import 'bannerview_method_channel.dart'; 30 | import 'platform_interface.dart'; 31 | 32 | class AndroidBannerView implements BannerViewPlatform { 33 | const AndroidBannerView(); 34 | 35 | @override 36 | Widget build({ 37 | required BuildContext context, 38 | required Map creationParams, 39 | required BannerViewPlatformCallbacksHandler 40 | bannerViewPlatformCallbacksHandler, 41 | BannerViewPlatformCreatedCallback? onBannerViewPlatformCreated, 42 | Set>? gestureRecognizers, 43 | }) { 44 | return GestureDetector( 45 | // We prevent text selection by intercepting the long press event. 46 | // This is a temporary stop gap due to issues with text selection on Android: 47 | // https://github.com/flutter/flutter/issues/24585 - the text selection 48 | // dialog is not responding to touch events. 49 | // https://github.com/flutter/flutter/issues/24584 - the text selection 50 | // handles are not showing. 51 | // TODO(amirh): remove this when the issues above are fixed. 52 | onLongPress: () {}, 53 | excludeFromSemantics: true, 54 | child: AndroidView( 55 | viewType: kBannerViewType, 56 | gestureRecognizers: gestureRecognizers ?? 57 | const >{}, 58 | layoutDirection: TextDirection.ltr, 59 | creationParams: creationParams, 60 | creationParamsCodec: const StandardMessageCodec(), 61 | hitTestBehavior: PlatformViewHitTestBehavior.opaque, 62 | onPlatformViewCreated: (id) { 63 | if (onBannerViewPlatformCreated == null) { 64 | return; 65 | } 66 | onBannerViewPlatformCreated(MethodChannelBannerViewPlatform( 67 | id, 68 | bannerViewPlatformCallbacksHandler, 69 | )); 70 | }, 71 | ), 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/src/view/banner/bannerview_ios.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'package:flutter/foundation.dart'; 24 | import 'package:flutter/gestures.dart'; 25 | import 'package:flutter/services.dart'; 26 | import 'package:flutter/widgets.dart'; 27 | 28 | import 'bannerview_method_channel.dart'; 29 | import 'platform_interface.dart'; 30 | 31 | class CupertinoBannerView implements BannerViewPlatform { 32 | const CupertinoBannerView(); 33 | 34 | @override 35 | Widget build({ 36 | BuildContext? context, 37 | Map? creationParams, 38 | required BannerViewPlatformCallbacksHandler 39 | bannerViewPlatformCallbacksHandler, 40 | BannerViewPlatformCreatedCallback? onBannerViewPlatformCreated, 41 | Set>? gestureRecognizers, 42 | }) { 43 | return UiKitView( 44 | viewType: kBannerViewType, 45 | onPlatformViewCreated: (id) { 46 | if (onBannerViewPlatformCreated == null) { 47 | return; 48 | } 49 | onBannerViewPlatformCreated(MethodChannelBannerViewPlatform( 50 | id, bannerViewPlatformCallbacksHandler)); 51 | }, 52 | creationParams: creationParams, 53 | gestureRecognizers: gestureRecognizers, 54 | creationParamsCodec: const StandardMessageCodec(), 55 | // BannerView content is not affected by the Android view's layout direction, 56 | // we explicitly set it here so that the widget doesn't require an ambient 57 | // directionality. 58 | layoutDirection: TextDirection.ltr, 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/src/view/feed/feedview_android.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'package:flutter/foundation.dart'; 24 | import 'package:flutter/gestures.dart'; 25 | import 'package:flutter/rendering.dart'; 26 | import 'package:flutter/services.dart'; 27 | import 'package:flutter/widgets.dart'; 28 | 29 | import 'feedview_method_channel.dart'; 30 | import 'platform_interface.dart'; 31 | 32 | class AndroidFeedView implements FeedViewPlatform { 33 | const AndroidFeedView(); 34 | 35 | @override 36 | Widget build({ 37 | required BuildContext context, 38 | required Map creationParams, 39 | required FeedViewPlatformCallbacksHandler feedViewPlatformCallbacksHandler, 40 | FeedViewPlatformCreatedCallback? onFeedViewPlatformCreated, 41 | Set>? gestureRecognizers, 42 | }) { 43 | return GestureDetector( 44 | // We prevent text selection by intercepting the long press event. 45 | // This is a temporary stop gap due to issues with text selection on Android: 46 | // https://github.com/flutter/flutter/issues/24585 - the text selection 47 | // dialog is not responding to touch events. 48 | // https://github.com/flutter/flutter/issues/24584 - the text selection 49 | // handles are not showing. 50 | // TODO(amirh): remove this when the issues above are fixed. 51 | onLongPress: () {}, 52 | excludeFromSemantics: true, 53 | child: AndroidView( 54 | viewType: kFeedViewType, 55 | gestureRecognizers: gestureRecognizers ?? 56 | const >{}, 57 | layoutDirection: TextDirection.ltr, 58 | creationParams: creationParams, 59 | creationParamsCodec: const StandardMessageCodec(), 60 | hitTestBehavior: PlatformViewHitTestBehavior.opaque, 61 | onPlatformViewCreated: (id) { 62 | if (onFeedViewPlatformCreated == null) { 63 | return; 64 | } 65 | onFeedViewPlatformCreated(MethodChannelFeedViewPlatform( 66 | id, 67 | feedViewPlatformCallbacksHandler, 68 | )); 69 | }, 70 | ), 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lib/src/view/feed/feedview_ios.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'package:flutter/foundation.dart'; 24 | import 'package:flutter/gestures.dart'; 25 | import 'package:flutter/services.dart'; 26 | import 'package:flutter/widgets.dart'; 27 | 28 | import 'feedview_method_channel.dart'; 29 | import 'platform_interface.dart'; 30 | 31 | class CupertinoFeedView implements FeedViewPlatform { 32 | const CupertinoFeedView(); 33 | 34 | @override 35 | Widget build({ 36 | BuildContext? context, 37 | Map? creationParams, 38 | FeedViewPlatformCallbacksHandler? feedViewPlatformCallbacksHandler, 39 | FeedViewPlatformCreatedCallback? onFeedViewPlatformCreated, 40 | Set>? gestureRecognizers, 41 | }) { 42 | return UiKitView( 43 | viewType: kFeedViewType, 44 | onPlatformViewCreated: (id) { 45 | if (onFeedViewPlatformCreated == null) { 46 | return; 47 | } 48 | onFeedViewPlatformCreated(MethodChannelFeedViewPlatform( 49 | id, feedViewPlatformCallbacksHandler!)); 50 | }, 51 | creationParams: creationParams, 52 | gestureRecognizers: gestureRecognizers, 53 | creationParamsCodec: const StandardMessageCodec(), 54 | // FeedView content is not affected by the Android view's layout direction, 55 | // we explicitly set it here so that the widget doesn't require an ambient 56 | // directionality. 57 | layoutDirection: TextDirection.ltr, 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/src/view/feed/feedview_method_channel.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'package:flutter/foundation.dart'; 24 | import 'package:flutter/services.dart'; 25 | 26 | import 'platform_interface.dart'; 27 | 28 | /// A [FeedViewPlatformController] that uses a method channel to control the feedview. 29 | class MethodChannelFeedViewPlatform implements FeedViewPlatformController { 30 | /// Constructs an instance that will listen for webviews broadcasting to the 31 | /// given [id], using the given [FeedViewPlatformCallbacksHandler]. 32 | MethodChannelFeedViewPlatform(int id, this._platformCallbacksHandler) 33 | : _channel = MethodChannel('${kFeedViewType}_$id') { 34 | _channel.setMethodCallHandler(_onMethodCall); 35 | } 36 | 37 | final FeedViewPlatformCallbacksHandler _platformCallbacksHandler; 38 | 39 | final MethodChannel _channel; 40 | 41 | Future _onMethodCall(MethodCall call) async { 42 | switch (call.method) { 43 | case "onClick": 44 | _platformCallbacksHandler.onClick(); 45 | break; 46 | case "onShow": 47 | _platformCallbacksHandler.onShow(); 48 | break; 49 | case "onDislike": 50 | final String option = call.arguments['option']; 51 | final bool enforce = call.arguments['enforce'] ?? false; 52 | _platformCallbacksHandler.onDislike(option, enforce); 53 | break; 54 | case "onRenderSuccess": 55 | _platformCallbacksHandler.onRenderSuccess(); 56 | break; 57 | case "onRenderFail": 58 | final int code = call.arguments['code']; 59 | final String message = call.arguments['message']; 60 | _platformCallbacksHandler.onRenderFail(code, message); 61 | break; 62 | } 63 | } 64 | 65 | @override 66 | Future addTouchableBounds(List bounds) async { 67 | if (defaultTargetPlatform != TargetPlatform.iOS) { 68 | return; 69 | } 70 | final json = >[]; 71 | for (final bound in bounds) { 72 | json.add({ 73 | 'x': bound.left, 74 | 'y': bound.top, 75 | 'w': bound.width, 76 | 'h': bound.height, 77 | }); 78 | } 79 | await _channel.invokeMethod('addTouchableBounds', json); 80 | } 81 | 82 | @override 83 | Future clearTouchableBounds() async { 84 | await _channel.invokeMethod('clearTouchableBounds'); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/src/view/feed/platform_interface.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'package:flutter/foundation.dart'; 24 | import 'package:flutter/gestures.dart'; 25 | import 'package:flutter/widgets.dart'; 26 | 27 | import '../platform_controller.dart'; 28 | 29 | const kFeedViewType = 'nullptrx.github.io/pangle_feedview'; 30 | 31 | abstract class FeedViewPlatform { 32 | Widget build({ 33 | required BuildContext context, 34 | required Map creationParams, 35 | required FeedViewPlatformCallbacksHandler feedViewPlatformCallbacksHandler, 36 | FeedViewPlatformCreatedCallback? onFeedViewPlatformCreated, 37 | Set>? gestureRecognizers, 38 | }); 39 | } 40 | 41 | /// Signature for callbacks reporting that a [FeedViewPlatformController] was created. 42 | /// 43 | /// See also the `onFeedViewPlatformCreated` argument for [FeedViewPlatform.build]. 44 | typedef FeedViewPlatformCreatedCallback = void Function( 45 | FeedViewPlatformController feedViewPlatformController); 46 | 47 | /// Interface for talking to the feedview's platform implementation. 48 | /// 49 | /// An instance implementing this interface is passed to the `onFeedViewPlatformCreated` callback that is 50 | /// passed to [FeedViewPlatformBuilder#onFeedViewPlatformCreated]. 51 | /// 52 | /// Platform implementations that live in a separate package should extend this class rather than 53 | /// implement it as pangle_flutter does not consider newly added methods to be breaking changes. 54 | /// Extending this class (using `extends`) ensures that the subclass will get the default 55 | /// implementation, while platform implementations that `implements` this interface will be broken 56 | /// by newly added [FeedViewPlatformController] methods. 57 | abstract class FeedViewPlatformController implements PlatformController {} 58 | 59 | /// Interface for callbacks made by [FeedViewPlatformController]. 60 | /// 61 | /// The feedview plugin implements this class, and passes an instance to the [FeedViewPlatformController]. 62 | /// [FeedViewPlatformController] is notifying this handler on events that happened on the platform's feedview. 63 | abstract class FeedViewPlatformCallbacksHandler { 64 | void onClick(); 65 | 66 | void onShow(); 67 | 68 | void onRenderSuccess(); 69 | 70 | void onRenderFail(int code, String message); 71 | 72 | /// [option] 73 | /// [enforce] 当enforce参数返回true时,代表穿山甲会主动关闭掉广告,广告移除后需要开发者对界面进行适配。 74 | void onDislike(String option, bool enforce); 75 | } 76 | -------------------------------------------------------------------------------- /lib/src/view/splash/platform_interface.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'package:flutter/foundation.dart'; 24 | import 'package:flutter/gestures.dart'; 25 | import 'package:flutter/widgets.dart'; 26 | 27 | import '../../constant.dart'; 28 | import '../platform_controller.dart'; 29 | 30 | const kSplashViewType = 'nullptrx.github.io/pangle_splashview'; 31 | 32 | abstract class SplashViewPlatform { 33 | Widget build({ 34 | required BuildContext context, 35 | required Map creationParams, 36 | required SplashViewPlatformCallbacksHandler 37 | splashViewPlatformCallbacksHandler, 38 | SplashViewPlatformCreatedCallback? onSplashViewPlatformCreated, 39 | Set>? gestureRecognizers, 40 | }); 41 | } 42 | 43 | /// Signature for callbacks reporting that a [SplashViewPlatformController] was created. 44 | /// 45 | /// See also the `onSplashViewPlatformCreated` argument for [SplashViewPlatform.build]. 46 | typedef SplashViewPlatformCreatedCallback = void Function( 47 | SplashViewPlatformController splashViewPlatformController); 48 | 49 | /// Interface for talking to the splashview's platform implementation. 50 | /// 51 | /// An instance implementing this interface is passed to the `onSplashViewPlatformCreated` callback that is 52 | /// passed to [SplashViewPlatformBuilder#onSplashViewPlatformCreated]. 53 | /// 54 | /// Platform implementations that live in a separate package should extend this class rather than 55 | /// implement it as pangle_flutter does not consider newly added methods to be breaking changes. 56 | /// Extending this class (using `extends`) ensures that the subclass will get the default 57 | /// implementation, while platform implementations that `implements` this interface will be broken 58 | /// by newly added [SplashViewPlatformController] methods. 59 | abstract class SplashViewPlatformController implements PlatformController {} 60 | 61 | /// Interface for callbacks made by [SplashViewPlatformController]. 62 | /// 63 | /// The splashview plugin implements this class, and passes an instance to the [SplashViewPlatformController]. 64 | /// [SplashViewPlatformController] is notifying this handler on events that happened on the platform's splashview. 65 | abstract class SplashViewPlatformCallbacksHandler { 66 | void onLoad(); 67 | 68 | void onShow(); 69 | 70 | void onClick(); 71 | 72 | void onClose(PangleSplashCloseType type); 73 | 74 | void onError(int code, String message); 75 | } 76 | -------------------------------------------------------------------------------- /lib/src/view/splash/splashview_android.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'package:flutter/foundation.dart'; 24 | import 'package:flutter/gestures.dart'; 25 | import 'package:flutter/rendering.dart'; 26 | import 'package:flutter/services.dart'; 27 | import 'package:flutter/widgets.dart'; 28 | 29 | import 'platform_interface.dart'; 30 | import 'splashview_method_channel.dart'; 31 | 32 | class AndroidSplashView implements SplashViewPlatform { 33 | const AndroidSplashView(); 34 | 35 | @override 36 | Widget build({ 37 | required BuildContext context, 38 | required Map creationParams, 39 | required SplashViewPlatformCallbacksHandler 40 | splashViewPlatformCallbacksHandler, 41 | SplashViewPlatformCreatedCallback? onSplashViewPlatformCreated, 42 | Set>? gestureRecognizers, 43 | }) { 44 | return GestureDetector( 45 | // We prevent text selection by intercepting the long press event. 46 | // This is a temporary stop gap due to issues with text selection on Android: 47 | // https://github.com/flutter/flutter/issues/24585 - the text selection 48 | // dialog is not responding to touch events. 49 | // https://github.com/flutter/flutter/issues/24584 - the text selection 50 | // handles are not showing. 51 | // TODO(amirh): remove this when the issues above are fixed. 52 | onLongPress: () {}, 53 | excludeFromSemantics: true, 54 | child: AndroidView( 55 | viewType: kSplashViewType, 56 | gestureRecognizers: gestureRecognizers ?? 57 | const >{}, 58 | layoutDirection: TextDirection.ltr, 59 | creationParams: creationParams, 60 | creationParamsCodec: const StandardMessageCodec(), 61 | hitTestBehavior: PlatformViewHitTestBehavior.opaque, 62 | onPlatformViewCreated: (id) { 63 | if (onSplashViewPlatformCreated == null) { 64 | return; 65 | } 66 | onSplashViewPlatformCreated(MethodChannelSplashViewPlatform( 67 | id, 68 | splashViewPlatformCallbacksHandler, 69 | )); 70 | }, 71 | ), 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/src/view/splash/splashview_ios.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'package:flutter/foundation.dart'; 24 | import 'package:flutter/gestures.dart'; 25 | import 'package:flutter/services.dart'; 26 | import 'package:flutter/widgets.dart'; 27 | 28 | import 'platform_interface.dart'; 29 | import 'splashview_method_channel.dart'; 30 | 31 | class CupertinoSplashView implements SplashViewPlatform { 32 | const CupertinoSplashView(); 33 | 34 | @override 35 | Widget build({ 36 | BuildContext? context, 37 | Map? creationParams, 38 | SplashViewPlatformCallbacksHandler? splashViewPlatformCallbacksHandler, 39 | SplashViewPlatformCreatedCallback? onSplashViewPlatformCreated, 40 | Set>? gestureRecognizers, 41 | }) { 42 | return UiKitView( 43 | viewType: kSplashViewType, 44 | onPlatformViewCreated: (id) { 45 | if (onSplashViewPlatformCreated == null) { 46 | return; 47 | } 48 | onSplashViewPlatformCreated(MethodChannelSplashViewPlatform( 49 | id, splashViewPlatformCallbacksHandler!)); 50 | }, 51 | creationParams: creationParams, 52 | gestureRecognizers: gestureRecognizers, 53 | creationParamsCodec: const StandardMessageCodec(), 54 | // SplashView content is not affected by the Android view's layout direction, 55 | // we explicitly set it here so that the widget doesn't require an ambient 56 | // directionality. 57 | layoutDirection: TextDirection.ltr, 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/src/view/splash/splashview_method_channel.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 nullptrX 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import 'package:flutter/foundation.dart'; 24 | import 'package:flutter/services.dart'; 25 | 26 | import '../../constant.dart'; 27 | import 'platform_interface.dart'; 28 | 29 | /// A [SplashViewPlatformController] that uses a method channel to control the splashview. 30 | class MethodChannelSplashViewPlatform implements SplashViewPlatformController { 31 | /// Constructs an instance that will listen for webviews broadcasting to the 32 | /// given [id], using the given [SplashViewPlatformCallbacksHandler]. 33 | MethodChannelSplashViewPlatform(int id, this._platformCallbacksHandler) 34 | : _channel = MethodChannel('${kSplashViewType}_$id') { 35 | _channel.setMethodCallHandler(_onMethodCall); 36 | } 37 | 38 | final SplashViewPlatformCallbacksHandler _platformCallbacksHandler; 39 | 40 | final MethodChannel _channel; 41 | 42 | Future _onMethodCall(MethodCall call) async { 43 | switch (call.method) { 44 | case "onLoad": 45 | _platformCallbacksHandler.onLoad(); 46 | break; 47 | case "onShow": 48 | _platformCallbacksHandler.onShow(); 49 | break; 50 | case "onClick": 51 | _platformCallbacksHandler.onClick(); 52 | break; 53 | case "onClose": 54 | final int type = call.arguments['type'] ?? 0; 55 | _platformCallbacksHandler.onClose(PangleSplashCloseType.values[type]); 56 | break; 57 | case "onError": 58 | final int code = call.arguments['code']; 59 | final String message = call.arguments['message']; 60 | _platformCallbacksHandler.onError(code, message); 61 | break; 62 | } 63 | } 64 | 65 | @override 66 | Future addTouchableBounds(List bounds) async { 67 | if (defaultTargetPlatform != TargetPlatform.iOS) { 68 | return; 69 | } 70 | final json = >[]; 71 | for (final bound in bounds) { 72 | json.add({ 73 | 'x': bound.left, 74 | 'y': bound.top, 75 | 'w': bound.width, 76 | 'h': bound.height, 77 | }); 78 | } 79 | await _channel.invokeMethod('addTouchableBounds', json); 80 | } 81 | 82 | @override 83 | Future clearTouchableBounds() async { 84 | await _channel.invokeMethod('clearTouchableBounds'); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: pangle_flutter 2 | description: A Flutter plugin that supports ByteDance Pangle SDK on Android and iOS. Such as Splash Ads, Rewarded Video Ads, etc. 3 | homepage: https://github.com/nullptrx/pangle_flutter 4 | repository: https://github.com/nullptrx/pangle_flutter 5 | issue_tracker: https://github.com/nullptrx/pangle_flutter/issues 6 | version: 2.0.1 7 | 8 | environment: 9 | sdk: '>=3.0.5 <4.0.0' 10 | flutter: ">=3.3.0" 11 | 12 | dependencies: 13 | flutter: 14 | sdk: flutter 15 | 16 | dev_dependencies: 17 | flutter_lints: ^2.0.1 18 | flutter_test: 19 | sdk: flutter 20 | 21 | flutter: 22 | plugin: 23 | platforms: 24 | android: 25 | package: io.github.nullptrx.pangleflutter 26 | pluginClass: PangleFlutterPlugin 27 | ios: 28 | pluginClass: PangleFlutterPlugin 29 | 30 | -------------------------------------------------------------------------------- /test/pangle_flutter_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | void main() { 5 | const MethodChannel channel = MethodChannel('nullptrx.github.io/pangle'); 6 | 7 | TestWidgetsFlutterBinding.ensureInitialized(); 8 | 9 | setUp(() { 10 | TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger 11 | .setMockMethodCallHandler( 12 | channel, 13 | (MethodCall methodCall) async { 14 | return '42'; 15 | }, 16 | ); 17 | }); 18 | 19 | tearDown(() { 20 | TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger 21 | .setMockMethodCallHandler(channel, null); 22 | }); 23 | 24 | test('getSdkVersion', () async { 25 | 26 | }); 27 | } 28 | --------------------------------------------------------------------------------