├── .gitignore ├── .metadata ├── LICENSE ├── README.md ├── android ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── yechaoa │ │ │ │ └── wanandroid_flutter │ │ │ │ └── MainActivity.java │ │ └── res │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_logo.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_logo.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_logo.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_logo.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_logo.png │ │ │ ├── values │ │ │ └── styles.xml │ │ │ └── xml │ │ │ └── network_security_config.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── key.properties ├── key │ ├── app-release.apk │ └── key.jks └── settings.gradle ├── apk └── app-debug.apk ├── ios ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ └── contents.xcworkspacedata └── Runner │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── 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 │ └── main.m ├── lib ├── common │ └── api.dart ├── entity │ ├── article_entity.dart │ ├── banner_entity.dart │ ├── common_entity.dart │ ├── hot_key_entity.dart │ ├── navi_entity.dart │ ├── project_entity.dart │ ├── project_list_entity.dart │ ├── tree_entity.dart │ └── user_entity.dart ├── entity_factory.dart ├── http │ └── httpUtil.dart ├── main.dart ├── pages │ ├── CollectPage.dart │ ├── about.dart │ ├── articleDetail.dart │ ├── homePage.dart │ ├── loginPage.dart │ ├── naviPage.dart │ ├── projectPage.dart │ ├── searchPage.dart │ ├── treeDetailPage.dart │ └── treePage.dart ├── res │ ├── colors.dart │ ├── fonts │ │ └── mononoki-Regular.ttf │ ├── images │ │ ├── a.jpg │ │ └── ic_logo.png │ └── strings.dart └── util │ ├── ToastUtil.dart │ ├── favoriteProvide.dart │ └── themeProvide.dart ├── pubspec.lock ├── pubspec.yaml ├── screenshot ├── android │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ └── 8.png └── ios │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ └── 8.png └── test └── widget_test.dart /.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 | -------------------------------------------------------------------------------- /.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: 8661d8aecd626f7f57ccbcb735553edc05a2e713 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /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 [yyyy] [name of copyright owner] 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 | # :collision:玩安卓 Flutter版本 2 | 3 | ![](https://img.shields.io/badge/CSDN-yechaoa-green.svg) 4 | ![](https://img.shields.io/badge/掘金-yechaoa-green.svg) 5 | ![](https://img.shields.io/badge/language-dart-orange.svg) 6 | ![](https://img.shields.io/hexpm/l/plug.svg) 7 | 8 | 9 | * [玩安卓 Flutter版本](https://github.com/yechaoa/wanandroid_flutter) 10 | * [玩安卓 Java版本](https://github.com/yechaoa/wanandroid_java) 11 | * [玩安卓 小程序版本](https://github.com/yechaoa/wanandroid_mini) 12 | * [玩安卓 Kotlin版本](https://github.com/yechaoa/wanandroid_kotlin) 13 | * [玩安卓 Jetpack版本](https://github.com/yechaoa/wanandroid_jetpack) 14 | 15 | 16 | `微信`:y819423475,感谢`鸿洋`提供的api。 17 | 18 | 19 | # ✍更新记录【2023-04-02】 20 | * Flutter:1.7.8+hotfix.3 -> 3.7.9 21 | * Dart:2.4.0 -> 2.19.6 22 | * 升级记录文档,[点这里](https://juejin.cn/post/7217435047489404985/) 23 | * 下载APK体验,[点这里](https://github.com/yechaoa/wanandroid_flutter/blob/master/apk/app-debug.apk) 24 | 25 | 26 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9fdd35b9e3d24088a6e2d53aaef5ff92~tplv-k3u1fbpfcp-zoom-1.image) 27 | 28 | # :fire:截图 29 | 30 | ### IOS效果 31 | 32 | | | | | | 33 | | :--: | :--: | :--: | :--: | 34 | | 登录注册 | 首页 | 合并列表 | 左右菜单 | 35 | 36 | | | | | | 37 | | :--: | :--: | :--: | :--: | 38 | | 左右滑动列表 | 搜索 | 侧边栏 | 关于 | 39 | 40 | ### Android效果 41 | 42 | | | | | | 43 | | :--: | :--: | :--: | :--: | 44 | | 侧滑菜单 | 首页 | 合并列表 | 左右菜单 | 45 | 46 | | | | | | 47 | | :--: | :--: | :--: | :--: | 48 | | 左右滑动列表 | 登录注册 | 搜索 | 分享 | 49 | 50 | 51 | 52 | # :beers:你能学到 53 | 54 | * [BottomNavigationBar 底部菜单](https://blog.csdn.net/yechaoa/article/details/89880284) 55 | * [FlutterJsonBeanFactory Json解析](https://blog.csdn.net/yechaoa/article/details/90035254) 56 | * [WebView 加载网页](https://blog.csdn.net/yechaoa/article/details/90175271) 57 | * [dio 网络请求](https://blog.csdn.net/yechaoa/article/details/90234708) 58 | * [ExpansionPanelList 可折叠列表](https://blog.csdn.net/yechaoa/article/details/90376584) 59 | * [Wrap 流布局](https://blog.csdn.net/yechaoa/article/details/90403760) 60 | * [Chip 标签](https://blog.csdn.net/yechaoa/article/details/90405997) 61 | * [TabBar 顶部菜单](https://blog.csdn.net/yechaoa/article/details/90482127) 62 | * [Card 卡片](https://blog.csdn.net/yechaoa/article/details/90483097) 63 | * [banner 轮播图](https://blog.csdn.net/yechaoa/article/details/90643476) 64 | * [Drawer 侧边栏](https://blog.csdn.net/yechaoa/article/details/90607772) 65 | * [SliverAppBar 可滑动折叠的AppBar](https://blog.csdn.net/yechaoa/article/details/90701321) 66 | * [PopupMenuButton 菜单popup](https://blog.csdn.net/yechaoa/article/details/90704165) 67 | * [Share 分享功能](https://blog.csdn.net/yechaoa/article/details/93980749) 68 | * [TextField 输入框](https://blog.csdn.net/yechaoa/article/details/90906689) 69 | * [font 自定义字体](https://blog.csdn.net/yechaoa/article/details/90906689) 70 | * [provide 状态管理](https://blog.csdn.net/yechaoa/article/details/97790854) 71 | * [theme 切换主题](https://blog.csdn.net/yechaoa/article/details/97918930) 72 | * [shared_preferences 本地存储](https://blog.csdn.net/yechaoa/article/details/97939357) 73 | * [Dismissible 滑动删除](https://blog.csdn.net/yechaoa/article/details/98081275) 74 | * [RefreshIndicator 下拉刷新](https://blog.csdn.net/yechaoa/article/details/98193911) 75 | * [Stack 重叠布局](https://blog.csdn.net/yechaoa/article/details/99302810) 76 | * [Flutter 打包发布](https://blog.csdn.net/yechaoa/article/details/99941335) 77 | * 等等 78 | 79 | 80 | # :point_right:感谢 81 | 82 | * [fluttertoast](https://github.com/PonnamKarthik/FlutterToast) 83 | * [dio](https://github.com/flutterchina/dio) 84 | * [cookie_jar](https://github.com/flutterchina/cookie_jar) 85 | * [flutter_webview_plugin](https://pub.dev/packages/flutter_webview_plugin#-readme-tab) 86 | * [flutter_swiper](https://github.com/best-flutter/flutter_swiper) 87 | * [share](https://github.com/flutter/plugins/tree/master/packages/share) 88 | * [provide](https://github.com/google/flutter-provide) 89 | * [shared_preferences](https://github.com/flutter/plugins/tree/master/packages/shared_preferences) 90 | * [flutter_easyrefresh](https://github.com/xuelongqy/flutter_easyrefresh) 91 | 92 | # :package:环境配置 93 | 94 | * [Flutter macOS 环境配置](https://blog.csdn.net/yechaoa/article/details/95389931) 95 | * [Flutter Windows 环境配置](https://blog.csdn.net/yechaoa/article/details/89150852) 96 | 97 | 98 |
99 | 100 | 101 | ``` 102 | Copyright [2019] [yechaoa] 103 | 104 | Licensed under the Apache License, Version 2.0 (the "License"); 105 | you may not use this file except in compliance with the License. 106 | You may obtain a copy of the License at 107 | 108 | http://www.apache.org/licenses/LICENSE-2.0 109 | 110 | Unless required by applicable law or agreed to in writing, software 111 | distributed under the License is distributed on an "AS IS" BASIS, 112 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 113 | See the License for the specific language governing permissions and 114 | limitations under the License. 115 | ``` 116 | -------------------------------------------------------------------------------- /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 keystoreProperties = new Properties() 10 | def keystorePropertiesFile = rootProject.file('key.properties') 11 | if (keystorePropertiesFile.exists()) { 12 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 13 | } 14 | 15 | def flutterRoot = localProperties.getProperty('flutter.sdk') 16 | if (flutterRoot == null) { 17 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 18 | } 19 | 20 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 21 | if (flutterVersionCode == null) { 22 | flutterVersionCode = '1' 23 | } 24 | 25 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 26 | if (flutterVersionName == null) { 27 | flutterVersionName = '1.0' 28 | } 29 | 30 | apply plugin: 'com.android.application' 31 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 32 | 33 | android { 34 | compileSdkVersion 28 35 | 36 | lintOptions { 37 | disable 'InvalidPackage' 38 | } 39 | 40 | defaultConfig { 41 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 42 | applicationId "com.yechaoa.wanandroid_flutter" 43 | minSdkVersion 16 44 | targetSdkVersion 28 45 | versionCode flutterVersionCode.toInteger() 46 | versionName flutterVersionName 47 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 48 | } 49 | 50 | //配置keystore签名 51 | signingConfigs { 52 | release { 53 | keyAlias keystoreProperties['keyAlias'] 54 | keyPassword keystoreProperties['keyPassword'] 55 | storeFile file(keystoreProperties['storeFile']) 56 | storePassword keystoreProperties['storePassword'] 57 | } 58 | debug { 59 | 60 | } 61 | } 62 | 63 | buildTypes { 64 | release { 65 | // TODO: Add your own signing config for the release build. 66 | // Signing with the debug keys for now, so `flutter run --release` works. 67 | signingConfig signingConfigs.release 68 | } 69 | debug { 70 | signingConfig signingConfigs.debug 71 | } 72 | } 73 | } 74 | 75 | flutter { 76 | source '../..' 77 | } 78 | 79 | dependencies { 80 | testImplementation 'junit:junit:4.12' 81 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 82 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' 83 | } 84 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 11 | 16 | 23 | 27 | 30 | 31 | 32 | 33 | 34 | 35 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/yechaoa/wanandroid_flutter/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.yechaoa.wanandroid_flutter; 2 | 3 | import android.os.Bundle; 4 | 5 | //import io.flutter.app.FlutterActivity; 6 | import io.flutter.embedding.android.FlutterActivity; 7 | import io.flutter.plugins.GeneratedPluginRegistrant; 8 | 9 | public class MainActivity extends FlutterActivity { 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | // GeneratedPluginRegistrant.registerWith(this); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/android/app/src/main/res/mipmap-hdpi/ic_logo.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/android/app/src/main/res/mipmap-mdpi/ic_logo.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/android/app/src/main/res/mipmap-xhdpi/ic_logo.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/android/app/src/main/res/mipmap-xxhdpi/ic_logo.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/android/app/src/main/res/mipmap-xxxhdpi/ic_logo.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:4.0.2' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /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-6.1.1-all.zip 7 | -------------------------------------------------------------------------------- /android/key.properties: -------------------------------------------------------------------------------- 1 | storeFile=../key/key.jks 2 | storePassword=wanandroid 3 | keyAlias=key 4 | keyPassword=wanandroid -------------------------------------------------------------------------------- /android/key/app-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/android/key/app-release.apk -------------------------------------------------------------------------------- /android/key/key.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/android/key/key.jks -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /apk/app-debug.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/apk/app-debug.apk -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 玩安卓 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 | -------------------------------------------------------------------------------- /ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/common/api.dart: -------------------------------------------------------------------------------- 1 | 2 | class Api { 3 | 4 | static const String BASE_URL = "https://www.wanandroid.com/"; 5 | 6 | static const String TEST_URL = "https://api.hencoder.com/author"; 7 | 8 | //首页banner 9 | static const String BANNER = "banner/json"; 10 | 11 | //首页文章列表 12 | static const String ARTICLE_LIST = "article/list/"; 13 | 14 | //体系数据 15 | static const String TREE = "tree/json"; 16 | 17 | //导航数据 18 | static const String NAVI = "navi/json"; 19 | 20 | //项目分类 21 | static const String PROJECT = "project/tree/json"; 22 | 23 | //项目列表数据 24 | static const String PROJECT_LIST = "project/list/"; 25 | 26 | //登录 27 | static const String LOGIN = "user/login"; 28 | 29 | //注册 30 | static const String REGISTER = "user/register"; 31 | 32 | //退出登录 33 | static const String LOGOUT = "user/logout/json"; 34 | 35 | //搜索热词 36 | static const String HOT_KEY = "hotkey/json"; 37 | 38 | //搜索 39 | static const String QUERY = "article/query/0/json"; 40 | 41 | //收藏文章列表 42 | static const String COLLECT_LIST = "lg/collect/list/"; 43 | 44 | //收藏站内文章 45 | static const String COLLECT = "lg/collect/"; 46 | 47 | //取消收藏-文章列表 48 | static const String UN_COLLECT_ORIGIN_ID = "lg/uncollect_originId/"; 49 | 50 | //取消收藏-收藏页面 51 | static const String UN_COLLECT = "lg/uncollect/"; 52 | 53 | } -------------------------------------------------------------------------------- /lib/entity/article_entity.dart: -------------------------------------------------------------------------------- 1 | class ArticleEntity { 2 | ArticleData data; 3 | int errorCode; 4 | String errorMsg; 5 | 6 | ArticleEntity({this.data, this.errorCode, this.errorMsg}); 7 | 8 | ArticleEntity.fromJson(Map json) { 9 | data = json['data'] != null ? new ArticleData.fromJson(json['data']) : null; 10 | errorCode = json['errorCode']; 11 | errorMsg = json['errorMsg']; 12 | } 13 | 14 | Map toJson() { 15 | final Map data = new Map(); 16 | if (this.data != null) { 17 | data['data'] = this.data.toJson(); 18 | } 19 | data['errorCode'] = this.errorCode; 20 | data['errorMsg'] = this.errorMsg; 21 | return data; 22 | } 23 | } 24 | 25 | class ArticleData { 26 | bool over; 27 | int pageCount; 28 | int total; 29 | int curPage; 30 | int offset; 31 | int size; 32 | List datas; 33 | 34 | ArticleData({this.over, this.pageCount, this.total, this.curPage, this.offset, this.size, this.datas}); 35 | 36 | ArticleData.fromJson(Map json) { 37 | over = json['over']; 38 | pageCount = json['pageCount']; 39 | total = json['total']; 40 | curPage = json['curPage']; 41 | offset = json['offset']; 42 | size = json['size']; 43 | if (json['datas'] != null) { 44 | datas = new List();(json['datas'] as List).forEach((v) { datas.add(new ArticleDataData.fromJson(v)); }); 45 | } 46 | } 47 | 48 | Map toJson() { 49 | final Map data = new Map(); 50 | data['over'] = this.over; 51 | data['pageCount'] = this.pageCount; 52 | data['total'] = this.total; 53 | data['curPage'] = this.curPage; 54 | data['offset'] = this.offset; 55 | data['size'] = this.size; 56 | if (this.datas != null) { 57 | data['datas'] = this.datas.map((v) => v.toJson()).toList(); 58 | } 59 | return data; 60 | } 61 | } 62 | 63 | class ArticleDataData { 64 | String superChapterName; 65 | int publishTime; 66 | int visible; 67 | String niceDate; 68 | String projectLink; 69 | String author; 70 | String prefix; 71 | int zan; 72 | String origin; 73 | String chapterName; 74 | String link; 75 | String title; 76 | int type; 77 | int userId; 78 | List tags; 79 | String apkLink; 80 | String envelopePic; 81 | int chapterId; 82 | int superChapterId; 83 | int id; 84 | int originId; 85 | bool fresh; 86 | bool collect; 87 | int courseId; 88 | String desc; 89 | 90 | ArticleDataData({this.superChapterName, this.publishTime, this.visible, this.niceDate, this.projectLink, this.author, this.prefix, this.zan, this.origin, this.chapterName, this.link, this.title, this.type, this.userId, this.tags, this.apkLink, this.envelopePic, this.chapterId, this.superChapterId, this.id,this.originId, this.fresh, this.collect, this.courseId, this.desc}); 91 | 92 | ArticleDataData.fromJson(Map json) { 93 | superChapterName = json['superChapterName']; 94 | publishTime = json['publishTime']; 95 | visible = json['visible']; 96 | niceDate = json['niceDate']; 97 | projectLink = json['projectLink']; 98 | author = json['author']; 99 | prefix = json['prefix']; 100 | zan = json['zan']; 101 | origin = json['origin']; 102 | chapterName = json['chapterName']; 103 | link = json['link']; 104 | title = json['title']; 105 | type = json['type']; 106 | userId = json['userId']; 107 | if (json['tags'] != null) { 108 | tags = new List(); 109 | } 110 | apkLink = json['apkLink']; 111 | envelopePic = json['envelopePic']; 112 | chapterId = json['chapterId']; 113 | superChapterId = json['superChapterId']; 114 | id = json['id']; 115 | originId = json['originId']; 116 | fresh = json['fresh']; 117 | collect = json['collect']; 118 | courseId = json['courseId']; 119 | desc = json['desc']; 120 | } 121 | 122 | Map toJson() { 123 | final Map data = new Map(); 124 | data['superChapterName'] = this.superChapterName; 125 | data['publishTime'] = this.publishTime; 126 | data['visible'] = this.visible; 127 | data['niceDate'] = this.niceDate; 128 | data['projectLink'] = this.projectLink; 129 | data['author'] = this.author; 130 | data['prefix'] = this.prefix; 131 | data['zan'] = this.zan; 132 | data['origin'] = this.origin; 133 | data['chapterName'] = this.chapterName; 134 | data['link'] = this.link; 135 | data['title'] = this.title; 136 | data['type'] = this.type; 137 | data['userId'] = this.userId; 138 | if (this.tags != null) { 139 | data['tags'] = []; 140 | } 141 | data['apkLink'] = this.apkLink; 142 | data['envelopePic'] = this.envelopePic; 143 | data['chapterId'] = this.chapterId; 144 | data['superChapterId'] = this.superChapterId; 145 | data['id'] = this.id; 146 | data['originId'] = this.originId; 147 | data['fresh'] = this.fresh; 148 | data['collect'] = this.collect; 149 | data['courseId'] = this.courseId; 150 | data['desc'] = this.desc; 151 | return data; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /lib/entity/banner_entity.dart: -------------------------------------------------------------------------------- 1 | class BannerEntity { 2 | List data; 3 | int errorCode; 4 | String errorMsg; 5 | 6 | BannerEntity({this.data, this.errorCode, this.errorMsg}); 7 | 8 | BannerEntity.fromJson(Map json) { 9 | if (json['data'] != null) { 10 | data = new List();(json['data'] as List).forEach((v) { data.add(new BannerData.fromJson(v)); }); 11 | } 12 | errorCode = json['errorCode']; 13 | errorMsg = json['errorMsg']; 14 | } 15 | 16 | Map toJson() { 17 | final Map data = new Map(); 18 | if (this.data != null) { 19 | data['data'] = this.data.map((v) => v.toJson()).toList(); 20 | } 21 | data['errorCode'] = this.errorCode; 22 | data['errorMsg'] = this.errorMsg; 23 | return data; 24 | } 25 | } 26 | 27 | class BannerData { 28 | String imagePath; 29 | int id; 30 | int isVisible; 31 | String title; 32 | int type; 33 | String url; 34 | String desc; 35 | int order; 36 | 37 | BannerData({this.imagePath, this.id, this.isVisible, this.title, this.type, this.url, this.desc, this.order}); 38 | 39 | BannerData.fromJson(Map json) { 40 | imagePath = json['imagePath']; 41 | id = json['id']; 42 | isVisible = json['isVisible']; 43 | title = json['title']; 44 | type = json['type']; 45 | url = json['url']; 46 | desc = json['desc']; 47 | order = json['order']; 48 | } 49 | 50 | Map toJson() { 51 | final Map data = new Map(); 52 | data['imagePath'] = this.imagePath; 53 | data['id'] = this.id; 54 | data['isVisible'] = this.isVisible; 55 | data['title'] = this.title; 56 | data['type'] = this.type; 57 | data['url'] = this.url; 58 | data['desc'] = this.desc; 59 | data['order'] = this.order; 60 | return data; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/entity/common_entity.dart: -------------------------------------------------------------------------------- 1 | class CommonEntity { 2 | dynamic data; 3 | int errorCode; 4 | String errorMsg; 5 | 6 | CommonEntity({this.data, this.errorCode, this.errorMsg}); 7 | 8 | CommonEntity.fromJson(Map json) { 9 | data = json['data']; 10 | errorCode = json['errorCode']; 11 | errorMsg = json['errorMsg']; 12 | } 13 | 14 | Map toJson() { 15 | final Map data = new Map(); 16 | data['data'] = this.data; 17 | data['errorCode'] = this.errorCode; 18 | data['errorMsg'] = this.errorMsg; 19 | return data; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/entity/hot_key_entity.dart: -------------------------------------------------------------------------------- 1 | class HotKeyEntity { 2 | List data; 3 | int errorCode; 4 | String errorMsg; 5 | 6 | HotKeyEntity({this.data, this.errorCode, this.errorMsg}); 7 | 8 | HotKeyEntity.fromJson(Map json) { 9 | if (json['data'] != null) { 10 | data = new List();(json['data'] as List).forEach((v) { data.add(new HotKeyData.fromJson(v)); }); 11 | } 12 | errorCode = json['errorCode']; 13 | errorMsg = json['errorMsg']; 14 | } 15 | 16 | Map toJson() { 17 | final Map data = new Map(); 18 | if (this.data != null) { 19 | data['data'] = this.data.map((v) => v.toJson()).toList(); 20 | } 21 | data['errorCode'] = this.errorCode; 22 | data['errorMsg'] = this.errorMsg; 23 | return data; 24 | } 25 | } 26 | 27 | class HotKeyData { 28 | int visible; 29 | String link; 30 | String name; 31 | int id; 32 | int order; 33 | 34 | HotKeyData({this.visible, this.link, this.name, this.id, this.order}); 35 | 36 | HotKeyData.fromJson(Map json) { 37 | visible = json['visible']; 38 | link = json['link']; 39 | name = json['name']; 40 | id = json['id']; 41 | order = json['order']; 42 | } 43 | 44 | Map toJson() { 45 | final Map data = new Map(); 46 | data['visible'] = this.visible; 47 | data['link'] = this.link; 48 | data['name'] = this.name; 49 | data['id'] = this.id; 50 | data['order'] = this.order; 51 | return data; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/entity/navi_entity.dart: -------------------------------------------------------------------------------- 1 | class NaviEntity { 2 | List data; 3 | int errorCode; 4 | String errorMsg; 5 | 6 | NaviEntity({this.data, this.errorCode, this.errorMsg}); 7 | 8 | NaviEntity.fromJson(Map json) { 9 | if (json['data'] != null) { 10 | data = new List();(json['data'] as List).forEach((v) { data.add(new NaviData.fromJson(v)); }); 11 | } 12 | errorCode = json['errorCode']; 13 | errorMsg = json['errorMsg']; 14 | } 15 | 16 | Map toJson() { 17 | final Map data = new Map(); 18 | if (this.data != null) { 19 | data['data'] = this.data.map((v) => v.toJson()).toList(); 20 | } 21 | data['errorCode'] = this.errorCode; 22 | data['errorMsg'] = this.errorMsg; 23 | return data; 24 | } 25 | } 26 | 27 | class NaviData { 28 | String name; 29 | List articles; 30 | int cid; 31 | 32 | NaviData({this.name, this.articles, this.cid}); 33 | 34 | NaviData.fromJson(Map json) { 35 | name = json['name']; 36 | if (json['articles'] != null) { 37 | articles = new List();(json['articles'] as List).forEach((v) { articles.add(new NaviDataArticle.fromJson(v)); }); 38 | } 39 | cid = json['cid']; 40 | } 41 | 42 | Map toJson() { 43 | final Map data = new Map(); 44 | data['name'] = this.name; 45 | if (this.articles != null) { 46 | data['articles'] = this.articles.map((v) => v.toJson()).toList(); 47 | } 48 | data['cid'] = this.cid; 49 | return data; 50 | } 51 | } 52 | 53 | class NaviDataArticle { 54 | String superChapterName; 55 | int publishTime; 56 | int visible; 57 | String niceDate; 58 | String projectLink; 59 | String author; 60 | String prefix; 61 | int zan; 62 | String origin; 63 | String chapterName; 64 | String link; 65 | String title; 66 | int type; 67 | int userId; 68 | List tags; 69 | String apkLink; 70 | String envelopePic; 71 | int chapterId; 72 | int superChapterId; 73 | int id; 74 | bool fresh; 75 | bool collect; 76 | int courseId; 77 | String desc; 78 | 79 | NaviDataArticle({this.superChapterName, this.publishTime, this.visible, this.niceDate, this.projectLink, this.author, this.prefix, this.zan, this.origin, this.chapterName, this.link, this.title, this.type, this.userId, this.tags, this.apkLink, this.envelopePic, this.chapterId, this.superChapterId, this.id, this.fresh, this.collect, this.courseId, this.desc}); 80 | 81 | NaviDataArticle.fromJson(Map json) { 82 | superChapterName = json['superChapterName']; 83 | publishTime = json['publishTime']; 84 | visible = json['visible']; 85 | niceDate = json['niceDate']; 86 | projectLink = json['projectLink']; 87 | author = json['author']; 88 | prefix = json['prefix']; 89 | zan = json['zan']; 90 | origin = json['origin']; 91 | chapterName = json['chapterName']; 92 | link = json['link']; 93 | title = json['title']; 94 | type = json['type']; 95 | userId = json['userId']; 96 | if (json['tags'] != null) { 97 | tags = new List(); 98 | } 99 | apkLink = json['apkLink']; 100 | envelopePic = json['envelopePic']; 101 | chapterId = json['chapterId']; 102 | superChapterId = json['superChapterId']; 103 | id = json['id']; 104 | fresh = json['fresh']; 105 | collect = json['collect']; 106 | courseId = json['courseId']; 107 | desc = json['desc']; 108 | } 109 | 110 | Map toJson() { 111 | final Map data = new Map(); 112 | data['superChapterName'] = this.superChapterName; 113 | data['publishTime'] = this.publishTime; 114 | data['visible'] = this.visible; 115 | data['niceDate'] = this.niceDate; 116 | data['projectLink'] = this.projectLink; 117 | data['author'] = this.author; 118 | data['prefix'] = this.prefix; 119 | data['zan'] = this.zan; 120 | data['origin'] = this.origin; 121 | data['chapterName'] = this.chapterName; 122 | data['link'] = this.link; 123 | data['title'] = this.title; 124 | data['type'] = this.type; 125 | data['userId'] = this.userId; 126 | if (this.tags != null) { 127 | data['tags'] = []; 128 | } 129 | data['apkLink'] = this.apkLink; 130 | data['envelopePic'] = this.envelopePic; 131 | data['chapterId'] = this.chapterId; 132 | data['superChapterId'] = this.superChapterId; 133 | data['id'] = this.id; 134 | data['fresh'] = this.fresh; 135 | data['collect'] = this.collect; 136 | data['courseId'] = this.courseId; 137 | data['desc'] = this.desc; 138 | return data; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /lib/entity/project_entity.dart: -------------------------------------------------------------------------------- 1 | class ProjectEntity { 2 | List data; 3 | int errorCode; 4 | String errorMsg; 5 | 6 | ProjectEntity({this.data, this.errorCode, this.errorMsg}); 7 | 8 | ProjectEntity.fromJson(Map json) { 9 | if (json['data'] != null) { 10 | data = new List();(json['data'] as List).forEach((v) { data.add(new ProjectData.fromJson(v)); }); 11 | } 12 | errorCode = json['errorCode']; 13 | errorMsg = json['errorMsg']; 14 | } 15 | 16 | Map toJson() { 17 | final Map data = new Map(); 18 | if (this.data != null) { 19 | data['data'] = this.data.map((v) => v.toJson()).toList(); 20 | } 21 | data['errorCode'] = this.errorCode; 22 | data['errorMsg'] = this.errorMsg; 23 | return data; 24 | } 25 | } 26 | 27 | class ProjectData { 28 | int visible; 29 | List children; 30 | String name; 31 | bool userControlSetTop; 32 | int id; 33 | int courseId; 34 | int parentChapterId; 35 | int order; 36 | 37 | ProjectData({this.visible, this.children, this.name, this.userControlSetTop, this.id, this.courseId, this.parentChapterId, this.order}); 38 | 39 | ProjectData.fromJson(Map json) { 40 | visible = json['visible']; 41 | if (json['children'] != null) { 42 | children = new List(); 43 | } 44 | name = json['name']; 45 | userControlSetTop = json['userControlSetTop']; 46 | id = json['id']; 47 | courseId = json['courseId']; 48 | parentChapterId = json['parentChapterId']; 49 | order = json['order']; 50 | } 51 | 52 | Map toJson() { 53 | final Map data = new Map(); 54 | data['visible'] = this.visible; 55 | if (this.children != null) { 56 | data['children'] = []; 57 | } 58 | data['name'] = this.name; 59 | data['userControlSetTop'] = this.userControlSetTop; 60 | data['id'] = this.id; 61 | data['courseId'] = this.courseId; 62 | data['parentChapterId'] = this.parentChapterId; 63 | data['order'] = this.order; 64 | return data; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/entity/project_list_entity.dart: -------------------------------------------------------------------------------- 1 | class ProjectListEntity { 2 | ProjectListData data; 3 | int errorCode; 4 | String errorMsg; 5 | 6 | ProjectListEntity({this.data, this.errorCode, this.errorMsg}); 7 | 8 | ProjectListEntity.fromJson(Map json) { 9 | data = json['data'] != null ? new ProjectListData.fromJson(json['data']) : null; 10 | errorCode = json['errorCode']; 11 | errorMsg = json['errorMsg']; 12 | } 13 | 14 | Map toJson() { 15 | final Map data = new Map(); 16 | if (this.data != null) { 17 | data['data'] = this.data.toJson(); 18 | } 19 | data['errorCode'] = this.errorCode; 20 | data['errorMsg'] = this.errorMsg; 21 | return data; 22 | } 23 | } 24 | 25 | class ProjectListData { 26 | bool over; 27 | int pageCount; 28 | int total; 29 | int curPage; 30 | int offset; 31 | int size; 32 | List datas; 33 | 34 | ProjectListData({this.over, this.pageCount, this.total, this.curPage, this.offset, this.size, this.datas}); 35 | 36 | ProjectListData.fromJson(Map json) { 37 | over = json['over']; 38 | pageCount = json['pageCount']; 39 | total = json['total']; 40 | curPage = json['curPage']; 41 | offset = json['offset']; 42 | size = json['size']; 43 | if (json['datas'] != null) { 44 | datas = new List();(json['datas'] as List).forEach((v) { datas.add(new ProjectListDataData.fromJson(v)); }); 45 | } 46 | } 47 | 48 | Map toJson() { 49 | final Map data = new Map(); 50 | data['over'] = this.over; 51 | data['pageCount'] = this.pageCount; 52 | data['total'] = this.total; 53 | data['curPage'] = this.curPage; 54 | data['offset'] = this.offset; 55 | data['size'] = this.size; 56 | if (this.datas != null) { 57 | data['datas'] = this.datas.map((v) => v.toJson()).toList(); 58 | } 59 | return data; 60 | } 61 | } 62 | 63 | class ProjectListDataData { 64 | String superChapterName; 65 | int publishTime; 66 | int visible; 67 | String niceDate; 68 | String projectLink; 69 | String author; 70 | String prefix; 71 | int zan; 72 | String origin; 73 | String chapterName; 74 | String link; 75 | String title; 76 | int type; 77 | int userId; 78 | List tags; 79 | String apkLink; 80 | String envelopePic; 81 | int chapterId; 82 | int superChapterId; 83 | int id; 84 | bool fresh; 85 | bool collect; 86 | int courseId; 87 | String desc; 88 | 89 | ProjectListDataData({this.superChapterName, this.publishTime, this.visible, this.niceDate, this.projectLink, this.author, this.prefix, this.zan, this.origin, this.chapterName, this.link, this.title, this.type, this.userId, this.tags, this.apkLink, this.envelopePic, this.chapterId, this.superChapterId, this.id, this.fresh, this.collect, this.courseId, this.desc}); 90 | 91 | ProjectListDataData.fromJson(Map json) { 92 | superChapterName = json['superChapterName']; 93 | publishTime = json['publishTime']; 94 | visible = json['visible']; 95 | niceDate = json['niceDate']; 96 | projectLink = json['projectLink']; 97 | author = json['author']; 98 | prefix = json['prefix']; 99 | zan = json['zan']; 100 | origin = json['origin']; 101 | chapterName = json['chapterName']; 102 | link = json['link']; 103 | title = json['title']; 104 | type = json['type']; 105 | userId = json['userId']; 106 | if (json['tags'] != null) { 107 | tags = new List();(json['tags'] as List).forEach((v) { tags.add(new ProjectListDataDatasTag.fromJson(v)); }); 108 | } 109 | apkLink = json['apkLink']; 110 | envelopePic = json['envelopePic']; 111 | chapterId = json['chapterId']; 112 | superChapterId = json['superChapterId']; 113 | id = json['id']; 114 | fresh = json['fresh']; 115 | collect = json['collect']; 116 | courseId = json['courseId']; 117 | desc = json['desc']; 118 | } 119 | 120 | Map toJson() { 121 | final Map data = new Map(); 122 | data['superChapterName'] = this.superChapterName; 123 | data['publishTime'] = this.publishTime; 124 | data['visible'] = this.visible; 125 | data['niceDate'] = this.niceDate; 126 | data['projectLink'] = this.projectLink; 127 | data['author'] = this.author; 128 | data['prefix'] = this.prefix; 129 | data['zan'] = this.zan; 130 | data['origin'] = this.origin; 131 | data['chapterName'] = this.chapterName; 132 | data['link'] = this.link; 133 | data['title'] = this.title; 134 | data['type'] = this.type; 135 | data['userId'] = this.userId; 136 | if (this.tags != null) { 137 | data['tags'] = this.tags.map((v) => v.toJson()).toList(); 138 | } 139 | data['apkLink'] = this.apkLink; 140 | data['envelopePic'] = this.envelopePic; 141 | data['chapterId'] = this.chapterId; 142 | data['superChapterId'] = this.superChapterId; 143 | data['id'] = this.id; 144 | data['fresh'] = this.fresh; 145 | data['collect'] = this.collect; 146 | data['courseId'] = this.courseId; 147 | data['desc'] = this.desc; 148 | return data; 149 | } 150 | } 151 | 152 | class ProjectListDataDatasTag { 153 | String name; 154 | String url; 155 | 156 | ProjectListDataDatasTag({this.name, this.url}); 157 | 158 | ProjectListDataDatasTag.fromJson(Map json) { 159 | name = json['name']; 160 | url = json['url']; 161 | } 162 | 163 | Map toJson() { 164 | final Map data = new Map(); 165 | data['name'] = this.name; 166 | data['url'] = this.url; 167 | return data; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /lib/entity/tree_entity.dart: -------------------------------------------------------------------------------- 1 | class TreeEntity { 2 | List data; 3 | int errorCode; 4 | String errorMsg; 5 | 6 | TreeEntity({this.data, this.errorCode, this.errorMsg}); 7 | 8 | TreeEntity.fromJson(Map json) { 9 | if (json['data'] != null) { 10 | data = new List();(json['data'] as List).forEach((v) { data.add(new TreeData.fromJson(v)); }); 11 | } 12 | errorCode = json['errorCode']; 13 | errorMsg = json['errorMsg']; 14 | } 15 | 16 | Map toJson() { 17 | final Map data = new Map(); 18 | if (this.data != null) { 19 | data['data'] = this.data.map((v) => v.toJson()).toList(); 20 | } 21 | data['errorCode'] = this.errorCode; 22 | data['errorMsg'] = this.errorMsg; 23 | return data; 24 | } 25 | } 26 | 27 | class TreeData { 28 | int visible; 29 | List children; 30 | String name; 31 | bool userControlSetTop; 32 | int id; 33 | int courseId; 34 | int parentChapterId; 35 | int order; 36 | bool isExpanded;//标识是否初始化 37 | 38 | TreeData({this.visible, this.children, this.name, this.userControlSetTop, this.id, this.courseId, this.parentChapterId, this.order,this.isExpanded}); 39 | 40 | TreeData.fromJson(Map json) { 41 | visible = json['visible']; 42 | if (json['children'] != null) { 43 | children = new List();(json['children'] as List).forEach((v) { children.add(new TreeDatachild.fromJson(v)); }); 44 | } 45 | name = json['name']; 46 | userControlSetTop = json['userControlSetTop']; 47 | id = json['id']; 48 | courseId = json['courseId']; 49 | parentChapterId = json['parentChapterId']; 50 | order = json['order']; 51 | isExpanded = json['isExpanded']; 52 | } 53 | 54 | Map toJson() { 55 | final Map data = new Map(); 56 | data['visible'] = this.visible; 57 | if (this.children != null) { 58 | data['children'] = this.children.map((v) => v.toJson()).toList(); 59 | } 60 | data['name'] = this.name; 61 | data['userControlSetTop'] = this.userControlSetTop; 62 | data['id'] = this.id; 63 | data['courseId'] = this.courseId; 64 | data['parentChapterId'] = this.parentChapterId; 65 | data['order'] = this.order; 66 | data['isExpanded'] = this.isExpanded; 67 | return data; 68 | } 69 | } 70 | 71 | class TreeDatachild { 72 | int visible; 73 | List children; 74 | String name; 75 | bool userControlSetTop; 76 | int id; 77 | int courseId; 78 | int parentChapterId; 79 | int order; 80 | 81 | TreeDatachild({this.visible, this.children, this.name, this.userControlSetTop, this.id, this.courseId, this.parentChapterId, this.order}); 82 | 83 | TreeDatachild.fromJson(Map json) { 84 | visible = json['visible']; 85 | if (json['children'] != null) { 86 | children = new List(); 87 | } 88 | name = json['name']; 89 | userControlSetTop = json['userControlSetTop']; 90 | id = json['id']; 91 | courseId = json['courseId']; 92 | parentChapterId = json['parentChapterId']; 93 | order = json['order']; 94 | } 95 | 96 | Map toJson() { 97 | final Map data = new Map(); 98 | data['visible'] = this.visible; 99 | if (this.children != null) { 100 | data['children'] = []; 101 | } 102 | data['name'] = this.name; 103 | data['userControlSetTop'] = this.userControlSetTop; 104 | data['id'] = this.id; 105 | data['courseId'] = this.courseId; 106 | data['parentChapterId'] = this.parentChapterId; 107 | data['order'] = this.order; 108 | return data; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /lib/entity/user_entity.dart: -------------------------------------------------------------------------------- 1 | class UserEntity { 2 | UserData data; 3 | int errorCode; 4 | String errorMsg; 5 | 6 | UserEntity({this.data, this.errorCode, this.errorMsg}); 7 | 8 | UserEntity.fromJson(Map json) { 9 | data = json['data'] != null ? new UserData.fromJson(json['data']) : null; 10 | errorCode = json['errorCode']; 11 | errorMsg = json['errorMsg']; 12 | } 13 | 14 | Map toJson() { 15 | final Map data = new Map(); 16 | if (this.data != null) { 17 | data['data'] = this.data.toJson(); 18 | } 19 | data['errorCode'] = this.errorCode; 20 | data['errorMsg'] = this.errorMsg; 21 | return data; 22 | } 23 | } 24 | 25 | class UserData { 26 | String password; 27 | List chapterTops; 28 | String icon; 29 | bool admin; 30 | List collectIds; 31 | int id; 32 | int type; 33 | String email; 34 | String token; 35 | String username; 36 | 37 | UserData({this.password, this.chapterTops, this.icon, this.admin, this.collectIds, this.id, this.type, this.email, this.token, this.username}); 38 | 39 | UserData.fromJson(Map json) { 40 | password = json['password']; 41 | if (json['chapterTops'] != null) { 42 | chapterTops = new List(); 43 | } 44 | icon = json['icon']; 45 | admin = json['admin']; 46 | collectIds = json['collectIds']?.cast(); 47 | id = json['id']; 48 | type = json['type']; 49 | email = json['email']; 50 | token = json['token']; 51 | username = json['username']; 52 | } 53 | 54 | Map toJson() { 55 | final Map data = new Map(); 56 | data['password'] = this.password; 57 | if (this.chapterTops != null) { 58 | data['chapterTops'] = []; 59 | } 60 | data['icon'] = this.icon; 61 | data['admin'] = this.admin; 62 | data['collectIds'] = this.collectIds; 63 | data['id'] = this.id; 64 | data['type'] = this.type; 65 | data['email'] = this.email; 66 | data['token'] = this.token; 67 | data['username'] = this.username; 68 | return data; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/entity_factory.dart: -------------------------------------------------------------------------------- 1 | import 'package:wanandroid_flutter/entity/article_entity.dart'; 2 | import 'package:wanandroid_flutter/entity/banner_entity.dart'; 3 | import 'package:wanandroid_flutter/entity/common_entity.dart'; 4 | import 'package:wanandroid_flutter/entity/hot_key_entity.dart'; 5 | import 'package:wanandroid_flutter/entity/navi_entity.dart'; 6 | import 'package:wanandroid_flutter/entity/project_entity.dart'; 7 | import 'package:wanandroid_flutter/entity/project_list_entity.dart'; 8 | import 'package:wanandroid_flutter/entity/tree_entity.dart'; 9 | import 'package:wanandroid_flutter/entity/user_entity.dart'; 10 | 11 | class EntityFactory { 12 | static T generateOBJ(json) { 13 | if (1 == 0) { 14 | return null; 15 | } else if (T.toString() == "ArticleEntity") { 16 | return ArticleEntity.fromJson(json) as T; 17 | } else if (T.toString() == "BannerEntity") { 18 | return BannerEntity.fromJson(json) as T; 19 | } else if (T.toString() == "CommonEntity") { 20 | return CommonEntity.fromJson(json) as T; 21 | } else if (T.toString() == "HotKeyEntity") { 22 | return HotKeyEntity.fromJson(json) as T; 23 | } else if (T.toString() == "NaviEntity") { 24 | return NaviEntity.fromJson(json) as T; 25 | } else if (T.toString() == "ProjectEntity") { 26 | return ProjectEntity.fromJson(json) as T; 27 | } else if (T.toString() == "ProjectListEntity") { 28 | return ProjectListEntity.fromJson(json) as T; 29 | } else if (T.toString() == "TreeEntity") { 30 | return TreeEntity.fromJson(json) as T; 31 | } else if (T.toString() == "UserEntity") { 32 | return UserEntity.fromJson(json) as T; 33 | } else { 34 | return null; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /lib/http/httpUtil.dart: -------------------------------------------------------------------------------- 1 | import 'package:cookie_jar/cookie_jar.dart'; 2 | import 'package:dio/dio.dart'; 3 | import 'package:dio_cookie_manager/dio_cookie_manager.dart'; 4 | import 'package:wanandroid_flutter/common/api.dart'; 5 | 6 | class HttpUtil { 7 | static HttpUtil instance; 8 | Dio dio; 9 | BaseOptions options; 10 | 11 | CancelToken cancelToken = CancelToken(); 12 | 13 | static HttpUtil getInstance() { 14 | if (null == instance) instance = HttpUtil(); 15 | return instance; 16 | } 17 | 18 | /* 19 | * config it and create 20 | */ 21 | HttpUtil() { 22 | //BaseOptions、Options、RequestOptions 都可以配置参数,优先级别依次递增,且可以根据优先级别覆盖参数 23 | options = BaseOptions( 24 | //请求基地址,可以包含子路径 25 | baseUrl: Api.BASE_URL, 26 | //连接服务器超时时间,单位是秒. 27 | connectTimeout: Duration(seconds: 10), 28 | //响应流上前后两次接受到数据的间隔,单位为秒。 29 | receiveTimeout: Duration(seconds: 5), 30 | //Http请求头. 31 | headers: { 32 | //do something 33 | "version": "1.0.0" 34 | }, 35 | //请求的Content-Type,默认值是"application/json; charset=utf-8",Headers.formUrlEncodedContentType会自动编码请求体. 36 | contentType: Headers.formUrlEncodedContentType, 37 | //表示期望以那种格式(方式)接受响应数据。接受四种类型 `json`, `stream`, `plain`, `bytes`. 默认值是 `json`, 38 | responseType: ResponseType.plain, 39 | ); 40 | 41 | dio = Dio(options); 42 | 43 | //Cookie管理 // First request, and save cookies (CookieManager do it). but 好像没生效嘛... 44 | final cookieJar = CookieJar(); 45 | dio.interceptors.add(CookieManager(cookieJar)); 46 | 47 | //添加拦截器 48 | dio.interceptors.add(InterceptorsWrapper(onRequest: (RequestOptions options, RequestInterceptorHandler handler) { 49 | 50 | print("请求之前 header = ${options.headers.toString()}"); 51 | // 如果你想完成请求并返回一些自定义数据,你可以使用 `handler.resolve(response)`。 52 | // 如果你想终止请求并触发一个错误,你可以使用 `handler.reject(error)`。 53 | return handler.next(options); //continue 54 | }, onResponse: (Response response, ResponseInterceptorHandler handler) { 55 | print("响应之前"); 56 | // 如果你想终止请求并触发一个错误,你可以使用 `handler.reject(error)`。 57 | return handler.next(response); // continue 58 | }, onError: (DioError e, ErrorInterceptorHandler handler) { 59 | print("错误之前"); 60 | // 如果你想完成请求并返回一些自定义数据,你可以使用 `handler.resolve(response)`。 61 | return handler.next(e); 62 | })); 63 | } 64 | 65 | /* 66 | * get请求 67 | */ 68 | get(url, {data, options, cancelToken}) async { 69 | Response response; 70 | try { 71 | response = await dio.get(url, queryParameters: data, options: options, cancelToken: cancelToken); 72 | print('get success---------${response.statusCode}'); 73 | print('get success---------${response.data}'); 74 | 75 | // response.data; 响应体 76 | // response.headers; 响应头 77 | // response.request; 请求体 78 | // response.statusCode; 状态码 79 | } on DioError catch (e) { 80 | print('get error---------$e'); 81 | formatError(e); 82 | } 83 | return response; 84 | } 85 | 86 | /* 87 | * post请求 88 | */ 89 | post(url, {data, options, cancelToken}) async { 90 | Response response; 91 | try { 92 | response = await dio.post(url, queryParameters: data, options: options, cancelToken: cancelToken); 93 | print('post success---------${response.data}'); 94 | } on DioError catch (e) { 95 | print('post error---------$e'); 96 | formatError(e); 97 | } 98 | return response; 99 | } 100 | 101 | /* 102 | * 下载文件 103 | */ 104 | downloadFile(urlPath, savePath) async { 105 | Response response; 106 | try { 107 | response = await dio.download(urlPath, savePath, onReceiveProgress: (int count, int total) { 108 | //进度 109 | print("$count $total"); 110 | }); 111 | print('downloadFile success---------${response.data}'); 112 | } on DioError catch (e) { 113 | print('downloadFile error---------$e'); 114 | formatError(e); 115 | } 116 | return response.data; 117 | } 118 | 119 | /* 120 | * error统一处理 121 | */ 122 | void formatError(DioError e) { 123 | if (e.type == DioErrorType.connectionTimeout) { 124 | // It occurs when url is opened timeout. 125 | print("连接超时"); 126 | } else if (e.type == DioErrorType.sendTimeout) { 127 | // It occurs when url is sent timeout. 128 | print("请求超时"); 129 | } else if (e.type == DioErrorType.receiveTimeout) { 130 | //It occurs when receiving timeout 131 | print("响应超时"); 132 | } else if (e.type == DioErrorType.badResponse) { 133 | // When the server response, but with a incorrect status, such as 404, 503... 134 | print("出现异常"); 135 | } else if (e.type == DioErrorType.cancel) { 136 | // When the request is cancelled, dio will throw a error with this type. 137 | print("请求取消"); 138 | } else { 139 | //DEFAULT Default error type, Some other Error. In this case, you can read the DioError.error if it is not null. 140 | print("未知错误"); 141 | } 142 | } 143 | 144 | /* 145 | * 取消请求 146 | * 147 | * 同一个cancel token 可以用于多个请求,当一个cancel token取消时,所有使用该cancel token的请求都会被取消。 148 | * 所以参数可选 149 | */ 150 | void cancelRequests(CancelToken token) { 151 | token.cancel("cancelled"); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:fluttertoast/fluttertoast.dart'; 3 | import 'package:provider/provider.dart'; 4 | import 'package:share/share.dart'; 5 | import 'package:shared_preferences/shared_preferences.dart'; 6 | import 'package:wanandroid_flutter/pages/CollectPage.dart'; 7 | import 'package:wanandroid_flutter/pages/about.dart'; 8 | import 'package:wanandroid_flutter/pages/articleDetail.dart'; 9 | import 'package:wanandroid_flutter/pages/homePage.dart'; 10 | import 'package:wanandroid_flutter/pages/naviPage.dart'; 11 | import 'package:wanandroid_flutter/pages/projectPage.dart'; 12 | import 'package:wanandroid_flutter/pages/searchPage.dart'; 13 | import 'package:wanandroid_flutter/pages/treePage.dart'; 14 | import 'package:wanandroid_flutter/res/colors.dart'; 15 | import 'package:wanandroid_flutter/res/strings.dart'; 16 | import 'package:wanandroid_flutter/util/favoriteProvide.dart'; 17 | import 'package:wanandroid_flutter/util/themeProvide.dart'; 18 | 19 | import 'common/api.dart'; 20 | import 'http/httpUtil.dart'; 21 | 22 | void main() async { 23 | //runApp前调用,初始化绑定,手势、渲染、服务等 24 | WidgetsFlutterBinding.ensureInitialized(); 25 | 26 | int themeIndex = await getTheme(); 27 | 28 | runApp(MultiProvider( 29 | providers: [ 30 | //将theme,favorite加到providers中 31 | ChangeNotifierProvider(create: (ctx) => ThemeProvide()), 32 | ChangeNotifierProvider(create: (ctx) => FavoriteProvide()) 33 | ], 34 | child: MyApp(themeIndex), 35 | )); 36 | } 37 | 38 | Future getTheme() async { 39 | SharedPreferences sp = await SharedPreferences.getInstance(); 40 | int themeIndex = sp.getInt(YColors.themeIndexKey); 41 | return null == themeIndex ? 0 : themeIndex; 42 | } 43 | 44 | class MyApp extends StatelessWidget { 45 | // This widget is the root of your application. 46 | final int themeIndex; 47 | 48 | MyApp(this.themeIndex); 49 | 50 | @override 51 | Widget build(BuildContext context) { 52 | // Calls `context.watch` to make [Count] rebuild when [Counter] changes. 53 | final int themeValue = context.watch().value; 54 | 55 | return MaterialApp( 56 | title: YStrings.appName, 57 | theme: ThemeData( 58 | // This is the theme of your application. 59 | //除了primaryColor,还有brightness、iconTheme、textTheme等等可以设置 60 | primaryColor: YColors.themeColor[themeValue != null ? themeValue : themeIndex]["primaryColor"], 61 | primaryColorDark: YColors.themeColor[themeValue != null ? themeValue : themeIndex]["primaryColorDark"], 62 | accentColor: YColors.themeColor[themeValue != null ? themeValue : themeIndex]["colorAccent"] 63 | 64 | // primaryColor: YColors.colorPrimary, 65 | // primaryColorDark: YColors.colorPrimaryDark, 66 | // accentColor: YColors.colorAccent, 67 | // dividerColor: YColors.dividerColor, 68 | ), 69 | home: MyHomePage(title: YStrings.appName), 70 | ); 71 | } 72 | } 73 | 74 | class MyHomePage extends StatefulWidget { 75 | MyHomePage({Key key, this.title}) : super(key: key); 76 | 77 | final String title; 78 | 79 | @override 80 | _MyHomePageState createState() => _MyHomePageState(); 81 | } 82 | 83 | class _MyHomePageState extends State { 84 | int _selectedIndex = 0; 85 | String title = YStrings.appName; 86 | 87 | var _pageController = PageController(initialPage: 0); 88 | 89 | var pages = [ 90 | HomePage(), 91 | TreePage(), 92 | NaviPage(), 93 | ProjectPage(), 94 | ]; 95 | 96 | @override 97 | Widget build(BuildContext context) { 98 | return Scaffold( 99 | appBar: AppBar( 100 | backgroundColor: Theme.of(context).primaryColor, 101 | title: Text(title), 102 | actions: [ 103 | IconButton( 104 | icon: Icon(Icons.search), 105 | tooltip: '搜索', 106 | onPressed: () { 107 | Navigator.push( 108 | context, 109 | MaterialPageRoute(builder: (context) => SearchPage()), 110 | ); 111 | }, 112 | ), 113 | ], 114 | ), 115 | body: PageView.builder( 116 | onPageChanged: _pageChange, 117 | controller: _pageController, 118 | itemCount: pages.length, 119 | itemBuilder: (BuildContext context, int index) { 120 | return pages.elementAt(_selectedIndex); 121 | }, 122 | ), 123 | bottomNavigationBar: BottomNavigationBar( 124 | items: [ 125 | BottomNavigationBarItem( 126 | icon: Icon(Icons.home), 127 | label: YStrings.home, 128 | ), 129 | BottomNavigationBarItem( 130 | icon: Icon(Icons.filter_list), 131 | label: YStrings.tree, 132 | ), 133 | BottomNavigationBarItem( 134 | icon: Icon(Icons.low_priority), 135 | label: YStrings.navi, 136 | ), 137 | BottomNavigationBarItem( 138 | icon: Icon(Icons.apps), 139 | label: YStrings.project, 140 | ), 141 | ], 142 | //当前选中下标 143 | currentIndex: _selectedIndex, 144 | //显示模式 145 | type: BottomNavigationBarType.fixed, 146 | //选中颜色 147 | fixedColor: Theme.of(context).primaryColor, 148 | //点击事件 149 | onTap: _onItemTapped, 150 | ), 151 | floatingActionButton: FloatingActionButton( 152 | backgroundColor: Theme.of(context).primaryColor, 153 | onPressed: showToast, 154 | tooltip: '点击选中最后一个', 155 | child: Icon(Icons.add, color: Colors.white), 156 | ), 157 | drawer: showDrawer(), 158 | ); 159 | } 160 | 161 | void _onItemTapped(int index) { 162 | //bottomNavigationBar 和 PageView 关联 163 | _pageController.animateToPage(index, duration: Duration(milliseconds: 300), curve: Curves.ease); 164 | } 165 | 166 | void _pageChange(int index) { 167 | setState(() { 168 | _selectedIndex = index; 169 | //根据下标修改标题 170 | switch (index) { 171 | case 0: 172 | title = YStrings.appName; 173 | break; 174 | case 1: 175 | title = YStrings.tree; 176 | break; 177 | case 2: 178 | title = YStrings.navi; 179 | break; 180 | case 3: 181 | title = YStrings.project; 182 | break; 183 | } 184 | }); 185 | } 186 | 187 | void showToast() { 188 | Fluttertoast.showToast( 189 | msg: "选中最后一个", 190 | toastLength: Toast.LENGTH_SHORT, 191 | gravity: ToastGravity.CENTER, 192 | timeInSecForIosWeb: 1, 193 | backgroundColor: Theme.of(context).primaryColor, 194 | textColor: Colors.white, 195 | fontSize: 16.0, 196 | ); 197 | _onItemTapped(3); 198 | } 199 | 200 | Widget showDrawer() { 201 | return Drawer( 202 | child: ListView( 203 | //ListView padding 不为空的时候,Drawer顶部的状态栏就不会有灰色背景 204 | padding: EdgeInsets.zero, 205 | children: [ 206 | UserAccountsDrawerHeader( 207 | // 顶部背景颜色 208 | decoration: BoxDecoration(color: Theme.of(context).primaryColor), 209 | //头像 210 | currentAccountPicture: GestureDetector( 211 | //圆形头像 212 | child: ClipOval( 213 | child: 214 | Image.network('https://profile-avatar.csdnimg.cn/f81b97e9519148ac9d7eca7681fb8698_yechaoa.jpg!1'), 215 | ), 216 | onTap: () { 217 | Navigator.of(context).pop(); 218 | Navigator.push( 219 | context, 220 | MaterialPageRoute(builder: (context) => AboutPage()), 221 | ); 222 | }, 223 | ), 224 | //其他头像 225 | otherAccountsPictures: [ 226 | IconButton( 227 | icon: Icon( 228 | Icons.stars, 229 | color: Colors.white, 230 | ), 231 | onPressed: () { 232 | Navigator.of(context).pop(); 233 | Navigator.push( 234 | context, 235 | MaterialPageRoute( 236 | builder: (context) => 237 | ArticleDetail(title: "来都来了,点个star吧🌹", url: "https://github.com/yechaoa/wanandroid_flutter"), 238 | ), 239 | ); 240 | }) 241 | ], 242 | accountName: Text( 243 | YStrings.proName, 244 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 22), 245 | ), 246 | accountEmail: Text(YStrings.github), 247 | ), 248 | 249 | ///功能列表 250 | ListTile( 251 | leading: Icon(Icons.favorite_border), 252 | title: Text("我的收藏"), 253 | trailing: Icon(Icons.chevron_right), 254 | onTap: () { 255 | Navigator.of(context).pop(); 256 | Navigator.push( 257 | context, 258 | MaterialPageRoute(builder: (context) => CollectPage()), 259 | ); 260 | }, 261 | ), 262 | ListTile( 263 | leading: Icon(Icons.color_lens), 264 | title: Text("切换主题"), 265 | trailing: Icon(Icons.chevron_right), 266 | onTap: () { 267 | Navigator.of(context).pop(); 268 | showThemeDialog(); 269 | }, 270 | ), 271 | ListTile( 272 | leading: Icon(Icons.share), 273 | title: Text("我要分享"), 274 | trailing: Icon(Icons.chevron_right), 275 | onTap: () { 276 | Navigator.of(context).pop(); 277 | Share.share('【玩安卓Flutter版】\nhttps://github.com/yechaoa/wanandroid_flutter'); 278 | }, 279 | ), 280 | ListTile( 281 | leading: Icon(Icons.info_outline), 282 | title: Text("关于项目"), 283 | trailing: Icon(Icons.chevron_right), 284 | onTap: () { 285 | //先关闭再跳转 286 | Navigator.of(context).pop(); 287 | Navigator.push( 288 | context, 289 | MaterialPageRoute(builder: (context) => AboutPage()), 290 | ); 291 | }, 292 | ), 293 | 294 | Divider(), 295 | 296 | ListTile( 297 | leading: Icon(Icons.block), 298 | title: Text("退出"), 299 | trailing: Icon(Icons.chevron_right), 300 | onTap: () { 301 | //关闭drawer 302 | Navigator.of(context).pop(); 303 | showLogoutDialog(); 304 | }, 305 | ), 306 | ], 307 | ), 308 | ); 309 | } 310 | 311 | void showLogoutDialog() { 312 | showDialog( 313 | context: context, 314 | barrierDismissible: false, // user must tap button! 315 | builder: (BuildContext context) { 316 | return AlertDialog( 317 | title: Text('提示'), 318 | content: SingleChildScrollView( 319 | child: ListBody( 320 | children: [ 321 | Text('确认退出吗?'), 322 | ], 323 | ), 324 | ), 325 | actions: [ 326 | TextButton( 327 | child: Text('取消', style: TextStyle(color: YColors.primaryText)), 328 | onPressed: () { 329 | Navigator.of(context).pop(); 330 | }, 331 | ), 332 | TextButton( 333 | child: Text('确定'), 334 | onPressed: () { 335 | //退出 336 | HttpUtil().get(Api.LOGOUT); 337 | Navigator.of(context).pop(); 338 | }, 339 | ), 340 | ], 341 | ); 342 | }, 343 | ); 344 | } 345 | 346 | void showThemeDialog() { 347 | showDialog( 348 | context: context, 349 | barrierDismissible: true, 350 | builder: (BuildContext context) { 351 | return AlertDialog( 352 | title: Text('切换主题'), 353 | content: SingleChildScrollView( 354 | child: Container( 355 | //包含ListView要指定宽高 356 | width: MediaQuery.of(context).size.width * 0.9, 357 | height: MediaQuery.of(context).size.height * 0.5, 358 | child: ListView.builder( 359 | shrinkWrap: true, 360 | itemCount: YColors.themeColor.keys.length, 361 | itemBuilder: (BuildContext context, int position) { 362 | return GestureDetector( 363 | child: Container( 364 | padding: EdgeInsets.all(20.0), 365 | margin: EdgeInsets.only(bottom: 15), 366 | color: YColors.themeColor[position]["primaryColor"], 367 | ), 368 | onTap: () async { 369 | // Calls `context.read` instead of `context.watch` so that it does not rebuild when [Counter] changes. 370 | context.read().setTheme(position); 371 | 372 | //存储主题下标 373 | SharedPreferences sp = await SharedPreferences.getInstance(); 374 | sp.setInt(YColors.themeIndexKey, position); 375 | Navigator.of(context).pop(); 376 | }, 377 | ); 378 | }, 379 | ), 380 | ), 381 | ), 382 | actions: [ 383 | TextButton( 384 | child: Text('关闭', style: TextStyle(color: Theme.of(context).primaryColor)), 385 | onPressed: () { 386 | Navigator.of(context).pop(); 387 | }, 388 | ), 389 | ], 390 | ); 391 | }, 392 | ); 393 | } 394 | } 395 | -------------------------------------------------------------------------------- /lib/pages/CollectPage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:easy_refresh/easy_refresh.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:wanandroid_flutter/common/api.dart'; 6 | import 'package:wanandroid_flutter/entity/article_entity.dart'; 7 | import 'package:wanandroid_flutter/entity/common_entity.dart'; 8 | import 'package:wanandroid_flutter/http/httpUtil.dart'; 9 | import 'package:wanandroid_flutter/util/ToastUtil.dart'; 10 | 11 | import 'articleDetail.dart'; 12 | import 'loginPage.dart'; 13 | 14 | class CollectPage extends StatefulWidget { 15 | @override 16 | State createState() { 17 | return _CollectPagePageState(); 18 | } 19 | } 20 | 21 | class _CollectPagePageState extends State { 22 | List articleDatas = List(); 23 | int _page = 0; 24 | 25 | @override 26 | void initState() { 27 | super.initState(); 28 | getHttp(); 29 | } 30 | 31 | void getHttp() async { 32 | try { 33 | var response = await HttpUtil().get(Api.COLLECT_LIST + "$_page/json"); 34 | Map map = json.decode(response.toString()); 35 | var articleEntity = ArticleEntity.fromJson(map); 36 | 37 | if (articleEntity.errorCode == -1001) { 38 | YToast.showBottom(context: context, msg: articleEntity.errorMsg); 39 | Navigator.of(context).pop(); 40 | Navigator.push( 41 | context, 42 | MaterialPageRoute(builder: (context) => LoginPage()), 43 | ); 44 | } else { 45 | setState(() { 46 | articleDatas = articleEntity.data.datas; 47 | }); 48 | } 49 | } catch (e) { 50 | print(e); 51 | } 52 | } 53 | 54 | @override 55 | Widget build(BuildContext context) { 56 | return Scaffold( 57 | appBar: AppBar( 58 | backgroundColor: Theme.of(context).primaryColor, 59 | title: Text("我的收藏"), 60 | ), 61 | body: EasyRefresh( 62 | header: PhoenixHeader(), 63 | footer: PhoenixFooter(), 64 | onRefresh: () async { 65 | await Future.delayed(Duration(seconds: 1), () { 66 | setState(() { 67 | _page = 0; 68 | }); 69 | getHttp(); 70 | }); 71 | }, 72 | onLoad: () async { 73 | await Future.delayed(Duration(seconds: 1), () async { 74 | setState(() { 75 | _page++; 76 | }); 77 | getMoreData(); 78 | }); 79 | }, 80 | child: CustomScrollView( 81 | slivers: [ 82 | SliverList( 83 | delegate: SliverChildBuilderDelegate((context, index) { 84 | return getItem(index); 85 | }, childCount: articleDatas.length)), 86 | ], 87 | ), 88 | ), 89 | ); 90 | } 91 | 92 | Widget getItem(int index) { 93 | final item = articleDatas[index]; 94 | return Dismissible( 95 | // Show a red background as the item is swiped away 96 | background: Container(color: Theme.of(context).primaryColor), 97 | // Each Dismissible must contain a Key. Keys allow Flutter to 98 | // uniquely identify Widgets. 99 | key: Key(item.title), 100 | // We also need to provide a function that will tell our app 101 | // what to do after an item has been swiped away. 102 | onDismissed: (direction) { 103 | // Remove the item from our data source 104 | articleDatas.removeAt(index); 105 | 106 | cancelCollect(item.id, item.originId == null ? -1 : item.originId); 107 | 108 | // Show a snackbar! This snackbar could also contain "Undo" actions. 109 | ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("已移除"))); 110 | }, 111 | child: getRow(index), 112 | ); 113 | } 114 | 115 | Widget getRow(int i) { 116 | return GestureDetector( 117 | child: Container( 118 | padding: EdgeInsets.all(10.0), 119 | child: ListTile( 120 | title: Text( 121 | articleDatas[i].title, 122 | maxLines: 2, 123 | overflow: TextOverflow.ellipsis, 124 | ), 125 | subtitle: Padding( 126 | padding: EdgeInsets.only(top: 10.0), 127 | child: Row( 128 | children: [ 129 | Container( 130 | padding: EdgeInsets.symmetric(horizontal: 6), 131 | decoration: BoxDecoration( 132 | border: Border.all( 133 | color: Theme.of(context).primaryColor, 134 | width: 1.0, 135 | ), 136 | borderRadius: BorderRadius.circular((20.0)), // 圆角度 137 | ), 138 | child: Text( 139 | articleDatas[i].chapterName, 140 | style: TextStyle(color: Theme.of(context).primaryColor), 141 | ), 142 | ), 143 | Container( 144 | margin: EdgeInsets.only(left: 15), 145 | child: Text(articleDatas[i].author), 146 | ), 147 | ], 148 | ), 149 | ), 150 | trailing: Icon(Icons.chevron_right), 151 | )), 152 | onTap: () { 153 | Navigator.push( 154 | context, 155 | MaterialPageRoute( 156 | builder: (context) => ArticleDetail( 157 | title: articleDatas[i].title, 158 | url: articleDatas[i].link, 159 | ), 160 | ), 161 | ); 162 | }, 163 | ); 164 | } 165 | 166 | @override 167 | void dispose() { 168 | super.dispose(); 169 | } 170 | 171 | Future cancelCollect(int id, int originId) async { 172 | var data = {'originId': originId}; 173 | var collectResponse = await HttpUtil().post(Api.UN_COLLECT + '$id/json', data: data); 174 | Map map = json.decode(collectResponse.toString()); 175 | var entity = CommonEntity.fromJson(map); 176 | if (entity.errorCode == -1001) { 177 | YToast.show(context: context, msg: entity.errorMsg); 178 | } else { 179 | //getHttp(); 180 | } 181 | } 182 | 183 | Future getMoreData() async { 184 | var response = await HttpUtil().get(Api.COLLECT_LIST + "$_page/json"); 185 | Map map = json.decode(response.toString()); 186 | var articleEntity = ArticleEntity.fromJson(map); 187 | setState(() { 188 | articleDatas.addAll(articleEntity.data.datas); 189 | }); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /lib/pages/about.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:share/share.dart'; 3 | import 'package:wanandroid_flutter/util/ToastUtil.dart'; 4 | 5 | import 'articleDetail.dart'; 6 | 7 | class AboutPage extends StatelessWidget { 8 | @override 9 | Widget build(BuildContext context) { 10 | var _list = [ 11 | "fluttertoast", 12 | "dio", 13 | "cookie_jar", 14 | "flutter_webview_plugin", 15 | "flutter_swiper", 16 | "share", 17 | "provide", 18 | "shared_preferences", 19 | "flutter_easyrefresh", 20 | ]; 21 | return Scaffold( 22 | body: CustomScrollView( 23 | slivers: [ 24 | SliverAppBar( 25 | backgroundColor: Theme.of(context).primaryColor, 26 | title: Text("关于项目"), 27 | expandedHeight: 230.0, 28 | floating: false, 29 | pinned: true, 30 | snap: false, 31 | actions: [ 32 | PopupMenuButton( 33 | icon: Icon(Icons.more_horiz), 34 | offset: Offset(100, 100), 35 | itemBuilder: (BuildContext context) => >[ 36 | const PopupMenuItem( 37 | value: "1", 38 | child: ListTile( 39 | leading: Icon(Icons.share), 40 | title: Text('分享'), 41 | ), 42 | ), 43 | PopupMenuDivider(), //分割线 44 | const PopupMenuItem( 45 | value: "2", 46 | child: ListTile( 47 | leading: Icon(Icons.settings), 48 | title: Text('设置'), 49 | ), 50 | ), 51 | ], 52 | tooltip: "点击弹出菜单", 53 | onSelected: (String result) { 54 | print(result); 55 | switch (result) { 56 | case "1": 57 | //需要重启,不然会有 MissingPluginException(No implementation found for method getAll on channel plugins.flutter.io/share)异常 58 | Share.share('【玩安卓Flutter版】\nhttps://github.com/yechaoa/wanandroid_flutter'); 59 | break; 60 | case "2": 61 | YToast.show(context: context, msg: "设置"); 62 | break; 63 | } 64 | }, 65 | onCanceled: () { 66 | print("取消"); 67 | }, 68 | ), 69 | ], 70 | flexibleSpace: FlexibleSpaceBar( 71 | //background: Image.asset("images/a.jpg", fit: BoxFit.fill), 72 | background: Image.network( 73 | "https://profile-avatar.csdnimg.cn/f81b97e9519148ac9d7eca7681fb8698_yechaoa.jpg!1", 74 | // "https://avatars3.githubusercontent.com/u/19725223?s=400&u=f399a2d73fd0445be63ee6bc1ea4a408a62454f5&v=4", 75 | fit: BoxFit.cover), 76 | ), 77 | ), 78 | SliverFixedExtentList( 79 | itemExtent: 900.0, 80 | delegate: SliverChildBuilderDelegate( 81 | (context, index) => Padding( 82 | padding: EdgeInsets.all(20), 83 | child: Column( 84 | crossAxisAlignment: CrossAxisAlignment.start, 85 | children: [ 86 | Padding( 87 | padding: EdgeInsets.all(10), 88 | child: Text( 89 | "wanandroid_flutter V2.0", 90 | style: TextStyle(fontSize: 25, fontFamily: 'mononoki'), 91 | ), 92 | ), 93 | Padding( 94 | padding: EdgeInsets.all(10), 95 | child: Text( 96 | "Author :yechaoa", 97 | style: TextStyle( 98 | decoration: TextDecoration.underline, 99 | decorationStyle: TextDecorationStyle.solid, 100 | fontSize: 18, 101 | color: Theme.of(context).primaryColor), 102 | ), 103 | ), 104 | Divider(), 105 | ListTile( 106 | title: Text("GitHub"), 107 | trailing: Icon(Icons.chevron_right), 108 | onTap: () { 109 | Navigator.push( 110 | context, 111 | MaterialPageRoute( 112 | builder: (context) => ArticleDetail( 113 | title: "别跑,点个star再走~🌹", url: "https://github.com/yechaoa/wanandroid_flutter"), 114 | ), 115 | ); 116 | }, 117 | ), 118 | Divider(), 119 | ListTile( 120 | title: Text("掘金"), 121 | trailing: Icon(Icons.chevron_right), 122 | onTap: () { 123 | Navigator.push( 124 | context, 125 | MaterialPageRoute( 126 | builder: (context) => ArticleDetail( 127 | title: "yechaoa's 掘金", url: "https://juejin.cn/user/659362706101735/posts"), 128 | ), 129 | ); 130 | }, 131 | ), 132 | Divider(), 133 | ListTile( 134 | title: Text("CSDN"), 135 | trailing: Icon(Icons.chevron_right), 136 | onTap: () { 137 | Navigator.push( 138 | context, 139 | MaterialPageRoute( 140 | builder: (context) => ArticleDetail( 141 | title: "yechaoa's CSDN", url: "https://blog.csdn.net/yechaoa"), 142 | ), 143 | ); 144 | }, 145 | ), 146 | Divider(), 147 | 148 | Padding( 149 | padding: EdgeInsets.all(10), 150 | child: Text( 151 | "用到的库:", 152 | style: TextStyle(fontSize: 16), 153 | ), 154 | ), 155 | ListView.builder( 156 | itemCount: _list.length, 157 | shrinkWrap: true, 158 | //禁掉ListView的滑动,跟CustomScrollView滑动冲突 159 | physics: NeverScrollableScrollPhysics(), 160 | itemBuilder: (BuildContext context, int position) { 161 | return Padding( 162 | padding: EdgeInsets.symmetric(vertical: 15, horizontal: 30), 163 | child: Text( 164 | _list[position].toString(), 165 | style: TextStyle(fontFamily: 'mononoki'), 166 | ), 167 | ); 168 | }, 169 | ), 170 | Divider(), 171 | ], 172 | ), 173 | ), 174 | childCount: 1, 175 | ), 176 | ), 177 | ], 178 | ), 179 | ); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /lib/pages/articleDetail.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 3 | import 'package:share/share.dart'; 4 | import 'package:wanandroid_flutter/res/colors.dart'; 5 | 6 | // ignore: must_be_immutable 7 | class ArticleDetail extends StatelessWidget { 8 | String url, title; 9 | 10 | ArticleDetail({Key key, @required this.title, @required this.url}) 11 | : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return MaterialApp( 16 | theme: ThemeData( 17 | primaryColor: Theme.of(context).primaryColor, 18 | ), 19 | routes: { 20 | "/": (_) => WebviewScaffold( 21 | url: "$url", 22 | appBar: AppBar( 23 | backgroundColor: Theme.of(context).primaryColor, 24 | //返回键 点击关闭 25 | leading: IconButton( 26 | icon: Icon(Icons.arrow_back), 27 | onPressed: () { 28 | Navigator.maybePop(context); 29 | }), 30 | title: Text("$title"), 31 | actions: [ 32 | IconButton( 33 | icon: Icon(Icons.share), 34 | tooltip: '分享', 35 | onPressed: () { 36 | Share.share('【$title】\n$url'); 37 | }, 38 | ), 39 | ], 40 | ), 41 | ), 42 | }, 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/pages/homePage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:easy_refresh/easy_refresh.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_swiper/flutter_swiper.dart'; 6 | import 'package:wanandroid_flutter/common/api.dart'; 7 | import 'package:wanandroid_flutter/entity/article_entity.dart'; 8 | import 'package:wanandroid_flutter/entity/banner_entity.dart'; 9 | import 'package:wanandroid_flutter/entity/common_entity.dart'; 10 | import 'package:wanandroid_flutter/http/httpUtil.dart'; 11 | import 'package:wanandroid_flutter/pages/articleDetail.dart'; 12 | import 'package:wanandroid_flutter/util/ToastUtil.dart'; 13 | 14 | import '../res/colors.dart'; 15 | import 'loginPage.dart'; 16 | 17 | class HomePage extends StatefulWidget { 18 | const HomePage({Key key}) : super(key: key); 19 | 20 | @override 21 | _HomePageState createState() => _HomePageState(); 22 | } 23 | 24 | class _HomePageState extends State { 25 | List bannerDatas = List(); 26 | List articleDatas = List(); 27 | 28 | EasyRefreshController _easyRefreshController; 29 | SwiperController _swiperController; 30 | 31 | int _page = 0; 32 | bool hasMore = true; 33 | 34 | @override 35 | void initState() { 36 | super.initState(); 37 | 38 | _easyRefreshController = EasyRefreshController( 39 | controlFinishRefresh: true, 40 | controlFinishLoad: true, 41 | ); 42 | _swiperController = SwiperController(); 43 | 44 | getHttp(); 45 | } 46 | 47 | void getHttp() async { 48 | try { 49 | //banner 50 | var bannerResponse = await HttpUtil().get(Api.BANNER); 51 | Map bannerMap = json.decode(bannerResponse.toString()); 52 | var bannerEntity = BannerEntity.fromJson(bannerMap); 53 | 54 | //article 55 | var articleResponse = await HttpUtil().get(Api.ARTICLE_LIST + "$_page/json"); 56 | Map articleMap = json.decode(articleResponse.toString()); 57 | var articleEntity = ArticleEntity.fromJson(articleMap); 58 | 59 | setState(() { 60 | bannerDatas = bannerEntity.data; 61 | articleDatas = articleEntity.data.datas; 62 | }); 63 | 64 | _swiperController.startAutoplay(); 65 | } catch (e) { 66 | print(e); 67 | } 68 | } 69 | 70 | @override 71 | Widget build(BuildContext context) { 72 | return Scaffold( 73 | body: EasyRefresh.builder( 74 | controller: _easyRefreshController, 75 | header: PhoenixHeader( 76 | skyColor: YColors.colorPrimary, 77 | position: IndicatorPosition.locator, 78 | safeArea: false, 79 | ), 80 | footer: PhoenixFooter( 81 | skyColor: YColors.colorPrimary, 82 | position: IndicatorPosition.locator, 83 | ), 84 | onRefresh: () async { 85 | await Future.delayed(Duration(seconds: 1), () { 86 | if (!mounted) { 87 | return; 88 | } 89 | setState(() { 90 | _page = 0; 91 | }); 92 | getHttp(); 93 | 94 | _easyRefreshController.finishRefresh(); 95 | _easyRefreshController.resetFooter(); 96 | }); 97 | }, 98 | onLoad: () async { 99 | await Future.delayed(Duration(seconds: 1), () { 100 | if (!mounted) { 101 | return; 102 | } 103 | setState(() { 104 | _page++; 105 | }); 106 | getMoreData(); 107 | 108 | _easyRefreshController.finishLoad(hasMore ? IndicatorResult.success : IndicatorResult.noMore); 109 | }); 110 | }, 111 | 112 | childBuilder: (context, physics) { 113 | return CustomScrollView( 114 | physics: physics, 115 | slivers: [ 116 | SliverAppBar( 117 | backgroundColor: Theme.of(context).primaryColor, 118 | expandedHeight: MediaQuery.of(context).size.width / 1.8 * 0.8 + 20, // +20 是上下的padding值 119 | pinned: false, 120 | flexibleSpace: FlexibleSpaceBar( 121 | background: getBanner(), 122 | ), 123 | ), 124 | const HeaderLocator.sliver(), 125 | SliverList( 126 | delegate: SliverChildBuilderDelegate((context, index) { 127 | return getRow(index, articleDatas.length); 128 | }, childCount: articleDatas.length), 129 | ), 130 | const FooterLocator.sliver(), 131 | ], 132 | ); 133 | }, 134 | ), 135 | ); 136 | } 137 | 138 | Widget getBanner() { 139 | return Container( 140 | width: MediaQuery.of(context).size.width, 141 | //1.8是banner宽高比,0.8是viewportFraction的值 142 | height: MediaQuery.of(context).size.width / 1.8 * 0.8, 143 | padding: EdgeInsets.only(top: 10, bottom: 10), 144 | child: Swiper( 145 | itemCount: bannerDatas.length, 146 | itemBuilder: (BuildContext context, int index) { 147 | return Container( 148 | decoration: BoxDecoration( 149 | borderRadius: BorderRadius.circular((10.0)), // 圆角度 150 | image: DecorationImage( 151 | image: NetworkImage(bannerDatas[index].imagePath), 152 | fit: BoxFit.fill, 153 | ), 154 | ), 155 | ); 156 | }, 157 | loop: false, 158 | autoplay: false, 159 | autoplayDelay: 3000, 160 | //触发时是否停止播放 161 | autoplayDisableOnInteraction: true, 162 | duration: 600, 163 | //默认分页按钮 164 | controller: _swiperController, 165 | //默认指示器 166 | pagination: SwiperPagination( 167 | // SwiperPagination.fraction 数字1/5,默认点 168 | builder: DotSwiperPaginationBuilder(size: 6, activeSize: 9), 169 | ), 170 | 171 | //视图宽度,即显示的item的宽度屏占比 172 | viewportFraction: 0.8, 173 | //两侧item的缩放比 174 | scale: 0.9, 175 | 176 | onTap: (int index) { 177 | //点击事件,返回下标 178 | print("index-----" + index.toString()); 179 | }, 180 | ), 181 | ); 182 | } 183 | 184 | Widget getRow(int i, int length) { 185 | // print("debug >>>>>"); 186 | // print(i); 187 | // print("debug <<<<<"); 188 | 189 | // 防止接口慢的情况 190 | if (length == 0) return null; 191 | 192 | return GestureDetector( 193 | child: Container( 194 | padding: EdgeInsets.symmetric(vertical: 10, horizontal: 5), 195 | child: ListTile( 196 | leading: IconButton( 197 | icon: articleDatas != null && articleDatas[i].collect 198 | ? Icon( 199 | Icons.favorite, 200 | color: Theme.of(context).primaryColor, 201 | ) 202 | : Icon(Icons.favorite_border), 203 | tooltip: '收藏', 204 | onPressed: () { 205 | if (articleDatas[i].collect) { 206 | cancelCollect(articleDatas[i].id); 207 | } else { 208 | addCollect(articleDatas[i].id); 209 | } 210 | }, 211 | ), 212 | title: Text( 213 | articleDatas[i].title, 214 | maxLines: 2, 215 | overflow: TextOverflow.ellipsis, 216 | ), 217 | subtitle: Padding( 218 | padding: EdgeInsets.only(top: 10.0), 219 | child: Row( 220 | children: [ 221 | Container( 222 | constraints: BoxConstraints(maxWidth: 150), 223 | padding: EdgeInsets.symmetric(horizontal: 6), 224 | decoration: BoxDecoration( 225 | border: Border.all( 226 | color: Theme.of(context).primaryColor, 227 | width: 1.0, 228 | ), 229 | borderRadius: BorderRadius.circular((20.0)), // 圆角度 230 | ), 231 | child: Text(articleDatas[i].superChapterName, 232 | style: TextStyle(color: Theme.of(context).primaryColor), 233 | overflow: TextOverflow.ellipsis, 234 | maxLines: 1)), 235 | Container( 236 | margin: EdgeInsets.only(left: 10), 237 | child: Text(articleDatas[i].author), 238 | ), 239 | ], 240 | ), 241 | ), 242 | trailing: Icon(Icons.chevron_right), 243 | )), 244 | onTap: () { 245 | if (0 == 1) return; 246 | Navigator.push( 247 | context, 248 | MaterialPageRoute( 249 | builder: (context) => ArticleDetail(title: articleDatas[i].title, url: articleDatas[i].link), 250 | ), 251 | ); 252 | }, 253 | ); 254 | } 255 | 256 | Future addCollect(int id) async { 257 | var collectResponse = await HttpUtil().post(Api.COLLECT + '$id/json'); 258 | Map map = json.decode(collectResponse.toString()); 259 | var entity = CommonEntity.fromJson(map); 260 | if (entity.errorCode == -1001) { 261 | YToast.showBottom(context: context, msg: entity.errorMsg); 262 | Navigator.push( 263 | context, 264 | MaterialPageRoute(builder: (context) => LoginPage()), 265 | ); 266 | } else { 267 | YToast.showBottom(context: context, msg: "收藏成功"); 268 | getHttp(); 269 | } 270 | } 271 | 272 | Future cancelCollect(int id) async { 273 | var collectResponse = await HttpUtil().post(Api.UN_COLLECT_ORIGIN_ID + '$id/json'); 274 | Map map = json.decode(collectResponse.toString()); 275 | var entity = CommonEntity.fromJson(map); 276 | if (entity.errorCode == -1001) { 277 | YToast.show(context: context, msg: entity.errorMsg); 278 | Navigator.push( 279 | context, 280 | MaterialPageRoute(builder: (context) => LoginPage()), 281 | ); 282 | } else { 283 | YToast.show(context: context, msg: "取消成功"); 284 | getHttp(); 285 | } 286 | } 287 | 288 | Future getMoreData() async { 289 | var response = await HttpUtil().get(Api.ARTICLE_LIST + "$_page/json"); 290 | Map map = json.decode(response.toString()); 291 | var articleEntity = ArticleEntity.fromJson(map); 292 | setState(() { 293 | articleDatas.addAll(articleEntity.data.datas); 294 | if (articleEntity.data.datas.length < 10) { 295 | hasMore = false; 296 | } 297 | }); 298 | } 299 | 300 | @override 301 | void dispose() { 302 | _easyRefreshController.dispose(); 303 | _swiperController.stopAutoplay(); 304 | _swiperController.dispose(); 305 | super.dispose(); 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /lib/pages/loginPage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:wanandroid_flutter/common/api.dart'; 5 | import 'package:wanandroid_flutter/entity/user_entity.dart'; 6 | import 'package:wanandroid_flutter/http/httpUtil.dart'; 7 | import 'package:wanandroid_flutter/main.dart'; 8 | import 'package:wanandroid_flutter/res/colors.dart'; 9 | import 'package:wanandroid_flutter/util/ToastUtil.dart'; 10 | 11 | class LoginPage extends StatefulWidget { 12 | @override 13 | State createState() { 14 | return _LoginPagePageState(); 15 | } 16 | } 17 | 18 | class _LoginPagePageState extends State with SingleTickerProviderStateMixin { 19 | 20 | var tabs = []; 21 | String btnText = "登录"; 22 | String bottomText = "没有账号?注册"; 23 | bool visible = true; 24 | GlobalKey _key = GlobalKey(); 25 | bool autoValidate = false; 26 | String username, password, rePassword; 27 | 28 | @override 29 | void initState() { 30 | super.initState(); 31 | } 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | return MaterialApp( 36 | theme: ThemeData( 37 | primaryColor: Theme.of(context).primaryColor, 38 | accentColor: Theme.of(context).accentColor, 39 | primaryColorDark: Theme.of(context).primaryColorDark, 40 | ), 41 | home: Container( 42 | decoration: BoxDecoration( 43 | gradient: LinearGradient( 44 | begin: Alignment.topCenter, 45 | end: Alignment.bottomCenter, 46 | colors: [ 47 | Theme.of(context).accentColor, 48 | Theme.of(context).primaryColorDark, 49 | ], 50 | ), 51 | ), 52 | child: Scaffold( 53 | backgroundColor: Colors.transparent, 54 | body: getBodyView(), 55 | ), 56 | ), 57 | ); 58 | } 59 | 60 | Widget getBodyView() { 61 | //可滑动布局,避免弹出键盘时会有溢出异常 62 | return ListView( 63 | children: [ 64 | SizedBox(height: 80), 65 | Stack(alignment: Alignment.topCenter, children: [ 66 | Container( 67 | margin: EdgeInsets.only(top: 50), 68 | padding: EdgeInsets.all(40), 69 | child: Card( 70 | elevation: 5, 71 | shape: RoundedRectangleBorder( 72 | borderRadius: BorderRadius.all(Radius.circular(15)), 73 | ), 74 | color: Colors.white, 75 | child: Padding( 76 | padding: EdgeInsets.all(20), 77 | child: Form( 78 | autovalidateMode: AutovalidateMode.onUserInteraction, 79 | key: _key, 80 | child: Column( 81 | crossAxisAlignment: CrossAxisAlignment.start, 82 | children: [ 83 | TextFormField( 84 | //键盘类型,即输入类型 85 | keyboardType: TextInputType.number, 86 | textInputAction: TextInputAction.next, 87 | decoration: InputDecoration( 88 | icon: Icon(Icons.person_outline), 89 | labelText: '请输入账号', 90 | ), 91 | validator: validateUsername, 92 | onSaved: (text) { 93 | setState(() { 94 | username = text; 95 | }); 96 | }, 97 | ), 98 | SizedBox(height: 20), 99 | TextFormField( 100 | //是否显示密码类型 101 | obscureText: true, 102 | keyboardType: TextInputType.number, 103 | decoration: InputDecoration( 104 | icon: Icon(Icons.lock_outline), 105 | labelText: '请输入密码', 106 | ), 107 | validator: validatePassword, 108 | onSaved: (text) { 109 | setState(() { 110 | password = text; 111 | }); 112 | }, 113 | ), 114 | SizedBox(height: 20), 115 | Offstage( 116 | offstage: visible, 117 | child: Column( 118 | children: [ 119 | TextFormField( 120 | obscureText: true, 121 | keyboardType: TextInputType.number, 122 | decoration: InputDecoration( 123 | icon: Icon(Icons.lock_outline), 124 | labelText: '请确认密码', 125 | ), 126 | validator: visible ? null : validateRePassword, 127 | onSaved: (text) { 128 | setState(() { 129 | rePassword = text; 130 | }); 131 | }, 132 | ), 133 | SizedBox(height: 20), 134 | ], 135 | ), 136 | ), 137 | SizedBox(height: 10), 138 | ], 139 | ), 140 | ), 141 | ), 142 | ), 143 | ), 144 | Positioned( 145 | top: 40, 146 | left: MediaQuery.of(context).size.width / 2 - 35, 147 | child: Center( 148 | child: Container( 149 | width: 70, 150 | height: 70, 151 | decoration: BoxDecoration( 152 | shape: BoxShape.circle, 153 | color: Colors.white, 154 | image: DecorationImage( 155 | image: AssetImage("lib/res/images/ic_logo.png"), 156 | ), 157 | boxShadow: [ 158 | BoxShadow( 159 | //左右、上下阴影的距离 160 | offset: Offset(0, 0), 161 | //阴影颜色 162 | color: Colors.grey, 163 | //模糊距离 164 | blurRadius: 8, 165 | //不模糊距离 166 | spreadRadius: 1, 167 | ), 168 | ], 169 | ), 170 | ), 171 | ), 172 | ), 173 | Positioned( 174 | bottom: 20, 175 | left: 130, 176 | right: 130, 177 | child: ElevatedButton( 178 | style: ElevatedButton.styleFrom(backgroundColor: Theme.of(context).primaryColor), 179 | child: Container( 180 | padding: EdgeInsets.all(10.0), 181 | child: Text( 182 | btnText, 183 | style: TextStyle(color: YColors.color_fff, fontSize: 20), 184 | ), 185 | ), 186 | onPressed: () { 187 | if (_key.currentState.validate()) { 188 | _key.currentState.save(); 189 | print(username + "--" + password + "**" + rePassword); 190 | doRequest(); 191 | } else { 192 | setState(() { 193 | autoValidate = true; 194 | }); 195 | } 196 | }, 197 | ), 198 | ), 199 | ]), 200 | GestureDetector( 201 | child: Text( 202 | bottomText, 203 | style: TextStyle(color: YColors.color_fff), 204 | textAlign: TextAlign.center, 205 | ), 206 | onTap: () { 207 | setState(() { 208 | if (visible) { 209 | btnText = "注册"; 210 | visible = false; 211 | bottomText = "已有账号?登录"; 212 | } else { 213 | btnText = "登录"; 214 | visible = true; 215 | bottomText = "没有账号?注册"; 216 | } 217 | }); 218 | }, 219 | ), 220 | ], 221 | ); 222 | } 223 | 224 | @override 225 | void dispose() { 226 | super.dispose(); 227 | } 228 | 229 | String validateUsername(String value) { 230 | if (value.isEmpty) 231 | return "账号不能为空"; 232 | else if (value.length < 6) return "账号最少6位"; 233 | return null; 234 | } 235 | 236 | String validatePassword(String value) { 237 | if (value.isEmpty) 238 | return "密码不能为空"; 239 | else if (value.length < 6) return "密码最少6位"; 240 | return null; 241 | } 242 | 243 | String validateRePassword(String value) { 244 | if (value.isEmpty) 245 | return "确认密码不能为空"; 246 | else if (value.length < 6) return "确认密码最少6位"; 247 | // else if (value != password) return "两次密码不一致"; 248 | return null; 249 | } 250 | 251 | Future doRequest() async { 252 | var data; 253 | if (visible) 254 | data = {'username': username, 'password': password}; 255 | else 256 | data = {'username': username, 'password': password, 'repassword': rePassword}; 257 | 258 | var response = await HttpUtil().post(visible ? Api.LOGIN : Api.REGISTER, data: data); 259 | Map userMap = json.decode(response.toString()); 260 | var userEntity = UserEntity.fromJson(userMap); 261 | if (userEntity.errorCode == 0) { 262 | YToast.show(context: context, msg: visible ? "登录成功~" : "注册成功~"); 263 | //跳转并关闭当前页面 264 | Navigator.pushAndRemoveUntil( 265 | context, 266 | MaterialPageRoute(builder: (context) => MyHomePage()), 267 | (route) => route == null, 268 | ); 269 | } else 270 | YToast.show(context: context, msg: userMap['errorMsg']); 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /lib/pages/naviPage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:wanandroid_flutter/common/api.dart'; 5 | import 'package:wanandroid_flutter/entity/navi_entity.dart'; 6 | import 'package:wanandroid_flutter/http/httpUtil.dart'; 7 | import 'package:wanandroid_flutter/pages/articleDetail.dart'; 8 | import 'package:wanandroid_flutter/res/colors.dart'; 9 | 10 | class NaviPage extends StatefulWidget { 11 | @override 12 | State createState() { 13 | return _NaviPageState(); 14 | } 15 | } 16 | 17 | class _NaviPageState extends State { 18 | List _datas = List(); //一级分类集合 19 | List articles = List(); //二级分类集合 20 | int index; //一级分类下标 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | getHttp(); 26 | } 27 | 28 | void getHttp() async { 29 | try { 30 | var response = await HttpUtil().get(Api.NAVI); 31 | Map userMap = json.decode(response.toString()); 32 | var naviEntity = NaviEntity.fromJson(userMap); 33 | 34 | /// 初始化 35 | setState(() { 36 | _datas = naviEntity.data; 37 | index = 0; 38 | }); 39 | } catch (e) { 40 | print(e); 41 | } 42 | } 43 | 44 | @override 45 | Widget build(BuildContext context) { 46 | return Row( 47 | mainAxisAlignment: MainAxisAlignment.start, 48 | children: [ 49 | Expanded( 50 | flex: 2, 51 | child: Container( 52 | color: YColors.color_fff, 53 | child: ListView.builder( 54 | itemCount: _datas.length, 55 | itemBuilder: (BuildContext context, int position) { 56 | return getRow(position); 57 | }, 58 | ), 59 | ), 60 | ), 61 | Expanded( 62 | flex: 5, 63 | child: ListView( 64 | children: [ 65 | Container( 66 | //height: double.infinity, 67 | alignment: Alignment.topLeft, 68 | padding: const EdgeInsets.all(10), 69 | color: YColors.color_F9F9F9, 70 | child: getChip(index), //传入一级分类下标 71 | ), 72 | ], 73 | )), 74 | ], 75 | ); 76 | } 77 | 78 | Widget getRow(int i) { 79 | Color textColor = Theme.of(context).primaryColor; //字体颜色 80 | return GestureDetector( 81 | child: Container( 82 | alignment: Alignment.center, 83 | padding: EdgeInsets.symmetric(vertical: 15, horizontal: 10), 84 | //Container下的color属性会与decoration下的border属性冲突,所以要用decoration下的color属性 85 | decoration: BoxDecoration( 86 | color: index == i ? YColors.color_F9F9F9 : Colors.white, 87 | border: Border( 88 | left: BorderSide( 89 | width: 5, 90 | color: 91 | index == i ? Theme.of(context).primaryColor : Colors.white), 92 | ), 93 | ), 94 | child: Text( 95 | _datas[i].name, 96 | style: TextStyle( 97 | color: index == i ? textColor : YColors.color_666, 98 | fontWeight: index == i ? FontWeight.w600 : FontWeight.w400, 99 | fontSize: 16, 100 | ), 101 | ), 102 | ), 103 | onTap: () { 104 | setState(() { 105 | index = i; //记录选中的下标 106 | textColor = YColors.colorPrimary; 107 | }); 108 | }, 109 | ); 110 | } 111 | 112 | Widget getChip(int i) { 113 | //更新对应下标数据 114 | _updateArticles(i); 115 | return Wrap( 116 | spacing: 10.0, //两个widget之间横向的间隔 117 | direction: Axis.horizontal, //方向 118 | alignment: WrapAlignment.start, //内容排序方式 119 | children: List.generate( 120 | articles.length, 121 | (int index) { 122 | return ActionChip( 123 | //标签文字 124 | label: Text( 125 | articles[index].title, 126 | style: TextStyle(fontSize: 16, color: YColors.color_666), 127 | ), 128 | //点击事件 129 | onPressed: () { 130 | Navigator.push( 131 | context, 132 | MaterialPageRoute( 133 | builder: (context) => ArticleDetail( 134 | title: articles[index].title, url: articles[index].link), 135 | ), 136 | ); 137 | }, 138 | elevation: 3, 139 | backgroundColor: Colors.grey.shade200, 140 | ); 141 | }, 142 | ).toList(), 143 | ); 144 | } 145 | 146 | /// 147 | /// 根据一级分类下标更新二级分类集合 148 | /// 149 | List _updateArticles(int i) { 150 | setState(() { 151 | if (_datas.length != 0) articles = _datas[i].articles; 152 | }); 153 | return articles; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /lib/pages/projectPage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:easy_refresh/easy_refresh.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:wanandroid_flutter/common/api.dart'; 6 | import 'package:wanandroid_flutter/entity/project_entity.dart'; 7 | import 'package:wanandroid_flutter/entity/project_list_entity.dart'; 8 | import 'package:wanandroid_flutter/http/httpUtil.dart'; 9 | import 'package:wanandroid_flutter/pages/articleDetail.dart'; 10 | import 'package:wanandroid_flutter/res/colors.dart'; 11 | 12 | class ProjectPage extends StatefulWidget { 13 | @override 14 | State createState() { 15 | return _ProjectPageState(); 16 | } 17 | } 18 | 19 | class _ProjectPageState extends State with TickerProviderStateMixin { 20 | TabController _controller; //tab控制器 21 | int _currentIndex = 0; //选中下标 22 | 23 | List _datas = List(); //tab集合 24 | List _listDatas = List(); //内容集合 25 | 26 | int _page = 1; //看文档时要注意page是从0还是1开始 27 | 28 | @override 29 | void initState() { 30 | super.initState(); 31 | //初始化controller 32 | _controller = TabController(vsync: this, length: 0); 33 | getHttp(); 34 | } 35 | 36 | void getHttp() async { 37 | try { 38 | var response = await HttpUtil().get(Api.PROJECT); 39 | Map userMap = json.decode(response.toString()); 40 | var projectEntity = ProjectEntity.fromJson(userMap); 41 | 42 | setState(() { 43 | _datas = projectEntity.data; 44 | _controller = TabController(vsync: this, length: _datas.length); 45 | }); 46 | 47 | getDetail(); 48 | 49 | //controller添加监听 50 | _controller.addListener(() => _onTabChanged()); 51 | } catch (e) { 52 | print(e); 53 | } 54 | } 55 | 56 | /// 57 | /// tab改变监听 58 | /// 59 | _onTabChanged() { 60 | if (_controller.index.toDouble() == _controller.animation.value) { 61 | //赋值 并更新数据 62 | this.setState(() { 63 | _currentIndex = _controller.index; 64 | }); 65 | getDetail(); 66 | } 67 | } 68 | 69 | /// 70 | /// 根据tab下标获取数据 71 | /// 72 | void getDetail() async { 73 | try { 74 | var data = {"cid": _datas[_currentIndex].id}; 75 | var response = await HttpUtil().get(Api.PROJECT_LIST + "$_page/json", data: data); 76 | Map userMap = json.decode(response.toString()); 77 | var projectListEntity = ProjectListEntity.fromJson(userMap); 78 | 79 | setState(() { 80 | _listDatas = projectListEntity.data.datas; 81 | }); 82 | } catch (e) { 83 | print(e); 84 | } 85 | } 86 | 87 | @override 88 | Widget build(BuildContext context) { 89 | return Scaffold( 90 | appBar: TabBar( 91 | //控制器 92 | controller: _controller, 93 | //选中的颜色 94 | labelColor: Theme.of(context).primaryColor, 95 | //选中的样式 96 | labelStyle: TextStyle(fontSize: 16), 97 | //未选中的颜色 98 | unselectedLabelColor: YColors.color_666, 99 | //未选中的样式 100 | unselectedLabelStyle: TextStyle(fontSize: 14), 101 | //下划线颜色 102 | indicatorColor: Theme.of(context).primaryColor, 103 | //是否可滑动 104 | isScrollable: true, 105 | //tab标签 106 | tabs: _datas.map((ProjectData choice) { 107 | return Tab( 108 | text: choice.name, 109 | ); 110 | }).toList(), 111 | //点击事件 112 | onTap: (int i) { 113 | print(i); 114 | }, 115 | ), 116 | body: TabBarView( 117 | controller: _controller, 118 | children: _datas.map((ProjectData choice) { 119 | return EasyRefresh( 120 | header: TaurusHeader(), 121 | footer: TaurusFooter(), 122 | onRefresh: () async { 123 | await Future.delayed(Duration(seconds: 1), () { 124 | setState(() { 125 | _page = 1; 126 | }); 127 | getHttp(); 128 | }); 129 | }, 130 | onLoad: () async { 131 | await Future.delayed(Duration(seconds: 1), () async { 132 | setState(() { 133 | _page++; 134 | }); 135 | getMoreData(); 136 | }); 137 | }, 138 | child: CustomScrollView( 139 | slivers: [ 140 | SliverList( 141 | delegate: SliverChildBuilderDelegate((context, index) { 142 | return getRow(index); 143 | }, childCount: _listDatas.length)), 144 | ], 145 | ), 146 | ); 147 | }).toList(), 148 | ), 149 | ); 150 | } 151 | 152 | Widget getRow(int i) { 153 | return GestureDetector( 154 | child: Container( 155 | alignment: Alignment.topLeft, 156 | padding: EdgeInsets.all(10), 157 | child: Card( 158 | elevation: 5, 159 | shape: RoundedRectangleBorder( 160 | borderRadius: BorderRadius.all(Radius.circular(10)), 161 | ), 162 | color: Colors.white, 163 | child: Padding( 164 | padding: EdgeInsets.all(10), 165 | child: Row( 166 | crossAxisAlignment: CrossAxisAlignment.start, 167 | children: [ 168 | Expanded( 169 | flex: 2, 170 | child: Image.network(_listDatas[i].envelopePic), 171 | ), 172 | Expanded( 173 | flex: 5, 174 | child: Column( 175 | mainAxisSize: MainAxisSize.max, 176 | crossAxisAlignment: CrossAxisAlignment.start, 177 | children: [ 178 | Padding( 179 | padding: EdgeInsets.symmetric(horizontal: 10), 180 | child: Text( 181 | _listDatas[i].title, 182 | style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), 183 | maxLines: 3, 184 | overflow: TextOverflow.ellipsis, 185 | ), 186 | ), 187 | Padding( 188 | padding: EdgeInsets.all(10), 189 | child: Text( 190 | _listDatas[i].desc, 191 | style: TextStyle(fontSize: 14, color: YColors.color_666), 192 | maxLines: 3, 193 | overflow: TextOverflow.ellipsis, 194 | ), 195 | ), 196 | Container( 197 | alignment: Alignment.bottomLeft, 198 | child: Row( 199 | children: [ 200 | Expanded( 201 | flex: 1, 202 | child: Padding( 203 | padding: EdgeInsets.all(10), 204 | child: Text( 205 | _listDatas[i].niceDate, 206 | style: TextStyle(fontSize: 14), 207 | ), 208 | ), 209 | ), 210 | Padding( 211 | padding: EdgeInsets.all(10), 212 | child: Text( 213 | _listDatas[i].author, 214 | style: TextStyle(fontSize: 14), 215 | textAlign: TextAlign.right, 216 | ), 217 | ) 218 | ], 219 | ), 220 | ), 221 | ], 222 | ), 223 | ), 224 | ], 225 | ), 226 | ), 227 | ), 228 | ), 229 | onTap: () { 230 | //点击item跳转到详情 231 | Navigator.push( 232 | context, 233 | MaterialPageRoute( 234 | builder: (context) => ArticleDetail(title: _listDatas[i].title, url: _listDatas[i].link), 235 | ), 236 | ); 237 | }, 238 | ); 239 | } 240 | 241 | Future getMoreData() async { 242 | var data = {"cid": _datas[_currentIndex].id}; 243 | var response = await HttpUtil().get(Api.PROJECT_LIST + "$_page/json", data: data); 244 | Map userMap = json.decode(response.toString()); 245 | var projectListEntity = ProjectListEntity.fromJson(userMap); 246 | setState(() { 247 | _listDatas.addAll(projectListEntity.data.datas); 248 | }); 249 | } 250 | 251 | @override 252 | void dispose() { 253 | _controller.dispose(); 254 | super.dispose(); 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /lib/pages/searchPage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:math'; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:wanandroid_flutter/common/api.dart'; 6 | import 'package:wanandroid_flutter/entity/article_entity.dart'; 7 | import 'package:wanandroid_flutter/entity/hot_key_entity.dart'; 8 | import 'package:wanandroid_flutter/http/httpUtil.dart'; 9 | import 'package:wanandroid_flutter/res/colors.dart'; 10 | 11 | import 'articleDetail.dart'; 12 | 13 | List articleDatas = List(); 14 | List hotKeyDatas = List(); 15 | String key = ""; 16 | var pageContext; 17 | 18 | class SearchPage extends StatefulWidget { 19 | SearchPage({Key key}) : super(key: key); 20 | 21 | @override 22 | _SearchPageState createState() => _SearchPageState(); 23 | } 24 | 25 | class _SearchPageState extends State { 26 | @override 27 | void initState() { 28 | super.initState(); 29 | 30 | getHttpByHoyKey(); 31 | } 32 | 33 | void getHttpByHoyKey() async { 34 | try { 35 | var hotKeyResponse = await HttpUtil().post(Api.HOT_KEY); 36 | Map hotKeyMap = json.decode(hotKeyResponse.toString()); 37 | var hotKeyEntity = HotKeyEntity.fromJson(hotKeyMap); 38 | 39 | setState(() { 40 | hotKeyDatas = hotKeyEntity.data; 41 | }); 42 | 43 | //3个参数:上下文,搜索代理,关键词,其中前两个必传,query可选 44 | showSearch(context: context, delegate: MySearchDelegate("输入你想搜的内容~")); 45 | } catch (e) { 46 | print(e); 47 | } 48 | } 49 | 50 | @override 51 | Widget build(BuildContext context) { 52 | pageContext = context; 53 | return Scaffold( 54 | backgroundColor: Colors.white, 55 | body: null, 56 | ); 57 | } 58 | 59 | void getHttpByKey(String key) async { 60 | try { 61 | var data = {'k': key}; 62 | var articleResponse = await HttpUtil().post(Api.QUERY, data: data); 63 | Map articleMap = json.decode(articleResponse.toString()); 64 | var articleEntity = ArticleEntity.fromJson(articleMap); 65 | 66 | setState(() { 67 | articleDatas = articleEntity.data.datas; 68 | }); 69 | } catch (e) { 70 | print(e); 71 | } 72 | } 73 | } 74 | 75 | class MySearchDelegate extends SearchDelegate { 76 | BuildContext get context => context; 77 | 78 | MySearchDelegate( 79 | String hintText, 80 | ) : super( 81 | searchFieldLabel: hintText, 82 | keyboardType: TextInputType.text, 83 | // 软键盘快捷键 84 | textInputAction: TextInputAction.search, 85 | // 输入的字体颜色 86 | searchFieldStyle: TextStyle(color: Colors.white), 87 | ); 88 | 89 | /// 搜索框右边的操作 返回的是一个Widget集合 90 | @override 91 | List buildActions(BuildContext context) { 92 | return [ 93 | TextButton( 94 | child: Text( 95 | "搜索", 96 | style: TextStyle(fontSize: 18, color: YColors.color_fff), 97 | ), 98 | onPressed: () async { 99 | var data = {'k': query}; 100 | var articleResponse = await HttpUtil().post(Api.QUERY, data: data); 101 | Map articleMap = json.decode(articleResponse.toString()); 102 | var articleEntity = ArticleEntity.fromJson(articleMap); 103 | articleDatas = articleEntity.data.datas; 104 | 105 | showResults(context); 106 | }, 107 | ), 108 | // 显示一个清除的按钮 109 | IconButton( 110 | icon: Icon(Icons.clear), 111 | onPressed: () { 112 | query = ""; 113 | showSuggestions(context); 114 | }, 115 | ), 116 | ]; 117 | } 118 | 119 | /// 搜索框左边的操作,一般是返回按钮 120 | @override 121 | Widget buildLeading(BuildContext context) { 122 | return IconButton( 123 | icon: AnimatedIcon( 124 | icon: AnimatedIcons.menu_arrow, 125 | progress: transitionAnimation, 126 | ), 127 | onPressed: () { 128 | close(context, null); 129 | Navigator.of(pageContext).pop(); 130 | }, 131 | ); 132 | } 133 | 134 | /// 搜索结果 135 | /// 源码 136 | /// onSubmitted: (String _) { 137 | /// widget.delegate.showResults(context); 138 | /// }, 139 | @override 140 | Widget buildResults(BuildContext context) { 141 | return ListView.builder( 142 | shrinkWrap: true, 143 | itemCount: articleDatas.length, 144 | itemBuilder: (BuildContext context, int position) { 145 | if (position.isOdd) Divider(); 146 | return GestureDetector( 147 | child: Container( 148 | padding: EdgeInsets.all(10.0), 149 | child: ListTile( 150 | title: Text( 151 | articleDatas[position].title.replaceAll("", "【").replaceAll("<\/em>", "】"), 152 | maxLines: 2, 153 | overflow: TextOverflow.ellipsis, 154 | style: TextStyle(color: Colors.black54), 155 | ), 156 | subtitle: Padding( 157 | padding: EdgeInsets.only(top: 10.0), 158 | child: Row( 159 | children: [ 160 | Container( 161 | constraints: BoxConstraints(maxWidth: 150), 162 | padding: EdgeInsets.symmetric(horizontal: 6), 163 | decoration: BoxDecoration( 164 | border: Border.all(color: Theme.of(context).primaryColor, width: 1.0), 165 | borderRadius: BorderRadius.circular((20.0)), // 圆角度 166 | ), 167 | child: Text( 168 | articleDatas[position].superChapterName, 169 | style: TextStyle( 170 | color: Theme.of(context).primaryColor, 171 | ), 172 | overflow: TextOverflow.ellipsis, 173 | maxLines: 1, 174 | ), 175 | ), 176 | Container( 177 | margin: EdgeInsets.only(left: 15), 178 | child: Text(articleDatas[position].author), 179 | ), 180 | ], 181 | ), 182 | ), 183 | trailing: Icon(Icons.chevron_right), 184 | )), 185 | onTap: () { 186 | Navigator.push( 187 | context, 188 | MaterialPageRoute( 189 | builder: (context) => ArticleDetail( 190 | title: articleDatas[position] 191 | .title 192 | .replaceAll("", "") 193 | .replaceAll("<\/em>", ""), 194 | url: articleDatas[position].link), 195 | ), 196 | ); 197 | }, 198 | ); 199 | }); 200 | } 201 | 202 | /// 搜索建议 调用的话就不能返回null 203 | @override 204 | Widget buildSuggestions(BuildContext context) { 205 | return Padding( 206 | padding: EdgeInsets.all(15), 207 | child: Column( 208 | crossAxisAlignment: CrossAxisAlignment.start, 209 | children: [ 210 | Text( 211 | "大家都在搜:", 212 | style: TextStyle( 213 | fontSize: 20, 214 | fontWeight: FontWeight.bold, 215 | color: YColors.color_666, 216 | ), 217 | ), 218 | SizedBox(height: 10), 219 | Wrap( 220 | spacing: 10.0, //两个widget之间横向的间隔 221 | direction: Axis.horizontal, //方向 222 | alignment: WrapAlignment.start, //内容排序方式 223 | children: List.generate( 224 | hotKeyDatas.length, 225 | (int index) { 226 | return ActionChip( 227 | //标签文字 228 | label: Text( 229 | hotKeyDatas[index].name, 230 | style: TextStyle( 231 | fontSize: 16, 232 | color: Colors.white, 233 | ), 234 | ), 235 | //点击事件 236 | onPressed: () async { 237 | query = hotKeyDatas[index].name; 238 | key = query; 239 | 240 | /// 请求数据 241 | var data = {'k': key}; 242 | var articleResponse = await HttpUtil().post(Api.QUERY, data: data); 243 | Map articleMap = json.decode(articleResponse.toString()); 244 | var articleEntity = ArticleEntity.fromJson(articleMap); 245 | 246 | articleDatas = articleEntity.data.datas; 247 | 248 | /// 显示结果 249 | showResults(context); 250 | }, 251 | elevation: 3, 252 | backgroundColor: Color.fromARGB( 253 | 180, 254 | Random().nextInt(255), 255 | Random().nextInt(255), 256 | Random().nextInt(255), 257 | ), 258 | ); 259 | }, 260 | ).toList(), 261 | ), 262 | ], 263 | ), 264 | ); 265 | } 266 | 267 | /// 主题样式 268 | /// 光标的颜色改不了,主题的颜色也无法覆盖 fuck 参考如下issue 269 | /// https://github.com/flutter/flutter/issues/45498 270 | /// https://github.com/flutter/flutter/issues/48857 271 | @override 272 | ThemeData appBarTheme(BuildContext context) { 273 | 274 | assert(context != null); 275 | final ThemeData theme = Theme.of(context); 276 | assert(theme != null); 277 | return theme.copyWith( 278 | //主题色 279 | primaryColor: theme.primaryColor, 280 | //图标颜色 281 | primaryIconTheme: theme.primaryIconTheme.copyWith(color: Colors.white), 282 | //状态栏 283 | primaryColorBrightness: Brightness.dark, 284 | //文字主题 285 | textTheme: theme.textTheme.copyWith( 286 | // 不生效 287 | // subtitle1: TextStyle(color: Colors.red, fontSize: 20.0), 288 | headline1: theme.textTheme.headline1.copyWith(color: theme.primaryTextTheme.headline1.color), 289 | subtitle1: TextStyle( 290 | color: Colors.red, 291 | fontWeight: FontWeight.bold, 292 | fontSize: 18, 293 | ), 294 | ), 295 | 296 | // 不生效 297 | backgroundColor: theme.primaryColor, 298 | 299 | // hintStyle 300 | inputDecorationTheme: InputDecorationTheme( 301 | hintStyle: TextStyle(color: Colors.white60), 302 | border: InputBorder.none, 303 | ), 304 | ); 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /lib/pages/treeDetailPage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:easy_refresh/easy_refresh.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:wanandroid_flutter/common/api.dart'; 6 | import 'package:wanandroid_flutter/entity/article_entity.dart'; 7 | import 'package:wanandroid_flutter/entity/common_entity.dart'; 8 | import 'package:wanandroid_flutter/entity/tree_entity.dart'; 9 | import 'package:wanandroid_flutter/http/httpUtil.dart'; 10 | import 'package:wanandroid_flutter/pages/articleDetail.dart'; 11 | import 'package:wanandroid_flutter/util/ToastUtil.dart'; 12 | 13 | import 'loginPage.dart'; 14 | 15 | class TreeDetailPage extends StatefulWidget { 16 | final int panelIndex; //一级分类选中下标 17 | final int index; //二级分类选中下标 18 | 19 | TreeDetailPage({Key key, @required this.panelIndex, @required this.index}) : super(key: key); 20 | 21 | @override 22 | State createState() { 23 | return _TreeDetailPageState(); 24 | } 25 | } 26 | 27 | class _TreeDetailPageState extends State with TickerProviderStateMixin { 28 | TabController _controller; //tab控制器 29 | int _currentIndex = 0; //选中下标 30 | 31 | List _datas = List(); //一级分类集合 32 | List _tabDatas = List(); //二级分类 即 tab集合 33 | List articleDatas = List(); //内容集合 34 | 35 | String _title = "标题"; 36 | 37 | int _page = 0; 38 | 39 | @override 40 | void initState() { 41 | super.initState(); 42 | //初始化controller 43 | _controller = TabController(vsync: this, length: 0); 44 | getHttp(); 45 | } 46 | 47 | Future getHttp() async { 48 | var response = await HttpUtil().get(Api.TREE); 49 | Map userMap = json.decode(response.toString()); 50 | var treeEntity = TreeEntity.fromJson(userMap); 51 | 52 | setState(() { 53 | _datas = treeEntity.data; 54 | _tabDatas = _datas[widget.panelIndex].children; 55 | _controller = TabController(vsync: this, length: _tabDatas.length); 56 | 57 | _currentIndex = widget.index; 58 | _title = _datas[widget.panelIndex].name; 59 | }); 60 | 61 | //controller添加监听 62 | _controller.addListener(() => _onTabChanged()); 63 | //选中指定下标 64 | _controller.animateTo(_currentIndex); 65 | 66 | getDetail(); 67 | } 68 | 69 | /// tab改变监听 70 | /// 滑动切换_controller.indexIsChanging一直返回false,所以这种判断方式不适用了 71 | /// 修改如下 72 | _onTabChanged() { 73 | if (_controller.index.toDouble() == _controller.animation.value) { 74 | //赋值 并更新数据 75 | this.setState(() { 76 | _currentIndex = _controller.index; 77 | }); 78 | getDetail(); 79 | } 80 | } 81 | 82 | /// 根据tab下标获取数据 83 | Future getDetail() async { 84 | //加载重置 85 | this.setState(() { 86 | _page = 0; 87 | }); 88 | var data = {"cid": _tabDatas[_currentIndex].id}; 89 | var response = await HttpUtil().get(Api.ARTICLE_LIST + "$_page/json", data: data); 90 | Map articleMap = json.decode(response.toString()); 91 | var articleEntity = ArticleEntity.fromJson(articleMap); 92 | 93 | if (0 == articleEntity.data.datas.length) { 94 | YToast.show(context: context, msg: "数据为空~"); 95 | } else { 96 | setState(() { 97 | articleDatas = articleEntity.data.datas; 98 | }); 99 | } 100 | } 101 | 102 | @override 103 | Widget build(BuildContext context) { 104 | return Scaffold( 105 | appBar: AppBar( 106 | backgroundColor: Theme.of(context).primaryColor, 107 | title: Text(_title), 108 | bottom: TabBar( 109 | controller: _controller, 110 | labelColor: Colors.white, 111 | labelStyle: TextStyle(fontSize: 16), 112 | unselectedLabelColor: Colors.white60, 113 | unselectedLabelStyle: TextStyle(fontSize: 16), 114 | indicatorColor: Colors.white, 115 | isScrollable: true, 116 | //indicator与文字同宽 117 | indicatorSize: TabBarIndicatorSize.label, 118 | tabs: _tabDatas.map((TreeDatachild choice) { 119 | return Tab( 120 | text: choice.name, 121 | ); 122 | }).toList(), 123 | onTap: (int i) { 124 | print(i); 125 | }, 126 | ), 127 | ), 128 | body: TabBarView( 129 | controller: _controller, 130 | children: _tabDatas.map((TreeDatachild choice) { 131 | return EasyRefresh( 132 | header: TaurusHeader(), 133 | footer: TaurusFooter(), 134 | onRefresh: () async { 135 | await Future.delayed(Duration(seconds: 1), () { 136 | setState(() { 137 | _page = 0; 138 | }); 139 | getDetail(); 140 | }); 141 | }, 142 | onLoad: () async { 143 | await Future.delayed(Duration(seconds: 1), () async { 144 | setState(() { 145 | _page++; 146 | }); 147 | getMoreData(); 148 | }); 149 | }, 150 | child: CustomScrollView( 151 | slivers: [ 152 | SliverList( 153 | delegate: SliverChildBuilderDelegate((context, index) { 154 | return getRow(index); 155 | }, childCount: articleDatas.length)), 156 | ], 157 | ), 158 | ); 159 | }).toList(), 160 | ), 161 | ); 162 | } 163 | 164 | Widget getRow(int i) { 165 | return GestureDetector( 166 | child: Container( 167 | padding: EdgeInsets.symmetric(vertical: 10, horizontal: 5), 168 | child: ListTile( 169 | leading: IconButton( 170 | icon: articleDatas[i].collect 171 | ? Icon( 172 | Icons.favorite, 173 | color: Theme.of(context).primaryColor, 174 | ) 175 | : Icon(Icons.favorite_border), 176 | tooltip: '收藏', 177 | onPressed: () { 178 | if (articleDatas[i].collect) { 179 | cancelCollect(articleDatas[i].id); 180 | } else { 181 | addCollect(articleDatas[i].id); 182 | } 183 | }, 184 | ), 185 | title: Text( 186 | articleDatas[i].title, 187 | maxLines: 2, 188 | overflow: TextOverflow.ellipsis, 189 | ), 190 | subtitle: Padding( 191 | padding: EdgeInsets.only(top: 10.0), 192 | child: Row( 193 | children: [ 194 | Container( 195 | padding: EdgeInsets.symmetric(horizontal: 6), 196 | decoration: BoxDecoration( 197 | border: Border.all( 198 | color: Theme.of(context).primaryColor, 199 | width: 1.0, 200 | ), 201 | borderRadius: BorderRadius.circular((20.0)), // 圆角度 202 | ), 203 | child: Text( 204 | articleDatas[i].superChapterName, 205 | style: TextStyle(color: Theme.of(context).primaryColor), 206 | ), 207 | ), 208 | Container( 209 | margin: EdgeInsets.only(left: 10), 210 | child: Text(articleDatas[i].author), 211 | ), 212 | ], 213 | ), 214 | ), 215 | trailing: Icon(Icons.chevron_right), 216 | ), 217 | ), 218 | onTap: () { 219 | Navigator.push( 220 | context, 221 | MaterialPageRoute( 222 | builder: (context) => ArticleDetail(title: articleDatas[i].title, url: articleDatas[i].link), 223 | ), 224 | ); 225 | }, 226 | ); 227 | } 228 | 229 | Future addCollect(int id) async { 230 | var collectResponse = await HttpUtil().post(Api.COLLECT + '$id/json'); 231 | Map map = json.decode(collectResponse.toString()); 232 | var entity = CommonEntity.fromJson(map); 233 | if (entity.errorCode == -1001) { 234 | YToast.show(context: context, msg: entity.errorMsg); 235 | Navigator.push( 236 | context, 237 | MaterialPageRoute(builder: (context) => LoginPage()), 238 | ); 239 | } else { 240 | YToast.show(context: context, msg: "收藏成功"); 241 | getHttp(); 242 | } 243 | } 244 | 245 | Future cancelCollect(int id) async { 246 | var collectResponse = await HttpUtil().post(Api.UN_COLLECT_ORIGIN_ID + '$id/json'); 247 | Map map = json.decode(collectResponse.toString()); 248 | var entity = CommonEntity.fromJson(map); 249 | if (entity.errorCode == -1001) { 250 | YToast.show(context: context, msg: entity.errorMsg); 251 | Navigator.push( 252 | context, 253 | MaterialPageRoute(builder: (context) => LoginPage()), 254 | ); 255 | } else { 256 | YToast.show(context: context, msg: "取消成功"); 257 | getHttp(); 258 | } 259 | } 260 | 261 | Future getMoreData() async { 262 | var data = {"cid": _tabDatas[_currentIndex].id}; 263 | var response = await HttpUtil().get(Api.ARTICLE_LIST + "$_page/json", data: data); 264 | Map articleMap = json.decode(response.toString()); 265 | var articleEntity = ArticleEntity.fromJson(articleMap); 266 | setState(() { 267 | articleDatas.addAll(articleEntity.data.datas); 268 | }); 269 | } 270 | 271 | @override 272 | void dispose() { 273 | _controller.dispose(); 274 | super.dispose(); 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /lib/pages/treePage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:math'; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:wanandroid_flutter/common/api.dart'; 6 | import 'package:wanandroid_flutter/entity/tree_entity.dart'; 7 | import 'package:wanandroid_flutter/http/httpUtil.dart'; 8 | import 'package:wanandroid_flutter/pages/treeDetailPage.dart'; 9 | import 'package:wanandroid_flutter/res/colors.dart'; 10 | import 'package:wanandroid_flutter/util/ToastUtil.dart'; 11 | 12 | class TreePage extends StatefulWidget { 13 | @override 14 | State createState() { 15 | return _TreePageState(); 16 | } 17 | } 18 | 19 | class _TreePageState extends State { 20 | List _datas = List(); 21 | ScrollController _scrollController = ScrollController(); 22 | int _panelIndex = 0; //展开下标 23 | List _icons = [ 24 | Icons.star_border, 25 | Icons.child_care, 26 | Icons.cloud_queue, 27 | Icons.ac_unit, 28 | Icons.lightbulb_outline, 29 | ]; 30 | 31 | @override 32 | void initState() { 33 | super.initState(); 34 | 35 | _scrollController.addListener(() { 36 | //当前位置==最大滑动范围 表示已经滑动到了底部 37 | if (_scrollController.position.pixels == 38 | _scrollController.position.maxScrollExtent) { 39 | YToast.show(context: context, msg: "滑动到了底部"); 40 | // do something 41 | //getMoreData(); 42 | } 43 | }); 44 | 45 | getHttp(); 46 | } 47 | 48 | void getHttp() async { 49 | try { 50 | var response = await HttpUtil().get(Api.TREE); 51 | Map userMap = json.decode(response.toString()); 52 | var treeEntity = TreeEntity.fromJson(userMap); 53 | 54 | //遍历赋值isExpanded标识,默认全部合并 55 | for (int i = 0; i < treeEntity.data.length; i++) { 56 | treeEntity.data[i].isExpanded = false; 57 | } 58 | 59 | setState(() { 60 | _datas = treeEntity.data; 61 | }); 62 | } catch (e) { 63 | print(e); 64 | } 65 | } 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | return Scaffold( 70 | body: RefreshIndicator( 71 | //指示器颜色 72 | color: Theme.of(context).primaryColor, 73 | //指示器显示时距顶部位置 74 | displacement: 40, 75 | child: SingleChildScrollView( 76 | controller: _scrollController, 77 | child: ExpansionPanelList( 78 | //开关动画时长 79 | animationDuration: Duration(milliseconds: 500), 80 | //开关回调 81 | expansionCallback: (panelIndex, isExpanded) { 82 | setState(() { 83 | _panelIndex = panelIndex; 84 | _datas[panelIndex].isExpanded = !isExpanded; 85 | }); 86 | }, 87 | //内容区 88 | children: _datas.map((TreeData treeData) { 89 | return ExpansionPanel( 90 | //标题 91 | headerBuilder: (context, isExpanded) { 92 | return ListTile( 93 | contentPadding: EdgeInsets.all(10.0), 94 | title: Text( 95 | treeData.name, 96 | style: TextStyle(color: Theme.of(context).primaryColor), 97 | ), 98 | //取随机icon 99 | leading: Icon(_icons[Random().nextInt(_icons.length)]), 100 | ); 101 | }, 102 | //展开内容 103 | body: Container( 104 | height: 200, 105 | padding: EdgeInsets.symmetric(horizontal: 5.0), 106 | child: ListView.builder( 107 | itemCount: treeData.children.length, 108 | itemBuilder: (BuildContext context, int position) { 109 | return getRow(position, treeData); 110 | }, 111 | ), 112 | ), 113 | //是否展开 114 | isExpanded: treeData.isExpanded, 115 | ); 116 | }).toList(), 117 | ), 118 | ), 119 | //下拉刷新回调 120 | onRefresh: () async { 121 | await Future.delayed(Duration(seconds: 1), () { 122 | YToast.show(context: context, msg: "下拉刷新"); 123 | // do something 124 | //getData(); 125 | }); 126 | }, 127 | ), 128 | ); 129 | } 130 | 131 | Widget getRow(int i, TreeData treeData) { 132 | return GestureDetector( 133 | child: Container( 134 | padding: EdgeInsets.symmetric(horizontal: 5.0), 135 | child: ListTile( 136 | title: Text( 137 | treeData.children[i].name, 138 | style: TextStyle(color: YColors.color_999), 139 | ), 140 | trailing: Icon( 141 | Icons.chevron_right, 142 | color: YColors.color_999, 143 | ), 144 | ), 145 | ), 146 | onTap: () { 147 | Navigator.push( 148 | context, 149 | MaterialPageRoute( 150 | builder: (context) => 151 | TreeDetailPage(panelIndex: _panelIndex, index: i), 152 | ), 153 | ); 154 | }, 155 | ); 156 | } 157 | 158 | @override 159 | void dispose() { 160 | _scrollController.dispose(); 161 | super.dispose(); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /lib/res/colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class YColors { 4 | static const Color colorPrimary = Color(0xff4caf50); 5 | static const Color colorPrimaryDark = Color(0xff388E3C); 6 | static const Color colorAccent = Color(0xff8BC34A); 7 | static const Color colorPrimaryLight = Color(0xffC8E6C9); 8 | 9 | static const Color primaryText = Color(0xff212121); 10 | static const Color secondaryText = Color(0xff757575); 11 | 12 | static const Color dividerColor = Color(0xffBDBDBD); 13 | 14 | static const Color bg = Color(0xffF9F9F9); 15 | static const Color color_F9F9F9 = Color(0xffF9F9F9); 16 | 17 | static const Color color_999 = Color(0xff999999); 18 | static const Color color_666 = Color(0xff666666); 19 | 20 | static const Color color_f3f3f3 = Color(0xfff3f3f3); 21 | static const Color color_f1f1f1 = Color(0xfff1f1f1); 22 | static const Color color_fff = Color(0xffffffff); 23 | 24 | // 主题下标key 25 | static const String themeIndexKey = "themeIndex"; 26 | 27 | /* 主题列表 */ 28 | static const Map themeColor = { 29 | 0: {//green 30 | "primaryColor": Color(0xff4caf50), 31 | "primaryColorDark": Color(0xff388E3C), 32 | "colorAccent": Color(0xff8BC34A), 33 | "colorPrimaryLight": Color(0xffC8E6C9), 34 | }, 35 | 1:{//red 36 | "primaryColor": Color(0xffF44336), 37 | "primaryColorDark": Color(0xffD32F2F), 38 | "colorAccent": Color(0xffFF5252), 39 | "colorPrimaryLight": Color(0xffFFCDD2), 40 | }, 41 | 2:{//blue 42 | "primaryColor": Color(0xff2196F3), 43 | "primaryColorDark": Color(0xff1976D2), 44 | "colorAccent": Color(0xff448AFF), 45 | "colorPrimaryLight": Color(0xffBBDEFB), 46 | }, 47 | 3:{//pink 48 | "primaryColor": Color(0xffE91E63), 49 | "primaryColorDark": Color(0xffC2185B), 50 | "colorAccent": Color(0xffFF4081), 51 | "colorPrimaryLight": Color(0xffF8BBD0), 52 | }, 53 | 4:{//purple 54 | "primaryColor": Color(0xff673AB7), 55 | "primaryColorDark": Color(0xff512DA8), 56 | "colorAccent": Color(0xff7C4DFF), 57 | "colorPrimaryLight": Color(0xffD1C4E9), 58 | }, 59 | 5:{//grey 60 | "primaryColor": Color(0xff9E9E9E), 61 | "primaryColorDark": Color(0xff616161), 62 | "colorAccent": Color(0xff9E9E9E), 63 | "colorPrimaryLight": Color(0xffF5F5F5), 64 | }, 65 | 6:{//black 66 | "primaryColor": Color(0xff333333), 67 | "primaryColorDark": Color(0xff000000), 68 | "colorAccent": Color(0xff666666), 69 | "colorPrimaryLight": Color(0xff999999), 70 | }, 71 | }; 72 | 73 | } 74 | -------------------------------------------------------------------------------- /lib/res/fonts/mononoki-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/lib/res/fonts/mononoki-Regular.ttf -------------------------------------------------------------------------------- /lib/res/images/a.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/lib/res/images/a.jpg -------------------------------------------------------------------------------- /lib/res/images/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/lib/res/images/ic_logo.png -------------------------------------------------------------------------------- /lib/res/strings.dart: -------------------------------------------------------------------------------- 1 | class YStrings{ 2 | static String appName="玩安卓"; 3 | static String proName="wanandroid_flutter"; 4 | static String github="https://github.com/yechaoa/wanandroid_flutter"; 5 | 6 | static String home="首页"; 7 | static String tree="体系"; 8 | static String navi="导航"; 9 | static String project="项目"; 10 | 11 | } -------------------------------------------------------------------------------- /lib/util/ToastUtil.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | import 'package:fluttertoast/fluttertoast.dart'; 4 | 5 | /// 6 | /// Toast 简单封装 7 | /// 8 | class YToast { 9 | static show({ 10 | @required BuildContext context, 11 | @required String msg, 12 | Toast toastLength, 13 | int timeInSecForIos = 1, 14 | double fontSize = 16.0, 15 | ToastGravity gravity, 16 | Color backgroundColor, 17 | Color textColor, 18 | }) { 19 | Fluttertoast.showToast( 20 | msg: msg, 21 | toastLength: Toast.LENGTH_SHORT, 22 | gravity: ToastGravity.CENTER, 23 | timeInSecForIosWeb: 1, 24 | backgroundColor: Theme.of(context).primaryColor, 25 | textColor: Colors.white, 26 | fontSize: 16.0); 27 | } 28 | 29 | static showBottom({ 30 | @required BuildContext context, 31 | @required String msg, 32 | Toast toastLength, 33 | int timeInSecForIos = 1, 34 | double fontSize = 16.0, 35 | ToastGravity gravity, 36 | Color backgroundColor, 37 | Color textColor, 38 | }) { 39 | Fluttertoast.showToast( 40 | msg: msg, 41 | toastLength: Toast.LENGTH_SHORT, 42 | gravity: ToastGravity.BOTTOM, 43 | timeInSecForIosWeb: 1, 44 | backgroundColor: Theme.of(context).primaryColor, 45 | textColor: Colors.white, 46 | fontSize: 16.0); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/util/favoriteProvide.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class FavoriteProvide with ChangeNotifier { 4 | bool _isFavorite = false; 5 | 6 | bool get value => _isFavorite; 7 | 8 | FavoriteProvide(); 9 | 10 | void changeFavorite(bool isFavorite) { 11 | _isFavorite = isFavorite; 12 | notifyListeners(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/util/themeProvide.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ThemeProvide with ChangeNotifier { 4 | int _themeIndex; 5 | 6 | int get value => _themeIndex; 7 | 8 | ThemeProvide(); 9 | 10 | void setTheme(int index) async { 11 | _themeIndex = index; 12 | notifyListeners(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /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: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 9 | url: "https://pub.flutter-io.cn" 10 | source: hosted 11 | version: "2.10.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: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c 25 | url: "https://pub.flutter-io.cn" 26 | source: hosted 27 | version: "1.2.1" 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: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 41 | url: "https://pub.flutter-io.cn" 42 | source: hosted 43 | version: "1.17.0" 44 | cookie_jar: 45 | dependency: transitive 46 | description: 47 | name: cookie_jar 48 | sha256: d1cc6516a190ba667941f722b6365d202caff3dacb38de24268b8d6ff1ec8a1d 49 | url: "https://pub.flutter-io.cn" 50 | source: hosted 51 | version: "3.0.1" 52 | cupertino_icons: 53 | dependency: "direct main" 54 | description: 55 | name: cupertino_icons 56 | sha256: a937da4c006989739ceb4d10e3bd6cce64ca85d0fe287fc5b2b9f6ee757dcee6 57 | url: "https://pub.flutter-io.cn" 58 | source: hosted 59 | version: "0.1.3" 60 | dio: 61 | dependency: "direct main" 62 | description: 63 | name: dio 64 | sha256: "0894a098594263fe1caaba3520e3016d8a855caeb010a882273189cca10f11e9" 65 | url: "https://pub.flutter-io.cn" 66 | source: hosted 67 | version: "5.1.1" 68 | dio_cookie_manager: 69 | dependency: "direct main" 70 | description: 71 | name: dio_cookie_manager 72 | sha256: "326f2940d2dc48428350615b53cbda6904934e0900fbf791d36be0c72e05e39f" 73 | url: "https://pub.flutter-io.cn" 74 | source: hosted 75 | version: "2.1.3" 76 | easy_refresh: 77 | dependency: "direct main" 78 | description: 79 | name: easy_refresh 80 | sha256: "4bf1e3bea60ca21ece286fcde16cdd64bdb7383b4901422a683404f77708c644" 81 | url: "https://pub.flutter-io.cn" 82 | source: hosted 83 | version: "3.3.1" 84 | fake_async: 85 | dependency: transitive 86 | description: 87 | name: fake_async 88 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 89 | url: "https://pub.flutter-io.cn" 90 | source: hosted 91 | version: "1.3.1" 92 | ffi: 93 | dependency: transitive 94 | description: 95 | name: ffi 96 | sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 97 | url: "https://pub.flutter-io.cn" 98 | source: hosted 99 | version: "2.0.1" 100 | file: 101 | dependency: transitive 102 | description: 103 | name: file 104 | sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" 105 | url: "https://pub.flutter-io.cn" 106 | source: hosted 107 | version: "6.1.4" 108 | flutter: 109 | dependency: "direct main" 110 | description: flutter 111 | source: sdk 112 | version: "0.0.0" 113 | flutter_page_indicator: 114 | dependency: transitive 115 | description: 116 | name: flutter_page_indicator 117 | sha256: a5b2992228c2827b69faed3977681a3f5c313c7f13d72272decbb2923d1d7176 118 | url: "https://pub.flutter-io.cn" 119 | source: hosted 120 | version: "0.0.3" 121 | flutter_swiper: 122 | dependency: "direct main" 123 | description: 124 | name: flutter_swiper 125 | sha256: e52a0e894abfa4099a5d4e5098a00597f3b55e25617cdd19e6fe6be5d24858c7 126 | url: "https://pub.flutter-io.cn" 127 | source: hosted 128 | version: "1.1.6" 129 | flutter_test: 130 | dependency: "direct dev" 131 | description: flutter 132 | source: sdk 133 | version: "0.0.0" 134 | flutter_web_plugins: 135 | dependency: transitive 136 | description: flutter 137 | source: sdk 138 | version: "0.0.0" 139 | flutter_webview_plugin: 140 | dependency: "direct main" 141 | description: 142 | path: "." 143 | ref: HEAD 144 | resolved-ref: f85db45aedefeab4c1df75cfc4c35f62d3c1c629 145 | url: "https://github.com/nuc134r/flutter_webview_plugin.git" 146 | source: git 147 | version: "0.3.11" 148 | fluttertoast: 149 | dependency: "direct main" 150 | description: 151 | name: fluttertoast 152 | sha256: "2f9c4d3f4836421f7067a28f8939814597b27614e021da9d63e5d3fb6e212d25" 153 | url: "https://pub.flutter-io.cn" 154 | source: hosted 155 | version: "8.2.1" 156 | http_parser: 157 | dependency: transitive 158 | description: 159 | name: http_parser 160 | sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" 161 | url: "https://pub.flutter-io.cn" 162 | source: hosted 163 | version: "4.0.2" 164 | js: 165 | dependency: transitive 166 | description: 167 | name: js 168 | sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" 169 | url: "https://pub.flutter-io.cn" 170 | source: hosted 171 | version: "0.6.5" 172 | matcher: 173 | dependency: transitive 174 | description: 175 | name: matcher 176 | sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" 177 | url: "https://pub.flutter-io.cn" 178 | source: hosted 179 | version: "0.12.13" 180 | material_color_utilities: 181 | dependency: transitive 182 | description: 183 | name: material_color_utilities 184 | sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 185 | url: "https://pub.flutter-io.cn" 186 | source: hosted 187 | version: "0.2.0" 188 | meta: 189 | dependency: transitive 190 | description: 191 | name: meta 192 | sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" 193 | url: "https://pub.flutter-io.cn" 194 | source: hosted 195 | version: "1.8.0" 196 | mime: 197 | dependency: transitive 198 | description: 199 | name: mime 200 | sha256: "5041f313f4e30fbd70dee7c0403785a4ee270f75830f590e5983960fddf743e4" 201 | url: "https://pub.flutter-io.cn" 202 | source: hosted 203 | version: "0.9.7" 204 | nested: 205 | dependency: transitive 206 | description: 207 | name: nested 208 | sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" 209 | url: "https://pub.flutter-io.cn" 210 | source: hosted 211 | version: "1.0.0" 212 | path: 213 | dependency: transitive 214 | description: 215 | name: path 216 | sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b 217 | url: "https://pub.flutter-io.cn" 218 | source: hosted 219 | version: "1.8.2" 220 | path_drawing: 221 | dependency: transitive 222 | description: 223 | name: path_drawing 224 | sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977 225 | url: "https://pub.flutter-io.cn" 226 | source: hosted 227 | version: "1.0.1" 228 | path_parsing: 229 | dependency: transitive 230 | description: 231 | name: path_parsing 232 | sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf 233 | url: "https://pub.flutter-io.cn" 234 | source: hosted 235 | version: "1.0.1" 236 | path_provider_linux: 237 | dependency: transitive 238 | description: 239 | name: path_provider_linux 240 | sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1" 241 | url: "https://pub.flutter-io.cn" 242 | source: hosted 243 | version: "2.1.10" 244 | path_provider_platform_interface: 245 | dependency: transitive 246 | description: 247 | name: path_provider_platform_interface 248 | sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" 249 | url: "https://pub.flutter-io.cn" 250 | source: hosted 251 | version: "2.0.6" 252 | path_provider_windows: 253 | dependency: transitive 254 | description: 255 | name: path_provider_windows 256 | sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130 257 | url: "https://pub.flutter-io.cn" 258 | source: hosted 259 | version: "2.1.5" 260 | platform: 261 | dependency: transitive 262 | description: 263 | name: platform 264 | sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" 265 | url: "https://pub.flutter-io.cn" 266 | source: hosted 267 | version: "3.1.0" 268 | plugin_platform_interface: 269 | dependency: transitive 270 | description: 271 | name: plugin_platform_interface 272 | sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" 273 | url: "https://pub.flutter-io.cn" 274 | source: hosted 275 | version: "2.1.4" 276 | process: 277 | dependency: transitive 278 | description: 279 | name: process 280 | sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" 281 | url: "https://pub.flutter-io.cn" 282 | source: hosted 283 | version: "4.2.4" 284 | provider: 285 | dependency: "direct main" 286 | description: 287 | name: provider 288 | sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f 289 | url: "https://pub.flutter-io.cn" 290 | source: hosted 291 | version: "6.0.5" 292 | share: 293 | dependency: "direct main" 294 | description: 295 | name: share 296 | sha256: fc1647d0c3a8eb648dc6d91d2207e7976c1d941e2165a09cba7c805b0c24467e 297 | url: "https://pub.flutter-io.cn" 298 | source: hosted 299 | version: "0.6.5+4" 300 | shared_preferences: 301 | dependency: "direct main" 302 | description: 303 | name: shared_preferences 304 | sha256: "78528fd87d0d08ffd3e69551173c026e8eacc7b7079c82eb6a77413957b7e394" 305 | url: "https://pub.flutter-io.cn" 306 | source: hosted 307 | version: "2.0.20" 308 | shared_preferences_android: 309 | dependency: transitive 310 | description: 311 | name: shared_preferences_android 312 | sha256: ad423a80fe7b4e48b50d6111b3ea1027af0e959e49d485712e134863d9c1c521 313 | url: "https://pub.flutter-io.cn" 314 | source: hosted 315 | version: "2.0.17" 316 | shared_preferences_foundation: 317 | dependency: transitive 318 | description: 319 | name: shared_preferences_foundation 320 | sha256: "1e755f8583229f185cfca61b1d80fb2344c9d660e1c69ede5450d8f478fa5310" 321 | url: "https://pub.flutter-io.cn" 322 | source: hosted 323 | version: "2.1.5" 324 | shared_preferences_linux: 325 | dependency: transitive 326 | description: 327 | name: shared_preferences_linux 328 | sha256: "3a59ed10890a8409ad0faad7bb2957dab4b92b8fbe553257b05d30ed8af2c707" 329 | url: "https://pub.flutter-io.cn" 330 | source: hosted 331 | version: "2.1.5" 332 | shared_preferences_platform_interface: 333 | dependency: transitive 334 | description: 335 | name: shared_preferences_platform_interface 336 | sha256: fb5cf25c0235df2d0640ac1b1174f6466bd311f621574997ac59018a6664548d 337 | url: "https://pub.flutter-io.cn" 338 | source: hosted 339 | version: "2.2.0" 340 | shared_preferences_web: 341 | dependency: transitive 342 | description: 343 | name: shared_preferences_web 344 | sha256: "0dc2633f215a3d4aa3184c9b2c5766f4711e4e5a6b256e62aafee41f89f1bfb8" 345 | url: "https://pub.flutter-io.cn" 346 | source: hosted 347 | version: "2.0.6" 348 | shared_preferences_windows: 349 | dependency: transitive 350 | description: 351 | name: shared_preferences_windows 352 | sha256: "71bcd669bb9cdb6b39f22c4a7728b6d49e934f6cba73157ffa5a54f1eed67436" 353 | url: "https://pub.flutter-io.cn" 354 | source: hosted 355 | version: "2.1.5" 356 | sky_engine: 357 | dependency: transitive 358 | description: flutter 359 | source: sdk 360 | version: "0.0.99" 361 | source_span: 362 | dependency: transitive 363 | description: 364 | name: source_span 365 | sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 366 | url: "https://pub.flutter-io.cn" 367 | source: hosted 368 | version: "1.9.1" 369 | stack_trace: 370 | dependency: transitive 371 | description: 372 | name: stack_trace 373 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 374 | url: "https://pub.flutter-io.cn" 375 | source: hosted 376 | version: "1.11.0" 377 | stream_channel: 378 | dependency: transitive 379 | description: 380 | name: stream_channel 381 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" 382 | url: "https://pub.flutter-io.cn" 383 | source: hosted 384 | version: "2.1.1" 385 | string_scanner: 386 | dependency: transitive 387 | description: 388 | name: string_scanner 389 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 390 | url: "https://pub.flutter-io.cn" 391 | source: hosted 392 | version: "1.2.0" 393 | term_glyph: 394 | dependency: transitive 395 | description: 396 | name: term_glyph 397 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 398 | url: "https://pub.flutter-io.cn" 399 | source: hosted 400 | version: "1.2.1" 401 | test_api: 402 | dependency: transitive 403 | description: 404 | name: test_api 405 | sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 406 | url: "https://pub.flutter-io.cn" 407 | source: hosted 408 | version: "0.4.16" 409 | transformer_page_view: 410 | dependency: transitive 411 | description: 412 | name: transformer_page_view 413 | sha256: "2210531bc4148831061c575070173f32693415be8bbbf5bd2159a38f2adff61c" 414 | url: "https://pub.flutter-io.cn" 415 | source: hosted 416 | version: "0.1.6" 417 | typed_data: 418 | dependency: transitive 419 | description: 420 | name: typed_data 421 | sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" 422 | url: "https://pub.flutter-io.cn" 423 | source: hosted 424 | version: "1.3.1" 425 | vector_math: 426 | dependency: transitive 427 | description: 428 | name: vector_math 429 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 430 | url: "https://pub.flutter-io.cn" 431 | source: hosted 432 | version: "2.1.4" 433 | win32: 434 | dependency: transitive 435 | description: 436 | name: win32 437 | sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 438 | url: "https://pub.flutter-io.cn" 439 | source: hosted 440 | version: "3.1.3" 441 | xdg_directories: 442 | dependency: transitive 443 | description: 444 | name: xdg_directories 445 | sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 446 | url: "https://pub.flutter-io.cn" 447 | source: hosted 448 | version: "1.0.0" 449 | sdks: 450 | dart: ">=2.18.0 <3.0.0" 451 | flutter: ">=3.0.0" 452 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: wanandroid_flutter 2 | description: wanandroid_flutter 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # In Android, build-name is used as versionName while build-number used as versionCode. 10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 12 | # Read more about iOS versioning at 13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 14 | version: 1.0.0+1 15 | 16 | environment: 17 | sdk: ">=2.1.0 <3.0.0" 18 | 19 | dependencies: 20 | flutter: 21 | sdk: flutter 22 | 23 | # The following adds the Cupertino Icons font to your application. 24 | # Use with the CupertinoIcons class for iOS style icons. 25 | cupertino_icons: ^0.1.2 26 | 27 | # https://github.com/PonnamKarthik/FlutterToast 28 | fluttertoast: ^8.2.1 29 | 30 | # https://github.com/flutterchina/dio 31 | dio: ^5.1.1 32 | 33 | # https://pub.dev/packages/dio_cookie_manager 34 | dio_cookie_manager: ^2.0.0 35 | 36 | # https://pub.dev/packages/flutter_webview_plugin#-readme-tab 37 | # flutter_webview_/\plugin: ^0.4.0 38 | flutter_webview_plugin: 39 | git: https://github.com/nuc134r/flutter_webview_plugin.git 40 | 41 | # https://github.com/best-flutter/flutter_swiper 42 | flutter_swiper: ^1.1.6 43 | 44 | # https://github.com/flutter/plugins/tree/master/packages/share 45 | share: ^0.6.1+1 46 | 47 | # https://github.com/google/flutter-provide 48 | # provide: ^1.0.2 49 | 50 | # https://pub.dev/packages/provider 51 | provider: ^6.0.0 52 | 53 | # https://github.com/flutter/plugins/tree/master/packages/shared_preferences 54 | shared_preferences: ^2.0.20 55 | 56 | # https://github.com/xuelongqy/flutter_easyrefresh 57 | easy_refresh: ^3.3.1 58 | 59 | dev_dependencies: 60 | flutter_test: 61 | sdk: flutter 62 | 63 | 64 | # For information on the generic Dart part of this file, see the 65 | # following page: https://www.dartlang.org/tools/pub/pubspec 66 | 67 | # The following section is specific to Flutter. 68 | flutter: 69 | 70 | # The following line ensures that the Material Icons font is 71 | # included with your application, so that you can use the icons in 72 | # the material Icons class. 73 | uses-material-design: true 74 | 75 | 76 | assets: 77 | - lib/res/images/a.jpg 78 | - lib/res/images/ic_logo.png 79 | 80 | fonts: 81 | - family: mononoki 82 | fonts: 83 | - asset: lib/res/fonts/mononoki-Regular.ttf 84 | weight: 500 85 | 86 | # To add assets to your application, add an assets section, like this: 87 | # assets: 88 | # - images/a_dot_burr.jpeg 89 | # - images/a_dot_ham.jpeg 90 | 91 | # An image asset can refer to one or more resolution-specific "variants", see 92 | # https://flutter.io/assets-and-images/#resolution-aware. 93 | 94 | # For details regarding adding assets from package dependencies, see 95 | # https://flutter.io/assets-and-images/#from-packages 96 | 97 | # To add custom fonts to your application, add a fonts section here, 98 | # in this "flutter" section. Each entry in this list should have a 99 | # "family" key with the font family name, and a "fonts" key with a 100 | # list giving the asset and other descriptors for the font. For 101 | # example: 102 | # fonts: 103 | # - family: Schyler 104 | # fonts: 105 | # - asset: fonts/Schyler-Regular.ttf 106 | # - asset: fonts/Schyler-Italic.ttf 107 | # style: italic 108 | # - family: Trajan Pro 109 | # fonts: 110 | # - asset: fonts/TrajanPro.ttf 111 | # - asset: fonts/TrajanPro_Bold.ttf 112 | # weight: 700 113 | # 114 | # For details regarding fonts from package dependencies, 115 | # see https://flutter.io/custom-fonts/#from-packages 116 | -------------------------------------------------------------------------------- /screenshot/android/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/screenshot/android/1.png -------------------------------------------------------------------------------- /screenshot/android/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/screenshot/android/2.png -------------------------------------------------------------------------------- /screenshot/android/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/screenshot/android/3.png -------------------------------------------------------------------------------- /screenshot/android/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/screenshot/android/4.png -------------------------------------------------------------------------------- /screenshot/android/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/screenshot/android/5.png -------------------------------------------------------------------------------- /screenshot/android/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/screenshot/android/6.png -------------------------------------------------------------------------------- /screenshot/android/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/screenshot/android/7.png -------------------------------------------------------------------------------- /screenshot/android/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/screenshot/android/8.png -------------------------------------------------------------------------------- /screenshot/ios/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/screenshot/ios/1.png -------------------------------------------------------------------------------- /screenshot/ios/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/screenshot/ios/2.png -------------------------------------------------------------------------------- /screenshot/ios/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/screenshot/ios/3.png -------------------------------------------------------------------------------- /screenshot/ios/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/screenshot/ios/4.png -------------------------------------------------------------------------------- /screenshot/ios/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/screenshot/ios/5.png -------------------------------------------------------------------------------- /screenshot/ios/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/screenshot/ios/6.png -------------------------------------------------------------------------------- /screenshot/ios/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/screenshot/ios/7.png -------------------------------------------------------------------------------- /screenshot/ios/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yechaoa/wanandroid_flutter/3c8696151a44d0170d6176af9d8e310f393b20d3/screenshot/ios/8.png -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:wanandroid_flutter/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp(0)); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | --------------------------------------------------------------------------------