├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── android ├── .gitignore ├── build.gradle ├── gradle.properties ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── kotlin │ └── com │ │ └── cjx │ │ └── x5_webview │ │ ├── JavascriptChannel.kt │ │ ├── X5WebView.kt │ │ ├── X5WebViewActivity.kt │ │ ├── X5WebViewFactory.kt │ │ └── X5WebViewPlugin.kt │ └── res │ └── xml │ ├── network_config.xml │ └── x5webview_file_paths.xml ├── example ├── .gitignore ├── .metadata ├── README.md ├── android │ ├── app │ │ ├── build.gradle │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── local.properties │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── cjx │ │ │ │ │ └── x5_webview_example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── key.properties │ └── settings.gradle ├── assets │ └── index.html ├── ios │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ └── contents.xcworkspacedata │ └── 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 │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── lib │ ├── demo.dart │ └── main.dart ├── pubspec.lock └── pubspec.yaml ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── SwiftX5WebviewPlugin.swift │ ├── X5WebviewPlugin.h │ └── X5WebviewPlugin.m └── x5_webview.podspec ├── lib ├── x5_sdk.dart └── x5_webview.dart ├── pubspec.lock ├── pubspec.yaml ├── test └── x5_webview_test.dart └── x5_webview.iml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | .flutter-plugins-dependencies 9 | ios/Flutter/flutter_export_environment.sh 10 | -------------------------------------------------------------------------------- /.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: 0ba67226ee62d6c9d663a6f8410fb4b2f1076046 8 | channel: dev 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.4.0 2 | 3 | * 升级x5 sdk,删除文件预览等不支持功能 4 | * 升级flutter到3.13.0 5 | * 升级其他插件以及kotlin等 6 | * 视频全屏处理 7 | 8 | ## 0.3.4 9 | 10 | * 删除旧版集成方式 11 | * 新增html选择文件功能 12 | 13 | ## 0.3.3 14 | 15 | * 内嵌调整 16 | 17 | ## 0.3.2 18 | 19 | * 增加domStorageEnabled 20 | * 调整demo 21 | 22 | 23 | ## 0.3.1 24 | 25 | * 升级kotlin 26 | * 升级x5sdk 27 | 28 | 29 | ## 0.3.0 30 | 31 | * 支持空安全 32 | * 升级android插件 33 | * 升级x5sdk 34 | 35 | 36 | ## 0.2.4 37 | 38 | * 增加内嵌UA和header,url拦截相关api 39 | 40 | ## 0.2.3 41 | 42 | * 删除内嵌多余代码 43 | * 使用AndroidViewSurface解决内嵌的x5webview键盘问题 44 | 45 | ## 0.2.2 46 | 47 | * X5WebviewActivity新增header和url拦截 48 | * X5WebView addJavascriptChannels变更 49 | * demo增加动态权限、actionBar样式修改以及内核状态查看 50 | 51 | ## 0.2.1 52 | 53 | * 兼容新旧两种插件api(详情见manifest和MainActivity的注释) 54 | 55 | ## 0.2.0 56 | 57 | * kotlin版本和gradle版本升级 58 | * x5sdk升级,使用gradle集成sdk,去除so文件。支持64位手机(无需繁琐操作) 59 | 60 | ## 0.1.5 61 | 62 | * 增加本地html加载示例 63 | * sdk升级,添加文件权限 64 | * 修复部分bug 65 | 66 | ## 0.1.4 67 | 68 | * 增加文件打开功能 69 | * 内嵌webview增加接口isX5WebViewLoadSuccess 70 | 71 | ## 0.1.3 72 | 73 | * actionBar获取异常处理 74 | * 增加js调用flutter功能 75 | * 内核增加下载安装监听 76 | 77 | ## 0.1.2 78 | 79 | * 修复编译出错的问题 80 | 81 | ## 0.1.1 82 | 83 | * 调整示例app,修复loadUrl报错等问题 84 | 85 | ## 0.1.0 86 | 87 | * 修改部分代码,提升pub分数 88 | 89 | ## 0.0.1 90 | 91 | * 集成android x5内核webview 92 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [2022] [cjx] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # x5_webview [![pub package](https://img.shields.io/pub/v/x5_webview.svg)](https://pub.flutter-io.cn/packages/x5_webview) 2 | 3 | * 一个基于腾讯x5引擎的webview的flutter插件,暂时只支持android使用 4 | * 提示:之前内嵌webview出现的一系列问题得到解决,请更新到最新版试用,谢谢支持。 5 | 6 | ## x5内核介绍 7 | 8 | * [x5内核](https://x5.tencent.com/tbs/product/tbs.html),腾讯为改善移动端web体验的一种内核架构。加载更快,更省流量,视频播放优化,文件助手等等 9 | 10 | ## 快速集成 11 | [![pub package](https://img.shields.io/pub/v/x5_webview.svg)](https://pub.flutter-io.cn/packages/x5_webview) 12 | 13 | [pub地址](https://pub.flutter-io.cn/packages/x5_webview) 14 | 15 | * pubspec.yaml文件添加 16 | ``` 17 | dependencies: 18 | x5_webview: ^x.x.x //最新版本见上方 19 | ``` 20 | 21 | * 初始化x5。(安卓6.0+需在init之前请求动态权限,可以使用[permission_handler](https://pub.flutter-io.cn/packages/permission_handler),详情见example/lib/main.dart) 22 | ``` 23 | //请求权限 24 | Map statuses = await [ 25 | Permission.phone, 26 | Permission.storage, 27 | ].request(); 28 | //判断权限 29 | if (!(statuses[Permission.phone].isGranted && 30 | statuses[Permission.storage].isGranted)) { 31 | print("权限被拒绝"); 32 | return; 33 | } 34 | 35 | var isOk = await X5Sdk.init(); 36 | print(isOk ? "X5内核成功加载" : "X5内核加载失败"); 37 | ``` 38 | 39 | * 如果你只是想要简单的展示web页面,可使用以下代码直接打开一个webActivity,性能更佳(推荐使用,视频播放也可以这个api) 40 | ``` 41 | X5Sdk.openWebActivity("https://www.baidu.com",title: "web页面"); 42 | ``` 43 | 44 | ## 使用内嵌webview 45 | 46 | ``` 47 | return Scaffold( 48 | appBar: AppBar( 49 | title: Text("X5WebView示例"), 50 | ), 51 | body: defaultTargetPlatform == TargetPlatform.android 52 | ? X5WebView( 53 | url: "http://debugtbs.qq.com", 54 | javaScriptEnabled: true, 55 | header: {"TestHeader": "测试"}, 56 | userAgentString: "my_ua", 57 | //Url拦截,传null不会拦截会自动跳转 58 | onUrlLoading: (willLoadUrl) { 59 | _controller.loadUrl(willLoadUrl); 60 | } 61 | onWebViewCreated: (control) { 62 | _controller = control; 63 | }, 64 | onPageFinished: () async { 65 | var url = await _controller.currentUrl(); 66 | print(url); 67 | var body = await _controller 68 | .evaluateJavascript('document.body.innerHTML'); 69 | print(body); 70 | }, 71 | ) 72 | : 73 | //可替换为其他已实现ios webview,此处使用webview_flutter 74 | WebView( 75 | initialUrl: "https://www.baidu.com", 76 | javascriptMode: JavascriptMode.unrestricted, 77 | onWebViewCreated: (control) { 78 | _otherController = control; 79 | var body = _otherController 80 | .evaluateJavascript('document.body.innerHTML'); 81 | print(body); 82 | }, 83 | ), 84 | ); 85 | ``` 86 | ## 内嵌webview js与flutter互调 87 | 88 | ### flutter调用js 89 | ``` 90 | var body = await _controller.evaluateJavascript("document.body.innerHTML"); 91 | ``` 92 | ### js调用flutter 93 | * flutter代码 94 | ``` 95 | X5WebView( 96 | ... 97 | javascriptChannels: JavascriptChannels( 98 | ["X5Web", "Toast"], (name, data) { 99 | switch (name) { 100 | ... 101 | } 102 | })) 103 | ``` 104 | * js代码 105 | ``` 106 | X5Web.postMessage("XXX") 107 | Toast.postMessage("YYY") 108 | ``` 109 | 110 | ## 打开本地html文件(使用assets文件,内嵌webview同理) 111 | ``` 112 | var fileS = await rootBundle.loadString("assets/index.html"); 113 | var url = Uri.dataFromString(fileS, 114 | mimeType: 'text/html', 115 | encoding: Encoding.getByName('utf-8')) 116 | .toString(); 117 | X5Sdk.openWebActivity(url, title: "本地html示例"); 118 | ``` 119 | 120 | ## 注意事项 121 | * minSdkVersion 19以上 122 | * 该插件暂时只支持Android手机,IOS会使用无效。ios可使用[webview_flutter](https://pub.flutter-io.cn/packages/webview_flutter)或其他已实现IOS WKWebView插件 123 | * 一般手机安装了QQ,微信,QQ浏览器等软件,手机里自动会有X5内核,如果没有X5内核会在wifi下自动下载,X5内核没有加载成功会自动使用系统内核[官网说明](https://x5.tencent.com/tbs/technical.html#/list/sdk/916172a5-f14e-40ed-9915-eaf74e9acba8/%E5%8A%A0%E8%BD%BD%E7%B1%BB)。详细配置可用手机打开以下链接查看X5内核的详情 124 | ``` 125 | http://debugtbs.qq.com 126 | ``` 127 | * 请使用真机测试,模拟器可能不能正常显示 128 | 129 | * 如果测试正常加载,打包后不能加载,可以尝试使用android studio打开android目录直接打包apk。或者使用以下命令行打包 130 | ``` 131 | flutter build apk --target-platform android-arm --no-shrink 132 | ``` 133 | 并增加混淆配置(详见example) 134 | ``` 135 | -dontwarn dalvik.** 136 | -dontwarn com.tencent.smtt.** 137 | 138 | -keep class com.tencent.smtt.** { 139 | *; 140 | } 141 | 142 | -keep class com.tencent.tbs.** { 143 | *; 144 | } 145 | ``` 146 | 147 | 148 | * android9.0版本webview联不了网在manifest添加 149 | ``` 150 | 153 | 154 | ``` 155 | * android7.0读写文件需要在manifest的application内添加(xml文件已在插件内,无需自己创建) 156 | ``` 157 | 158 | 163 | 166 | 167 | ``` 168 | 169 | * X5Sdk.openWebActivity actionbar颜色自定义 170 | ``` 171 | //1. 172 | implementation "androidx.appcompat:appcompat:1.1.0" 173 | 174 | //2. 175 | 183 | 184 | //3. 185 | 188 | 189 | ``` 190 | 191 | * 有比较急的问题可以加我QQ:793710663 192 | 193 | ## 示例程序下载 194 | 195 | [apk下载](https://www.pgyer.com/x5_webview) 196 | 197 | ![二维码](https://www.pgyer.com/app/qrcode/x5_webview) -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'com.cjx.x5_webview' 2 | version '1.0-SNAPSHOT' 3 | 4 | buildscript { 5 | ext.kotlin_version = '1.7.22' 6 | repositories { 7 | maven { url 'https://maven.aliyun.com/repository/google' } 8 | maven { url 'https://maven.aliyun.com/repository/central' } 9 | maven { url 'https://maven.aliyun.com/repository/public' } 10 | maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } 11 | } 12 | 13 | dependencies { 14 | classpath 'com.android.tools.build:gradle:7.1.2' 15 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 16 | } 17 | } 18 | 19 | rootProject.allprojects { 20 | repositories { 21 | maven { url 'https://maven.aliyun.com/repository/google' } 22 | maven { url 'https://maven.aliyun.com/repository/central' } 23 | maven { url 'https://maven.aliyun.com/repository/public' } 24 | maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } 25 | } 26 | // project.configurations.all { 27 | // resolutionStrategy.eachDependency { details -> 28 | // if (details.requested.group == 'androidx.core') { 29 | // details.useVersion "1.0.1" 30 | // } 31 | // if (details.requested.group == 'androidx.lifecycle') { 32 | // details.useVersion "2.0.0" 33 | // } 34 | // if (details.requested.group == 'androidx.versionedparcelable') { 35 | // details.useVersion "1.0.0" 36 | // } 37 | // } 38 | // } 39 | } 40 | 41 | apply plugin: 'com.android.library' 42 | apply plugin: 'kotlin-android' 43 | 44 | android { 45 | compileSdkVersion 32 46 | 47 | compileOptions { 48 | sourceCompatibility JavaVersion.VERSION_1_8 49 | targetCompatibility JavaVersion.VERSION_1_8 50 | } 51 | 52 | kotlinOptions { 53 | jvmTarget = '1.8' 54 | } 55 | 56 | sourceSets { 57 | main.java.srcDirs += 'src/main/kotlin' 58 | } 59 | 60 | defaultConfig { 61 | minSdkVersion 19 62 | } 63 | } 64 | 65 | dependencies { 66 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 67 | api 'com.tencent.tbs:tbssdk:44286' 68 | } 69 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'x5_webview' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 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 | 38 | 44 | 45 | 46 | 47 | 48 | 49 | 53 | 54 | 59 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /android/src/main/kotlin/com/cjx/x5_webview/JavascriptChannel.kt: -------------------------------------------------------------------------------- 1 | package com.cjx.x5_webview 2 | 3 | import android.content.Context 4 | import android.os.Handler 5 | import android.os.Looper 6 | import android.webkit.JavascriptInterface 7 | import io.flutter.plugin.common.MethodChannel 8 | 9 | class JavascriptChannel(private val name: String, private val channel: MethodChannel, private val context: Context?) { 10 | 11 | @JavascriptInterface 12 | fun postMessage(msg: String) { 13 | val postRunnable = Runnable { 14 | val arg = hashMapOf() 15 | arg["name"] = name 16 | arg["msg"] = msg 17 | channel.invokeMethod("onJavascriptChannelCallBack", arg) 18 | } 19 | val handler = Handler(context?.mainLooper!!) 20 | 21 | if (handler.looper == Looper.myLooper()) { 22 | postRunnable.run() 23 | } else { 24 | handler.post(postRunnable) 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/com/cjx/x5_webview/X5WebView.kt: -------------------------------------------------------------------------------- 1 | package com.cjx.x5_webview 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.net.Uri 7 | import android.util.Log 8 | import android.view.View 9 | import android.view.WindowManager 10 | import android.view.WindowManager.LayoutParams 11 | import com.tencent.smtt.export.external.interfaces.IX5WebChromeClient 12 | import com.tencent.smtt.export.external.interfaces.WebResourceRequest 13 | import com.tencent.smtt.sdk.ValueCallback 14 | import com.tencent.smtt.sdk.WebChromeClient 15 | import com.tencent.smtt.sdk.WebView 16 | import com.tencent.smtt.sdk.WebViewClient 17 | import io.flutter.plugin.common.BinaryMessenger 18 | import io.flutter.plugin.common.MethodCall 19 | import io.flutter.plugin.common.MethodChannel 20 | import io.flutter.plugin.platform.PlatformView 21 | 22 | 23 | class X5WebView(private val context: Activity?, private val id: Int, private val params: Map, private val messenger: BinaryMessenger?, private val containerView: View?) : PlatformView, MethodChannel.MethodCallHandler { 24 | private var webView: WebView = WebView(context) 25 | private val channel: MethodChannel = MethodChannel(messenger!!, "com.cjx/x5WebView_$id") 26 | // private var openFileCallback:ValueCallback? = null 27 | 28 | init { 29 | channel.setMethodCallHandler(this) 30 | webView.apply { 31 | // settings.useWideViewPort = true 32 | settings.javaScriptEnabled = params["javaScriptEnabled"] as Boolean 33 | settings.domStorageEnabled = params["domStorageEnabled"] as Boolean 34 | settings.allowFileAccess=true 35 | settings.databaseEnabled=true 36 | 37 | // settings.javaScriptCanOpenWindowsAutomatically = true 38 | // settings.layoutAlgorithm=LayoutAlgorithm.SINGLE_COLUMN 39 | 40 | if (params["javascriptChannels"] != null) { 41 | val names = params["javascriptChannels"] as List 42 | for (name in names) { 43 | webView.addJavascriptInterface(JavascriptChannel(name, channel, context), name) 44 | } 45 | } 46 | if (params["header"] != null) { 47 | val header = params["header"] as Map 48 | loadUrl(params["url"].toString(), header) 49 | } else { 50 | loadUrl(params["url"].toString()) 51 | } 52 | 53 | if(params["userAgentString"] != null){ 54 | settings.userAgentString=params["userAgentString"].toString() 55 | } 56 | 57 | val urlInterceptEnabled = params["urlInterceptEnabled"] as Boolean 58 | 59 | webViewClient = object : WebViewClient() { 60 | override fun shouldOverrideUrlLoading(view: WebView, loadUrl: String?): Boolean { 61 | 62 | if (urlInterceptEnabled) { 63 | val arg = hashMapOf() 64 | arg["url"] = loadUrl?:"" 65 | channel.invokeMethod("onUrlLoading", arg) 66 | return true 67 | } 68 | view.loadUrl(url) 69 | return super.shouldOverrideUrlLoading(view, url) 70 | } 71 | 72 | override fun shouldOverrideUrlLoading(view: WebView, requset: WebResourceRequest?): Boolean { 73 | if (urlInterceptEnabled) { 74 | val arg = hashMapOf() 75 | arg["url"] = requset?.url.toString() 76 | channel.invokeMethod("onUrlLoading", arg) 77 | return true 78 | } 79 | view.loadUrl(requset?.url.toString()) 80 | return super.shouldOverrideUrlLoading(view, requset) 81 | } 82 | 83 | override fun onPageFinished(p0: WebView?, url: String) { 84 | super.onPageFinished(p0, url) 85 | //向flutter通信 86 | val arg = hashMapOf() 87 | arg["url"] = url 88 | channel.invokeMethod("onPageFinished", arg) 89 | } 90 | 91 | } 92 | 93 | val windowManager= context.getSystemService(Context.WINDOW_SERVICE) as WindowManager 94 | var fullView:View?=null 95 | 96 | webChromeClient = object : WebChromeClient() { 97 | override fun onShowCustomView(view: View?, call: IX5WebChromeClient.CustomViewCallback?) { 98 | super.onShowCustomView(view, call) 99 | // view 为内核生成的全屏视图,需要添加到相应的布局位置(如:全屏幕) 100 | // customViewCallback 用于主动控制全屏退出 101 | if(view!=null){ 102 | windowManager.addView(view,LayoutParams(LayoutParams.TYPE_APPLICATION)) 103 | view.systemUiVisibility = (View.SYSTEM_UI_FLAG_LOW_PROFILE 104 | or View.SYSTEM_UI_FLAG_FULLSCREEN 105 | or View.SYSTEM_UI_FLAG_LAYOUT_STABLE 106 | or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 107 | or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) 108 | fullView=view 109 | } 110 | 111 | channel.invokeMethod("onShowCustomView", null) 112 | } 113 | 114 | override fun onHideCustomView() { 115 | super.onHideCustomView() 116 | //退出全屏 117 | if(fullView!=null){ 118 | windowManager.removeView(fullView) 119 | fullView=null 120 | } 121 | 122 | channel.invokeMethod("onHideCustomView", null) 123 | } 124 | 125 | override fun onProgressChanged(p0: WebView?, p1: Int) { 126 | super.onProgressChanged(p0, p1) 127 | //加载进度 128 | val arg = hashMapOf() 129 | arg["progress"] = p1 130 | channel.invokeMethod("onProgressChanged", arg) 131 | } 132 | 133 | override fun openFileChooser(p0: ValueCallback?, p1: String?, p2: String?) { 134 | val act=context as Activity 135 | X5WebViewPlugin.chooserCallback=p0 136 | 137 | Log.e("--cjx","p1:$p1 --- p2:$p2") 138 | 139 | act.startActivityForResult(Intent(Intent.ACTION_PICK).apply { 140 | type=p1 141 | } 142 | ,21211) 143 | 144 | } 145 | 146 | } 147 | 148 | // val data= Bundle() 149 | //true表示标准全屏,false表示X5全屏;不设置默认false, 150 | // data.putBoolean("standardFullScreen",true) 151 | //false:关闭小窗;true:开启小窗;不设置默认true, 152 | // data.putBoolean("supportLiteWnd",false) 153 | //1:以页面内开始播放,2:以全屏开始播放;不设置默认:1 154 | // data.putInt("DefaultVideoScreen",2) 155 | // x5WebViewExtension.invokeMiscMethod("setVideoParams",data) 156 | } 157 | 158 | } 159 | 160 | 161 | override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { 162 | when (call.method) { 163 | "loadUrl" -> { 164 | Log.e("cjxaaloadurl",call.arguments.toString()) 165 | val arg = call.arguments as Map 166 | val url = arg["url"].toString() 167 | val headers = arg["headers"] as? Map 168 | webView.loadUrl(url, headers) 169 | result.success(null) 170 | } 171 | "canGoBack" -> { 172 | result.success(webView.canGoBack()) 173 | } 174 | "canGoForward" -> { 175 | result.success(webView.canGoForward()) 176 | } 177 | "goBack" -> { 178 | webView.goBack() 179 | result.success(null) 180 | } 181 | "goForward" -> { 182 | webView.goForward() 183 | result.success(null) 184 | } 185 | "goBackOrForward" -> { 186 | val arg = call.arguments as Map 187 | val point = arg["i"] as Int 188 | webView.goBackOrForward(point) 189 | result.success(null) 190 | } 191 | "reload" -> { 192 | webView.reload() 193 | result.success(null) 194 | } 195 | "currentUrl" -> { 196 | result.success(webView.url) 197 | } 198 | "evaluateJavascript" -> { 199 | val arg = call.arguments as Map 200 | val js = arg["js"].toString() 201 | webView.evaluateJavascript(js) { value -> result.success(value) } 202 | } 203 | 204 | "addJavascriptChannels" -> { 205 | val arg = call.arguments as Map 206 | val names = arg["names"] as List 207 | for (name in names) { 208 | webView.addJavascriptInterface(JavascriptChannel(name, channel, context), name) 209 | } 210 | webView.reload() 211 | result.success(null) 212 | 213 | } 214 | "isX5WebViewLoadSuccess" -> { 215 | val exception = webView.x5WebViewExtension 216 | if (exception == null) { 217 | result.success(false) 218 | } else { 219 | result.success(true) 220 | } 221 | } 222 | 223 | else -> { 224 | result.notImplemented() 225 | } 226 | } 227 | } 228 | 229 | override fun getView(): View { 230 | return webView 231 | } 232 | 233 | override fun dispose() { 234 | channel.setMethodCallHandler(null) 235 | webView.destroy() 236 | } 237 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/com/cjx/x5_webview/X5WebViewActivity.kt: -------------------------------------------------------------------------------- 1 | package com.cjx.x5_webview 2 | 3 | import android.app.Activity 4 | import android.content.Intent 5 | import android.graphics.PixelFormat 6 | import android.net.Uri 7 | import android.os.Build 8 | import android.os.Bundle 9 | import android.util.Log 10 | import android.view.MenuItem 11 | import android.view.View 12 | import android.view.Window 13 | import android.view.WindowManager 14 | import android.widget.FrameLayout 15 | import android.widget.Toast 16 | import com.tencent.smtt.export.external.interfaces.IX5WebChromeClient 17 | import com.tencent.smtt.export.external.interfaces.PermissionRequest 18 | import com.tencent.smtt.export.external.interfaces.WebResourceRequest 19 | import com.tencent.smtt.sdk.* 20 | import io.flutter.plugin.common.MethodChannel 21 | import kotlin.collections.HashMap 22 | 23 | class X5WebViewActivity : Activity() { 24 | var chooserCallback: ValueCallback? = null 25 | var webView: WebView? = null 26 | override fun onCreate(savedInstanceState: Bundle?) { 27 | super.onCreate(savedInstanceState) 28 | requestWindowFeature(Window.FEATURE_ACTION_BAR) 29 | window.setFormat(PixelFormat.TRANSLUCENT) 30 | webView = WebView(this) 31 | setContentView(webView) 32 | 33 | initView() 34 | } 35 | 36 | private fun initView() { 37 | actionBar?.show() 38 | actionBar?.setDisplayHomeAsUpEnabled(true) 39 | title = intent.getStringExtra("title") ?: "" 40 | webView?.apply { 41 | layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT) 42 | val headers = intent.getSerializableExtra("headers") as HashMap 43 | settings.javaScriptEnabled = true 44 | settings.domStorageEnabled = true 45 | settings.allowFileAccess=true 46 | settings.databaseEnabled=true 47 | 48 | loadUrl(intent.getStringExtra("url"), headers) 49 | val isUrlIntercept=intent.getBooleanExtra("isUrlIntercept",false) 50 | webViewClient = object : WebViewClient() { 51 | override fun shouldOverrideUrlLoading(view: WebView, url: String?): Boolean { 52 | Log.e("X5WebViewActivity", "openurl:$url") 53 | if(isUrlIntercept){ 54 | val map=HashMap() 55 | map["url"] = url?:"" 56 | map["headers"] = HashMap() 57 | Log.e("X5WebViewActivity", "X5WebViewPlugin.methodChannel:${X5WebViewPlugin.methodChannel==null}") 58 | X5WebViewPlugin.methodChannel?.invokeMethod("onUrlLoad",map) 59 | return isUrlIntercept 60 | } 61 | view.loadUrl(url) 62 | 63 | return super.shouldOverrideUrlLoading(view, url) 64 | } 65 | 66 | override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest?): Boolean { 67 | Log.e("X5WebViewActivity", "openurl2:" + request?.url.toString()) 68 | if(isUrlIntercept){ 69 | val map=HashMap() 70 | map["url"] = request?.url.toString() 71 | map["headers"] = request?.requestHeaders?:HashMap() 72 | Log.e("X5WebViewActivity", "X5WebViewPlugin.methodChannel:${X5WebViewPlugin.methodChannel==null}") 73 | X5WebViewPlugin.methodChannel?.invokeMethod("onUrlLoad",map) 74 | return isUrlIntercept 75 | } 76 | view.loadUrl(request?.url.toString()) 77 | return super.shouldOverrideUrlLoading(view, request) 78 | } 79 | 80 | 81 | 82 | } 83 | 84 | var fullView:View?=null 85 | webChromeClient = object : WebChromeClient() { 86 | 87 | override fun onShowCustomView(view: View?, call: IX5WebChromeClient.CustomViewCallback?) { 88 | super.onShowCustomView(view, call) 89 | // view 为内核生成的全屏视图,需要添加到相应的布局位置(如:全屏幕) 90 | // customViewCallback 用于主动控制全屏退出 91 | if(view!=null){ 92 | windowManager.addView(view, WindowManager.LayoutParams( 93 | WindowManager.LayoutParams.TYPE_APPLICATION 94 | )) 95 | 96 | view.systemUiVisibility = (View.SYSTEM_UI_FLAG_LOW_PROFILE 97 | or View.SYSTEM_UI_FLAG_FULLSCREEN 98 | or View.SYSTEM_UI_FLAG_LAYOUT_STABLE 99 | or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 100 | or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) 101 | fullView=view 102 | } 103 | } 104 | 105 | override fun onHideCustomView() { 106 | super.onHideCustomView() 107 | //退出全屏 108 | if(fullView!=null){ 109 | windowManager.removeView(fullView) 110 | fullView=null 111 | } 112 | } 113 | 114 | override fun openFileChooser(p0: ValueCallback?, p1: String?, p2: String?) { 115 | chooserCallback=p0 116 | Log.e("--cjx","p1:$p1 --- p2:$p2") 117 | startActivityForResult(Intent(Intent.ACTION_PICK).apply { 118 | type=p1 119 | } 120 | ,21212) 121 | 122 | } 123 | override fun onPermissionRequest(p0: PermissionRequest?) { 124 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 125 | p0?.grant(p0.getResources()) 126 | } 127 | } 128 | 129 | } 130 | } 131 | } 132 | 133 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 134 | when (item.itemId) { 135 | android.R.id.home -> { 136 | finish() 137 | } 138 | } 139 | 140 | return super.onOptionsItemSelected(item) 141 | } 142 | 143 | 144 | override fun onDestroy() { 145 | super.onDestroy() 146 | webView?.destroy() 147 | } 148 | 149 | override fun onPause() { 150 | super.onPause() 151 | webView?.onPause() 152 | } 153 | 154 | override fun onResume() { 155 | super.onResume() 156 | webView?.onResume() 157 | } 158 | 159 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { 160 | if(data!=null&&requestCode==21212&&chooserCallback!=null){ 161 | chooserCallback?.onReceiveValue(data.data) 162 | }else{ 163 | chooserCallback?.onReceiveValue(null) 164 | } 165 | chooserCallback=null 166 | 167 | super.onActivityResult(requestCode, resultCode, data) 168 | } 169 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/com/cjx/x5_webview/X5WebViewFactory.kt: -------------------------------------------------------------------------------- 1 | package com.cjx.x5_webview 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.view.View 6 | import io.flutter.plugin.common.BinaryMessenger 7 | import io.flutter.plugin.common.StandardMessageCodec 8 | import io.flutter.plugin.platform.PlatformView 9 | import io.flutter.plugin.platform.PlatformViewFactory 10 | 11 | class X5WebViewFactory(private val msg: BinaryMessenger?, private val act: Activity?, private val containerView: View?) : PlatformViewFactory(StandardMessageCodec.INSTANCE) { 12 | override fun create(context: Context?, viewId: Int, args: Any?): PlatformView { 13 | return X5WebView(act, viewId, args as Map, msg, containerView) 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/com/cjx/x5_webview/X5WebViewPlugin.kt: -------------------------------------------------------------------------------- 1 | package com.cjx.x5_webview 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.net.Uri 7 | import android.os.Bundle 8 | import android.util.Log 9 | import android.widget.Toast 10 | import com.tencent.smtt.export.external.TbsCoreSettings 11 | import com.tencent.smtt.sdk.* 12 | import io.flutter.embedding.engine.plugins.FlutterPlugin 13 | import io.flutter.embedding.engine.plugins.activity.ActivityAware 14 | import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding 15 | import io.flutter.plugin.common.MethodCall 16 | import io.flutter.plugin.common.MethodChannel 17 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler 18 | import io.flutter.plugin.common.MethodChannel.Result 19 | import io.flutter.plugin.common.PluginRegistry.Registrar 20 | import java.io.File 21 | 22 | class X5WebViewPlugin : MethodCallHandler, FlutterPlugin, ActivityAware { 23 | 24 | var mContext: Context? = null 25 | var mActivity: Activity? = null 26 | var mFlutterPluginBinding: FlutterPlugin.FlutterPluginBinding? = null 27 | companion object { 28 | var methodChannel: MethodChannel? = null 29 | var chooserCallback: ValueCallback? = null 30 | } 31 | 32 | override fun onMethodCall(call: MethodCall, result: Result) { 33 | when (call.method) { 34 | "init" -> { 35 | val map = hashMapOf() 36 | map[TbsCoreSettings.TBS_SETTINGS_USE_SPEEDY_CLASSLOADER] = true 37 | map[TbsCoreSettings.TBS_SETTINGS_USE_DEXLOADER_SERVICE] = true 38 | QbSdk.initTbsSettings(map) 39 | QbSdk.setTbsListener(object : TbsListener { 40 | override fun onDownloadFinish(p0: Int) { 41 | Log.e("X5Sdk","onDownloadFinish"+p0) 42 | mActivity?.runOnUiThread { 43 | methodChannel?.invokeMethod("onDownloadFinish", p0) 44 | } 45 | } 46 | 47 | override fun onInstallFinish(p0: Int) { 48 | Log.e("X5Sdk","onInstallFinish"+p0) 49 | mActivity?.runOnUiThread { 50 | methodChannel?.invokeMethod("onInstallFinish", p0) 51 | } 52 | 53 | } 54 | 55 | override fun onDownloadProgress(p0: Int) { 56 | Log.e("X5Sdk","onDownloadProgress"+p0) 57 | mActivity?.runOnUiThread { 58 | methodChannel?.invokeMethod("onDownloadProgress", p0) 59 | } 60 | 61 | } 62 | 63 | }) 64 | QbSdk.initX5Environment(mContext?.applicationContext, object : QbSdk.PreInitCallback { 65 | override fun onCoreInitFinished() { 66 | Log.e("X5Sdk","onCoreInitFinished") 67 | } 68 | 69 | override fun onViewInitFinished(p0: Boolean) { 70 | //x5內核初始化完成的回调,为true表示x5内核加载成功,否则表示x5内核加载失败,会自动切换到系统内核。 71 | Log.e("X5Sdk","onViewInitFinished") 72 | result.success(p0) 73 | } 74 | 75 | }) 76 | } 77 | "canUseTbsPlayer" -> { 78 | //返回是否可以使用tbsPlayer 79 | result.success(TbsVideo.canUseTbsPlayer(mContext)) 80 | } 81 | "openVideo" -> { 82 | val url = call.argument("url") 83 | val screenMode = call.argument("screenMode") ?: 103 84 | val bundle = Bundle() 85 | bundle.putInt("screenMode", screenMode) 86 | TbsVideo.openVideo(mContext, url, bundle) 87 | result.success(null) 88 | } 89 | // "openFile" -> { 90 | // //context:调起 miniqb 的 Activity 的 context。此参数只能是 activity 类型的 context,不能设置为 Application 91 | // //的 context。 92 | // //filePath:文件路径。格式为 android 本地存储路径格式,例如:/sdcard/Download/xxx.doc. 不支持 file:/// 93 | // //格式。暂不支持在线文件。 94 | // //extraParams:miniqb 的扩展功能。为非必填项,可传入 null 使用默认设置。 95 | // //其格式是一个 key 对应一个 value。在文件查看器的产品形态中,当前支持 的 key 包括: 96 | // //local: “true”表示是进入文件查看器,如果不设置或设置为“false”,则进入 miniqb 浏览器模式。不是必 97 | // //须设置项。 98 | // //style: “0”表示文件查看器使用默认的 UI 样式。“1”表示文件查看器使用微信的 UI 样式。不设置此 key 99 | // //或设置错误值,则为默认 UI 样式。 100 | // //topBarBgColor: 定制文件查看器的顶部栏背景色。格式为“#xxxxxx”,例“#2CFC47”;不设置此 key 或设置 101 | // //错误值,则为默认 UI 样式。 102 | // //menuData: 该参数用来定制文件右上角弹出菜单,可传入菜单项的 icon 的文本,用户点击菜单项后,sdk 103 | // //会通过 startActivity+intent 的方式回调。menuData 是 jsonObject 类型,结构格式如下: public static final String jsondata = 104 | // //"{ 105 | // //pkgName:\"com.example.thirdfile\", " 106 | // //+ "className:\"com.example.thirdfile.IntentActivity\"," 107 | // //+ "thirdCtx: {pp:123}," 108 | // //+ "menuItems:" 109 | // //+ "[" 110 | // //+ "{id:0,iconResId:"+ R.drawable.ic_launcher +",text:\"menu0\"}, 111 | // //{id:1,iconResId:" + R.drawable.bookmark_edit_icon + ",text:\"menu1\"}, {id:2,iconResId:"+ R.drawable.bookmark_folder_icon +",text:\"菜单2\"}" + "]" 112 | // //+" 113 | // //}"; 114 | // //pkgName 和 className 是回调时的包名和类名。 115 | // //thirdCtx 是三方参数,需要是 jsonObject 类型,sdk 不会处理该参数,只是在菜单点击事件发生的时候原样 回传给调用方。 116 | // //menuItems 是 json 数组,表示菜单中的每一项。 117 | // //ValueCallback:提供 miniqb 打开/关闭时给调用方回调通知,以便应用层做相应处理。 在单独进程打开文件的场景中,回调参数出现如下字符时,表示可以关闭当前进程,避免内存占用。 openFileReader open in QB 118 | // //filepath error 119 | // //TbsReaderDialogClosed 120 | // //default browser: 121 | // //filepath error 122 | // //fileReaderClosed 123 | // 124 | // 125 | // val filePath = call.argument("filePath") 126 | // val params = hashMapOf() 127 | // params["local"] = call.argument("local") ?: "false" 128 | // params["style"] = call.argument("style") ?: "0" 129 | // params["topBarBgColor"] = call.argument("topBarBgColor") ?: "#2CFC47" 130 | // var menuData = call.argument("menuData") 131 | // if (menuData != null) { 132 | // params["menuData"] = menuData 133 | // } 134 | // if (!File(filePath).exists()) { 135 | // Toast.makeText(mContext, "文件不存在,请确认$filePath 是否正确", Toast.LENGTH_LONG).show() 136 | // result.success("文件不存在,请确认$filePath 是否正确") 137 | // return 138 | // } 139 | // QbSdk.canOpenFile(mActivity, filePath) { canOpenFile -> 140 | // if (canOpenFile) { 141 | // QbSdk.openFileReader(mActivity, filePath, params) { msg -> 142 | // Log.d("cjxQbSdk", msg) 143 | // val close= listOf("filepath error","TbsReaderDialogClosed","default browser","fileReaderClosed") 144 | // if(close.contains(msg)){ 145 | // QbSdk.closeFileReader(mActivity) 146 | // result.success(null) 147 | // } 148 | // } 149 | // } else { 150 | // Toast.makeText(mContext, "X5Sdk无法打开此文件", Toast.LENGTH_LONG).show() 151 | // result.success("X5Sdk无法打开此文件") 152 | // } 153 | // } 154 | // } 155 | 156 | "openWebActivity" -> { 157 | val url = call.argument("url") 158 | val title = call.argument("title") 159 | val headers = call.argument>("headers")?:HashMap() 160 | val isUrlIntercept=call.argument("isUrlIntercept") 161 | val intent = Intent(mActivity, X5WebViewActivity::class.java) 162 | intent.putExtra("url", url) 163 | intent.putExtra("title", title) 164 | intent.putExtra("headers", headers) 165 | intent.putExtra("isUrlIntercept", isUrlIntercept) 166 | mActivity?.startActivity(intent) 167 | result.success(null) 168 | } 169 | "getCarshInfo" -> { 170 | val info = WebView.getCrashExtraMessage(mContext) 171 | result.success(info) 172 | } 173 | "setDownloadWithoutWifi" -> { 174 | val isWithoutWifi = call.argument("isWithoutWifi") 175 | QbSdk.setDownloadWithoutWifi(isWithoutWifi ?: false) 176 | result.success(null) 177 | } 178 | 179 | else -> { 180 | result.notImplemented() 181 | } 182 | } 183 | } 184 | 185 | //新方式集成插件 186 | override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { 187 | mFlutterPluginBinding = binding 188 | if(mActivity==null){ 189 | return 190 | } 191 | this.mContext = mActivity 192 | methodChannel = MethodChannel(mFlutterPluginBinding?.binaryMessenger!!, "com.cjx/x5Video") 193 | methodChannel?.setMethodCallHandler(this) 194 | mFlutterPluginBinding?.platformViewRegistry?.registerViewFactory("com.cjx/x5WebView", X5WebViewFactory(mFlutterPluginBinding?.binaryMessenger, mActivity, null)) 195 | 196 | 197 | } 198 | 199 | override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { 200 | Log.e("onDetachedFromEngine", "onDetachedFromEngine") 201 | QbSdk.setTbsListener(null) 202 | mFlutterPluginBinding = null 203 | methodChannel?.setMethodCallHandler(null) 204 | methodChannel = null 205 | mContext=null 206 | } 207 | 208 | override fun onDetachedFromActivity() { 209 | Log.e("onDetachedFromActivity", "onDetachedFromActivity") 210 | } 211 | 212 | override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { 213 | } 214 | 215 | override fun onAttachedToActivity(binding: ActivityPluginBinding) { 216 | binding.addActivityResultListener { requestCode, resultCode, data -> 217 | 218 | 219 | Log.e("--cjx","data:$data --- requestCode:$requestCode resultCode:$resultCode") 220 | 221 | if(data!=null&&requestCode==21211&&chooserCallback!=null){ 222 | chooserCallback?.onReceiveValue(data.data) 223 | }else{ 224 | chooserCallback?.onReceiveValue(null) 225 | } 226 | chooserCallback=null 227 | 228 | return@addActivityResultListener false 229 | } 230 | this.mActivity = binding.activity 231 | 232 | Log.e("onAttachedToActivity", "onAttachedToActivity") 233 | if (mFlutterPluginBinding == null) { 234 | Log.e("onAttachedToActivity", "mFlutterPluginBinding==null") 235 | return 236 | } 237 | this.mContext = binding.activity.applicationContext 238 | methodChannel = MethodChannel(mFlutterPluginBinding?.binaryMessenger!!, "com.cjx/x5Video") 239 | methodChannel?.setMethodCallHandler(this) 240 | mFlutterPluginBinding?.platformViewRegistry?.registerViewFactory("com.cjx/x5WebView", X5WebViewFactory(mFlutterPluginBinding?.binaryMessenger, mActivity, null)) 241 | 242 | 243 | } 244 | 245 | override fun onDetachedFromActivityForConfigChanges() { 246 | } 247 | 248 | } 249 | -------------------------------------------------------------------------------- /android/src/main/res/xml/network_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/res/xml/x5webview_file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # Visual Studio Code related 19 | .vscode/ 20 | 21 | # Flutter/Dart/Pub related 22 | **/doc/api/ 23 | .dart_tool/ 24 | .flutter-plugins 25 | .packages 26 | .pub-cache/ 27 | .pub/ 28 | /build/ 29 | 30 | # Android related 31 | **/android/**/gradle-wrapper.jar 32 | **/android/.gradle 33 | **/android/captures/ 34 | **/android/gradlew 35 | **/android/gradlew.bat 36 | **/android/local.properties 37 | **/android/**/GeneratedPluginRegistrant.java 38 | 39 | # iOS/XCode related 40 | **/ios/**/*.mode1v3 41 | **/ios/**/*.mode2v3 42 | **/ios/**/*.moved-aside 43 | **/ios/**/*.pbxuser 44 | **/ios/**/*.perspectivev3 45 | **/ios/**/*sync/ 46 | **/ios/**/.sconsign.dblite 47 | **/ios/**/.tags* 48 | **/ios/**/.vagrant/ 49 | **/ios/**/DerivedData/ 50 | **/ios/**/Icon? 51 | **/ios/**/Pods/ 52 | **/ios/**/.symlinks/ 53 | **/ios/**/profile 54 | **/ios/**/xcuserdata 55 | **/ios/.generated/ 56 | **/ios/Flutter/App.framework 57 | **/ios/Flutter/Flutter.framework 58 | **/ios/Flutter/Generated.xcconfig 59 | **/ios/Flutter/app.flx 60 | **/ios/Flutter/app.zip 61 | **/ios/Flutter/flutter_assets/ 62 | **/ios/ServiceDefinitions.json 63 | **/ios/Runner/GeneratedPluginRegistrant.* 64 | 65 | # Exceptions to above rules. 66 | !**/ios/**/default.mode1v3 67 | !**/ios/**/default.mode2v3 68 | !**/ios/**/default.pbxuser 69 | !**/ios/**/default.perspectivev3 70 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 71 | !/android/key.properties 72 | -------------------------------------------------------------------------------- /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: 0ba67226ee62d6c9d663a6f8410fb4b2f1076046 8 | channel: dev 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # x5_webview_example 2 | 3 | Demonstrates how to use the x5_webview 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 | -------------------------------------------------------------------------------- /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 -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new Exception("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | 29 | def keystorePropertiesFile = rootProject.file("key.properties") 30 | def keystoreProperties = new Properties() 31 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 32 | 33 | 34 | android { 35 | compileSdkVersion flutter.compileSdkVersion 36 | 37 | compileOptions { 38 | sourceCompatibility JavaVersion.VERSION_1_8 39 | targetCompatibility JavaVersion.VERSION_1_8 40 | } 41 | 42 | kotlinOptions { 43 | jvmTarget = '1.8' 44 | } 45 | 46 | sourceSets { 47 | main.java.srcDirs += 'src/main/kotlin' 48 | } 49 | 50 | 51 | defaultConfig { 52 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 53 | applicationId "com.cjx.x5_webview_example" 54 | minSdkVersion 19 55 | targetSdkVersion flutter.targetSdkVersion 56 | versionCode flutterVersionCode.toInteger() 57 | versionName flutterVersionName 58 | } 59 | 60 | signingConfigs { 61 | release { 62 | keyAlias keystoreProperties['keyAlias'] 63 | keyPassword keystoreProperties['keyPassword'] 64 | storeFile file(keystoreProperties['storeFile']) 65 | storePassword keystoreProperties['storePassword'] 66 | } 67 | } 68 | buildTypes { 69 | release { 70 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 71 | signingConfig signingConfigs.release 72 | } 73 | debug{ 74 | signingConfig signingConfigs.debug 75 | } 76 | } 77 | } 78 | 79 | flutter { 80 | source '../..' 81 | } 82 | 83 | dependencies { 84 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 85 | implementation "androidx.appcompat:appcompat:1.1.0" 86 | } 87 | -------------------------------------------------------------------------------- /example/android/app/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu May 09 15:24:07 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip 7 | -------------------------------------------------------------------------------- /example/android/app/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /example/android/app/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /example/android/app/local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Thu May 09 15:22:33 CST 2019 8 | sdk.dir=/Users/cjx/Library/Android/sdk 9 | -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | #-dontshrink 24 | #-dontoptimize 25 | #-dontobfuscate 26 | 27 | -dontwarn dalvik.** 28 | -dontwarn com.tencent.smtt.** 29 | 30 | -keep class com.tencent.smtt.** { 31 | *; 32 | } 33 | 34 | -keep class com.tencent.tbs.** { 35 | *; 36 | } -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 16 | 17 | 18 | 26 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 45 | 48 | 49 | 51 | 52 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/cjx/x5_webview_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.cjx.x5_webview_example 2 | 3 | 4 | //最新版集成 5 | import io.flutter.embedding.android.FlutterActivity 6 | 7 | class MainActivity: FlutterActivity() { 8 | } 9 | 10 | //新版集成 11 | //import androidx.annotation.NonNull 12 | //import io.flutter.embedding.android.FlutterActivity 13 | //import io.flutter.embedding.engine.FlutterEngine 14 | //import io.flutter.plugins.GeneratedPluginRegistrant 15 | // 16 | //class MainActivity : FlutterActivity() { 17 | // override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { 18 | // GeneratedPluginRegistrant.registerWith(flutterEngine) 19 | // } 20 | //} 21 | 22 | //旧版集成 23 | //import android.os.Bundle 24 | // 25 | //import io.flutter.app.FlutterActivity 26 | //import io.flutter.plugins.GeneratedPluginRegistrant 27 | // 28 | //class MainActivity: FlutterActivity() { 29 | // override fun onCreate(savedInstanceState: Bundle?) { 30 | // super.onCreate(savedInstanceState) 31 | // GeneratedPluginRegistrant.registerWith(this) 32 | // } 33 | //} -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.7.22' 3 | repositories { 4 | // google() 5 | //// jcenter() 6 | // mavenCentral() 7 | maven { url 'https://maven.aliyun.com/repository/google' } 8 | maven { url 'https://maven.aliyun.com/repository/central' } 9 | maven { url 'https://maven.aliyun.com/repository/public' } 10 | maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } 11 | } 12 | 13 | dependencies { 14 | classpath 'com.android.tools.build:gradle:7.1.2' 15 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | // google() 22 | //// jcenter() 23 | // mavenCentral() 24 | maven { url 'https://maven.aliyun.com/repository/google' } 25 | maven { url 'https://maven.aliyun.com/repository/central' } 26 | maven { url 'https://maven.aliyun.com/repository/public' } 27 | maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } 28 | } 29 | } 30 | 31 | rootProject.buildDir = '../build' 32 | subprojects { 33 | project.buildDir = "${rootProject.buildDir}/${project.name}" 34 | } 35 | subprojects { 36 | project.evaluationDependsOn(':app') 37 | } 38 | 39 | tasks.register("clean", Delete) { 40 | delete rootProject.buildDir 41 | } 42 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | Android.useDeprecatedNdk=true 3 | #android.enableR8=true 4 | android.useAndroidX=true 5 | android.enableJetifier=true 6 | android.injected.testOnly=false -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip 7 | -------------------------------------------------------------------------------- /example/android/key.properties: -------------------------------------------------------------------------------- 1 | storePassword=cjx123 2 | keyPassword=cjx123 3 | keyAlias=cjx 4 | storeFile=../cjx_x5.jks -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /example/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 |
18 |

本地html测试

19 |
20 | 21 |
22 | 25 | 26 | 31 | 32 | 37 | 38 | 39 |
40 | 43 | 44 |
45 | 46 | 47 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | pods_ary = [] 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) { |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | pods_ary.push({:name => podname, :path => podpath}); 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | } 32 | return pods_ary 33 | end 34 | 35 | target 'Runner' do 36 | use_frameworks! 37 | 38 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 39 | # referring to absolute paths on developers' machines. 40 | system('rm -rf .symlinks') 41 | system('mkdir -p .symlinks/plugins') 42 | 43 | # Flutter Pods 44 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') 45 | if generated_xcode_build_settings.empty? 46 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." 47 | end 48 | generated_xcode_build_settings.map { |p| 49 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR' 50 | symlink = File.join('.symlinks', 'flutter') 51 | File.symlink(File.dirname(p[:path]), symlink) 52 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) 53 | end 54 | } 55 | 56 | # Plugin Pods 57 | plugin_pods = parse_KV_file('../.flutter-plugins') 58 | plugin_pods.map { |p| 59 | symlink = File.join('.symlinks', 'plugins', p[:name]) 60 | File.symlink(p[:path], symlink) 61 | pod p[:name], :path => File.join(symlink, 'ios') 62 | } 63 | end 64 | 65 | post_install do |installer| 66 | installer.pods_project.targets.each do |target| 67 | target.build_configurations.each do |config| 68 | config.build_settings['ENABLE_BITCODE'] = 'NO' 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 13 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 14 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 15 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 16 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 17 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 18 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 19 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 20 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXCopyFilesBuildPhase section */ 24 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 25 | isa = PBXCopyFilesBuildPhase; 26 | buildActionMask = 2147483647; 27 | dstPath = ""; 28 | dstSubfolderSpec = 10; 29 | files = ( 30 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, 31 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, 32 | ); 33 | name = "Embed Frameworks"; 34 | runOnlyForDeploymentPostprocessing = 0; 35 | }; 36 | /* End PBXCopyFilesBuildPhase section */ 37 | 38 | /* Begin PBXFileReference section */ 39 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 40 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 41 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 42 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 43 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 44 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 45 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 46 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 47 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 48 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 49 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 51 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 52 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 53 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54 | /* End PBXFileReference section */ 55 | 56 | /* Begin PBXFrameworksBuildPhase section */ 57 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 58 | isa = PBXFrameworksBuildPhase; 59 | buildActionMask = 2147483647; 60 | files = ( 61 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 62 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 63 | ); 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | /* End PBXFrameworksBuildPhase section */ 67 | 68 | /* Begin PBXGroup section */ 69 | 9740EEB11CF90186004384FC /* Flutter */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | 3B80C3931E831B6300D905FE /* App.framework */, 73 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 74 | 9740EEBA1CF902C7004384FC /* Flutter.framework */, 75 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 76 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 77 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 78 | ); 79 | name = Flutter; 80 | sourceTree = ""; 81 | }; 82 | 97C146E51CF9000F007C117D = { 83 | isa = PBXGroup; 84 | children = ( 85 | 9740EEB11CF90186004384FC /* Flutter */, 86 | 97C146F01CF9000F007C117D /* Runner */, 87 | 97C146EF1CF9000F007C117D /* Products */, 88 | ); 89 | sourceTree = ""; 90 | }; 91 | 97C146EF1CF9000F007C117D /* Products */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 97C146EE1CF9000F007C117D /* Runner.app */, 95 | ); 96 | name = Products; 97 | sourceTree = ""; 98 | }; 99 | 97C146F01CF9000F007C117D /* Runner */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 103 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 104 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 105 | 97C147021CF9000F007C117D /* Info.plist */, 106 | 97C146F11CF9000F007C117D /* Supporting Files */, 107 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 108 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 109 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 110 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 111 | ); 112 | path = Runner; 113 | sourceTree = ""; 114 | }; 115 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | ); 119 | name = "Supporting Files"; 120 | sourceTree = ""; 121 | }; 122 | /* End PBXGroup section */ 123 | 124 | /* Begin PBXNativeTarget section */ 125 | 97C146ED1CF9000F007C117D /* Runner */ = { 126 | isa = PBXNativeTarget; 127 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 128 | buildPhases = ( 129 | 9740EEB61CF901F6004384FC /* Run Script */, 130 | 97C146EA1CF9000F007C117D /* Sources */, 131 | 97C146EB1CF9000F007C117D /* Frameworks */, 132 | 97C146EC1CF9000F007C117D /* Resources */, 133 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 134 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 135 | ); 136 | buildRules = ( 137 | ); 138 | dependencies = ( 139 | ); 140 | name = Runner; 141 | productName = Runner; 142 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 143 | productType = "com.apple.product-type.application"; 144 | }; 145 | /* End PBXNativeTarget section */ 146 | 147 | /* Begin PBXProject section */ 148 | 97C146E61CF9000F007C117D /* Project object */ = { 149 | isa = PBXProject; 150 | attributes = { 151 | LastUpgradeCheck = 0910; 152 | ORGANIZATIONNAME = "The Chromium Authors"; 153 | TargetAttributes = { 154 | 97C146ED1CF9000F007C117D = { 155 | CreatedOnToolsVersion = 7.3.1; 156 | LastSwiftMigration = 0910; 157 | }; 158 | }; 159 | }; 160 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 161 | compatibilityVersion = "Xcode 3.2"; 162 | developmentRegion = English; 163 | hasScannedForEncodings = 0; 164 | knownRegions = ( 165 | en, 166 | Base, 167 | ); 168 | mainGroup = 97C146E51CF9000F007C117D; 169 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 170 | projectDirPath = ""; 171 | projectRoot = ""; 172 | targets = ( 173 | 97C146ED1CF9000F007C117D /* Runner */, 174 | ); 175 | }; 176 | /* End PBXProject section */ 177 | 178 | /* Begin PBXResourcesBuildPhase section */ 179 | 97C146EC1CF9000F007C117D /* Resources */ = { 180 | isa = PBXResourcesBuildPhase; 181 | buildActionMask = 2147483647; 182 | files = ( 183 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 184 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 185 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 186 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 187 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 188 | ); 189 | runOnlyForDeploymentPostprocessing = 0; 190 | }; 191 | /* End PBXResourcesBuildPhase section */ 192 | 193 | /* Begin PBXShellScriptBuildPhase section */ 194 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 195 | isa = PBXShellScriptBuildPhase; 196 | buildActionMask = 2147483647; 197 | files = ( 198 | ); 199 | inputPaths = ( 200 | ); 201 | name = "Thin Binary"; 202 | outputPaths = ( 203 | ); 204 | runOnlyForDeploymentPostprocessing = 0; 205 | shellPath = /bin/sh; 206 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; 207 | }; 208 | 9740EEB61CF901F6004384FC /* Run Script */ = { 209 | isa = PBXShellScriptBuildPhase; 210 | buildActionMask = 2147483647; 211 | files = ( 212 | ); 213 | inputPaths = ( 214 | ); 215 | name = "Run Script"; 216 | outputPaths = ( 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | shellPath = /bin/sh; 220 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 221 | }; 222 | /* End PBXShellScriptBuildPhase section */ 223 | 224 | /* Begin PBXSourcesBuildPhase section */ 225 | 97C146EA1CF9000F007C117D /* Sources */ = { 226 | isa = PBXSourcesBuildPhase; 227 | buildActionMask = 2147483647; 228 | files = ( 229 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 230 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 231 | ); 232 | runOnlyForDeploymentPostprocessing = 0; 233 | }; 234 | /* End PBXSourcesBuildPhase section */ 235 | 236 | /* Begin PBXVariantGroup section */ 237 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 238 | isa = PBXVariantGroup; 239 | children = ( 240 | 97C146FB1CF9000F007C117D /* Base */, 241 | ); 242 | name = Main.storyboard; 243 | sourceTree = ""; 244 | }; 245 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 246 | isa = PBXVariantGroup; 247 | children = ( 248 | 97C147001CF9000F007C117D /* Base */, 249 | ); 250 | name = LaunchScreen.storyboard; 251 | sourceTree = ""; 252 | }; 253 | /* End PBXVariantGroup section */ 254 | 255 | /* Begin XCBuildConfiguration section */ 256 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 257 | isa = XCBuildConfiguration; 258 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 259 | buildSettings = { 260 | ALWAYS_SEARCH_USER_PATHS = NO; 261 | CLANG_ANALYZER_NONNULL = YES; 262 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 263 | CLANG_CXX_LIBRARY = "libc++"; 264 | CLANG_ENABLE_MODULES = YES; 265 | CLANG_ENABLE_OBJC_ARC = YES; 266 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 267 | CLANG_WARN_BOOL_CONVERSION = YES; 268 | CLANG_WARN_COMMA = YES; 269 | CLANG_WARN_CONSTANT_CONVERSION = YES; 270 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 271 | CLANG_WARN_EMPTY_BODY = YES; 272 | CLANG_WARN_ENUM_CONVERSION = YES; 273 | CLANG_WARN_INFINITE_RECURSION = YES; 274 | CLANG_WARN_INT_CONVERSION = YES; 275 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 276 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 277 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 278 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 279 | CLANG_WARN_STRICT_PROTOTYPES = YES; 280 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 281 | CLANG_WARN_UNREACHABLE_CODE = YES; 282 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 283 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 284 | COPY_PHASE_STRIP = NO; 285 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 286 | ENABLE_NS_ASSERTIONS = NO; 287 | ENABLE_STRICT_OBJC_MSGSEND = YES; 288 | GCC_C_LANGUAGE_STANDARD = gnu99; 289 | GCC_NO_COMMON_BLOCKS = YES; 290 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 291 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 292 | GCC_WARN_UNDECLARED_SELECTOR = YES; 293 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 294 | GCC_WARN_UNUSED_FUNCTION = YES; 295 | GCC_WARN_UNUSED_VARIABLE = YES; 296 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 297 | MTL_ENABLE_DEBUG_INFO = NO; 298 | SDKROOT = iphoneos; 299 | TARGETED_DEVICE_FAMILY = "1,2"; 300 | VALIDATE_PRODUCT = YES; 301 | }; 302 | name = Profile; 303 | }; 304 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 305 | isa = XCBuildConfiguration; 306 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 307 | buildSettings = { 308 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 309 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 310 | DEVELOPMENT_TEAM = S8QB4VV633; 311 | ENABLE_BITCODE = NO; 312 | FRAMEWORK_SEARCH_PATHS = ( 313 | "$(inherited)", 314 | "$(PROJECT_DIR)/Flutter", 315 | ); 316 | INFOPLIST_FILE = Runner/Info.plist; 317 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 318 | LIBRARY_SEARCH_PATHS = ( 319 | "$(inherited)", 320 | "$(PROJECT_DIR)/Flutter", 321 | ); 322 | PRODUCT_BUNDLE_IDENTIFIER = com.cjx.x5WebviewExample; 323 | PRODUCT_NAME = "$(TARGET_NAME)"; 324 | SWIFT_VERSION = 4.0; 325 | VERSIONING_SYSTEM = "apple-generic"; 326 | }; 327 | name = Profile; 328 | }; 329 | 97C147031CF9000F007C117D /* Debug */ = { 330 | isa = XCBuildConfiguration; 331 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 332 | buildSettings = { 333 | ALWAYS_SEARCH_USER_PATHS = NO; 334 | CLANG_ANALYZER_NONNULL = YES; 335 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 336 | CLANG_CXX_LIBRARY = "libc++"; 337 | CLANG_ENABLE_MODULES = YES; 338 | CLANG_ENABLE_OBJC_ARC = YES; 339 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 340 | CLANG_WARN_BOOL_CONVERSION = YES; 341 | CLANG_WARN_COMMA = YES; 342 | CLANG_WARN_CONSTANT_CONVERSION = YES; 343 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 344 | CLANG_WARN_EMPTY_BODY = YES; 345 | CLANG_WARN_ENUM_CONVERSION = YES; 346 | CLANG_WARN_INFINITE_RECURSION = YES; 347 | CLANG_WARN_INT_CONVERSION = YES; 348 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 349 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 350 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 351 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 352 | CLANG_WARN_STRICT_PROTOTYPES = YES; 353 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 354 | CLANG_WARN_UNREACHABLE_CODE = YES; 355 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 356 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 357 | COPY_PHASE_STRIP = NO; 358 | DEBUG_INFORMATION_FORMAT = dwarf; 359 | ENABLE_STRICT_OBJC_MSGSEND = YES; 360 | ENABLE_TESTABILITY = YES; 361 | GCC_C_LANGUAGE_STANDARD = gnu99; 362 | GCC_DYNAMIC_NO_PIC = NO; 363 | GCC_NO_COMMON_BLOCKS = YES; 364 | GCC_OPTIMIZATION_LEVEL = 0; 365 | GCC_PREPROCESSOR_DEFINITIONS = ( 366 | "DEBUG=1", 367 | "$(inherited)", 368 | ); 369 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 370 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 371 | GCC_WARN_UNDECLARED_SELECTOR = YES; 372 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 373 | GCC_WARN_UNUSED_FUNCTION = YES; 374 | GCC_WARN_UNUSED_VARIABLE = YES; 375 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 376 | MTL_ENABLE_DEBUG_INFO = YES; 377 | ONLY_ACTIVE_ARCH = YES; 378 | SDKROOT = iphoneos; 379 | TARGETED_DEVICE_FAMILY = "1,2"; 380 | }; 381 | name = Debug; 382 | }; 383 | 97C147041CF9000F007C117D /* Release */ = { 384 | isa = XCBuildConfiguration; 385 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 386 | buildSettings = { 387 | ALWAYS_SEARCH_USER_PATHS = NO; 388 | CLANG_ANALYZER_NONNULL = YES; 389 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 390 | CLANG_CXX_LIBRARY = "libc++"; 391 | CLANG_ENABLE_MODULES = YES; 392 | CLANG_ENABLE_OBJC_ARC = YES; 393 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 394 | CLANG_WARN_BOOL_CONVERSION = YES; 395 | CLANG_WARN_COMMA = YES; 396 | CLANG_WARN_CONSTANT_CONVERSION = YES; 397 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 398 | CLANG_WARN_EMPTY_BODY = YES; 399 | CLANG_WARN_ENUM_CONVERSION = YES; 400 | CLANG_WARN_INFINITE_RECURSION = YES; 401 | CLANG_WARN_INT_CONVERSION = YES; 402 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 403 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 404 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 405 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 406 | CLANG_WARN_STRICT_PROTOTYPES = YES; 407 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 408 | CLANG_WARN_UNREACHABLE_CODE = YES; 409 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 410 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 411 | COPY_PHASE_STRIP = NO; 412 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 413 | ENABLE_NS_ASSERTIONS = NO; 414 | ENABLE_STRICT_OBJC_MSGSEND = YES; 415 | GCC_C_LANGUAGE_STANDARD = gnu99; 416 | GCC_NO_COMMON_BLOCKS = YES; 417 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 418 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 419 | GCC_WARN_UNDECLARED_SELECTOR = YES; 420 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 421 | GCC_WARN_UNUSED_FUNCTION = YES; 422 | GCC_WARN_UNUSED_VARIABLE = YES; 423 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 424 | MTL_ENABLE_DEBUG_INFO = NO; 425 | SDKROOT = iphoneos; 426 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 427 | TARGETED_DEVICE_FAMILY = "1,2"; 428 | VALIDATE_PRODUCT = YES; 429 | }; 430 | name = Release; 431 | }; 432 | 97C147061CF9000F007C117D /* Debug */ = { 433 | isa = XCBuildConfiguration; 434 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 435 | buildSettings = { 436 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 437 | CLANG_ENABLE_MODULES = YES; 438 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 439 | ENABLE_BITCODE = NO; 440 | FRAMEWORK_SEARCH_PATHS = ( 441 | "$(inherited)", 442 | "$(PROJECT_DIR)/Flutter", 443 | ); 444 | INFOPLIST_FILE = Runner/Info.plist; 445 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 446 | LIBRARY_SEARCH_PATHS = ( 447 | "$(inherited)", 448 | "$(PROJECT_DIR)/Flutter", 449 | ); 450 | PRODUCT_BUNDLE_IDENTIFIER = com.cjx.x5WebviewExample; 451 | PRODUCT_NAME = "$(TARGET_NAME)"; 452 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 453 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 454 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 455 | SWIFT_VERSION = 4.0; 456 | VERSIONING_SYSTEM = "apple-generic"; 457 | }; 458 | name = Debug; 459 | }; 460 | 97C147071CF9000F007C117D /* Release */ = { 461 | isa = XCBuildConfiguration; 462 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 463 | buildSettings = { 464 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 465 | CLANG_ENABLE_MODULES = YES; 466 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 467 | ENABLE_BITCODE = NO; 468 | FRAMEWORK_SEARCH_PATHS = ( 469 | "$(inherited)", 470 | "$(PROJECT_DIR)/Flutter", 471 | ); 472 | INFOPLIST_FILE = Runner/Info.plist; 473 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 474 | LIBRARY_SEARCH_PATHS = ( 475 | "$(inherited)", 476 | "$(PROJECT_DIR)/Flutter", 477 | ); 478 | PRODUCT_BUNDLE_IDENTIFIER = com.cjx.x5WebviewExample; 479 | PRODUCT_NAME = "$(TARGET_NAME)"; 480 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 481 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 482 | SWIFT_VERSION = 4.0; 483 | VERSIONING_SYSTEM = "apple-generic"; 484 | }; 485 | name = Release; 486 | }; 487 | /* End XCBuildConfiguration section */ 488 | 489 | /* Begin XCConfigurationList section */ 490 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 491 | isa = XCConfigurationList; 492 | buildConfigurations = ( 493 | 97C147031CF9000F007C117D /* Debug */, 494 | 97C147041CF9000F007C117D /* Release */, 495 | 249021D3217E4FDB00AE95B9 /* Profile */, 496 | ); 497 | defaultConfigurationIsVisible = 0; 498 | defaultConfigurationName = Release; 499 | }; 500 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 501 | isa = XCConfigurationList; 502 | buildConfigurations = ( 503 | 97C147061CF9000F007C117D /* Debug */, 504 | 97C147071CF9000F007C117D /* Release */, 505 | 249021D4217E4FDB00AE95B9 /* Profile */, 506 | ); 507 | defaultConfigurationIsVisible = 0; 508 | defaultConfigurationName = Release; 509 | }; 510 | /* End XCConfigurationList section */ 511 | 512 | }; 513 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 514 | } 515 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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: [UIApplicationLaunchOptionsKey: 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" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/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/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/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/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/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/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/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/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/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/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/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/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/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/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/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/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/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/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/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/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/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/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/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/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/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/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/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/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /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/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/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/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.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | x5_webview_example 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 | 45 | 46 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /example/lib/demo.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:x5_webview/x5_webview.dart'; 4 | import 'package:webview_flutter/webview_flutter.dart'; 5 | 6 | class DemoWebViewPage extends StatefulWidget { 7 | final url; 8 | 9 | DemoWebViewPage(this.url); 10 | 11 | @override 12 | _DemoWebViewPageState createState() => _DemoWebViewPageState(url); 13 | } 14 | 15 | class _DemoWebViewPageState extends State { 16 | X5WebViewController? _controller; 17 | final url; 18 | 19 | _DemoWebViewPageState(this.url); 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | return WillPopScope( 24 | child: Scaffold( 25 | appBar: AppBar( 26 | title: Text("X5WebView示例"), 27 | actions: [ 28 | IconButton( 29 | icon: Icon(Icons.close), 30 | onPressed: () { 31 | Navigator.pop(context); 32 | }) 33 | ], 34 | ), 35 | body: Column(children: [ 36 | Expanded( 37 | child: defaultTargetPlatform == TargetPlatform.android 38 | ? X5WebView( 39 | url: url, 40 | javaScriptEnabled: true, 41 | domStorageEnabled: true, 42 | header: {"TestHeader": "测试", "MSG": "在?在干嘛?吃饭了没?"}, 43 | userAgentString: "aaaa", 44 | javascriptChannels: JavascriptChannels( 45 | ["X5Web", "Toast"], (name, data) { 46 | switch (name) { 47 | case "X5Web": 48 | showDialog( 49 | context: context, 50 | builder: (context) { 51 | return AlertDialog( 52 | title: Text("获取到的字符串为:"), 53 | content: Text(data), 54 | ); 55 | }); 56 | break; 57 | case "Toast": 58 | print(data); 59 | break; 60 | } 61 | }), 62 | onWebViewCreated: (control) { 63 | _controller = control; 64 | // var listName = ["X5Web", "Toast"]; 65 | // _controller.addJavascriptChannels(listName, 66 | // (name, data) { 67 | // switch (name) { 68 | // case "X5Web": 69 | // showDialog( 70 | // context: context, 71 | // builder: (context) { 72 | // return AlertDialog( 73 | // title: Text("获取到的字符串为:"), 74 | // content: Text(data), 75 | // ); 76 | // }); 77 | // break; 78 | // case "Toast": 79 | // print(data); 80 | // break; 81 | // } 82 | // }); 83 | }, 84 | onPageFinished: () async { 85 | var url = await _controller?.currentUrl(); 86 | print(url); 87 | }, 88 | onProgressChanged: (progress) { 89 | print("webview加载进度------$progress%"); 90 | }, 91 | onUrlLoading: (_url) { 92 | print(_url); 93 | showDialog( 94 | context: context, 95 | builder: (_) { 96 | return AlertDialog( 97 | content: Text("url==$_url"), 98 | actions: [ 99 | TextButton( 100 | child: Text("我知道了"), 101 | onPressed: () { 102 | Navigator.pop(context); 103 | }, 104 | ) 105 | ], 106 | ); 107 | }); 108 | }, 109 | ) 110 | : 111 | //可替换为webview_flutter 112 | Container()), 113 | ElevatedButton( 114 | onPressed: () { 115 | _controller?.evaluateJavascript( 116 | 'document.getElementById("input").value="flutter调用js成功!"'); 117 | }, 118 | child: Text("flutter调用js(更改文字)"), 119 | ) 120 | ]), 121 | ), 122 | onWillPop: () async { 123 | var canGoBack = await _controller!.canGoBack(); 124 | if (canGoBack) { 125 | _controller?.goBack(); 126 | return false; 127 | } else { 128 | return true; 129 | } 130 | }); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter/services.dart'; 7 | import 'package:path_provider/path_provider.dart'; 8 | import 'package:permission_handler/permission_handler.dart'; 9 | import 'package:x5_webview/x5_sdk.dart'; 10 | 11 | import 'demo.dart'; 12 | 13 | void main() { 14 | runApp(MyApp()); 15 | } 16 | 17 | class MyApp extends StatelessWidget { 18 | @override 19 | Widget build(BuildContext context) { 20 | return MaterialApp( 21 | home: HomePage(), 22 | theme: ThemeData(primarySwatch: Colors.blue), 23 | ); 24 | } 25 | } 26 | 27 | class HomePage extends StatefulWidget { 28 | @override 29 | _HomePageState createState() => _HomePageState(); 30 | } 31 | 32 | class _HomePageState extends State { 33 | var crashInfo; 34 | bool isLoadOk = false; 35 | @override 36 | void initState() { 37 | super.initState(); 38 | loadX5(); 39 | } 40 | 41 | @override 42 | Widget build(BuildContext context) { 43 | return Scaffold( 44 | appBar: AppBar( 45 | title: const Text('Plugin example app'), 46 | ), 47 | body: Center( 48 | child: ListView( 49 | children: [ 50 | ElevatedButton( 51 | onPressed: () async { 52 | X5Sdk.openWebActivity("http://debugtbs.qq.com", 53 | title: "X5内核信息"); 54 | }, 55 | child: Text("查看X5内核信息")), 56 | // ElevatedButton( 57 | // onPressed: () async { 58 | // showInputDialog( 59 | // onConfirm: (url) async { 60 | // await X5Sdk.openVideo(url, screenMode: 104); 61 | // }, 62 | // defaultText: "https://vjs.zencdn.net/v/oceans.mp4"); 63 | // 64 | // // var canUseTbsPlayer = await X5Sdk.canUseTbsPlayer(); 65 | // // if (canUseTbsPlayer) { 66 | // // showInputDialog( 67 | // // onConfirm: (url) async { 68 | // // await X5Sdk.openVideo(url, screenMode: 104); 69 | // // }, 70 | // // defaultText: "https://vjs.zencdn.net/v/oceans.mp4"); 71 | // // } else { 72 | // // print("x5Video不可用"); 73 | // // } 74 | // }, 75 | // child: Text("x5video直接播放视频")), 76 | // ElevatedButton( 77 | // onPressed: () async { 78 | // showDialog( 79 | // context: context, 80 | // builder: (context) { 81 | // return AlertDialog( 82 | // title: Text("X5Sdk打开本地文件示例"), 83 | // content: Text("请先下载再打开"), 84 | // actions: [ 85 | // TextButton( 86 | // onPressed: () async { 87 | // Navigator.pop(context); 88 | // }, 89 | // child: Text("取消"), 90 | // ), 91 | // TextButton( 92 | // onPressed: () async { 93 | // try { 94 | // showDialog( 95 | // context: context, 96 | // barrierDismissible: false, 97 | // builder: (context) { 98 | // return AlertDialog( 99 | // content: Column( 100 | // mainAxisSize: MainAxisSize.min, 101 | // children: [ 102 | // CircularProgressIndicator(), 103 | // Padding( 104 | // padding: 105 | // EdgeInsets.only(top: 20), 106 | // ), 107 | // Text("等待下载") 108 | // ], 109 | // ), 110 | // ); 111 | // }); 112 | // var dir = await getExternalStorageDirectory(); 113 | // print(await getExternalStorageDirectory()); 114 | // print(await getApplicationSupportDirectory()); 115 | // print( 116 | // await getApplicationDocumentsDirectory()); 117 | // var response = await Dio().download( 118 | // "http://lc-QMTBhNKI.cn-n1.lcfile.com/fc441aa8ff4738cc3f85/FileList.xlsx", 119 | // "${dir?.path}/FileList.xlsx"); 120 | // print(response.data); 121 | // Navigator.pop(context); 122 | // } on DioError catch (e) { 123 | // Navigator.pop(context); 124 | // print(e.message); 125 | // } 126 | // }, 127 | // child: Text("下载"), 128 | // ), 129 | // TextButton( 130 | // onPressed: () async { 131 | // var dir = await getExternalStorageDirectory(); 132 | // print(dir); 133 | // var msg = await X5Sdk.openFile( 134 | // "${dir?.path}/FileList.xlsx", 135 | // style: "1", 136 | // topBarBgColor: "#2196F3"); 137 | // print(msg); 138 | // }, 139 | // child: Text("打开"), 140 | // ) 141 | // ], 142 | // ); 143 | // }); 144 | // }, 145 | // child: Text("x5sdk打开本地文件示例")), 146 | ElevatedButton( 147 | onPressed: () async { 148 | // Navigator.of(context).push( 149 | // CupertinoPageRoute(builder: (BuildContext context) { 150 | // return DemoWebViewPage("http://bin.amazeui.org/tizayo"); 151 | // })); 152 | 153 | //网络url 154 | // showInputDialog( 155 | // onConfirm: (url) { 156 | // Navigator.of(context).push( 157 | // CupertinoPageRoute(builder: (BuildContext context) { 158 | // return DemoWebViewPage(url); 159 | // })); 160 | // }, 161 | // defaultText: "http://bin.amazeui.org/tizayo"); 162 | 163 | //本地html 164 | var fileHtmlContents = 165 | await rootBundle.loadString("assets/index.html"); 166 | var url = Uri.dataFromString(fileHtmlContents, 167 | mimeType: 'text/html', 168 | encoding: Encoding.getByName('utf-8')) 169 | .toString(); 170 | Navigator.of(context) 171 | .push(CupertinoPageRoute(builder: (BuildContext context) { 172 | return DemoWebViewPage(url); 173 | })); 174 | }, 175 | child: Text("flutter内嵌x5webview")), 176 | ElevatedButton( 177 | onPressed: () async { 178 | showInputDialog( 179 | onConfirm: (url) { 180 | openUrl(url); 181 | }, 182 | defaultText: 183 | "https://www.bilibili.com/video/BV1hD4y1X7Rm"); 184 | }, 185 | child: Text("x5webviewActivity")), 186 | ElevatedButton( 187 | onPressed: () async { 188 | var fileHtmlContents = 189 | await rootBundle.loadString("assets/index.html"); 190 | var url = Uri.dataFromString(fileHtmlContents, 191 | mimeType: 'text/html', 192 | encoding: Encoding.getByName('utf-8')) 193 | .toString(); 194 | 195 | await X5Sdk.openWebActivity(url, title: "本地html示例"); 196 | }, 197 | child: Text("本地html")), 198 | ElevatedButton( 199 | onPressed: () async { 200 | loadX5(); 201 | }, 202 | child: Text("重新加载内核")), 203 | Text( 204 | "内核状态:\n${crashInfo == null ? "未加载" : isLoadOk ? "加载成功---\n" + crashInfo.toString() : "加载失败---\n" + crashInfo.toString()}") 205 | ], 206 | ), 207 | ), 208 | ); 209 | } 210 | 211 | void showInputDialog( 212 | {required ConfirmCallBack onConfirm, String defaultText = ""}) { 213 | final _controller = TextEditingController(text: defaultText); 214 | showDialog( 215 | context: context, 216 | builder: (context) { 217 | return AlertDialog( 218 | title: Text("输入链接测试"), 219 | content: TextField( 220 | controller: _controller, 221 | ), 222 | actions: [ 223 | TextButton( 224 | onPressed: () { 225 | Navigator.pop(context); 226 | }, 227 | child: Text("取消")), 228 | TextButton( 229 | onPressed: () async { 230 | Navigator.pop(context); 231 | onConfirm(_controller.text); 232 | }, 233 | child: Text("跳转")) 234 | ], 235 | ); 236 | }); 237 | } 238 | 239 | var isLoad = false; 240 | 241 | void loadX5() async { 242 | if (isLoad) { 243 | showMsg("你已经加载过x5内核了,如果需要重新加载,请重启"); 244 | return; 245 | } 246 | 247 | //请求动态权限,6.0安卓及以上必有 248 | Map statuses = await [ 249 | Permission.phone, 250 | Permission.storage, 251 | ].request(); 252 | //判断权限 253 | if (!(statuses[Permission.phone]!.isGranted && 254 | statuses[Permission.storage]!.isGranted)) { 255 | showDialog( 256 | context: context, 257 | builder: (context) { 258 | return AlertDialog( 259 | content: Text("请同意所有权限后再尝试加载X5"), 260 | actions: [ 261 | TextButton( 262 | onPressed: () { 263 | Navigator.pop(context); 264 | }, 265 | child: Text("取消")), 266 | TextButton( 267 | onPressed: () { 268 | Navigator.pop(context); 269 | loadX5(); 270 | }, 271 | child: Text("再次加载")), 272 | TextButton( 273 | onPressed: () { 274 | Navigator.pop(context); 275 | openAppSettings(); 276 | }, 277 | child: Text("打开设置页面")), 278 | ], 279 | ); 280 | }); 281 | return; 282 | } 283 | 284 | //没有x5内核,是否在非wifi模式下载内核。默认false 285 | await X5Sdk.setDownloadWithoutWifi(true); 286 | 287 | //内核下载安装监听 288 | //int DOWNLOAD_CANCEL_NOT_WIFI 111,非Wi-Fi,不发起下载 setDownloadWithoutWifi(boolean) 进行设置 289 | // int DOWNLOAD_CANCEL_REQUESTING 133,下载请求中,不重复发起,取消下载 290 | // int DOWNLOAD_FLOW_CANCEL -134,带宽不允许,下载取消。Debug阶段可webview访问 debugtbs.qq.com 安装线上内核 291 | // int DOWNLOAD_NO_NEED_REQUEST -122,不发起下载请求,以下触发请求的条件均不符合: 292 | // 1、距离最后请求时间24小时后(可调整系统时间) 293 | // 2、请求成功超过时间间隔,网络原因重试小于11次 294 | // 3、App版本变更 295 | // int DOWNLOAD_SUCCESS 100,内核下载成功 296 | // int INSTALL_FOR_PREINIT_CALLBACK 243,预加载中间态,非异常,可忽略 297 | // int INSTALL_SUCCESS 200,首次安装成功 298 | // int NETWORK_UNAVAILABLE 101,网络不可用 299 | // int STARTDOWNLOAD_OUT_OF_MAXTIME 127,发起下载次数超过1次(一次进程只允许发起一次下载) 300 | await X5Sdk.setX5SdkListener(X5SdkListener(onInstallFinish: (int code) { 301 | print("X5内核安装完成"); 302 | }, onDownloadFinish: (int code) { 303 | print("X5内核下载完成"); 304 | }, onDownloadProgress: (int progress) { 305 | print("X5内核下载中---$progress%"); 306 | })); 307 | print("----开始加载内核----"); 308 | var isOk = await X5Sdk.init(); 309 | print(isOk ? "X5内核成功加载" : "X5内核加载失败"); 310 | 311 | var x5CrashInfo = await X5Sdk.getCrashInfo(); 312 | print(x5CrashInfo); 313 | if (isOk) { 314 | x5CrashInfo = 315 | "tbs_core_version" + x5CrashInfo.split("tbs_core_version")[1]; 316 | } 317 | setState(() { 318 | isLoadOk = isOk; 319 | crashInfo = x5CrashInfo; 320 | }); 321 | 322 | isLoad = true; 323 | } 324 | 325 | void showMsg(String msg) { 326 | showDialog( 327 | context: context, 328 | builder: (context) { 329 | return AlertDialog( 330 | content: Text(msg), 331 | actions: [ 332 | TextButton( 333 | onPressed: () { 334 | Navigator.pop(context); 335 | }, 336 | child: Text("我知道了")) 337 | ], 338 | ); 339 | }); 340 | } 341 | 342 | void openUrl(String url) { 343 | X5Sdk.openWebActivity(url, title: "web页面", callback: (url, headers) { 344 | print("拦截到url================$url"); 345 | print("headers================$headers"); 346 | //创建新的一个web页面,不创建callback传null 347 | openUrl(url); 348 | }); 349 | } 350 | } 351 | 352 | typedef ConfirmCallBack = Function(String url); 353 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 9 | url: "https://pub.flutter-io.cn" 10 | source: hosted 11 | version: "2.11.0" 12 | boolean_selector: 13 | dependency: transitive 14 | description: 15 | name: boolean_selector 16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 17 | url: "https://pub.flutter-io.cn" 18 | source: hosted 19 | version: "2.1.1" 20 | characters: 21 | dependency: transitive 22 | description: 23 | name: characters 24 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 25 | url: "https://pub.flutter-io.cn" 26 | source: hosted 27 | version: "1.3.0" 28 | clock: 29 | dependency: transitive 30 | description: 31 | name: clock 32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 33 | url: "https://pub.flutter-io.cn" 34 | source: hosted 35 | version: "1.1.1" 36 | collection: 37 | dependency: transitive 38 | description: 39 | name: collection 40 | sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 41 | url: "https://pub.flutter-io.cn" 42 | source: hosted 43 | version: "1.17.2" 44 | cupertino_icons: 45 | dependency: "direct main" 46 | description: 47 | name: cupertino_icons 48 | sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be 49 | url: "https://pub.flutter-io.cn" 50 | source: hosted 51 | version: "1.0.5" 52 | dio: 53 | dependency: "direct main" 54 | description: 55 | name: dio 56 | sha256: ce75a1b40947fea0a0e16ce73337122a86762e38b982e1ccb909daa3b9bc4197 57 | url: "https://pub.flutter-io.cn" 58 | source: hosted 59 | version: "5.3.2" 60 | fake_async: 61 | dependency: transitive 62 | description: 63 | name: fake_async 64 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 65 | url: "https://pub.flutter-io.cn" 66 | source: hosted 67 | version: "1.3.1" 68 | ffi: 69 | dependency: transitive 70 | description: 71 | name: ffi 72 | sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" 73 | url: "https://pub.flutter-io.cn" 74 | source: hosted 75 | version: "2.1.0" 76 | file: 77 | dependency: "direct overridden" 78 | description: 79 | name: file 80 | sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" 81 | url: "https://pub.flutter-io.cn" 82 | source: hosted 83 | version: "7.0.0" 84 | flutter: 85 | dependency: "direct main" 86 | description: flutter 87 | source: sdk 88 | version: "0.0.0" 89 | flutter_test: 90 | dependency: "direct dev" 91 | description: flutter 92 | source: sdk 93 | version: "0.0.0" 94 | http_parser: 95 | dependency: transitive 96 | description: 97 | name: http_parser 98 | sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" 99 | url: "https://pub.flutter-io.cn" 100 | source: hosted 101 | version: "4.0.2" 102 | matcher: 103 | dependency: transitive 104 | description: 105 | name: matcher 106 | sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" 107 | url: "https://pub.flutter-io.cn" 108 | source: hosted 109 | version: "0.12.16" 110 | material_color_utilities: 111 | dependency: transitive 112 | description: 113 | name: material_color_utilities 114 | sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" 115 | url: "https://pub.flutter-io.cn" 116 | source: hosted 117 | version: "0.5.0" 118 | meta: 119 | dependency: transitive 120 | description: 121 | name: meta 122 | sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" 123 | url: "https://pub.flutter-io.cn" 124 | source: hosted 125 | version: "1.9.1" 126 | path: 127 | dependency: transitive 128 | description: 129 | name: path 130 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" 131 | url: "https://pub.flutter-io.cn" 132 | source: hosted 133 | version: "1.8.3" 134 | path_provider: 135 | dependency: "direct main" 136 | description: 137 | name: path_provider 138 | sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa 139 | url: "https://pub.flutter-io.cn" 140 | source: hosted 141 | version: "2.1.1" 142 | path_provider_android: 143 | dependency: transitive 144 | description: 145 | name: path_provider_android 146 | sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" 147 | url: "https://pub.flutter-io.cn" 148 | source: hosted 149 | version: "2.2.0" 150 | path_provider_foundation: 151 | dependency: transitive 152 | description: 153 | name: path_provider_foundation 154 | sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" 155 | url: "https://pub.flutter-io.cn" 156 | source: hosted 157 | version: "2.3.1" 158 | path_provider_linux: 159 | dependency: transitive 160 | description: 161 | name: path_provider_linux 162 | sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 163 | url: "https://pub.flutter-io.cn" 164 | source: hosted 165 | version: "2.2.1" 166 | path_provider_platform_interface: 167 | dependency: transitive 168 | description: 169 | name: path_provider_platform_interface 170 | sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" 171 | url: "https://pub.flutter-io.cn" 172 | source: hosted 173 | version: "2.1.1" 174 | path_provider_windows: 175 | dependency: transitive 176 | description: 177 | name: path_provider_windows 178 | sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" 179 | url: "https://pub.flutter-io.cn" 180 | source: hosted 181 | version: "2.2.1" 182 | permission_handler: 183 | dependency: "direct main" 184 | description: 185 | name: permission_handler 186 | sha256: "63e5216aae014a72fe9579ccd027323395ce7a98271d9defa9d57320d001af81" 187 | url: "https://pub.flutter-io.cn" 188 | source: hosted 189 | version: "10.4.3" 190 | permission_handler_android: 191 | dependency: transitive 192 | description: 193 | name: permission_handler_android 194 | sha256: d74e77a5ecd38649905db0a7d05ef16bed42ff263b9efb73ed794317c5764ec3 195 | url: "https://pub.flutter-io.cn" 196 | source: hosted 197 | version: "10.3.4" 198 | permission_handler_apple: 199 | dependency: transitive 200 | description: 201 | name: permission_handler_apple 202 | sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" 203 | url: "https://pub.flutter-io.cn" 204 | source: hosted 205 | version: "9.1.4" 206 | permission_handler_platform_interface: 207 | dependency: transitive 208 | description: 209 | name: permission_handler_platform_interface 210 | sha256: "7c6b1500385dd1d2ca61bb89e2488ca178e274a69144d26bbd65e33eae7c02a9" 211 | url: "https://pub.flutter-io.cn" 212 | source: hosted 213 | version: "3.11.3" 214 | permission_handler_windows: 215 | dependency: transitive 216 | description: 217 | name: permission_handler_windows 218 | sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 219 | url: "https://pub.flutter-io.cn" 220 | source: hosted 221 | version: "0.1.3" 222 | platform: 223 | dependency: transitive 224 | description: 225 | name: platform 226 | sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" 227 | url: "https://pub.flutter-io.cn" 228 | source: hosted 229 | version: "3.1.0" 230 | plugin_platform_interface: 231 | dependency: transitive 232 | description: 233 | name: plugin_platform_interface 234 | sha256: "075f927ebbab4262ace8d0b283929ac5410c0ac4e7fc123c76429564facfb757" 235 | url: "https://pub.flutter-io.cn" 236 | source: hosted 237 | version: "2.1.2" 238 | process: 239 | dependency: transitive 240 | description: 241 | name: process 242 | sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" 243 | url: "https://pub.flutter-io.cn" 244 | source: hosted 245 | version: "4.2.4" 246 | sky_engine: 247 | dependency: transitive 248 | description: flutter 249 | source: sdk 250 | version: "0.0.99" 251 | source_span: 252 | dependency: transitive 253 | description: 254 | name: source_span 255 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 256 | url: "https://pub.flutter-io.cn" 257 | source: hosted 258 | version: "1.10.0" 259 | stack_trace: 260 | dependency: transitive 261 | description: 262 | name: stack_trace 263 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 264 | url: "https://pub.flutter-io.cn" 265 | source: hosted 266 | version: "1.11.0" 267 | stream_channel: 268 | dependency: transitive 269 | description: 270 | name: stream_channel 271 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" 272 | url: "https://pub.flutter-io.cn" 273 | source: hosted 274 | version: "2.1.1" 275 | string_scanner: 276 | dependency: transitive 277 | description: 278 | name: string_scanner 279 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 280 | url: "https://pub.flutter-io.cn" 281 | source: hosted 282 | version: "1.2.0" 283 | term_glyph: 284 | dependency: transitive 285 | description: 286 | name: term_glyph 287 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 288 | url: "https://pub.flutter-io.cn" 289 | source: hosted 290 | version: "1.2.1" 291 | test_api: 292 | dependency: transitive 293 | description: 294 | name: test_api 295 | sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" 296 | url: "https://pub.flutter-io.cn" 297 | source: hosted 298 | version: "0.6.0" 299 | typed_data: 300 | dependency: transitive 301 | description: 302 | name: typed_data 303 | sha256: "53bdf7e979cfbf3e28987552fd72f637e63f3c8724c9e56d9246942dc2fa36ee" 304 | url: "https://pub.flutter-io.cn" 305 | source: hosted 306 | version: "1.3.0" 307 | vector_math: 308 | dependency: transitive 309 | description: 310 | name: vector_math 311 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 312 | url: "https://pub.flutter-io.cn" 313 | source: hosted 314 | version: "2.1.4" 315 | web: 316 | dependency: transitive 317 | description: 318 | name: web 319 | sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 320 | url: "https://pub.flutter-io.cn" 321 | source: hosted 322 | version: "0.1.4-beta" 323 | webview_flutter: 324 | dependency: "direct main" 325 | description: 326 | name: webview_flutter 327 | sha256: "04a0782fb058b7c71f2048935583488f4d32e9147ca403abc4e58f1de9964629" 328 | url: "https://pub.flutter-io.cn" 329 | source: hosted 330 | version: "4.2.3" 331 | webview_flutter_android: 332 | dependency: transitive 333 | description: 334 | name: webview_flutter_android 335 | sha256: "0d8f5ac96a155e672129bf94c7abf625de01241d44d269dbaff083f1b4deb1aa" 336 | url: "https://pub.flutter-io.cn" 337 | source: hosted 338 | version: "3.9.5" 339 | webview_flutter_platform_interface: 340 | dependency: transitive 341 | description: 342 | name: webview_flutter_platform_interface 343 | sha256: "0ca3cfcc6781a7de701d580917af4a9efc4e3e129f8ead95a80587f0a749480a" 344 | url: "https://pub.flutter-io.cn" 345 | source: hosted 346 | version: "2.5.0" 347 | webview_flutter_wkwebview: 348 | dependency: transitive 349 | description: 350 | name: webview_flutter_wkwebview 351 | sha256: ed749f94ac9e814d04a258a9255cf69cfa4cc6006ff59542aea7fb4590144972 352 | url: "https://pub.flutter-io.cn" 353 | source: hosted 354 | version: "3.7.3" 355 | win32: 356 | dependency: transitive 357 | description: 358 | name: win32 359 | sha256: "9e82a402b7f3d518fb9c02d0e9ae45952df31b9bf34d77baf19da2de03fc2aaa" 360 | url: "https://pub.flutter-io.cn" 361 | source: hosted 362 | version: "5.0.7" 363 | x5_webview: 364 | dependency: "direct main" 365 | description: 366 | path: ".." 367 | relative: true 368 | source: path 369 | version: "0.4.0" 370 | xdg_directories: 371 | dependency: transitive 372 | description: 373 | name: xdg_directories 374 | sha256: "060b6e1c891d956f72b5ac9463466c37cce3fa962a921532fc001e86fe93438e" 375 | url: "https://pub.flutter-io.cn" 376 | source: hosted 377 | version: "0.2.0+1" 378 | sdks: 379 | dart: ">=3.1.0 <4.0.0" 380 | flutter: ">=3.13.0" 381 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: x5_webview_example 2 | description: Demonstrates how to use the x5_webview plugin. 3 | publish_to: 'none' 4 | 5 | version: 0.4.0+40 6 | 7 | environment: 8 | sdk: ">=3.1.0 <4.0.0" 9 | 10 | dependency_overrides: 11 | file: 7.0.0 12 | 13 | dependencies: 14 | flutter: 15 | sdk: flutter 16 | cupertino_icons: ^1.0.2 17 | 18 | dio: ^5.3.2 19 | webview_flutter: ^4.2.3 20 | path_provider: ^2.1.0 21 | permission_handler: ^10.4.3 22 | x5_webview: 23 | path: ../ 24 | 25 | dev_dependencies: 26 | flutter_test: 27 | sdk: flutter 28 | 29 | flutter: 30 | uses-material-design: true 31 | assets: 32 | - assets/ 33 | 34 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VenusCao/x5_webview_flutter/7b3394d7b9fa0d42329b0ab92f5f3fc562d452e5/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/SwiftX5WebviewPlugin.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | 4 | public class SwiftX5WebviewPlugin: NSObject, FlutterPlugin { 5 | public static func register(with registrar: FlutterPluginRegistrar) { 6 | let channel = FlutterMethodChannel(name: "x5_webview", binaryMessenger: registrar.messenger()) 7 | let instance = SwiftX5WebviewPlugin() 8 | registrar.addMethodCallDelegate(instance, channel: channel) 9 | } 10 | 11 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 12 | result("iOS " + UIDevice.current.systemVersion) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ios/Classes/X5WebviewPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface X5WebviewPlugin : NSObject 4 | @end 5 | -------------------------------------------------------------------------------- /ios/Classes/X5WebviewPlugin.m: -------------------------------------------------------------------------------- 1 | #import "X5WebviewPlugin.h" 2 | #import 3 | 4 | @implementation X5WebviewPlugin 5 | + (void)registerWithRegistrar:(NSObject*)registrar { 6 | [SwiftX5WebviewPlugin registerWithRegistrar:registrar]; 7 | } 8 | @end 9 | -------------------------------------------------------------------------------- /ios/x5_webview.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 3 | # 4 | Pod::Spec.new do |s| 5 | s.name = 'x5_webview' 6 | s.version = '0.0.1' 7 | s.summary = 'A new Flutter plugin.' 8 | s.description = <<-DESC 9 | A new Flutter plugin. 10 | DESC 11 | s.homepage = 'http://example.com' 12 | s.license = { :file => '../LICENSE' } 13 | s.author = { 'Your Company' => 'email@example.com' } 14 | s.source = { :path => '.' } 15 | s.source_files = 'Classes/**/*' 16 | s.public_header_files = 'Classes/**/*.h' 17 | s.dependency 'Flutter' 18 | 19 | s.ios.deployment_target = '8.0' 20 | end 21 | 22 | -------------------------------------------------------------------------------- /lib/x5_sdk.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/services.dart'; 5 | 6 | class X5Sdk { 7 | static const MethodChannel _channel = const MethodChannel('com.cjx/x5Video'); 8 | 9 | ///加载内核,没有内核会自动下载,加载失败会自动调用系统内核。 10 | ///不要重复请求。如需要重新加载可重启应用 11 | ///android 6.0+调用之前需动态请求权限(电话和存储权限) 12 | static Future init() async { 13 | if (defaultTargetPlatform == TargetPlatform.android) { 14 | bool res = await _channel.invokeMethod("init"); 15 | return res; 16 | } else { 17 | return false; 18 | } 19 | } 20 | 21 | ///获取x5的日志 22 | static Future getCrashInfo() async { 23 | if (defaultTargetPlatform == TargetPlatform.android) { 24 | var res = await _channel.invokeMethod("getCarshInfo"); 25 | return res; 26 | } else { 27 | return ""; 28 | } 29 | } 30 | 31 | ///设置是否在非wifi环境下载内核,默认false 32 | static Future setDownloadWithoutWifi(bool isDownloadWithoutWifi) async { 33 | if (defaultTargetPlatform == TargetPlatform.android) { 34 | final Map params = { 35 | 'isWithoutWifi': isDownloadWithoutWifi, 36 | }; 37 | 38 | await _channel.invokeMethod("setDownloadWithoutWifi", params); 39 | return true; 40 | } else { 41 | return false; 42 | } 43 | } 44 | 45 | ///是否能直接使用x5内核播放视频 46 | static Future canUseTbsPlayer() async { 47 | if (defaultTargetPlatform == TargetPlatform.android) { 48 | bool res = await _channel.invokeMethod("canUseTbsPlayer"); 49 | return res; 50 | } else { 51 | return false; 52 | } 53 | } 54 | 55 | ///screenMode 播放参数, 56 | ///102竖屏全屏(第一次点击全屏无效) 57 | ///103横屏全屏(暂停后会报错), 58 | ///104竖屏全屏(默认) 59 | ///105竖屏全屏(双击后才会出现面板) 60 | ///默认104 61 | static Future openVideo(String url, {int screenMode = 104}) async { 62 | if (defaultTargetPlatform == TargetPlatform.android) { 63 | final Map params = {'screenMode': screenMode, 'url': url}; 64 | return await _channel.invokeMethod("openVideo", params); 65 | } else { 66 | return; 67 | } 68 | } 69 | 70 | ///打开简单的x5webview 71 | static Future openWebActivity(String url, 72 | {String? title, Map? headers, InterceptUrlCallBack? callback}) async { 73 | if (defaultTargetPlatform == TargetPlatform.android) { 74 | final Map params = { 75 | 'title': title ?? "", 76 | 'url': url, 77 | 'headers': headers ?? {}, 78 | 'isUrlIntercept': callback != null 79 | }; 80 | if (callback != null) { 81 | _channel.setMethodCallHandler((call) async { 82 | try { 83 | if (call.method == "onUrlLoad") { 84 | print("onUrlLoad----${call.arguments}"); 85 | Map arg = call.arguments; 86 | callback(arg["url"], Map.from(arg["headers"])); 87 | } 88 | } catch (e) { 89 | print(e); 90 | } 91 | }); 92 | } 93 | 94 | return await _channel.invokeMethod("openWebActivity", params); 95 | } else { 96 | return; 97 | } 98 | } 99 | 100 | ///设置内核下载安装事件 101 | static Future setX5SdkListener(X5SdkListener listener) async { 102 | _channel.setMethodCallHandler((call) async { 103 | switch (call.method) { 104 | case "onInstallFinish": 105 | listener.onInstallFinish(call.arguments); 106 | break; 107 | case "onDownloadFinish": 108 | listener.onDownloadFinish(call.arguments); 109 | break; 110 | case "onDownloadProgress": 111 | listener.onDownloadProgress(call.arguments); 112 | break; 113 | default: 114 | throw MissingPluginException('${call.method} was invoked but has no handler'); 115 | } 116 | }); 117 | } 118 | } 119 | 120 | typedef void InstallFinish(int code); 121 | typedef void DownloadFinish(int code); 122 | typedef void DownloadProgress(int progress); 123 | 124 | typedef void InterceptUrlCallBack(String url, Map headers); 125 | 126 | ///X5内核的下载和安装监听 127 | /// 128 | //int DOWNLOAD_CANCEL_NOT_WIFI 111,非Wi-Fi,不发起下载 setDownloadWithoutWifi(boolean) 进行设置 129 | // int DOWNLOAD_CANCEL_REQUESTING 133,下载请求中,不重复发起,取消下载 130 | // int DOWNLOAD_FLOW_CANCEL -134,带宽不允许,下载取消。Debug阶段可webview访问 debugtbs.qq.com 安装线上内核 131 | // int DOWNLOAD_NO_NEED_REQUEST -122,不发起下载请求,以下触发请求的条件均不符合: 132 | // 1、距离最后请求时间24小时后(可调整系统时间) 133 | // 2、请求成功超过时间间隔,网络原因重试小于11次 134 | // 3、App版本变更 135 | // int DOWNLOAD_SUCCESS 100,内核下载成功 136 | // int INSTALL_FOR_PREINIT_CALLBACK 243,预加载中间态,非异常,可忽略 137 | // int INSTALL_SUCCESS 200,首次安装成功 138 | // int NETWORK_UNAVAILABLE 101,网络不可用 139 | // int STARTDOWNLOAD_OUT_OF_MAXTIME 127,发起下载次数超过1次(一次进程只允许发起一次下载) 140 | class X5SdkListener { 141 | ///安装完成监听 142 | InstallFinish onInstallFinish; 143 | 144 | ///下载完成监听 145 | DownloadFinish onDownloadFinish; 146 | 147 | ///下载进度监听 148 | DownloadProgress onDownloadProgress; 149 | 150 | X5SdkListener({required this.onInstallFinish, required this.onDownloadFinish, required this.onDownloadProgress}); 151 | } 152 | -------------------------------------------------------------------------------- /lib/x5_webview.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/gestures.dart'; 5 | import 'package:flutter/rendering.dart'; 6 | import 'package:flutter/services.dart'; 7 | import 'package:flutter/widgets.dart'; 8 | 9 | typedef void X5WebViewCreatedCallback(X5WebViewController controller); 10 | typedef void PageFinishedCallback(); 11 | typedef void ShowCustomViewCallback(); 12 | typedef void HideCustomViewCallback(); 13 | typedef void ProgressChangedCallback(int progress); 14 | typedef void MessageReceived(String name, String data); 15 | typedef void UrlLoading(String url); 16 | 17 | class X5WebView extends StatefulWidget { 18 | final url; 19 | final X5WebViewCreatedCallback? onWebViewCreated; 20 | final PageFinishedCallback? onPageFinished; 21 | final ShowCustomViewCallback? onShowCustomView; 22 | final HideCustomViewCallback? onHideCustomView; 23 | final ProgressChangedCallback? onProgressChanged; 24 | final bool javaScriptEnabled; 25 | final bool domStorageEnabled; 26 | final JavascriptChannels? javascriptChannels; 27 | final UrlLoading? onUrlLoading; 28 | final Map? header; 29 | final String? userAgentString; 30 | final bool useExpensiveAndroidView; 31 | 32 | const X5WebView( 33 | {Key? key, 34 | this.url, 35 | this.javaScriptEnabled = true, 36 | this.onWebViewCreated, 37 | this.onPageFinished, 38 | this.onShowCustomView, 39 | this.onHideCustomView, 40 | this.javascriptChannels, 41 | this.onProgressChanged, 42 | this.onUrlLoading, 43 | this.header, 44 | this.domStorageEnabled = true, 45 | this.useExpensiveAndroidView = false, 46 | this.userAgentString}) 47 | : super(key: key); 48 | 49 | @override 50 | _X5WebViewState createState() => _X5WebViewState(); 51 | } 52 | 53 | class _X5WebViewState extends State { 54 | @override 55 | Widget build(BuildContext context) { 56 | if (defaultTargetPlatform == TargetPlatform.android) { 57 | //输入兼容性不好 58 | // return AndroidView( 59 | // viewType: 'com.cjx/x5WebView', 60 | // onPlatformViewCreated: _onPlatformViewCreated, 61 | // creationParamsCodec: const StandardMessageCodec(), 62 | // creationParams: _CreationParams.fromWidget(widget).toMap(), 63 | // layoutDirection: TextDirection.rtl, 64 | // ); 65 | // } else if (defaultTargetPlatform == TargetPlatform.iOS) { 66 | // // 添加ios WebView 67 | // return Container(); 68 | // } else { 69 | // return Container(); 70 | return PlatformViewLink( 71 | viewType: "com.cjx/x5WebView", 72 | surfaceFactory: (_, controller) { 73 | return AndroidViewSurface( 74 | controller: controller as AndroidViewController, 75 | gestureRecognizers: const >{}, 76 | hitTestBehavior: PlatformViewHitTestBehavior.opaque, 77 | ); 78 | }, 79 | onCreatePlatformView: (params) { 80 | if (widget.useExpensiveAndroidView) { 81 | PlatformViewsService.initExpensiveAndroidView( 82 | id: params.id, 83 | viewType: 'com.cjx/x5WebView', 84 | // WebView content is not affected by the Android view's layout direction, 85 | // we explicitly set it here so that the widget doesn't require an ambient 86 | // directionality. 87 | layoutDirection: TextDirection.ltr, 88 | creationParams: _CreationParams.fromWidget(widget).toMap(), 89 | creationParamsCodec: const StandardMessageCodec(), 90 | ) 91 | ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated) 92 | ..addOnPlatformViewCreatedListener((int id) { 93 | _onPlatformViewCreated(id); 94 | }) 95 | ..create(); 96 | } 97 | 98 | return PlatformViewsService.initSurfaceAndroidView( 99 | id: params.id, 100 | viewType: 'com.cjx/x5WebView', 101 | // WebView content is not affected by the Android view's layout direction, 102 | // we explicitly set it here so that the widget doesn't require an ambient 103 | // directionality. 104 | layoutDirection: TextDirection.ltr, 105 | creationParams: _CreationParams.fromWidget(widget).toMap(), 106 | creationParamsCodec: const StandardMessageCodec(), 107 | ) 108 | ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated) 109 | ..addOnPlatformViewCreatedListener((int id) { 110 | _onPlatformViewCreated(id); 111 | }) 112 | ..create(); 113 | }, 114 | ); 115 | } else { 116 | return Container(); 117 | } 118 | } 119 | 120 | void _onPlatformViewCreated(int id) { 121 | if (widget.onWebViewCreated == null) { 122 | return; 123 | } 124 | final X5WebViewController controller = X5WebViewController._(id, widget); 125 | widget.onWebViewCreated!(controller); 126 | } 127 | } 128 | 129 | class X5WebViewController { 130 | X5WebView _widget; 131 | 132 | X5WebViewController._( 133 | int id, 134 | this._widget, 135 | ) : _channel = MethodChannel('com.cjx/x5WebView_$id') { 136 | _channel.setMethodCallHandler(_onMethodCall); 137 | } 138 | 139 | final MethodChannel _channel; 140 | 141 | Future loadUrl(String url, {Map? headers}) async { 142 | return await _channel.invokeMethod('loadUrl', { 143 | 'url': url, 144 | 'headers': headers, 145 | }); 146 | } 147 | 148 | Future isX5WebViewLoadSuccess() async { 149 | return await _channel.invokeMethod('isX5WebViewLoadSuccess'); 150 | } 151 | 152 | Future evaluateJavascript(String js) async { 153 | return await _channel.invokeMethod('evaluateJavascript', { 154 | 'js': js, 155 | }); 156 | } 157 | 158 | /// 直接使用X5WebView(javascriptChannels:JavascriptChannels(names, (name, data) { })) 159 | @deprecated 160 | Future addJavascriptChannels( 161 | List names, MessageReceived callback) async { 162 | await _channel.invokeMethod("addJavascriptChannels", {'names': names}); 163 | _channel.setMethodCallHandler((call) async { 164 | if (call.method == "onJavascriptChannelCallBack") { 165 | Map arg = call.arguments; 166 | callback(arg["name"], arg["msg"]); 167 | } 168 | }); 169 | } 170 | 171 | Future goBackOrForward(int i) async { 172 | return await _channel.invokeMethod('goBackOrForward', { 173 | 'i': i, 174 | }); 175 | } 176 | 177 | Future canGoBack() async { 178 | return await _channel.invokeMethod('canGoBack'); 179 | } 180 | 181 | Future canGoForward() async { 182 | return await _channel.invokeMethod('canGoForward'); 183 | } 184 | 185 | Future goBack() async { 186 | return await _channel.invokeMethod('goBack'); 187 | } 188 | 189 | Future goForward() async { 190 | return await _channel.invokeMethod('goForward'); 191 | } 192 | 193 | Future reload() async { 194 | return await _channel.invokeMethod('reload'); 195 | } 196 | 197 | Future currentUrl() async { 198 | return await _channel.invokeMethod('currentUrl'); 199 | } 200 | 201 | Future _onMethodCall(MethodCall call) async { 202 | switch (call.method) { 203 | case "onPageFinished": 204 | if (_widget.onPageFinished != null) { 205 | _widget.onPageFinished!(); 206 | } 207 | break; 208 | case "onJavascriptChannelCallBack": 209 | if (_widget.javascriptChannels?.callback != null) { 210 | Map arg = call.arguments; 211 | _widget.javascriptChannels?.callback(arg["name"], arg["msg"]); 212 | } 213 | break; 214 | case "onShowCustomView": 215 | if (_widget.onShowCustomView != null) { 216 | _widget.onShowCustomView!(); 217 | } 218 | break; 219 | case "onHideCustomView": 220 | if (_widget.onHideCustomView != null) { 221 | _widget.onHideCustomView!(); 222 | } 223 | break; 224 | case "onProgressChanged": 225 | if (_widget.onProgressChanged != null) { 226 | Map arg = call.arguments; 227 | _widget.onProgressChanged!(arg["progress"]); 228 | } 229 | break; 230 | case "onUrlLoading": 231 | if (_widget.onUrlLoading != null) { 232 | Map arg = call.arguments; 233 | _widget.onUrlLoading!(arg["url"]); 234 | } 235 | break; 236 | 237 | default: 238 | throw MissingPluginException( 239 | '${call.method} was invoked but has no handler'); 240 | } 241 | } 242 | } 243 | 244 | class _CreationParams { 245 | _CreationParams( 246 | {required this.url, 247 | this.javaScriptEnabled = true, 248 | this.domStorageEnabled = true, 249 | this.javascriptChannels, 250 | this.urlInterceptEnabled = false, 251 | this.header, 252 | this.userAgentString}); 253 | 254 | static _CreationParams fromWidget(X5WebView widget) { 255 | return _CreationParams( 256 | url: widget.url, 257 | javaScriptEnabled: widget.javaScriptEnabled, 258 | domStorageEnabled: widget.domStorageEnabled, 259 | javascriptChannels: widget.javascriptChannels?.names, 260 | urlInterceptEnabled: widget.onUrlLoading != null, 261 | userAgentString: widget.userAgentString, 262 | header: widget.header); 263 | } 264 | 265 | final String url; 266 | final bool javaScriptEnabled; 267 | final bool domStorageEnabled; 268 | final List? javascriptChannels; 269 | final Map? header; 270 | final bool urlInterceptEnabled; 271 | final String? userAgentString; 272 | 273 | Map toMap() { 274 | return { 275 | 'url': url, 276 | 'javaScriptEnabled': javaScriptEnabled, 277 | 'domStorageEnabled': domStorageEnabled, 278 | "javascriptChannels": javascriptChannels, 279 | "urlInterceptEnabled": urlInterceptEnabled, 280 | "header": header, 281 | "userAgentString": userAgentString 282 | }; 283 | } 284 | } 285 | 286 | class JavascriptChannels { 287 | List names; 288 | MessageReceived callback; 289 | 290 | JavascriptChannels(this.names, this.callback); 291 | } 292 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 9 | url: "https://pub.flutter-io.cn" 10 | source: hosted 11 | version: "2.11.0" 12 | boolean_selector: 13 | dependency: transitive 14 | description: 15 | name: boolean_selector 16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 17 | url: "https://pub.flutter-io.cn" 18 | source: hosted 19 | version: "2.1.1" 20 | characters: 21 | dependency: transitive 22 | description: 23 | name: characters 24 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 25 | url: "https://pub.flutter-io.cn" 26 | source: hosted 27 | version: "1.3.0" 28 | clock: 29 | dependency: transitive 30 | description: 31 | name: clock 32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 33 | url: "https://pub.flutter-io.cn" 34 | source: hosted 35 | version: "1.1.1" 36 | collection: 37 | dependency: transitive 38 | description: 39 | name: collection 40 | sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 41 | url: "https://pub.flutter-io.cn" 42 | source: hosted 43 | version: "1.17.2" 44 | fake_async: 45 | dependency: transitive 46 | description: 47 | name: fake_async 48 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 49 | url: "https://pub.flutter-io.cn" 50 | source: hosted 51 | version: "1.3.1" 52 | flutter: 53 | dependency: "direct main" 54 | description: flutter 55 | source: sdk 56 | version: "0.0.0" 57 | flutter_test: 58 | dependency: "direct dev" 59 | description: flutter 60 | source: sdk 61 | version: "0.0.0" 62 | matcher: 63 | dependency: transitive 64 | description: 65 | name: matcher 66 | sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" 67 | url: "https://pub.flutter-io.cn" 68 | source: hosted 69 | version: "0.12.16" 70 | material_color_utilities: 71 | dependency: transitive 72 | description: 73 | name: material_color_utilities 74 | sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" 75 | url: "https://pub.flutter-io.cn" 76 | source: hosted 77 | version: "0.5.0" 78 | meta: 79 | dependency: transitive 80 | description: 81 | name: meta 82 | sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" 83 | url: "https://pub.flutter-io.cn" 84 | source: hosted 85 | version: "1.9.1" 86 | path: 87 | dependency: transitive 88 | description: 89 | name: path 90 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" 91 | url: "https://pub.flutter-io.cn" 92 | source: hosted 93 | version: "1.8.3" 94 | sky_engine: 95 | dependency: transitive 96 | description: flutter 97 | source: sdk 98 | version: "0.0.99" 99 | source_span: 100 | dependency: transitive 101 | description: 102 | name: source_span 103 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 104 | url: "https://pub.flutter-io.cn" 105 | source: hosted 106 | version: "1.10.0" 107 | stack_trace: 108 | dependency: transitive 109 | description: 110 | name: stack_trace 111 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 112 | url: "https://pub.flutter-io.cn" 113 | source: hosted 114 | version: "1.11.0" 115 | stream_channel: 116 | dependency: transitive 117 | description: 118 | name: stream_channel 119 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" 120 | url: "https://pub.flutter-io.cn" 121 | source: hosted 122 | version: "2.1.1" 123 | string_scanner: 124 | dependency: transitive 125 | description: 126 | name: string_scanner 127 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 128 | url: "https://pub.flutter-io.cn" 129 | source: hosted 130 | version: "1.2.0" 131 | term_glyph: 132 | dependency: transitive 133 | description: 134 | name: term_glyph 135 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 136 | url: "https://pub.flutter-io.cn" 137 | source: hosted 138 | version: "1.2.1" 139 | test_api: 140 | dependency: transitive 141 | description: 142 | name: test_api 143 | sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" 144 | url: "https://pub.flutter-io.cn" 145 | source: hosted 146 | version: "0.6.0" 147 | vector_math: 148 | dependency: transitive 149 | description: 150 | name: vector_math 151 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 152 | url: "https://pub.flutter-io.cn" 153 | source: hosted 154 | version: "2.1.4" 155 | web: 156 | dependency: transitive 157 | description: 158 | name: web 159 | sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 160 | url: "https://pub.flutter-io.cn" 161 | source: hosted 162 | version: "0.1.4-beta" 163 | sdks: 164 | dart: ">=3.1.0 <4.0.0" 165 | flutter: ">=3.13.0" 166 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: x5_webview 2 | description: A Webview Flutter plugin of Tencent X5 Kernel,Improving WebView browsing experience,Support Android Platform Only. 3 | version: 0.4.0 4 | #author: caojinxing <793710663@qq.com> 5 | homepage: https://github.com/VenusCao/x5_webview_flutter 6 | issue_tracker: https://github.com/VenusCao/x5_webview_flutter/issues 7 | 8 | environment: 9 | sdk: ">=3.1.0 <4.0.0" 10 | flutter: ">=3.13.0" 11 | 12 | dependencies: 13 | flutter: 14 | sdk: flutter 15 | 16 | dev_dependencies: 17 | flutter_test: 18 | sdk: flutter 19 | 20 | flutter: 21 | plugin: 22 | platforms: 23 | android: 24 | package: com.cjx.x5_webview 25 | pluginClass: X5WebViewPlugin 26 | -------------------------------------------------------------------------------- /test/x5_webview_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('x5_webview'); 6 | 7 | setUp(() { 8 | channel.setMockMethodCallHandler((MethodCall methodCall) async { 9 | return '42'; 10 | }); 11 | }); 12 | 13 | tearDown(() { 14 | channel.setMockMethodCallHandler(null); 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /x5_webview.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | --------------------------------------------------------------------------------