├── .flutter-plugins-dependencies ├── .github └── FUNDING.yml ├── .gitignore ├── .metadata ├── CHANGELOG.md ├── CHANGELOGS.md ├── LICENSE ├── README.md ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── thl │ │ │ └── flutterwanandroid │ │ │ └── MainActivity.java │ │ └── res │ │ ├── drawable │ │ └── launch_background.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_new.png │ │ └── splash_bg.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── settings_aar.gradle ├── assets ├── data │ └── china.json └── images │ └── 3.0x │ ├── ali_connors.png │ ├── ali_connors_sml.png │ ├── guide1.png │ ├── guide2.png │ ├── guide3.png │ ├── guide4.png │ ├── ic_launcher_news.png │ ├── icon_wan_android.png │ ├── qrcode.png │ └── splash_bg.png ├── flutter_wanandroid.iml ├── flutter_wanandroid_android.iml ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── flutter_export_environment.sh ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-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 │ └── zh-Hans-CN.lproj │ ├── LaunchScreen.strings │ └── Main.strings ├── lib ├── blocs │ ├── application_bloc.dart │ ├── bloc_index.dart │ ├── bloc_provider.dart │ ├── collect_bloc.dart │ ├── com_list_bloc.dart │ ├── main_bloc.dart │ └── tab_bloc.dart ├── common │ ├── common.dart │ ├── component_index.dart │ ├── global.dart │ └── sp_helper.dart ├── data │ ├── api │ │ └── apis.dart │ ├── protocol │ │ └── models.dart │ └── repository │ │ ├── collect_repository.dart │ │ ├── user_repository.dart │ │ └── wan_repository.dart ├── db │ └── db.dart ├── demos │ ├── city_select_page.dart │ ├── date_page.dart │ ├── image_size_page.dart │ ├── index.dart │ ├── main_demos.dart │ ├── money_page.dart │ ├── pinyin_page.dart │ ├── regex_page.dart │ ├── round_portrait_page.dart │ ├── test_page.dart │ ├── timeline_page.dart │ ├── timer_page.dart │ └── widget_page.dart ├── event │ └── event.dart ├── main.dart ├── models │ └── models.dart ├── res │ ├── colors.dart │ ├── dimens.dart │ ├── index.dart │ ├── strings.dart │ └── styles.dart ├── ui │ ├── dialog │ │ └── simple_dialog.dart │ ├── pages │ │ ├── about_page.dart │ │ ├── author_page.dart │ │ ├── backdrop_demo.dart │ │ ├── collection_page.dart │ │ ├── events_page.dart │ │ ├── home_page.dart │ │ ├── language_page.dart │ │ ├── main_left_page.dart │ │ ├── main_page.dart │ │ ├── other_page.dart │ │ ├── page_index.dart │ │ ├── rec_hot_page.dart │ │ ├── repos_page.dart │ │ ├── search_page.dart │ │ ├── setting_page.dart │ │ ├── share_page.dart │ │ ├── splash_page.dart │ │ ├── system_page.dart │ │ ├── tab_page.dart │ │ └── user │ │ │ ├── user_login_page.dart │ │ │ └── user_register_page.dart │ └── widgets │ │ ├── article_item.dart │ │ ├── com_item.dart │ │ ├── com_list_page.dart │ │ ├── com_list_view.dart │ │ ├── header_item.dart │ │ ├── likebtn │ │ ├── circle_painter.dart │ │ ├── dot_painter.dart │ │ ├── like_button.dart │ │ ├── like_button_util.dart │ │ └── model.dart │ │ ├── refresh_scaffold.dart │ │ ├── repos_item.dart │ │ ├── tree_item.dart │ │ ├── web_scaffold.dart │ │ ├── widget_index.dart │ │ └── widgets.dart └── utils │ ├── http_utils.dart │ ├── navigator_util.dart │ ├── util_index.dart │ └── utils.dart ├── pkgget ├── pubspec.yaml ├── test └── widget_test.dart └── uploadMaster /.flutter-plugins-dependencies: -------------------------------------------------------------------------------- 1 | {"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"path_provider","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\path_provider-1.2.0\\\\","dependencies":[]},{"name":"share","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\share-0.6.5+4\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\shared_preferences-2.0.5\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\sqflite-1.3.2+4\\\\","dependencies":[]},{"name":"url_launcher","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\url_launcher-5.7.10\\\\","dependencies":[]},{"name":"webview_flutter","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\webview_flutter-1.0.7\\\\","dependencies":[]}],"android":[{"name":"path_provider","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\path_provider-1.2.0\\\\","dependencies":[]},{"name":"share","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\share-0.6.5+4\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\shared_preferences-2.0.5\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\sqflite-1.3.2+4\\\\","dependencies":[]},{"name":"url_launcher","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\url_launcher-5.7.10\\\\","dependencies":[]},{"name":"webview_flutter","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\webview_flutter-1.0.7\\\\","dependencies":[]}],"macos":[{"name":"shared_preferences_macos","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\shared_preferences_macos-2.0.0\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\sqflite-1.3.2+4\\\\","dependencies":[]},{"name":"url_launcher_macos","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\url_launcher_macos-0.0.1+9\\\\","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\path_provider_linux-2.0.0\\\\","dependencies":[]},{"name":"shared_preferences_linux","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\shared_preferences_linux-2.0.0\\\\","dependencies":["path_provider_linux"]},{"name":"url_launcher_linux","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\url_launcher_linux-0.0.1+4\\\\","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\path_provider_windows-2.0.0\\\\","dependencies":[]},{"name":"shared_preferences_windows","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\shared_preferences_windows-2.0.0\\\\","dependencies":["path_provider_windows"]},{"name":"url_launcher_windows","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\url_launcher_windows-0.0.1+3\\\\","dependencies":[]}],"web":[{"name":"shared_preferences_web","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\shared_preferences_web-2.0.0\\\\","dependencies":[]},{"name":"url_launcher_web","path":"C:\\\\Users\\\\thl\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\url_launcher_web-0.1.5+3\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"path_provider","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"share","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_linux","shared_preferences_macos","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_macos","dependencies":[]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"sqflite","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_web","url_launcher_linux","url_launcher_macos","url_launcher_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]},{"name":"webview_flutter","dependencies":[]}],"date_created":"2021-03-17 16:00:16.748140","version":"2.0.0"} -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ['https://sky24n.github.io/Sky24n/image/ali_pay.jpg', 'https://sky24n.github.io/Sky24n/image/wechat_pay.jpg'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | 9 | # If you're building an application, you may want to check-in your pubspec.lock 10 | pubspec.lock 11 | 12 | .flutter-plugins 13 | 14 | */.idea/ 15 | .idea/ 16 | /.idea/workspace.xml 17 | /.idea/libraries -------------------------------------------------------------------------------- /.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: 5ab9e70727d858def3a586db7fb98ee580352957 8 | channel: beta 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 更新说明 2 | ### v0.2.5 (2019.11.16) 3 | 1.基础库升级。 4 | 2.一些优化~。 5 | 6 | ### v0.2.2 (2019.07.02) 7 | 1.基础库升级。 8 | 2.修复OPPO R15详情页问题。 9 | 3.一些优化~。 10 | 11 | ### v0.2.1 (2019.05.08) 12 | 1.新增登录/注册。 13 | 2.新增收藏功能。 14 | 3.一些优化~。 15 | 16 | 温馨提醒: 17 | ① 默认主题色修改为deepPurpleAccent,与登录/注册页面元素保持一致。 18 | ② 设置新增升级提示次数,可关闭升级提醒,但超过5个版本未升级需要下载最新版。 19 | ③ 快速滚动至顶部按钮展示逻辑优化。 20 | 21 | 22 | ### v0.2.0 (2019.03.29) 23 | 1.新增分享~。 24 | 2.新增网络状态页。 25 | 26 | ### v0.1.9 (2019.03.16) 27 | 1.闪屏页支持视频。 28 | 2.支持App应用内升级。 29 | 3.玩安卓Api升级为https。 30 | 4.Flutter Demos 新增 获取图片尺寸示例。 31 | 32 | 温馨提醒: 33 | ① 为了方便大家体验应用内升级,服务端版本号为:v0.2.0,Apk版本始终为v0.1.9。 34 | ② 由于Apk文件是放在Github上面的,可能下载速度会比较慢。 35 | ③ 为了保护掘金作者原创文章,热门文章修改为从第二页开始获取。 36 | 37 | ### v0.1.7 (2019.03.04) 38 | 1、App新Logo。 39 | 2、闪屏页优化。 40 | 3、升级WebView,新增loading,点击TitleBar返回可回退网页。 41 | 4、新增内置浏览器,修复oppo R15, R11st无法查看详情页,若其他手机无法查看详情页,请自行修改为内置浏览器。 42 | 43 | ### v0.1.6 (2019.01.18) 44 | 1、主页新增Github Trending,新版本如未显示,请下拉刷新。 45 | 2、新增热门Tab,掘金热门文章! 46 | 3、重构HomePage。 47 | 48 | ### v0.1.3 (2019.01.09) 49 | ① 新WebView 重构项目。 50 | 51 | ### v0.1.2 (2018.12.20) 52 | ① 网络框架DioUtil 53 | ② 合并[flutter_demos][flutter_demos_github] 54 | 55 | ### v0.1.1 (2018.11.19) 56 | ① 新增启动页 57 | ② 新增引导页 58 | ③ 修复banner无法点击bug,一些优化 59 | 60 | ### v0.1.0 (2018.11.16) 61 | ① 堪称完美的UI界面almost 62 | ② 支持国际化 63 | ③ 支持更换主题色 64 | 65 | ## 其他 66 | ### [Flutter工具类库 flustars][flustars_github] 67 | #### v0.1.8(2018.12.29) 68 | ScreenUtil 屏幕适配更新。 69 | 方案一、不依赖context 70 | ``` 71 | 步骤 1 72 | //如果设计稿尺寸默认配置一致,无需该设置。 配置设计稿尺寸 默认 360.0 / 640.0 / 3.0 73 | setDesignWHD(_designW,_designH,_designD); 74 | 75 | 步骤 2 76 | // 在MainPageState build 调用MediaQuery.of(context) 77 | class MainPageState extends State { 78 | @override 79 | Widget build(BuildContext context) { 80 | 81 | // 在 MainPageState build 调用 MediaQuery.of(context) 82 | MediaQuery.of(context); 83 | 84 | double width = ScreenUtil.getInstance().screenWidth; 85 | double height = ScreenUtil.getInstance().screenHeight; 86 | return new Scaffold( 87 | appBar: new AppBar(), 88 | ); 89 | } 90 | } 91 | 92 | 步骤 3 93 | ScreenUtil.getInstance().screenWidth 94 | ScreenUtil.getInstance().screenHeight 95 | ScreenUtil.getInstance().screenDensity 96 | ScreenUtil.getInstance().statusBarHeight 97 | ScreenUtil.getInstance().bottomBarHeight 98 | //屏幕适配相关 99 | ScreenUtil.getInstance().getWidth(size); //返回根据屏幕宽适配后尺寸(单位 dp or pt) 100 | ScreenUtil.getInstance().getHeight(size); //返回根据屏幕高适配后尺寸 (单位 dp or pt) 101 | ScreenUtil.getInstance().getWidthPx(sizePx); //sizePx 单位px 102 | ScreenUtil.getInstance().getHeightPx(sizePx); //sizePx 单位px 103 | ScreenUtil.getInstance().getSp(fontSize); //返回根据屏幕宽适配后字体尺寸 104 | 105 | ``` 106 | 方案二、依赖context 107 | ``` 108 | //如果设计稿尺寸默认配置一致,无需该设置。 配置设计稿尺寸 默认 360.0 / 640.0 / 3.0 109 | setDesignWHD(_designW,_designH,_designD); 110 | 111 | ScreenUtil.getScreenW(context); //屏幕 宽 112 | ScreenUtil.getScreenH(context); //屏幕 高 113 | ScreenUtil.getScreenDensity(context); //屏幕 像素密度 114 | ScreenUtil.getStatusBarH(context); //状态栏高度 115 | ScreenUtil.getBottomBarH(context); //bottombar 高度 116 | //屏幕适配相关 117 | ScreenUtil.getScaleW(context, size); //返回根据屏幕宽适配后尺寸(单位 dp or pt) 118 | ScreenUtil.getScaleH(context, size); //返回根据屏幕高适配后尺寸 (单位 dp or pt) 119 | ScreenUtil.getScaleSp(context, size) ;//返回根据屏幕宽适配后字体尺寸 120 | ``` 121 | #### v0.1.6(2018.12.20) 122 | 新增网络请求工具DioUtil, 单例模式,可输出请求日志。 123 | ``` 124 | // 打开debug模式. 125 | DioUtil.openDebug(); 126 | 127 | // 配置网络参数. 128 | Options options = DioUtil.getDefOptions(); 129 | options.baseUrl = "http://www.wanandroid.com/"; 130 | HttpConfig config = new HttpConfig(options: options); 131 | DioUtil().setConfig(config); 132 | 133 | // 两种单例请求方式. 134 | DioUtil().request(Method.get, "banner/json"); 135 | DioUtil.getInstance().request(Method.get, "banner/json"); 136 | 137 | //示例 138 | LoginReq req = new LoginReq('username', 'password'); 139 | DioUtil().request(Method.post, "user/login",data: req.toJson()); 140 | 141 | //示例 142 | FormData formData = new FormData.from({ 143 | "username": "username", 144 | "password": "password", 145 | }); 146 | DioUtil().requestR(Method.post, "user/login",data: rformData); 147 | 148 | //解析示例 149 | class WanRepository { 150 | Future> getBanner() async { 151 | BaseResp baseResp = await DioUtil().request( 152 | Method.get, WanAndroidApi.getPath(path: WanAndroidApi.BANNER)); 153 | List bannerList; 154 | if (baseResp.code != Constant.status_success) { 155 | return new Future.error(baseResp.msg); 156 | } 157 | if (baseResp.data != null) { 158 | bannerList = baseResp.data.map((value) { 159 | return BannerModel.fromJson(value); 160 | }).toList(); 161 | } 162 | return bannerList; 163 | } 164 | } 165 | 166 | // 网络请求日志 167 | I/flutter ( 5922): ----------------Http Log---------------- 168 | I/flutter ( 5922): [statusCode]: 200 169 | I/flutter ( 5922): [request ]: method: GET baseUrl: http://www.wanandroid.com/ path: lg/collect/list/0/json 170 | I/flutter ( 5922): [reqdata ]: null 171 | I/flutter ( 5922): [response ]: {data: {curPage: 1, datas: [], offset: 0, over: true, pageCount: 0, size: 20, total: 0}, errorCode: 0, errorMsg: } 172 | ``` 173 | 174 | 175 | [flutter_demos_github]: https://github.com/Sky24n/flutter_demos 176 | [flustars_github]: https://github.com/Sky24n/flustars -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2018, Sky24n 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.class 3 | .gradle 4 | /local.properties 5 | /.idea/workspace.xml 6 | /.idea/libraries 7 | .DS_Store 8 | /build 9 | /captures 10 | GeneratedPluginRegistrant.java 11 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 29 29 | 30 | lintOptions { 31 | disable 'InvalidPackage' 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId "com.thl.flutterwanandroid" 37 | minSdkVersion 16 38 | targetSdkVersion 29 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 13 | 20 | 24 | 27 | 32 | 35 | 36 | 37 | 38 | 39 | 40 | 42 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/thl/flutterwanandroid/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.thl.flutterwanandroid; 2 | 3 | import io.flutter.embedding.android.FlutterActivity; 4 | 5 | public class MainActivity extends FlutterActivity { 6 | } 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_new.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/splash_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/android/app/src/main/res/mipmap-xxhdpi/splash_bg.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.5.0' 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.useAndroidX=true 3 | android.enableJetifier=true 4 | android.enableR8=true 5 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /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-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /android/settings_aar.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /assets/images/3.0x/ali_connors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/assets/images/3.0x/ali_connors.png -------------------------------------------------------------------------------- /assets/images/3.0x/ali_connors_sml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/assets/images/3.0x/ali_connors_sml.png -------------------------------------------------------------------------------- /assets/images/3.0x/guide1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/assets/images/3.0x/guide1.png -------------------------------------------------------------------------------- /assets/images/3.0x/guide2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/assets/images/3.0x/guide2.png -------------------------------------------------------------------------------- /assets/images/3.0x/guide3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/assets/images/3.0x/guide3.png -------------------------------------------------------------------------------- /assets/images/3.0x/guide4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/assets/images/3.0x/guide4.png -------------------------------------------------------------------------------- /assets/images/3.0x/ic_launcher_news.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/assets/images/3.0x/ic_launcher_news.png -------------------------------------------------------------------------------- /assets/images/3.0x/icon_wan_android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/assets/images/3.0x/icon_wan_android.png -------------------------------------------------------------------------------- /assets/images/3.0x/qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/assets/images/3.0x/qrcode.png -------------------------------------------------------------------------------- /assets/images/3.0x/splash_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/assets/images/3.0x/splash_bg.png -------------------------------------------------------------------------------- /flutter_wanandroid.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /flutter_wanandroid_android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/app.flx 37 | /Flutter/app.zip 38 | /Flutter/flutter_assets/ 39 | /Flutter/App.framework 40 | /Flutter/Flutter.framework 41 | /Flutter/Generated.xcconfig 42 | /ServiceDefinitions.json 43 | 44 | Pods/ 45 | .symlinks/ 46 | -------------------------------------------------------------------------------- /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/Flutter/flutter_export_environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a generated file; do not edit or check into version control. 3 | export "FLUTTER_ROOT=/Users/thl/Library/Android/flutter" 4 | export "FLUTTER_APPLICATION_PATH=/Users/thl/AndroidStudioProjects/test workspace/flutter_wanandroid" 5 | export "FLUTTER_TARGET=lib/main.dart" 6 | export "FLUTTER_BUILD_DIR=build" 7 | export "SYMROOT=${SOURCE_ROOT}/../build/ios" 8 | export "OTHER_LDFLAGS=$(inherited) -framework Flutter" 9 | export "FLUTTER_FRAMEWORK_DIR=/Users/thl/Library/Android/flutter/bin/cache/artifacts/engine/ios" 10 | export "FLUTTER_BUILD_NAME=0.2.6" 11 | export "FLUTTER_BUILD_NUMBER=26" 12 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | pods_ary = [] 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) { |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | pods_ary.push({:name => podname, :path => podpath}); 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | } 32 | return pods_ary 33 | end 34 | 35 | target 'Runner' do 36 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 37 | # referring to absolute paths on developers' machines. 38 | system('rm -rf .symlinks') 39 | system('mkdir -p .symlinks/plugins') 40 | 41 | # Flutter Pods 42 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') 43 | if generated_xcode_build_settings.empty? 44 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." 45 | end 46 | generated_xcode_build_settings.map { |p| 47 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR' 48 | symlink = File.join('.symlinks', 'flutter') 49 | File.symlink(File.dirname(p[:path]), symlink) 50 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) 51 | end 52 | } 53 | 54 | # Plugin Pods 55 | plugin_pods = parse_KV_file('../.flutter-plugins') 56 | plugin_pods.map { |p| 57 | symlink = File.join('.symlinks', 'plugins', p[:name]) 58 | File.symlink(p[:path], symlink) 59 | pod p[:name], :path => File.join(symlink, 'ios') 60 | } 61 | end 62 | 63 | post_install do |installer| 64 | installer.pods_project.targets.each do |target| 65 | target.build_configurations.each do |config| 66 | config.build_settings['ENABLE_BITCODE'] = 'NO' 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - device_info (0.0.1): 3 | - Flutter 4 | - flustars (0.0.1): 5 | - Flutter 6 | - Flutter (1.0.0) 7 | - FMDB (2.7.5): 8 | - FMDB/standard (= 2.7.5) 9 | - FMDB/standard (2.7.5) 10 | - path_provider (0.0.1): 11 | - Flutter 12 | - shared_preferences (0.0.1): 13 | - Flutter 14 | - sqflite (0.0.1): 15 | - Flutter 16 | - FMDB (~> 2.7.2) 17 | - url_launcher (0.0.1): 18 | - Flutter 19 | - webview_flutter (0.0.1): 20 | - Flutter 21 | 22 | DEPENDENCIES: 23 | - device_info (from `.symlinks/plugins/device_info/ios`) 24 | - flustars (from `.symlinks/plugins/flustars/ios`) 25 | - Flutter (from `.symlinks/flutter/ios`) 26 | - path_provider (from `.symlinks/plugins/path_provider/ios`) 27 | - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) 28 | - sqflite (from `.symlinks/plugins/sqflite/ios`) 29 | - url_launcher (from `.symlinks/plugins/url_launcher/ios`) 30 | - webview_flutter (from `.symlinks/plugins/webview_flutter/ios`) 31 | 32 | SPEC REPOS: 33 | https://github.com/cocoapods/specs.git: 34 | - FMDB 35 | 36 | EXTERNAL SOURCES: 37 | device_info: 38 | :path: ".symlinks/plugins/device_info/ios" 39 | flustars: 40 | :path: ".symlinks/plugins/flustars/ios" 41 | Flutter: 42 | :path: ".symlinks/flutter/ios" 43 | path_provider: 44 | :path: ".symlinks/plugins/path_provider/ios" 45 | shared_preferences: 46 | :path: ".symlinks/plugins/shared_preferences/ios" 47 | sqflite: 48 | :path: ".symlinks/plugins/sqflite/ios" 49 | url_launcher: 50 | :path: ".symlinks/plugins/url_launcher/ios" 51 | webview_flutter: 52 | :path: ".symlinks/plugins/webview_flutter/ios" 53 | 54 | SPEC CHECKSUMS: 55 | device_info: 76ce0b32e13034d1883be4a382433648f9dcee63 56 | flustars: 0e81b1510ccac786ec044db1565e499b83e65da1 57 | Flutter: 9d0fac939486c9aba2809b7982dfdbb47a7b0296 58 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a 59 | path_provider: 09407919825bfe3c2deae39453b7a5b44f467873 60 | shared_preferences: 5a1d487c427ee18fcd3ea1f2a131569481834b53 61 | sqflite: d1612813fa7db7c667bed9f1d1b508deffc56999 62 | url_launcher: 92b89c1029a0373879933c21642958c874539095 63 | webview_flutter: 9bd234c07e99632356cba112df56fd1d47d6b6e3 64 | 65 | PODFILE CHECKSUM: aff02bfeed411c636180d6812254b2daeea14d09 66 | 67 | COCOAPODS: 1.5.3 68 | -------------------------------------------------------------------------------- /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.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Original 7 | 8 | 9 | -------------------------------------------------------------------------------- /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/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/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/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/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/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/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/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/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/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/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/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/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/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/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/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/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/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/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/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/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/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/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/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/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/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/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/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/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/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/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/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/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 | flutter_wanandroid 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 | io.flutter.embedded_views_preview 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | NSAppTransportSecurity 47 | 48 | NSAllowsArbitraryLoads 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Runner/zh-Hans-CN.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ios/Runner/zh-Hans-CN.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/blocs/application_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_wanandroid/blocs/bloc_provider.dart'; 2 | import 'package:rxdart/rxdart.dart'; 3 | 4 | class ApplicationBloc implements BlocBase { 5 | BehaviorSubject _appEvent = BehaviorSubject(); 6 | 7 | Sink get _appEventSink => _appEvent.sink; 8 | 9 | Stream get appEventStream => _appEvent.stream; 10 | 11 | @override 12 | void dispose() { 13 | _appEvent.close(); 14 | } 15 | 16 | @override 17 | Future getData({String labelId, int page}) { 18 | // TODO: implement getData 19 | return null; 20 | } 21 | 22 | @override 23 | Future onLoadMore({String labelId}) { 24 | // TODO: implement onLoadMore 25 | return null; 26 | } 27 | 28 | @override 29 | Future onRefresh({String labelId}) { 30 | // TODO: implement onRefresh 31 | return null; 32 | } 33 | 34 | void sendAppEvent(int type) { 35 | _appEventSink.add(type); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/blocs/bloc_index.dart: -------------------------------------------------------------------------------- 1 | export 'bloc_provider.dart'; 2 | export 'application_bloc.dart'; 3 | export 'main_bloc.dart'; 4 | export 'com_list_bloc.dart'; 5 | export 'tab_bloc.dart'; 6 | export 'collect_bloc.dart'; 7 | -------------------------------------------------------------------------------- /lib/blocs/bloc_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | abstract class BlocBase { 4 | Future getData({String labelId, int page}); 5 | 6 | Future onRefresh({String labelId}); 7 | 8 | Future onLoadMore({String labelId}); 9 | 10 | void dispose(); 11 | } 12 | 13 | class BlocProvider extends StatefulWidget { 14 | BlocProvider({ 15 | Key key, 16 | @required this.child, 17 | @required this.bloc, 18 | this.userDispose: true, 19 | }) : super(key: key); 20 | 21 | final T bloc; 22 | final Widget child; 23 | final bool userDispose; 24 | 25 | @override 26 | _BlocProviderState createState() => _BlocProviderState(); 27 | 28 | static T of(BuildContext context) { 29 | BlocProvider provider = 30 | context.findAncestorWidgetOfExactType>(); 31 | return provider.bloc; 32 | } 33 | } 34 | 35 | class _BlocProviderState extends State> { 36 | @override 37 | void dispose() { 38 | if (widget.userDispose) widget.bloc.dispose(); 39 | super.dispose(); 40 | } 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | return widget.child; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/blocs/collect_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | 3 | import 'package:flutter_wanandroid/blocs/bloc_provider.dart'; 4 | import 'package:flutter_wanandroid/data/protocol/models.dart'; 5 | import 'package:flutter_wanandroid/data/repository/collect_repository.dart'; 6 | import 'package:flutter_wanandroid/event/event.dart'; 7 | import 'package:flutter_wanandroid/utils/util_index.dart'; 8 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 9 | import 'package:rxdart/rxdart.dart'; 10 | 11 | class CollectBloc implements BlocBase { 12 | BehaviorSubject> _collectListBs = 13 | BehaviorSubject>(); 14 | 15 | Sink> get _collectListSink => _collectListBs.sink; 16 | 17 | Stream> get collectListStream => 18 | _collectListBs.stream.asBroadcastStream(); 19 | 20 | List _collectList; 21 | int _collectPage = 0; 22 | 23 | CollectRepository _collectRepository = new CollectRepository(); 24 | 25 | @override 26 | Future getData({String labelId, int page}) { 27 | return getCollectList(labelId, page); 28 | } 29 | 30 | @override 31 | Future onLoadMore({String labelId}) { 32 | int _page = ++_collectPage; 33 | return getData(labelId: labelId, page: _page); 34 | } 35 | 36 | @override 37 | Future onRefresh({String labelId, bool isReload}) { 38 | LogUtil.e("CollectBloc onRefresh...... $labelId"); 39 | _collectPage = 0; 40 | if (isReload == true) { 41 | _collectListSink.add(null); 42 | } 43 | return getData(labelId: labelId, page: _collectPage); 44 | } 45 | 46 | Future getCollectList(String labelId, int page) { 47 | return _collectRepository.getCollectList(page).then((list) { 48 | if (_collectList == null) { 49 | _collectList = new List(); 50 | } 51 | if (page == 0) { 52 | _collectList.clear(); 53 | } 54 | _collectList.addAll(list); 55 | _collectListSink.add(UnmodifiableListView(_collectList)); 56 | _homeEventSink?.add(new StatusEvent( 57 | labelId, 58 | ObjectUtil.isEmpty(list) 59 | ? RefreshStatus.noMore 60 | : RefreshStatus.idle)); 61 | }).catchError((_) { 62 | if (ObjectUtil.isEmpty(_collectList)) { 63 | _collectListBs.sink.addError("error"); 64 | } 65 | _collectPage--; 66 | _homeEventSink?.add(new StatusEvent(labelId, RefreshStatus.failed)); 67 | }); 68 | } 69 | 70 | Sink _homeEventSink; 71 | 72 | void setHomeEventSink(Sink eventSink) { 73 | _homeEventSink = eventSink; 74 | } 75 | 76 | @override 77 | void dispose() { 78 | _collectListBs.close(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/blocs/com_list_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | 3 | import 'package:flutter_wanandroid/common/component_index.dart'; 4 | import 'package:flutter_wanandroid/data/repository/wan_repository.dart'; 5 | 6 | class ComListBloc implements BlocBase { 7 | BehaviorSubject> _comListData = 8 | BehaviorSubject>(); 9 | 10 | Sink> get _comListSink => _comListData.sink; 11 | 12 | Stream> get comListStream => _comListData.stream; 13 | 14 | List comList; 15 | int _comListPage = 0; 16 | 17 | BehaviorSubject _comListEvent = BehaviorSubject(); 18 | 19 | Sink get _comListEventSink => _comListEvent.sink; 20 | 21 | Stream get comListEventStream => 22 | _comListEvent.stream.asBroadcastStream(); 23 | 24 | WanRepository wanRepository = new WanRepository(); 25 | 26 | @override 27 | Future getData({String labelId, int cid, int page}) { 28 | switch (labelId) { 29 | case Ids.titleReposTree: 30 | return getRepos(labelId, cid, page); 31 | break; 32 | case Ids.titleWxArticleTree: 33 | return getWxArticle(labelId, cid, page); 34 | break; 35 | case Ids.titleSystemTree: 36 | return getArticle(labelId, cid, page); 37 | break; 38 | default: 39 | return Future.delayed(new Duration(seconds: 1)); 40 | break; 41 | } 42 | } 43 | 44 | @override 45 | Future onLoadMore({String labelId, int cid}) { 46 | int _page = 0; 47 | _page = ++_comListPage; 48 | return getData(labelId: labelId, cid: cid, page: _page); 49 | } 50 | 51 | @override 52 | Future onRefresh({String labelId, int cid}) { 53 | switch (labelId) { 54 | case Ids.titleReposTree: 55 | _comListPage = 1; 56 | break; 57 | case Ids.titleWxArticleTree: 58 | _comListPage = 1; 59 | break; 60 | case Ids.titleSystemTree: 61 | _comListPage = 0; 62 | break; 63 | default: 64 | break; 65 | } 66 | 67 | return getData(labelId: labelId, cid: cid, page: _comListPage); 68 | } 69 | 70 | Future getRepos(String labelId, int cid, int page) async { 71 | ComReq _comReq = new ComReq(cid); 72 | return wanRepository 73 | .getProjectList(page: page, data: _comReq.toJson()) 74 | .then((list) { 75 | if (comList == null) comList = new List(); 76 | if (page == 1) { 77 | comList.clear(); 78 | } 79 | comList.addAll(list); 80 | _comListSink.add(UnmodifiableListView(comList)); 81 | _comListEventSink.add(new StatusEvent(labelId, 82 | ObjectUtil.isEmpty(list) ? RefreshStatus.noMore : RefreshStatus.idle, 83 | cid: cid)); 84 | }).catchError((_) { 85 | _comListPage--; 86 | _comListEventSink.add(new StatusEvent(labelId, RefreshStatus.failed)); 87 | }); 88 | } 89 | 90 | Future getWxArticle(String labelId, int cid, int page) async { 91 | return wanRepository.getWxArticleList(id: cid, page: page).then((list) { 92 | if (comList == null) comList = new List(); 93 | if (page == 1) { 94 | comList.clear(); 95 | } 96 | comList.addAll(list); 97 | _comListSink.add(UnmodifiableListView(comList)); 98 | _comListEventSink.add(new StatusEvent(labelId, 99 | ObjectUtil.isEmpty(list) ? RefreshStatus.noMore : RefreshStatus.idle, 100 | cid: cid)); 101 | }).catchError((_) { 102 | _comListPage--; 103 | _comListEventSink.add(new StatusEvent(labelId, RefreshStatus.failed)); 104 | }); 105 | } 106 | 107 | Future getArticle(String labelId, int cid, int page) async { 108 | ComReq _comReq = new ComReq(cid); 109 | return wanRepository 110 | .getArticleList(page: page, data: _comReq.toJson()) 111 | .then((list) { 112 | if (comList == null) comList = new List(); 113 | if (page == 0) { 114 | comList.clear(); 115 | } 116 | comList.addAll(list); 117 | _comListSink.add(UnmodifiableListView(comList)); 118 | _comListEventSink.add(new StatusEvent(labelId, 119 | ObjectUtil.isEmpty(list) ? RefreshStatus.noMore : RefreshStatus.idle, 120 | cid: cid)); 121 | }).catchError((_) { 122 | _comListPage--; 123 | _comListEventSink.add(new StatusEvent(labelId, RefreshStatus.failed)); 124 | }); 125 | } 126 | 127 | @override 128 | void dispose() { 129 | _comListData.close(); 130 | _comListEvent.close(); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /lib/blocs/tab_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | 3 | import 'package:flutter_wanandroid/common/component_index.dart'; 4 | import 'package:flutter_wanandroid/data/repository/wan_repository.dart'; 5 | 6 | class TabBloc implements BlocBase { 7 | BehaviorSubject> _tabTree = 8 | BehaviorSubject>(); 9 | 10 | Sink> get _tabTreeSink => _tabTree.sink; 11 | 12 | Stream> get tabTreeStream => _tabTree.stream; 13 | 14 | List treeList; 15 | 16 | WanRepository wanRepository = new WanRepository(); 17 | 18 | @override 19 | Future getData({String labelId, int page}) { 20 | switch (labelId) { 21 | case Ids.titleReposTree: 22 | return getProjectTree(labelId); 23 | break; 24 | case Ids.titleWxArticleTree: 25 | return getWxArticleTree(labelId); 26 | break; 27 | case Ids.titleSystemTree: 28 | return getSystemTree(labelId); 29 | break; 30 | default: 31 | return Future.delayed(new Duration(seconds: 1)); 32 | break; 33 | } 34 | } 35 | 36 | @override 37 | Future onLoadMore({String labelId}) { 38 | return null; 39 | } 40 | 41 | @override 42 | Future onRefresh({String labelId}) { 43 | return getData(labelId: labelId); 44 | } 45 | 46 | void bindSystemData(TreeModel model) { 47 | if (model == null) return; 48 | treeList = model.children; 49 | } 50 | 51 | Future getProjectTree(String labelId) { 52 | return wanRepository.getProjectTree().then((list) { 53 | _tabTreeSink.add(UnmodifiableListView(list)); 54 | }); 55 | } 56 | 57 | Future getWxArticleTree(String labelId) { 58 | return wanRepository.getWxArticleChapters().then((list) { 59 | _tabTreeSink.add(UnmodifiableListView(list)); 60 | }); 61 | } 62 | 63 | Future getSystemTree(String labelId) { 64 | return Future.delayed(new Duration(milliseconds: 500)).then((_) { 65 | _tabTreeSink.add(UnmodifiableListView(treeList)); 66 | }); 67 | } 68 | 69 | Future getTree(String labelId) { 70 | return wanRepository.getProjectTree().then((list) { 71 | if (treeList == null) { 72 | treeList = new List(); 73 | } 74 | treeList.clear(); 75 | treeList.addAll(list); 76 | _tabTreeSink.add(UnmodifiableListView(treeList)); 77 | // _homeEventSink.add(new StatusEvent( 78 | // labelId, 79 | // ObjectUtil.isEmpty(list) 80 | // ? RefreshStatus.noMore 81 | // : RefreshStatus.idle)); 82 | }).catchError((_) { 83 | // _homeEventSink.add(new StatusEvent(labelId, RefreshStatus.failed)); 84 | }); 85 | } 86 | 87 | @override 88 | void dispose() { 89 | _tabTree.close(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/common/common.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | 3 | class Constant { 4 | static const String keyLanguage = 'key_language'; 5 | 6 | static const int status_success = 0; 7 | 8 | static const String server_address = wan_android; 9 | 10 | static const String wan_android = "https://www.wanandroid.com/"; 11 | 12 | static const int type_sys_update = 1; 13 | static const int type_refresh_all = 5; 14 | 15 | static const String key_theme_color = 'key_theme_color'; 16 | static const String key_guide = 'key_guide'; 17 | static const String key_splash_model = 'key_splash_models'; 18 | } 19 | 20 | class AppConfig { 21 | static const String appId = 'com.thl.flutterwanandroid'; 22 | static const String appName = 'flutter_wanandroid'; 23 | static const String version = '0.2.5'; 24 | static const bool isDebug = kDebugMode; 25 | } 26 | 27 | class LoadStatus { 28 | static const int fail = -1; 29 | static const int loading = 0; 30 | static const int success = 1; 31 | static const int empty = 2; 32 | } 33 | -------------------------------------------------------------------------------- /lib/common/component_index.dart: -------------------------------------------------------------------------------- 1 | export 'dart:convert'; 2 | 3 | export 'package:flutter_wanandroid/blocs/bloc_index.dart'; 4 | export 'package:flutter_wanandroid/ui/widgets/widget_index.dart'; 5 | export 'package:flutter_wanandroid/res/index.dart'; 6 | export 'package:flutter_wanandroid/utils/util_index.dart'; 7 | 8 | export 'package:base_library/base_library.dart'; 9 | 10 | export 'package:flutter_wanandroid/common/common.dart'; 11 | export 'package:flutter_wanandroid/common/sp_helper.dart'; 12 | export 'package:flutter_wanandroid/event/event.dart'; 13 | 14 | export 'package:flutter_wanandroid/data/protocol/models.dart'; 15 | export 'package:flutter_wanandroid/models/models.dart'; 16 | 17 | export 'package:rxdart/rxdart.dart'; 18 | 19 | export 'package:flukit/flukit.dart'; 20 | export 'package:cached_network_image/cached_network_image.dart'; 21 | export 'package:pull_to_refresh/pull_to_refresh.dart'; 22 | -------------------------------------------------------------------------------- /lib/common/global.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter/widgets.dart'; 6 | import 'package:flustars/flustars.dart'; 7 | 8 | class Global { 9 | //初始化全局信息 10 | static Future init(VoidCallback callback) async { 11 | WidgetsFlutterBinding.ensureInitialized(); 12 | await SpUtil.getInstance(); 13 | callback(); 14 | if (Platform.isAndroid) { 15 | // 以下两行 设置android状态栏为透明的沉浸。写在组件渲染之后,是为了在渲染后进行set赋值,覆盖状态栏,写在渲染之前MaterialApp组件会覆盖掉这个值。 16 | SystemUiOverlayStyle systemUiOverlayStyle = 17 | SystemUiOverlayStyle(statusBarColor: Colors.transparent); 18 | SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/common/sp_helper.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:common_utils/common_utils.dart'; 4 | import 'package:flustars/flustars.dart'; 5 | import 'package:flutter_wanandroid/common/common.dart'; 6 | import 'package:flutter_wanandroid/data/protocol/models.dart'; 7 | import 'package:flutter_wanandroid/models/models.dart'; 8 | 9 | class SpHelper { 10 | /// T 用于区分存储类型 11 | /// Example. 12 | /// SpHelper.putObject(key, value); 13 | /// SpHelper.putObject(key, value); 14 | /// SpHelper.putObject(key, value); 15 | /// SpHelper.putObject(key, value); 16 | /// SpHelper.putObject(key, value); 17 | /// 18 | /// SpHelper.putObject(key, UserModel); 19 | /// 20 | static void _putObject(String key, Object value) { 21 | switch (T) { 22 | case int: 23 | SpUtil.putInt(key, value); 24 | break; 25 | case double: 26 | SpUtil.putDouble(key, value); 27 | break; 28 | case bool: 29 | SpUtil.putBool(key, value); 30 | break; 31 | case String: 32 | SpUtil.putString(key, value); 33 | break; 34 | case List: 35 | SpUtil.putStringList(key, value); 36 | break; 37 | default: 38 | SpUtil.putObject(key, value); 39 | break; 40 | } 41 | } 42 | 43 | static String getThemeColor() { 44 | return SpUtil.getString(Constant.key_theme_color, 45 | defValue: 'deepPurpleAccent'); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/data/api/apis.dart: -------------------------------------------------------------------------------- 1 | class WanAndroidApi { 2 | /// 首页banner http://www.wanandroid.com/banner/json 3 | static const String BANNER = "banner"; 4 | 5 | /// 最新项目tab (首页的第二个tab) http://wanandroid.com/article/listproject/0/json 6 | static const String ARTICLE_LISTPROJECT = "article/listproject"; 7 | 8 | /// 项目分类 http://www.wanandroid.com/project/tree/json 9 | static const String PROJECT_TREE = "project/tree"; 10 | 11 | /// 项目列表数据 http://www.wanandroid.com/project/list/1/json?cid=294 12 | static const String PROJECT_LIST = "project/list"; 13 | 14 | /// 体系数据 http://www.wanandroid.com/tree/json 15 | static const String TREE = "tree"; 16 | 17 | /// 首页文章列表 http://www.wanandroid.com/article/list/0/json 18 | /// 知识体系下的文章 http://www.wanandroid.com/article/list/0/json?cid=60 19 | static const String ARTICLE_LIST = "article/list"; 20 | 21 | /// 获取公众号列表 http://wanandroid.com/wxarticle/chapters/json 22 | static const String WXARTICLE_CHAPTERS = "wxarticle/chapters"; 23 | 24 | /// 查看某个公众号历史数据 http://wanandroid.com/wxarticle/list/405/1/json 25 | /// 在某个公众号中搜索历史文章 http://wanandroid.com/wxarticle/list/405/1/json?k=Java 26 | static const String WXARTICLE_LIST = "wxarticle/list"; 27 | 28 | static const String user_register = "user/register"; //注册 29 | static const String user_login = "user/login"; //登录 30 | static const String user_logout = "user/logout"; //退出 31 | 32 | static const String lg_collect_list = "lg/collect/list"; //收藏文章列表 33 | static const String lg_collect = "lg/collect"; //收藏站内文章 34 | static const String lg_uncollect_originid = "lg/uncollect_originId"; //取消收藏 35 | 36 | static String getPath({String path: '', int page, String resType: 'json'}) { 37 | StringBuffer sb = new StringBuffer(path); 38 | if (page != null) { 39 | sb.write('/$page'); 40 | } 41 | if (resType != null && resType.isNotEmpty) { 42 | sb.write('/$resType'); 43 | } 44 | return sb.toString(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/data/repository/collect_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:base_library/base_library.dart'; 2 | import 'package:flutter_wanandroid/common/common.dart'; 3 | import 'package:flutter_wanandroid/data/api/apis.dart'; 4 | import 'package:flutter_wanandroid/data/protocol/models.dart'; 5 | 6 | class CollectRepository { 7 | Future> getCollectList(int page) async { 8 | BaseResp> baseResp = await DioUtil() 9 | .request>( 10 | Method.get, 11 | WanAndroidApi.getPath( 12 | path: WanAndroidApi.lg_collect_list, page: page)); 13 | if (baseResp.code != Constant.status_success) { 14 | return new Future.error(baseResp.msg); 15 | } 16 | List list; 17 | if (baseResp.data != null) { 18 | ComData comData = ComData.fromJson(baseResp.data); 19 | list = comData.datas?.map((value) { 20 | ReposModel model = ReposModel.fromJson(value); 21 | model.collect = true; 22 | return model; 23 | })?.toList(); 24 | } 25 | return list; 26 | } 27 | 28 | Future collect(int id) async { 29 | BaseResp baseResp = await DioUtil().request(Method.post, 30 | WanAndroidApi.getPath(path: WanAndroidApi.lg_collect, page: id)); 31 | if (baseResp.code != Constant.status_success) { 32 | return Future.error(baseResp.msg); 33 | } 34 | return true; 35 | } 36 | 37 | Future unCollect(int id) async { 38 | BaseResp baseResp = await DioUtil().request( 39 | Method.post, 40 | WanAndroidApi.getPath( 41 | path: WanAndroidApi.lg_uncollect_originid, page: id)); 42 | if (baseResp.code != Constant.status_success) { 43 | return Future.error(baseResp.msg); 44 | } 45 | return true; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/data/repository/user_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:base_library/base_library.dart'; 2 | import 'package:flutter_wanandroid/common/common.dart'; 3 | import 'package:flutter_wanandroid/data/api/apis.dart'; 4 | import 'package:flutter_wanandroid/data/protocol/models.dart'; 5 | import 'package:flutter_wanandroid/utils/util_index.dart'; 6 | 7 | class UserRepository { 8 | Future login(LoginReq req) async { 9 | BaseRespR> baseResp = await DioUtil() 10 | .requestR>(Method.post, WanAndroidApi.user_login, 11 | data: req.toJson()); 12 | if (baseResp.code != Constant.status_success) { 13 | return Future.error(baseResp.msg); 14 | } 15 | baseResp.response.headers.forEach((String name, List values) { 16 | if (name == "set-cookie") { 17 | String cookie = values.toString(); 18 | LogUtil.e("set-cookie: " + cookie); 19 | SpUtil.putString(BaseConstant.keyAppToken, cookie); 20 | DioUtil().setCookie(cookie); 21 | //CacheUtil().setLogin(true); 22 | } 23 | }); 24 | UserModel model = UserModel.fromJson(baseResp.data); 25 | //CacheUtil().setUserModel(model); 26 | SpUtil.putObject(BaseConstant.keyUserModel, model); 27 | return model; 28 | } 29 | 30 | Future register(RegisterReq req) async { 31 | BaseRespR> baseResp = await DioUtil() 32 | .requestR>( 33 | Method.post, WanAndroidApi.user_register, 34 | data: req.toJson()); 35 | if (baseResp.code != Constant.status_success) { 36 | return Future.error(baseResp.msg); 37 | } 38 | baseResp.response.headers.forEach((String name, List values) { 39 | if (name == "set-cookie") { 40 | String cookie = values.toString(); 41 | LogUtil.e("set-cookie: " + cookie); 42 | SpUtil.putString(BaseConstant.keyAppToken, cookie); 43 | DioUtil().setCookie(cookie); 44 | //CacheUtil().setLogin(true); 45 | } 46 | }); 47 | UserModel model = UserModel.fromJson(baseResp.data); 48 | //CacheUtil().setUserModel(model); 49 | SpUtil.putObject(BaseConstant.keyUserModel, model); 50 | return model; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/data/repository/wan_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:base_library/base_library.dart'; 2 | import 'package:flutter_wanandroid/common/common.dart'; 3 | import 'package:flutter_wanandroid/data/api/apis.dart'; 4 | import 'package:flutter_wanandroid/data/protocol/models.dart'; 5 | 6 | class WanRepository { 7 | Future> getBanner() async { 8 | BaseResp baseResp = await DioUtil().request( 9 | Method.get, WanAndroidApi.getPath(path: WanAndroidApi.BANNER)); 10 | List bannerList; 11 | if (baseResp.code != Constant.status_success) { 12 | return new Future.error(baseResp.msg); 13 | } 14 | if (baseResp.data != null) { 15 | bannerList = baseResp.data.map((value) { 16 | return BannerModel.fromJson(value); 17 | }).toList(); 18 | } 19 | return bannerList; 20 | } 21 | 22 | Future> getArticleListProject(int page) async { 23 | BaseResp> baseResp = await DioUtil() 24 | .request>( 25 | Method.get, 26 | WanAndroidApi.getPath( 27 | path: WanAndroidApi.ARTICLE_LISTPROJECT, page: page)); 28 | List list; 29 | if (baseResp.code != Constant.status_success) { 30 | return new Future.error(baseResp.msg); 31 | } 32 | if (baseResp.data != null) { 33 | ComData comData = ComData.fromJson(baseResp.data); 34 | list = comData.datas.map((value) { 35 | return ReposModel.fromJson(value); 36 | }).toList(); 37 | } 38 | return list; 39 | } 40 | 41 | Future> getArticleList({int page, data}) async { 42 | BaseResp> baseResp = await DioUtil() 43 | .request>(Method.get, 44 | WanAndroidApi.getPath(path: WanAndroidApi.ARTICLE_LIST, page: page), 45 | data: data); 46 | List list; 47 | if (baseResp.code != Constant.status_success) { 48 | return new Future.error(baseResp.msg); 49 | } 50 | if (baseResp.data != null) { 51 | ComData comData = ComData.fromJson(baseResp.data); 52 | list = comData.datas.map((value) { 53 | return ReposModel.fromJson(value); 54 | }).toList(); 55 | } 56 | return list; 57 | } 58 | 59 | Future> getTree() async { 60 | BaseResp baseResp = await DioUtil().request( 61 | Method.get, WanAndroidApi.getPath(path: WanAndroidApi.TREE)); 62 | List treeList; 63 | if (baseResp.code != Constant.status_success) { 64 | return new Future.error(baseResp.msg); 65 | } 66 | if (baseResp.data != null) { 67 | treeList = baseResp.data.map((value) { 68 | return TreeModel.fromJson(value); 69 | }).toList(); 70 | } 71 | return treeList; 72 | } 73 | 74 | Future> getProjectList({int page: 1, data}) async { 75 | BaseResp> baseResp = await DioUtil() 76 | .request>(Method.get, 77 | WanAndroidApi.getPath(path: WanAndroidApi.PROJECT_LIST, page: page), 78 | data: data); 79 | List list; 80 | if (baseResp.code != Constant.status_success) { 81 | return new Future.error(baseResp.msg); 82 | } 83 | if (baseResp.data != null) { 84 | ComData comData = ComData.fromJson(baseResp.data); 85 | list = comData.datas.map((value) { 86 | return ReposModel.fromJson(value); 87 | }).toList(); 88 | } 89 | return list; 90 | } 91 | 92 | Future> getWxArticleList({int id, int page: 1, data}) async { 93 | BaseResp> baseResp = await DioUtil() 94 | .request>( 95 | Method.get, 96 | WanAndroidApi.getPath( 97 | path: WanAndroidApi.WXARTICLE_LIST + '/$id', page: page), 98 | data: data); 99 | List list; 100 | if (baseResp.code != Constant.status_success) { 101 | return new Future.error(baseResp.msg); 102 | } 103 | if (baseResp.data != null) { 104 | ComData comData = ComData.fromJson(baseResp.data); 105 | list = comData.datas.map((value) { 106 | return ReposModel.fromJson(value); 107 | }).toList(); 108 | } 109 | return list; 110 | } 111 | 112 | Future> getWxArticleChapters() async { 113 | BaseResp baseResp = await DioUtil().request(Method.get, 114 | WanAndroidApi.getPath(path: WanAndroidApi.WXARTICLE_CHAPTERS)); 115 | List treeList; 116 | if (baseResp.code != Constant.status_success) { 117 | return new Future.error(baseResp.msg); 118 | } 119 | if (baseResp.data != null) { 120 | treeList = baseResp.data.map((value) { 121 | return TreeModel.fromJson(value); 122 | }).toList(); 123 | } 124 | return treeList; 125 | } 126 | 127 | Future> getProjectTree() async { 128 | BaseResp baseResp = await DioUtil().request( 129 | Method.get, WanAndroidApi.getPath(path: WanAndroidApi.PROJECT_TREE)); 130 | List treeList; 131 | if (baseResp.code != Constant.status_success) { 132 | return new Future.error(baseResp.msg); 133 | } 134 | if (baseResp.data != null) { 135 | treeList = baseResp.data.map((value) { 136 | return TreeModel.fromJson(value); 137 | }).toList(); 138 | } 139 | return treeList; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /lib/db/db.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/lib/db/db.dart -------------------------------------------------------------------------------- /lib/demos/image_size_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flustars/flustars.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_wanandroid/res/styles.dart'; 7 | 8 | class ImageSizePage extends StatefulWidget { 9 | final String title; 10 | 11 | ImageSizePage(this.title); 12 | 13 | @override 14 | State createState() { 15 | return new _ImageSizePageState(); 16 | } 17 | } 18 | 19 | class _ImageSizePageState extends State { 20 | String cacheImgInfo1 = "[CachedNetworkImage] loading..."; 21 | String netImgInfo1 = "[网络图片1] loading..."; 22 | String localImgInfo1 = "[本地图片 ali_connors] loading..."; 23 | String netImgInfoE = "[网络图片E] loading..."; 24 | String netUrl1 = 25 | "https://upload-images.jianshu.io/upload_images/13222938-74a4dc4115d76790.png"; 26 | String netUrl2 = 27 | "https://upload-images.jianshu.io/upload_images/13222938-74a4dc4115d76790.png"; 28 | String netUrlE = "https://xxx.png"; 29 | String localImgUrl = "assets/images/3.0x/ali_connors.png"; 30 | 31 | @override 32 | void initState() { 33 | super.initState(); 34 | _loadCachedNetworkImage(); 35 | _loadNetUrl1(); 36 | _loadLocalUrl1(); 37 | _initAsync2(); 38 | } 39 | 40 | void _loadCachedNetworkImage() async { 41 | Image image = new Image(image: new CachedNetworkImageProvider(netUrl2)); 42 | Rect rect = await WidgetUtil.getImageWH(image: image); 43 | cacheImgInfo1 = 44 | "[CachedNetworkImage] width: ${rect.width}, height: ${rect.height}"; 45 | setState(() {}); 46 | 47 | // Image imageAsset = new Image.asset(""); 48 | // Image imageFile = new Image.file(File("path")); 49 | // Image imageNetwork = new Image.network("url"); 50 | // Image imageMemory = new Image.memory(null); 51 | } 52 | 53 | void _loadNetUrl1() async { 54 | Rect rect = await WidgetUtil.getImageWH(url: netUrl1); 55 | netImgInfo1 = "[网络图片1] width: ${rect.width}, height: ${rect.height}"; 56 | setState(() {}); 57 | } 58 | 59 | void _loadLocalUrl1() async { 60 | Rect locImg1 = await WidgetUtil.getImageWH(localUrl: localImgUrl); 61 | localImgInfo1 = 62 | "[本地图片 ali_connors] width: ${locImg1.width}, height: ${locImg1.height}"; 63 | setState(() {}); 64 | } 65 | 66 | void _initAsync2() { 67 | WidgetUtil.getImageWHE(url: netUrlE).then((Rect rect) { 68 | netImgInfoE = "[网络图片2] width: ${rect.width}, height: ${rect.height}"; 69 | setState(() {}); 70 | }).catchError((error) { 71 | netImgInfoE = "[网络图片2] error" + error.toString(); 72 | setState(() {}); 73 | }); 74 | } 75 | 76 | Widget _buildItem(String info, {double height = 50}) { 77 | return new Container( 78 | alignment: Alignment.center, 79 | height: height, 80 | child: new Text( 81 | "$info", 82 | style: TextStyles.listContent, 83 | ), 84 | decoration: Decorations.bottom, 85 | ); 86 | } 87 | 88 | @override 89 | Widget build(BuildContext context) { 90 | return Scaffold( 91 | appBar: new AppBar( 92 | title: new Text(widget.title), 93 | centerTitle: true, 94 | ), 95 | body: new ListView( 96 | children: [ 97 | _buildItem(cacheImgInfo1), 98 | _buildItem(netImgInfo1), 99 | _buildItem(localImgInfo1), 100 | _buildItem(netImgInfoE, height: 100), 101 | ], 102 | ), 103 | ); 104 | } 105 | 106 | @override 107 | void dispose() { 108 | super.dispose(); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /lib/demos/index.dart: -------------------------------------------------------------------------------- 1 | export 'city_select_page.dart'; 2 | export 'date_page.dart'; 3 | export 'pinyin_page.dart'; 4 | export 'regex_page.dart'; 5 | export 'widget_page.dart'; 6 | export 'timer_page.dart'; 7 | export 'money_page.dart'; 8 | export 'timeline_page.dart'; 9 | export 'round_portrait_page.dart'; 10 | export 'image_size_page.dart'; -------------------------------------------------------------------------------- /lib/demos/main_demos.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/demos/index.dart'; 3 | import 'package:flutter_wanandroid/utils/navigator_util.dart'; 4 | import 'package:flutter_wanandroid/utils/util_index.dart'; 5 | 6 | class ItemModel { 7 | String title; 8 | Widget page; 9 | 10 | ItemModel(this.title, this.page); 11 | } 12 | 13 | class MainDemosPage extends StatefulWidget { 14 | @override 15 | State createState() { 16 | return new MainDemosPageState(); 17 | } 18 | } 19 | 20 | class MainDemosPageState extends State { 21 | List mItemList = new List(); 22 | 23 | @override 24 | void initState() { 25 | super.initState(); 26 | 27 | mItemList.add(new ItemModel("Github【common_utils】", null)); 28 | mItemList.add(new ItemModel("汉字转拼音", new PinyinPage("汉字转拼音"))); 29 | mItemList.add(new ItemModel("城市列表", CityListPage())); 30 | mItemList.add(new ItemModel("Date Util", new DatePage("Date Util"))); 31 | mItemList.add(new ItemModel("Regex Util", new RegexUtilPage("Regex Util"))); 32 | mItemList.add(new ItemModel("Widget Util", new WidgetPage("Widget Util"))); 33 | mItemList.add(new ItemModel("Timer Util", new TimerPage("Timer Util"))); 34 | mItemList.add(new ItemModel("Money Util", new MoneyPage("Money Util"))); 35 | mItemList 36 | .add(new ItemModel("Timeline Util", new TimelinePage("Timeline Util"))); 37 | mItemList.add(new ItemModel("圆形/圆角头像", new RoundPortraitPage('圆形/圆角头像'))); 38 | mItemList.add(new ItemModel("获取图片尺寸", new ImageSizePage('获取图片尺寸'))); 39 | } 40 | 41 | Widget buildItem(ItemModel model) { 42 | return new InkWell( 43 | onTap: () { 44 | if (model.page == null) { 45 | NavigatorUtil.pushWeb(context, 46 | url: 'https://github.com/Sky24n/common_utils', 47 | title: 'Github【common_utils】'); 48 | } else { 49 | NavigatorUtil.pushPage(context, model.page, pageName: model.title); 50 | } 51 | }, 52 | child: new Container( 53 | height: 50.0, 54 | child: new Center( 55 | child: new Text( 56 | model.title, 57 | style: new TextStyle(fontSize: 14.0, color: Color(0xFF666666)), 58 | ), 59 | ), 60 | decoration: new BoxDecoration( 61 | borderRadius: BorderRadius.all(Radius.circular(2.0)), 62 | color: Colors.white, 63 | border: 64 | new Border.all(width: 0.33, color: Color(0XFFEFEFEF))))); 65 | } 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | return new Scaffold( 70 | appBar: new AppBar( 71 | title: new Text("Flutter Demos"), 72 | centerTitle: true, 73 | ), 74 | body: new ListView.builder( 75 | itemCount: mItemList.length, 76 | itemBuilder: (BuildContext context, int index) { 77 | ItemModel model = mItemList[index]; 78 | return buildItem(model); 79 | }), 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/demos/round_portrait_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/utils/utils.dart'; 3 | 4 | class RoundPortraitPage extends StatelessWidget { 5 | final String title; 6 | 7 | const RoundPortraitPage(this.title, {Key key}) : super(key: key); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return new Scaffold( 12 | appBar: new AppBar( 13 | title: new Text(title), 14 | centerTitle: true, 15 | ), 16 | // backgroundColor: Colors.grey, 17 | body: new Container( 18 | alignment: Alignment.topCenter, 19 | padding: EdgeInsets.all(16.0), 20 | child: new Column( 21 | children: [ 22 | new ClipOval( 23 | child: new Image.asset(Utils.getImgPath('ali_connors')), 24 | ), 25 | new CircleAvatar( 26 | radius: 36.0, 27 | backgroundImage: AssetImage( 28 | Utils.getImgPath('ali_connors'), 29 | ), 30 | ), 31 | new Container( 32 | width: 72.0, 33 | height: 72.0, 34 | decoration: BoxDecoration( 35 | shape: BoxShape.circle, 36 | image: DecorationImage( 37 | image: AssetImage( 38 | Utils.getImgPath('ali_connors'), 39 | ), 40 | ), 41 | ), 42 | ), 43 | new ClipRRect( 44 | borderRadius: BorderRadius.circular(6.0), 45 | child: new Image.asset(Utils.getImgPath('ali_connors')), 46 | ), 47 | new Container( 48 | width: 88.0, 49 | height: 88.0, 50 | decoration: BoxDecoration( 51 | shape: BoxShape.rectangle, 52 | borderRadius: BorderRadius.circular(6.0), 53 | image: DecorationImage( 54 | image: AssetImage( 55 | Utils.getImgPath('ali_connors'), 56 | ), 57 | ), 58 | ), 59 | ), 60 | ], 61 | ), 62 | ), 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/demos/test_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class TestPage extends StatefulWidget { 4 | final String title; 5 | 6 | TestPage(this.title); 7 | 8 | @override 9 | State createState() { 10 | return new _TestPageState(); 11 | } 12 | } 13 | 14 | class _TestPageState extends State { 15 | @override 16 | Widget build(BuildContext context) { 17 | return Scaffold( 18 | appBar: new AppBar( 19 | title: new Text(widget.title), 20 | centerTitle: true, 21 | ), 22 | body: new Column( 23 | children: [ 24 | new Card( 25 | elevation: 4.0, 26 | margin: const EdgeInsets.all(10.0), 27 | child: new Container(), 28 | ), 29 | ], 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/demos/widget_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flustars/flustars.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class WidgetPage extends StatefulWidget { 5 | final String title; 6 | 7 | WidgetPage(this.title); 8 | 9 | @override 10 | State createState() { 11 | return new _WidgetPageState(); 12 | } 13 | } 14 | 15 | class _WidgetPageState extends State { 16 | double testHeight = 50.0; 17 | bool isOpen = false; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return Scaffold( 22 | appBar: new AppBar( 23 | title: new Text(widget.title), 24 | centerTitle: true, 25 | ), 26 | body: new Stack( 27 | children: [ 28 | new Container( 29 | child: new Center( 30 | child: new Padding( 31 | padding: EdgeInsets.all(10.0), 32 | child: new TestPage(testHeight), 33 | ), 34 | ), 35 | ), 36 | new Container( 37 | child: new TestPage2(), 38 | margin: EdgeInsets.all(10.0), 39 | ) 40 | ], 41 | ), 42 | floatingActionButton: new FloatingActionButton( 43 | onPressed: () { 44 | setState(() { 45 | isOpen = !isOpen; 46 | testHeight = isOpen ? 100.0 : 50.0; 47 | }); 48 | }, 49 | tooltip: 'Increment', 50 | child: new Icon(Icons.add), 51 | )); 52 | } 53 | } 54 | 55 | class TestPage extends StatefulWidget { 56 | final double _height; 57 | 58 | TestPage(this._height); 59 | 60 | @override 61 | State createState() { 62 | return new _TestPageState(); 63 | } 64 | } 65 | 66 | class _TestPageState extends State { 67 | WidgetUtil widgetUtil = new WidgetUtil(); 68 | String mCenterTxt = ""; 69 | 70 | @override 71 | Widget build(BuildContext context) { 72 | widgetUtil.asyncPrepare(context, true, (Rect rect) { 73 | setState(() { 74 | mCenterTxt = "width: " + 75 | rect.width.toString() + 76 | "\n" + 77 | "height: " + 78 | rect.height.toString(); 79 | }); 80 | }); 81 | return Container( 82 | height: widget._height, 83 | color: Colors.cyan[200], 84 | child: new Center(child: new Text(mCenterTxt)), 85 | ); 86 | } 87 | } 88 | 89 | class TestPage2 extends StatefulWidget { 90 | @override 91 | State createState() { 92 | return new _TestPage2State(); 93 | } 94 | } 95 | 96 | class _TestPage2State extends State { 97 | String defText = "点击获取Widget在屏幕上的坐标"; 98 | String contentText = ""; 99 | 100 | @override 101 | Widget build(BuildContext context) { 102 | if (contentText == "") { 103 | contentText = defText; 104 | } 105 | return new Container( 106 | height: 100.0, 107 | color: Colors.cyan[100], 108 | child: new InkWell( 109 | onTap: () { 110 | setState(() { 111 | Offset offset = WidgetUtil.getWidgetLocalToGlobal(context); 112 | contentText = defText + "\n" + "Offset: " + offset.toString(); 113 | }); 114 | }, 115 | child: new Center( 116 | child: new Text(contentText), 117 | ), 118 | ), 119 | ); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /lib/event/event.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/blocs/bloc_index.dart'; 3 | 4 | class StatusEvent { 5 | String labelId; 6 | int status; 7 | int cid; 8 | 9 | StatusEvent(this.labelId, this.status, {this.cid}); 10 | } 11 | 12 | class ComEvent { 13 | int id; 14 | Object data; 15 | 16 | ComEvent({ 17 | this.id, 18 | this.data, 19 | }); 20 | } 21 | 22 | class Event { 23 | static void sendAppComEvent(BuildContext context, ComEvent event) { 24 | // BlocProvider.of(context).sendAppComEvent(event); 25 | } 26 | 27 | static void sendAppEvent(BuildContext context, int id) { 28 | BlocProvider.of(context).sendAppEvent(id); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_localizations/flutter_localizations.dart'; 4 | import 'package:flutter_wanandroid/common/component_index.dart'; 5 | import 'package:flutter_wanandroid/ui/pages/main_page.dart'; 6 | import 'package:flutter_wanandroid/ui/pages/page_index.dart'; 7 | 8 | import 'common/global.dart'; 9 | 10 | void main() { 11 | Global.init(() { 12 | runApp(BlocProvider( 13 | bloc: ApplicationBloc(), 14 | child: BlocProvider(child: MyApp(), bloc: MainBloc()), 15 | )); 16 | }); 17 | } 18 | 19 | class MyApp extends StatefulWidget { 20 | @override 21 | State createState() { 22 | return MyAppState(); 23 | } 24 | } 25 | 26 | class MyAppState extends State { 27 | Locale _locale; 28 | Color _themeColor = Colours.app_main; 29 | 30 | @override 31 | void initState() { 32 | super.initState(); 33 | setInitDir(initStorageDir: true); 34 | setLocalizedValues(localizedValues); 35 | init(); 36 | } 37 | 38 | void _init() { 39 | // DioUtil.openDebug(); 40 | Options options = DioUtil.getDefOptions(); 41 | options.baseUrl = Constant.server_address; 42 | String cookie = SpUtil.getString(BaseConstant.keyAppToken); 43 | if (ObjectUtil.isNotEmpty(cookie)) { 44 | Map _headers = new Map(); 45 | _headers["Cookie"] = cookie; 46 | options.headers = _headers; 47 | } 48 | HttpConfig config = new HttpConfig(options: options); 49 | DioUtil().setConfig(config); 50 | } 51 | 52 | void init() { 53 | _init(); 54 | _initListener(); 55 | _loadLocale(); 56 | } 57 | 58 | void _initListener() { 59 | final ApplicationBloc bloc = BlocProvider.of(context); 60 | bloc.appEventStream.listen((value) { 61 | _loadLocale(); 62 | }); 63 | } 64 | 65 | void _loadLocale() { 66 | setState(() { 67 | LanguageModel model = 68 | SpUtil.getObj(Constant.keyLanguage, (v) => LanguageModel.fromJson(v)); 69 | if (model != null) { 70 | _locale = new Locale(model.languageCode, model.countryCode); 71 | } else { 72 | _locale = null; 73 | } 74 | 75 | String _colorKey = SpHelper.getThemeColor(); 76 | if (themeColorMap[_colorKey] != null) 77 | _themeColor = themeColorMap[_colorKey]; 78 | }); 79 | } 80 | 81 | @override 82 | void dispose() { 83 | super.dispose(); 84 | } 85 | 86 | @override 87 | Widget build(BuildContext context) { 88 | return new MaterialApp( 89 | routes: { 90 | BaseConstant.routeMain: (ctx) => MainPage(), 91 | }, 92 | home: new SplashPage(), 93 | theme: ThemeData.light().copyWith( 94 | brightness: Brightness.dark, 95 | primaryColor: _themeColor, 96 | accentColor: _themeColor, 97 | indicatorColor: Colors.white, 98 | ), 99 | locale: _locale, 100 | localizationsDelegates: [ 101 | GlobalMaterialLocalizations.delegate, 102 | GlobalWidgetsLocalizations.delegate, 103 | CustomLocalizations.delegate 104 | ], 105 | supportedLocales: CustomLocalizations.supportedLocales, 106 | ); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /lib/models/models.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class LanguageModel { 4 | String titleId; 5 | String languageCode; 6 | String countryCode; 7 | bool isSelected; 8 | 9 | LanguageModel(this.titleId, this.languageCode, this.countryCode, 10 | {this.isSelected: false}); 11 | 12 | LanguageModel.fromJson(Map json) 13 | : titleId = json['titleId'], 14 | languageCode = json['languageCode'], 15 | countryCode = json['countryCode'], 16 | isSelected = json['isSelected']; 17 | 18 | Map toJson() => { 19 | 'titleId': titleId, 20 | 'languageCode': languageCode, 21 | 'countryCode': countryCode, 22 | 'isSelected': isSelected, 23 | }; 24 | 25 | @override 26 | String toString() { 27 | StringBuffer sb = new StringBuffer('{'); 28 | sb.write("\"titleId\":\"$titleId\""); 29 | sb.write(",\"languageCode\":\"$languageCode\""); 30 | sb.write(",\"countryCode\":\"$countryCode\""); 31 | sb.write('}'); 32 | return sb.toString(); 33 | } 34 | } 35 | 36 | class SplashModel { 37 | String title; 38 | String content; 39 | String url; 40 | String imgUrl; 41 | 42 | SplashModel({this.title, this.content, this.url, this.imgUrl}); 43 | 44 | SplashModel.fromJson(Map json) 45 | : title = json['title'], 46 | content = json['content'], 47 | url = json['url'], 48 | imgUrl = json['imgUrl']; 49 | 50 | Map toJson() => { 51 | 'title': title, 52 | 'content': content, 53 | 'url': url, 54 | 'imgUrl': imgUrl, 55 | }; 56 | 57 | @override 58 | String toString() { 59 | StringBuffer sb = new StringBuffer('{'); 60 | sb.write("\"title\":\"$title\""); 61 | sb.write(",\"content\":\"$content\""); 62 | sb.write(",\"url\":\"$url\""); 63 | sb.write(",\"imgUrl\":\"$imgUrl\""); 64 | sb.write('}'); 65 | return sb.toString(); 66 | } 67 | } 68 | 69 | //class VersionModel { 70 | // String title; 71 | // String content; 72 | // String url; 73 | // String version; 74 | // 75 | // VersionModel({this.title, this.content, this.url, this.version}); 76 | // 77 | // VersionModel.fromJson(Map json) 78 | // : title = json['title'], 79 | // content = json['content'], 80 | // url = json['url'], 81 | // version = json['version']; 82 | // 83 | // Map toJson() => { 84 | // 'title': title, 85 | // 'content': content, 86 | // 'url': url, 87 | // 'version': version, 88 | // }; 89 | // 90 | // @override 91 | // String toString() { 92 | // StringBuffer sb = new StringBuffer('{'); 93 | // sb.write("\"title\":\"$title\""); 94 | // sb.write(",\"content\":\"$content\""); 95 | // sb.write(",\"url\":\"$url\""); 96 | // sb.write(",\"version\":\"$version\""); 97 | // sb.write('}'); 98 | // return sb.toString(); 99 | // } 100 | //} 101 | 102 | class ComModel { 103 | String version; 104 | String title; 105 | String content; 106 | String extra; 107 | String url; 108 | String imgUrl; 109 | String author; 110 | String updatedAt; 111 | 112 | int typeId; 113 | String titleId; 114 | 115 | Widget page; 116 | 117 | ComModel( 118 | {this.version, 119 | this.title, 120 | this.content, 121 | this.extra, 122 | this.url, 123 | this.imgUrl, 124 | this.author, 125 | this.updatedAt, 126 | this.typeId, 127 | this.titleId, 128 | this.page}); 129 | 130 | ComModel.fromJson(Map json) 131 | : version = json['version'], 132 | title = json['title'], 133 | content = json['content'], 134 | extra = json['extra'], 135 | url = json['url'], 136 | imgUrl = json['imgUrl'], 137 | author = json['author'], 138 | updatedAt = json['updatedAt']; 139 | 140 | Map toJson() => { 141 | 'version': version, 142 | 'title': title, 143 | 'content': content, 144 | 'extra': extra, 145 | 'url': url, 146 | 'imgUrl': imgUrl, 147 | 'author': author, 148 | 'updatedAt': updatedAt, 149 | }; 150 | 151 | @override 152 | String toString() { 153 | StringBuffer sb = new StringBuffer('{'); 154 | sb.write("\"version\":\"$version\""); 155 | sb.write(",\"title\":\"$title\""); 156 | sb.write(",\"content\":\"$content\""); 157 | sb.write(",\"url\":\"$url\""); 158 | sb.write(",\"imgUrl\":\"$imgUrl\""); 159 | sb.write(",\"author\":\"$author\""); 160 | sb.write(",\"updatedAt\":\"$updatedAt\""); 161 | sb.write('}'); 162 | return sb.toString(); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /lib/res/colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class _Colours { 4 | static const Color app_main = Color(0xFF666666); 5 | 6 | static const Color transparent_80 = Color(0x80000000); // 7 | 8 | static const Color text_dark = Color(0xFF333333); 9 | static const Color text_normal = Color(0xFF666666); 10 | static const Color text_gray = Color(0xFF999999); 11 | 12 | static const Color divider = Color(0xffe5e5e5); 13 | 14 | static const Color gray_33 = Color(0xFF333333); //51 15 | static const Color gray_66 = Color(0xFF666666); //102 16 | static const Color gray_99 = Color(0xFF999999); //153 17 | static const Color common_orange = Color(0XFFFC9153); //252 145 83 18 | static const Color gray_ef = Color(0XFFEFEFEF); //153 19 | 20 | static const Color gray_f0 = Color(0xfff0f0f0); // 21 | static const Color gray_f5 = Color(0xfff5f5f5); // 22 | static const Color gray_cc = Color(0xffcccccc); // 23 | static const Color gray_ce = Color(0xffcecece); // 24 | static const Color green_1 = Color(0xff009688); // 25 | static const Color green_62 = Color(0xff626262); // 26 | static const Color green_e5 = Color(0xffe5e5e5); // 27 | 28 | } 29 | 30 | Map circleAvatarMap = { 31 | 'A': Colors.blueAccent, 32 | 'B': Colors.blue, 33 | 'C': Colors.cyan, 34 | 'D': Colors.deepPurple, 35 | 'E': Colors.deepPurpleAccent, 36 | 'F': Colors.blue, 37 | 'G': Colors.green, 38 | 'H': Colors.lightBlue, 39 | 'I': Colors.indigo, 40 | 'J': Colors.blue, 41 | 'K': Colors.blue, 42 | 'L': Colors.lightGreen, 43 | 'M': Colors.blue, 44 | 'N': Colors.brown, 45 | 'O': Colors.orange, 46 | 'P': Colors.purple, 47 | 'Q': Colors.black, 48 | 'R': Colors.red, 49 | 'S': Colors.blue, 50 | 'T': Colors.teal, 51 | 'U': Colors.purpleAccent, 52 | 'V': Colors.black, 53 | 'W': Colors.brown, 54 | 'X': Colors.blue, 55 | 'Y': Colors.yellow, 56 | 'Z': Colors.grey, 57 | '#': Colors.blue, 58 | }; 59 | 60 | Map themeColorMap = { 61 | 'gray': _Colours.gray_33, 62 | 'blue': Colors.blue, 63 | 'blueAccent': Colors.blueAccent, 64 | 'cyan': Colors.cyan, 65 | 'deepPurple': Colors.deepPurple, 66 | 'deepPurpleAccent': Colors.deepPurpleAccent, 67 | 'deepOrange': Colors.deepOrange, 68 | 'green': Colors.green, 69 | 'indigo': Colors.indigo, 70 | 'indigoAccent': Colors.indigoAccent, 71 | 'orange': Colors.orange, 72 | 'purple': Colors.purple, 73 | 'pink': Colors.pink, 74 | 'red': Colors.red, 75 | 'teal': Colors.teal, 76 | 'black': Colors.black, 77 | }; 78 | -------------------------------------------------------------------------------- /lib/res/dimens.dart: -------------------------------------------------------------------------------- 1 | class Dimens { 2 | static const double font_sp10 = 10; 3 | static const double font_sp12 = 12; 4 | static const double font_sp14 = 14; 5 | static const double font_sp16 = 16; 6 | static const double font_sp18 = 18; 7 | 8 | static const double gap_dp5 = 5; 9 | static const double gap_dp10 = 10; 10 | static const double gap_dp12 = 12; 11 | static const double gap_dp15 = 15; 12 | static const double gap_dp16 = 16; 13 | } 14 | -------------------------------------------------------------------------------- /lib/res/index.dart: -------------------------------------------------------------------------------- 1 | export 'colors.dart'; 2 | //export 'dimens.dart'; 3 | export 'strings.dart'; 4 | //export 'styles.dart'; 5 | -------------------------------------------------------------------------------- /lib/res/styles.dart: -------------------------------------------------------------------------------- 1 | import 'package:base_library/base_library.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | import 'package:flutter_wanandroid/res/index.dart'; 4 | 5 | class TextStyles { 6 | static TextStyle listTitle = TextStyle( 7 | fontSize: Dimens.font_sp16, 8 | color: Colours.text_dark, 9 | fontWeight: FontWeight.bold, 10 | ); 11 | static TextStyle listContent = TextStyle( 12 | fontSize: Dimens.font_sp14, 13 | color: Colours.text_normal, 14 | ); 15 | static TextStyle listExtra = TextStyle( 16 | fontSize: Dimens.font_sp12, 17 | color: Colours.text_gray, 18 | ); 19 | } 20 | 21 | class Decorations { 22 | static Decoration bottom = BoxDecoration( 23 | border: Border(bottom: BorderSide(width: 0.33, color: Colours.divider))); 24 | } 25 | /// 间隔 26 | class Gaps { 27 | /// 水平间隔 28 | static Widget hGap5 = new SizedBox(width: Dimens.gap_dp5); 29 | static Widget hGap10 = new SizedBox(width: Dimens.gap_dp10); 30 | static Widget hGap15 = new SizedBox(width: Dimens.gap_dp15); 31 | 32 | /// 垂直间隔 33 | static Widget vGap5 = new SizedBox(height: Dimens.gap_dp5); 34 | static Widget vGap10 = new SizedBox(height: Dimens.gap_dp10); 35 | static Widget vGap15 = new SizedBox(height: Dimens.gap_dp15); 36 | } 37 | -------------------------------------------------------------------------------- /lib/ui/dialog/simple_dialog.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sky24n/flutter_wanandroid/51061b34a4923bfeaad02f9d1fa53f296391d9e7/lib/ui/dialog/simple_dialog.dart -------------------------------------------------------------------------------- /lib/ui/pages/about_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | import 'package:flutter_wanandroid/ui/pages/author_page.dart'; 4 | import 'package:flutter_wanandroid/ui/pages/other_page.dart'; 5 | 6 | class AboutPage extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | final MainBloc bloc = BlocProvider.of(context); 10 | ComModel github = ComModel( 11 | title: 'GitHub', 12 | url: 'https://github.com/Sky24n/flutter_wanandroid', 13 | extra: 'Go Star'); 14 | ComModel author = ComModel(title: '作者', page: AuthorPage()); 15 | ComModel other = ComModel(title: 'Big Thanks', page: OtherPage()); 16 | 17 | return Scaffold( 18 | appBar: AppBar( 19 | title: Text(IntlUtil.getString(context, Ids.titleAbout)), 20 | centerTitle: true, 21 | ), 22 | body: ListView( 23 | children: [ 24 | Container( 25 | height: 160.0, 26 | alignment: Alignment.center, 27 | child: Column( 28 | mainAxisAlignment: MainAxisAlignment.center, 29 | mainAxisSize: MainAxisSize.min, 30 | children: [ 31 | Card( 32 | color: Theme.of(context).primaryColor, 33 | elevation: 0.0, 34 | shape: RoundedRectangleBorder( 35 | borderRadius: BorderRadius.all(Radius.circular(6.0))), 36 | child: Image.asset( 37 | Utils.getImgPath('ic_launcher_news'), 38 | width: 72.0, 39 | fit: BoxFit.fill, 40 | height: 72.0, 41 | ), 42 | ), 43 | Gaps.vGap5, 44 | Text( 45 | '版本号 ' + AppConfig.version, 46 | style: TextStyle(color: Colours.gray_99, fontSize: 14.0), 47 | ) 48 | ], 49 | ), 50 | decoration: BoxDecoration( 51 | color: Colors.white, 52 | border: Border.all(width: 0.33, color: Colours.divider))), 53 | ComArrowItem(github), 54 | ComArrowItem(author), 55 | StreamBuilder( 56 | stream: bloc.versionStream, 57 | builder: 58 | (BuildContext context, AsyncSnapshot snapshot) { 59 | VersionModel model = snapshot.data; 60 | return Container( 61 | child: Material( 62 | color: Colors.white, 63 | child: ListTile( 64 | onTap: () { 65 | if (model == null) { 66 | bloc.getVersion(); 67 | } else { 68 | if (Utils.getUpdateStatus(model.version) > 0) { 69 | //NavigatorUtil.launchInBrowser(model.url, title: model.title); 70 | showDialog( 71 | context: context, 72 | barrierDismissible: false, 73 | builder: (BuildContext context) => UpgradeDialog( 74 | versionModel: model, 75 | valueChanged: (value) { 76 | // InstallPlugin.installApk( 77 | // value, AppConfig.appId) 78 | // .then((result) { 79 | // LogUtil.e('install apk $result'); 80 | // }).catchError((error) { 81 | // LogUtil.e('install apk error: $error'); 82 | // }); 83 | }, 84 | ), 85 | ); 86 | } 87 | } 88 | }, 89 | title: Text('版本更新'), 90 | //dense: true, 91 | trailing: Row( 92 | mainAxisSize: MainAxisSize.min, 93 | children: [ 94 | Text( 95 | model == null 96 | ? '' 97 | : (Utils.getUpdateStatus(model.version) == 0 98 | ? '已是最新版' 99 | : '有新版本,去更新吧'), 100 | style: TextStyle( 101 | color: (model != null && 102 | Utils.getUpdateStatus(model.version) != 103 | 0) 104 | ? Colors.red 105 | : Colors.grey, 106 | fontSize: 14.0), 107 | ), 108 | Icon( 109 | Icons.navigate_next, 110 | color: Colors.grey, 111 | ), 112 | ], 113 | ), 114 | ), 115 | ), 116 | decoration: Decorations.bottom, 117 | ); 118 | }), 119 | ComArrowItem(other), 120 | ], 121 | ), 122 | ); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /lib/ui/pages/author_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | 4 | class AuthorPage extends StatelessWidget { 5 | @override 6 | Widget build(BuildContext context) { 7 | List list = new List(); 8 | list.add(new ComModel(typeId: 0)); 9 | list.add(new ComModel( 10 | title: "Github", url: "https://github.com/Sky24n", extra: "Go Follow")); 11 | list.add(new ComModel( 12 | title: "简书", 13 | url: "https://www.jianshu.com/u/cbf2ad25d33a", 14 | extra: "+关注")); 15 | list.add(new ComModel( 16 | title: "掘金", 17 | url: "https://juejin.im/user/5b9e8a92e51d453df0440422", 18 | extra: "+关注")); 19 | list.add(new ComModel( 20 | title: "我的Flutter开源库集合", 21 | url: "https://www.jianshu.com/p/9e5cc4ba3a8e")); 22 | return new Scaffold( 23 | appBar: new AppBar( 24 | title: new Text('作者'), 25 | centerTitle: true, 26 | ), 27 | body: new ListView.builder( 28 | itemCount: list.length, 29 | itemBuilder: (BuildContext context, int index) { 30 | ComModel model = list[index]; 31 | if (model.typeId == 0) { 32 | return new Container( 33 | child: new Material( 34 | color: Colors.white, 35 | child: new ListTile( 36 | onTap: () {}, 37 | title: new Text( 38 | '您的Star就是我的动力', 39 | style: new TextStyle(color: Colors.red), 40 | textAlign: TextAlign.center, 41 | ), 42 | //dense: true, 43 | ), 44 | ), 45 | decoration: Decorations.bottom, 46 | ); 47 | } 48 | return new ComArrowItem(model); 49 | }), 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/ui/pages/collection_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | 4 | class CollectionPage extends StatelessWidget { 5 | const CollectionPage({Key key, this.labelId}) : super(key: key); 6 | 7 | final String labelId; 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | LogUtil.e("ReposPage build...... $labelId"); 12 | RefreshController _controller = new RefreshController(); 13 | CollectBloc bloc = BlocProvider.of(context); 14 | MainBloc mainBloc = BlocProvider.of(context); 15 | mainBloc.homeEventStream.listen((event) { 16 | if (labelId == event.labelId) { 17 | _controller.sendBack(false, event.status); 18 | } 19 | }); 20 | bloc.setHomeEventSink(mainBloc.homeEventSink); 21 | return new Scaffold( 22 | appBar: new AppBar( 23 | elevation: 0.0, 24 | title: new Text(IntlUtil.getString(context, labelId)), 25 | centerTitle: true, 26 | ), 27 | body: new StreamBuilder( 28 | stream: bloc.collectListStream, 29 | builder: 30 | (BuildContext context, AsyncSnapshot> snapshot) { 31 | int loadStatus = 32 | Utils.getLoadStatus(snapshot.hasError, snapshot.data); 33 | if (loadStatus == LoadStatus.loading) { 34 | bloc.onRefresh(labelId: labelId); 35 | } 36 | return new RefreshScaffold( 37 | labelId: labelId, 38 | loadStatus: loadStatus, 39 | controller: _controller, 40 | onRefresh: ({bool isReload}) { 41 | return bloc.onRefresh(labelId: labelId, isReload: isReload); 42 | }, 43 | onLoadMore: (up) { 44 | bloc.onLoadMore(labelId: labelId); 45 | }, 46 | itemCount: snapshot.data == null ? 0 : snapshot.data.length, 47 | itemBuilder: (BuildContext context, int index) { 48 | ReposModel model = snapshot.data[index]; 49 | return ObjectUtil.isEmpty(model.envelopePic) 50 | ? new ArticleItem(model, labelId: labelId) 51 | : new ReposItem(model, labelId: labelId); 52 | }, 53 | ); 54 | }), 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/ui/pages/events_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | 4 | bool isEventsInit = true; 5 | 6 | class EventsPage extends StatelessWidget { 7 | const EventsPage({Key key, this.labelId}) : super(key: key); 8 | 9 | final String labelId; 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | LogUtil.e("EventsPage build......"); 14 | RefreshController _controller = new RefreshController(); 15 | final MainBloc bloc = BlocProvider.of(context); 16 | bloc.homeEventStream.listen((event) { 17 | if (labelId == event.labelId) { 18 | _controller.sendBack(false, event.status); 19 | } 20 | }); 21 | 22 | if (isEventsInit) { 23 | LogUtil.e("EventsPage init......"); 24 | isEventsInit = false; 25 | Observable.just(1).delay(new Duration(milliseconds: 500)).listen((_) { 26 | bloc.onRefresh(labelId: labelId); 27 | }); 28 | } 29 | 30 | return new StreamBuilder( 31 | stream: bloc.eventsStream, 32 | builder: 33 | (BuildContext context, AsyncSnapshot> snapshot) { 34 | return new RefreshScaffold( 35 | labelId: labelId, 36 | loadStatus: Utils.getLoadStatus(snapshot.hasError, snapshot.data), 37 | controller: _controller, 38 | onRefresh: ({bool isReload}) { 39 | return bloc.onRefresh(labelId: labelId, isReload: isReload); 40 | }, 41 | onLoadMore: (up) { 42 | bloc.onLoadMore(labelId: labelId); 43 | }, 44 | itemCount: snapshot.data == null ? 0 : snapshot.data.length, 45 | itemBuilder: (BuildContext context, int index) { 46 | ReposModel model = snapshot.data[index]; 47 | return new ArticleItem(model); 48 | }, 49 | ); 50 | }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/ui/pages/language_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | 4 | class LanguagePage extends StatefulWidget { 5 | @override 6 | State createState() { 7 | return _LanguagePageState(); 8 | } 9 | } 10 | 11 | class _LanguagePageState extends State { 12 | List _list = new List(); 13 | 14 | LanguageModel _currentLanguage; 15 | 16 | @override 17 | void initState() { 18 | super.initState(); 19 | 20 | _list.add(LanguageModel(Ids.languageAuto, '', '')); 21 | _list.add(LanguageModel(Ids.languageZH, 'zh', 'CH')); 22 | _list.add(LanguageModel(Ids.languageTW, 'zh', 'TW')); 23 | _list.add(LanguageModel(Ids.languageHK, 'zh', 'HK')); 24 | _list.add(LanguageModel(Ids.languageEN, 'en', 'US')); 25 | 26 | _currentLanguage = 27 | SpUtil.getObj(Constant.keyLanguage, (v) => LanguageModel.fromJson(v)); 28 | ; 29 | if (ObjectUtil.isEmpty(_currentLanguage)) { 30 | _currentLanguage = _list[0]; 31 | } 32 | 33 | _updateData(); 34 | } 35 | 36 | void _updateData() { 37 | LogUtil.e('currentLanguage: ' + _currentLanguage.toString()); 38 | String language = _currentLanguage.countryCode; 39 | for (int i = 0, length = _list.length; i < length; i++) { 40 | _list[i].isSelected = (_list[i].countryCode == language); 41 | } 42 | } 43 | 44 | @override 45 | Widget build(BuildContext context) { 46 | final ApplicationBloc bloc = BlocProvider.of(context); 47 | return new Scaffold( 48 | appBar: new AppBar( 49 | title: new Text( 50 | IntlUtil.getString(context, Ids.titleLanguage), 51 | style: new TextStyle(fontSize: 16.0), 52 | ), 53 | actions: [ 54 | new Padding( 55 | padding: EdgeInsets.all(12.0), 56 | child: new SizedBox( 57 | width: 64.0, 58 | child: new RaisedButton( 59 | textColor: Colors.white, 60 | color: Colors.indigoAccent, 61 | child: Text( 62 | IntlUtil.getString(context, Ids.save), 63 | style: new TextStyle(fontSize: 12.0), 64 | ), 65 | onPressed: () { 66 | SpUtil.putObject( 67 | Constant.keyLanguage, 68 | ObjectUtil.isEmpty(_currentLanguage.languageCode) 69 | ? null 70 | : _currentLanguage); 71 | bloc.sendAppEvent(Constant.type_sys_update); 72 | Navigator.pop(context); 73 | }, 74 | ), 75 | ), 76 | ), 77 | ], 78 | ), 79 | body: new ListView.builder( 80 | itemCount: _list.length, 81 | itemBuilder: (BuildContext context, int index) { 82 | LanguageModel model = _list[index]; 83 | return new ListTile( 84 | title: new Text( 85 | (model.titleId == Ids.languageAuto 86 | ? IntlUtil.getString(context, model.titleId) 87 | : IntlUtil.getString(context, model.titleId, 88 | languageCode: 'zh', countryCode: 'CH')), 89 | style: new TextStyle(fontSize: 13.0), 90 | ), 91 | trailing: new Radio( 92 | value: true, 93 | groupValue: model.isSelected == true, 94 | activeColor: Colors.indigoAccent, 95 | onChanged: (value) { 96 | setState(() { 97 | _currentLanguage = model; 98 | _updateData(); 99 | }); 100 | }), 101 | onTap: () { 102 | setState(() { 103 | _currentLanguage = model; 104 | _updateData(); 105 | }); 106 | }, 107 | ); 108 | }), 109 | ); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /lib/ui/pages/main_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | import 'package:flutter_wanandroid/ui/pages/main_left_page.dart'; 4 | import 'package:flutter_wanandroid/ui/pages/page_index.dart'; 5 | 6 | class _Page { 7 | final String labelId; 8 | 9 | _Page(this.labelId); 10 | } 11 | 12 | final List<_Page> _allPages = <_Page>[ 13 | _Page(Ids.titleHome), 14 | _Page(Ids.titleRepos), 15 | _Page(Ids.titleEvents), 16 | _Page(Ids.titleSystem), 17 | ]; 18 | 19 | class MainPage extends StatelessWidget { 20 | @override 21 | Widget build(BuildContext context) { 22 | LogUtil.e("MainPagess build......"); 23 | return DefaultTabController( 24 | length: _allPages.length, 25 | child: Scaffold( 26 | appBar: AppBar( 27 | leading: Builder(builder: (BuildContext ctx) { 28 | return IconButton( 29 | icon: Container( 30 | decoration: BoxDecoration( 31 | shape: BoxShape.circle, 32 | image: DecorationImage( 33 | image: AssetImage( 34 | Utils.getImgPath('ali_connors'), 35 | ), 36 | ), 37 | ), 38 | ), 39 | onPressed: () { 40 | Scaffold.of(ctx).openDrawer(); 41 | }); 42 | }), 43 | centerTitle: true, 44 | title: TabLayout(), 45 | actions: [ 46 | IconButton( 47 | icon: Icon(Icons.search), 48 | onPressed: () { 49 | NavigatorUtil.pushPage(context, SearchPage(), 50 | pageName: "SearchPage"); 51 | // NavigatorUtil.pushPage(context, TestPage()); 52 | // NavigatorUtil.pushPage(context, DemoApp()); 53 | }) 54 | ], 55 | ), 56 | body: TabBarViewLayout(), 57 | drawer: Drawer( 58 | child: MainLeftPage(), 59 | ), 60 | )); 61 | } 62 | } 63 | 64 | class TabLayout extends StatelessWidget { 65 | @override 66 | Widget build(BuildContext context) { 67 | return TabBar( 68 | isScrollable: true, 69 | labelPadding: EdgeInsets.all(12.0), 70 | indicatorSize: TabBarIndicatorSize.label, 71 | tabs: _allPages 72 | .map((_Page page) => 73 | Tab(text: IntlUtil.getString(context, page.labelId))) 74 | .toList(), 75 | ); 76 | } 77 | } 78 | 79 | class TabBarViewLayout extends StatelessWidget { 80 | Widget buildTabView(BuildContext context, _Page page) { 81 | String labelId = page.labelId; 82 | switch (labelId) { 83 | case Ids.titleHome: 84 | return HomePage(labelId: labelId); 85 | break; 86 | case Ids.titleRepos: 87 | return ReposPage(labelId: labelId); 88 | break; 89 | case Ids.titleEvents: 90 | return EventsPage(labelId: labelId); 91 | break; 92 | case Ids.titleSystem: 93 | return SystemPage(labelId: labelId); 94 | break; 95 | default: 96 | return Container(); 97 | break; 98 | } 99 | } 100 | 101 | @override 102 | Widget build(BuildContext context) { 103 | LogUtil.e("TabBarViewLayout build......."); 104 | return TabBarView( 105 | children: _allPages.map((_Page page) { 106 | return buildTabView(context, page); 107 | }).toList()); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /lib/ui/pages/other_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | 4 | class OtherPage extends StatelessWidget { 5 | @override 6 | Widget build(BuildContext context) { 7 | List list = new List(); 8 | list.add(new ComModel( 9 | title: "WanAndroid Api", url: "http://www.wanandroid.com/blog/show/2")); 10 | list.add(new ComModel( 11 | title: "界面参考Gitme", url: "https://flutterchina.club/app/gm.html")); 12 | list.add(new ComModel( 13 | title: "Github Trending Api", 14 | url: "https://github.com/huchenme/github-trending-api")); 15 | return new Scaffold( 16 | appBar: new AppBar( 17 | title: new Text('其他'), 18 | centerTitle: true, 19 | ), 20 | body: new ListView.builder( 21 | itemCount: list.length, 22 | itemBuilder: (BuildContext context, int index) { 23 | ComModel model = list[index]; 24 | return new ComArrowItem(model); 25 | }), 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/ui/pages/page_index.dart: -------------------------------------------------------------------------------- 1 | //export 'main_page.dart'; 2 | //export 'main_left_page.dart'; 3 | export 'home_page.dart'; 4 | export 'repos_page.dart'; 5 | export 'events_page.dart'; 6 | export 'system_page.dart'; 7 | export 'setting_page.dart'; 8 | export 'search_page.dart'; 9 | export 'language_page.dart'; 10 | export 'collection_page.dart'; 11 | export 'about_page.dart'; 12 | export 'share_page.dart'; 13 | export 'author_page.dart'; 14 | export 'other_page.dart'; 15 | export 'splash_page.dart'; 16 | export 'rec_hot_page.dart'; 17 | 18 | export 'backdrop_demo.dart'; 19 | -------------------------------------------------------------------------------- /lib/ui/pages/rec_hot_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | 4 | class RecHotPage extends StatelessWidget { 5 | const RecHotPage({Key key, this.title, this.titleId}) : super(key: key); 6 | 7 | final String title; 8 | final String titleId; 9 | 10 | Widget _buildImg(String imgUrl) { 11 | if (ObjectUtil.isEmpty(imgUrl)) { 12 | return new Container( 13 | width: 0.0, 14 | ); 15 | } 16 | return new Container( 17 | width: 72, 18 | height: 128, 19 | alignment: Alignment.center, 20 | margin: EdgeInsets.only(left: 10.0), 21 | child: new CachedNetworkImage( 22 | width: 72, 23 | height: 128, 24 | fit: BoxFit.fill, 25 | imageUrl: imgUrl, 26 | placeholder: (context, url) => new ProgressView(), 27 | errorWidget: (context, url, error) => new Icon(Icons.error), 28 | ), 29 | ); 30 | } 31 | 32 | @override 33 | Widget build(BuildContext context) { 34 | RefreshController _controller = new RefreshController(); 35 | final MainBloc bloc = BlocProvider.of(context); 36 | String labelId = title ?? IntlUtil.getString(context, titleId); 37 | bloc.homeEventStream.listen((event) { 38 | if (labelId == event.labelId) { 39 | _controller.sendBack(false, event.status); 40 | } 41 | }); 42 | 43 | Observable.just(1).delay(new Duration(milliseconds: 500)).listen((_) { 44 | bloc.getHotRecList(labelId); 45 | }); 46 | 47 | return new Scaffold( 48 | appBar: new AppBar( 49 | elevation: 0.0, 50 | title: new Text(title ?? IntlUtil.getString(context, titleId)), 51 | centerTitle: true, 52 | ), 53 | body: new StreamBuilder( 54 | stream: bloc.recListStream, 55 | builder: 56 | (BuildContext context, AsyncSnapshot> snapshot) { 57 | return new RefreshScaffold( 58 | labelId: title ?? IntlUtil.getString(context, titleId), 59 | loadStatus: Utils.getLoadStatus(snapshot.hasError, snapshot.data), 60 | controller: _controller, 61 | enablePullUp: false, 62 | onRefresh: ({bool isReload}) { 63 | return bloc.getHotRecList(labelId); 64 | }, 65 | itemCount: snapshot.data == null ? 0 : snapshot.data.length, 66 | itemBuilder: (BuildContext context, int index) { 67 | ComModel model = snapshot.data[index]; 68 | return new InkWell( 69 | onTap: () { 70 | NavigatorUtil.pushWeb(context, 71 | title: model.title, url: model.url, isHome: true); 72 | }, 73 | child: new Container( 74 | alignment: Alignment.topLeft, 75 | padding: EdgeInsets.all(16.0), 76 | child: new Row( 77 | crossAxisAlignment: CrossAxisAlignment.start, 78 | children: [ 79 | new Expanded( 80 | child: new Column( 81 | crossAxisAlignment: CrossAxisAlignment.start, 82 | children: [ 83 | new Text( 84 | model.title, 85 | maxLines: 1, 86 | overflow: TextOverflow.ellipsis, 87 | style: TextStyles.listTitle, 88 | ), 89 | Gaps.vGap10, 90 | new Text( 91 | model.content == null ? "" : model.content, 92 | maxLines: 3, 93 | overflow: TextOverflow.ellipsis, 94 | style: TextStyles.listContent, 95 | ), 96 | Gaps.vGap5, 97 | new Row( 98 | children: [ 99 | new Text( 100 | model.author, 101 | style: TextStyles.listExtra, 102 | ), 103 | Gaps.hGap10, 104 | new Text( 105 | Utils.getTimeLine( 106 | context, 107 | DateUtil.getDateMsByTimeStr( 108 | model.updatedAt)), 109 | style: TextStyles.listExtra, 110 | ), 111 | ], 112 | ) 113 | ], 114 | )), 115 | _buildImg(model.imgUrl) 116 | ], 117 | ), 118 | decoration: new BoxDecoration( 119 | color: Colors.white, 120 | border: new Border.all( 121 | width: 0.33, color: Colours.divider))), 122 | ); 123 | }, 124 | ); 125 | }), 126 | ); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /lib/ui/pages/repos_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | 4 | bool isReposInit = true; 5 | 6 | class ReposPage extends StatelessWidget { 7 | const ReposPage({Key key, this.labelId}) : super(key: key); 8 | 9 | final String labelId; 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | LogUtil.e("ReposPage build......"); 14 | RefreshController _controller = new RefreshController(); 15 | final MainBloc bloc = BlocProvider.of(context); 16 | bloc.homeEventStream.listen((event) { 17 | if (labelId == event.labelId) { 18 | _controller.sendBack(false, event.status); 19 | } 20 | }); 21 | 22 | if (isReposInit) { 23 | isReposInit = false; 24 | Observable.just(1).delay(new Duration(milliseconds: 500)).listen((_) { 25 | bloc.onRefresh(labelId: labelId); 26 | }); 27 | } 28 | 29 | return new StreamBuilder( 30 | stream: bloc.reposStream, 31 | builder: 32 | (BuildContext context, AsyncSnapshot> snapshot) { 33 | return new RefreshScaffold( 34 | labelId: labelId, 35 | loadStatus: Utils.getLoadStatus(snapshot.hasError, snapshot.data), 36 | controller: _controller, 37 | onRefresh: ({bool isReload}) { 38 | return bloc.onRefresh(labelId: labelId, isReload: isReload); 39 | }, 40 | onLoadMore: (up) { 41 | bloc.onLoadMore(labelId: labelId); 42 | }, 43 | itemCount: snapshot.data == null ? 0 : snapshot.data.length, 44 | itemBuilder: (BuildContext context, int index) { 45 | ReposModel model = snapshot.data[index]; 46 | return new ReposItem(model); 47 | }, 48 | ); 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/ui/pages/search_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | 4 | class SearchPage extends StatelessWidget { 5 | @override 6 | Widget build(BuildContext context) { 7 | return Scaffold( 8 | appBar: AppBar(title: Text('搜索')), 9 | body: ProgressView(), 10 | floatingActionButton: FloatingActionButton( 11 | child: const Icon(Icons.search), 12 | onPressed: () {}, 13 | ), 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/ui/pages/setting_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | import 'package:flutter_wanandroid/ui/pages/language_page.dart'; 4 | 5 | class SettingPage extends StatelessWidget { 6 | @override 7 | Widget build(BuildContext context) { 8 | LogUtil.e("SettingPage build......"); 9 | final ApplicationBloc bloc = BlocProvider.of(context); 10 | LanguageModel languageModel = 11 | SpUtil.getObj(Constant.keyLanguage, (v) => LanguageModel.fromJson(v)); 12 | return new Scaffold( 13 | appBar: AppBar( 14 | title: Text( 15 | IntlUtil.getString(context, Ids.titleSetting), 16 | ), 17 | centerTitle: true, 18 | ), 19 | body: ListView( 20 | children: [ 21 | new ExpansionTile( 22 | title: new Row( 23 | children: [ 24 | Icon( 25 | Icons.color_lens, 26 | color: Colours.gray_66, 27 | ), 28 | Padding( 29 | padding: EdgeInsets.only(left: 10.0), 30 | child: Text( 31 | IntlUtil.getString(context, Ids.titleTheme), 32 | ), 33 | ) 34 | ], 35 | ), 36 | children: [ 37 | new Wrap( 38 | children: themeColorMap.keys.map((String key) { 39 | Color value = themeColorMap[key]; 40 | return new InkWell( 41 | onTap: () { 42 | SpUtil.putString(Constant.key_theme_color, key); 43 | bloc.sendAppEvent(Constant.type_sys_update); 44 | }, 45 | child: new Container( 46 | margin: EdgeInsets.all(5.0), 47 | width: 36.0, 48 | height: 36.0, 49 | color: value, 50 | ), 51 | ); 52 | }).toList(), 53 | ) 54 | ], 55 | ), 56 | new ListTile( 57 | title: new Row( 58 | children: [ 59 | Icon( 60 | Icons.language, 61 | color: Colours.gray_66, 62 | ), 63 | Padding( 64 | padding: EdgeInsets.only(left: 10.0), 65 | child: Text( 66 | IntlUtil.getString(context, Ids.titleLanguage), 67 | ), 68 | ) 69 | ], 70 | ), 71 | trailing: Row( 72 | mainAxisSize: MainAxisSize.min, 73 | children: [ 74 | Text( 75 | languageModel == null 76 | ? IntlUtil.getString(context, Ids.languageAuto) 77 | : IntlUtil.getString(context, languageModel.titleId, 78 | languageCode: 'zh', countryCode: 'CH'), 79 | style: TextStyle( 80 | fontSize: 14.0, 81 | color: Colours.gray_99, 82 | )), 83 | Icon(Icons.keyboard_arrow_right) 84 | ], 85 | ), 86 | onTap: () { 87 | NavigatorUtil.pushPage(context, LanguagePage(), 88 | pageName: Ids.titleLanguage); 89 | }, 90 | ) 91 | ], 92 | ), 93 | ); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /lib/ui/pages/share_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | 4 | class SharePage extends StatelessWidget { 5 | @override 6 | Widget build(BuildContext context) { 7 | LogUtil.e("SharePage " + ScreenUtil.getInstance().screenWidth.toString()); 8 | 9 | return new Scaffold( 10 | backgroundColor: Theme.of(context).primaryColor, 11 | appBar: new AppBar( 12 | title: new Text(IntlUtil.getString(context, Ids.titleShare)), 13 | centerTitle: true, 14 | elevation: 0.0, 15 | ), 16 | body: new Container( 17 | alignment: Alignment.center, 18 | child: new Column( 19 | mainAxisSize: MainAxisSize.min, 20 | children: [ 21 | new Text( 22 | '扫描二维码下载', 23 | style: new TextStyle(color: Colors.white, fontSize: 16.0), 24 | ), 25 | Gaps.vGap15, 26 | new Card( 27 | child: new Container( 28 | alignment: Alignment.center, 29 | width: ScreenUtil.getInstance().getWidth(300), 30 | height: ScreenUtil.getInstance().getWidth(300), 31 | child: new Image.asset( 32 | Utils.getImgPath('qrcode'), 33 | width: ScreenUtil.getInstance().getWidth(200), 34 | height: ScreenUtil.getInstance().getWidth(200), 35 | fit: BoxFit.fill, 36 | ), 37 | ), 38 | ) 39 | ], 40 | ), 41 | ), 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/ui/pages/system_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | 4 | bool isSystemInit = true; 5 | 6 | class SystemPage extends StatelessWidget { 7 | const SystemPage({Key key, this.labelId}) : super(key: key); 8 | 9 | final String labelId; 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | LogUtil.e("SystemPage build......"); 14 | RefreshController _controller = new RefreshController(); 15 | final MainBloc bloc = BlocProvider.of(context); 16 | // bloc.homeEventStream.listen((event) { 17 | // if (labelId == event.labelId) { 18 | // _controller.sendBack(false, event.status); 19 | // } 20 | // }); 21 | 22 | if (isSystemInit) { 23 | LogUtil.e("SystemPage init......"); 24 | isSystemInit = false; 25 | Observable.just(1).delay(new Duration(milliseconds: 500)).listen((_) { 26 | bloc.onRefresh(labelId: labelId); 27 | }); 28 | } 29 | 30 | return new StreamBuilder( 31 | stream: bloc.treeStream, 32 | builder: 33 | (BuildContext context, AsyncSnapshot> snapshot) { 34 | return new RefreshScaffold( 35 | labelId: labelId, 36 | loadStatus: Utils.getLoadStatus(snapshot.hasError, snapshot.data), 37 | controller: _controller, 38 | enablePullUp: false, 39 | onRefresh: ({bool isReload}) { 40 | return bloc.onRefresh(labelId: labelId, isReload: isReload); 41 | }, 42 | onLoadMore: (up) {}, 43 | itemCount: snapshot.data == null ? 0 : snapshot.data.length, 44 | itemBuilder: (BuildContext context, int index) { 45 | TreeModel model = snapshot.data[index]; 46 | return new TreeItem(model); 47 | }, 48 | ); 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/ui/pages/tab_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | 4 | class TabPage extends StatefulWidget { 5 | const TabPage( 6 | {Key key, this.labelId, this.title, this.titleId, this.treeModel}) 7 | : super(key: key); 8 | 9 | final String labelId; 10 | final String title; 11 | final String titleId; 12 | final TreeModel treeModel; 13 | 14 | @override 15 | State createState() { 16 | return new TabPageState(); 17 | } 18 | } 19 | 20 | class TabPageState extends State { 21 | List> _children = new List(); 22 | 23 | @override 24 | void initState() { 25 | super.initState(); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | final TabBloc bloc = BlocProvider.of(context); 31 | bloc.bindSystemData(widget.treeModel); 32 | 33 | return new Scaffold( 34 | appBar: new AppBar( 35 | elevation: 0.0, 36 | title: new Text( 37 | widget.title ?? IntlUtil.getString(context, widget.titleId)), 38 | centerTitle: true, 39 | ), 40 | body: new StreamBuilder( 41 | stream: bloc.tabTreeStream, 42 | builder: 43 | (BuildContext context, AsyncSnapshot> snapshot) { 44 | if (snapshot.data == null) { 45 | Observable.just(1) 46 | .delay(new Duration(milliseconds: 500)) 47 | .listen((_) { 48 | bloc.getData(labelId: widget.labelId); 49 | }); 50 | return new ProgressView(); 51 | } 52 | _children = snapshot.data 53 | .map((TreeModel model) { 54 | return new BlocProvider( 55 | child: new ComListPage( 56 | labelId: widget.labelId, 57 | cid: model.id, 58 | ), 59 | bloc: new ComListBloc(), 60 | ); 61 | }) 62 | .cast>() 63 | .toList(); 64 | return new DefaultTabController( 65 | length: snapshot.data == null ? 0 : snapshot.data.length, 66 | child: new Column(children: [ 67 | new Material( 68 | color: Theme.of(context).primaryColor, 69 | //elevation: 4.0, 70 | child: new SizedBox( 71 | height: 48.0, 72 | width: double.infinity, 73 | child: new TabBar( 74 | isScrollable: true, 75 | //labelPadding: EdgeInsets.all(12.0), 76 | indicatorSize: TabBarIndicatorSize.label, 77 | tabs: snapshot.data 78 | ?.map( 79 | (TreeModel model) => new Tab(text: model.name)) 80 | ?.toList(), 81 | ), 82 | ), 83 | ), 84 | new Expanded(child: new TabBarView(children: _children)) 85 | ])); 86 | }), 87 | ); 88 | } 89 | 90 | @override 91 | void dispose() { 92 | for (int i = 0, length = _children.length; i < length; i++) { 93 | _children[i].bloc.dispose(); 94 | } 95 | super.dispose(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /lib/ui/pages/user/user_login_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | import 'package:flutter_wanandroid/data/repository/user_repository.dart'; 4 | import 'package:flutter_wanandroid/ui/pages/user/user_register_page.dart'; 5 | 6 | class UserLoginPage extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return new Scaffold( 10 | resizeToAvoidBottomInset: false, 11 | body: new Stack( 12 | children: [ 13 | new Image.asset( 14 | Util.getImgPath("ic_login_bg"), 15 | package: BaseConstant.packageBase, 16 | width: double.infinity, 17 | height: double.infinity, 18 | fit: BoxFit.cover, 19 | ), 20 | new LoginBody() 21 | ], 22 | ), 23 | ); 24 | } 25 | } 26 | 27 | class LoginBody extends StatelessWidget { 28 | @override 29 | Widget build(BuildContext context) { 30 | TextEditingController _controllerName = new TextEditingController(); 31 | TextEditingController _controllerPwd = new TextEditingController(); 32 | UserRepository userRepository = new UserRepository(); 33 | UserModel userModel = 34 | SpUtil.getObj(BaseConstant.keyUserModel, (v) => UserModel.fromJson(v)); 35 | _controllerName.text = userModel?.username ?? ""; 36 | 37 | void _userLogin() { 38 | String username = _controllerName.text; 39 | String password = _controllerPwd.text; 40 | if (username.isEmpty || username.length < 6) { 41 | Util.showSnackBar(context, username.isEmpty ? "请输入用户名~" : "用户名至少6位~"); 42 | return; 43 | } 44 | if (password.isEmpty || password.length < 6) { 45 | Util.showSnackBar(context, username.isEmpty ? "请输入密码~" : "密码至少6位~"); 46 | return; 47 | } 48 | LoginReq req = new LoginReq(username, password); 49 | userRepository.login(req).then((UserModel model) { 50 | LogUtil.e("LoginResp: ${model.toString()}"); 51 | Util.showSnackBar(context, "登录成功~"); 52 | Observable.just(1).delay(new Duration(milliseconds: 500)).listen((_) { 53 | Event.sendAppEvent(context, Constant.type_refresh_all); 54 | RouteUtil.goMain(context); 55 | }); 56 | }).catchError((error) { 57 | LogUtil.e("LoginResp error: ${error.toString()}"); 58 | Util.showSnackBar(context, error.toString()); 59 | }); 60 | } 61 | 62 | return new Column( 63 | children: [ 64 | new Expanded(child: new Container()), 65 | new Expanded( 66 | child: new Container( 67 | margin: EdgeInsets.only(left: 20, top: 15, right: 20), 68 | child: new Column( 69 | children: [ 70 | LoginItem( 71 | controller: _controllerName, 72 | prefixIcon: Icons.person, 73 | hintText: IntlUtil.getString(context, Ids.user_name), 74 | ), 75 | Gaps.vGap15, 76 | LoginItem( 77 | controller: _controllerPwd, 78 | prefixIcon: Icons.lock, 79 | hasSuffixIcon: true, 80 | hintText: IntlUtil.getString(context, Ids.user_pwd), 81 | ), 82 | new Container( 83 | padding: EdgeInsets.only(top: Dimens.gap_dp15), 84 | alignment: Alignment.centerRight, 85 | child: new InkWell( 86 | child: new Text( 87 | IntlUtil.getString(context, Ids.user_forget_pwd), 88 | style: new TextStyle( 89 | color: Colours.gray_99, fontSize: Dimens.font_sp14), 90 | ), 91 | onTap: () { 92 | Util.showSnackBar(context, "请联系管理员~"); 93 | }, 94 | ), 95 | ), 96 | new RoundButton( 97 | text: IntlUtil.getString(context, Ids.user_login), 98 | margin: EdgeInsets.only(top: 20), 99 | onPressed: () { 100 | _userLogin(); 101 | }, 102 | ), 103 | Gaps.vGap15, 104 | new InkWell( 105 | onTap: () { 106 | NavigatorUtil.pushPage(context, new UserRegisterPage()); 107 | }, 108 | child: new RichText( 109 | text: new TextSpan(children: [ 110 | new TextSpan( 111 | style: 112 | new TextStyle(fontSize: 14, color: Colours.text_gray), 113 | text: 114 | IntlUtil.getString(context, Ids.user_new_user_hint)), 115 | new TextSpan( 116 | style: new TextStyle( 117 | fontSize: 14, color: Theme.of(context).primaryColor), 118 | text: IntlUtil.getString(context, Ids.user_register)) 119 | ])), 120 | ), 121 | ], 122 | ), 123 | )), 124 | ], 125 | ); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /lib/ui/pages/user/user_register_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | import 'package:flutter_wanandroid/data/repository/user_repository.dart'; 4 | 5 | class UserRegisterPage extends StatelessWidget { 6 | @override 7 | Widget build(BuildContext context) { 8 | return new Scaffold( 9 | resizeToAvoidBottomInset: false, 10 | body: new Stack( 11 | children: [ 12 | new Image.asset( 13 | Util.getImgPath("ic_login_bg"), 14 | package: BaseConstant.packageBase, 15 | width: double.infinity, 16 | height: double.infinity, 17 | fit: BoxFit.cover, 18 | ), 19 | new UserRegisterBody() 20 | ], 21 | ), 22 | ); 23 | } 24 | } 25 | 26 | class UserRegisterBody extends StatelessWidget { 27 | @override 28 | Widget build(BuildContext context) { 29 | TextEditingController _controllerName = new TextEditingController(); 30 | TextEditingController _controllerPwd = new TextEditingController(); 31 | TextEditingController _controllerRePwd = new TextEditingController(); 32 | UserRepository userRepository = new UserRepository(); 33 | 34 | void _userRegister() { 35 | String username = _controllerName.text; 36 | String password = _controllerPwd.text; 37 | String passwordRe = _controllerRePwd.text; 38 | if (username.isEmpty || username.length < 6) { 39 | Util.showSnackBar(context, username.isEmpty ? "请输入用户名~" : "用户名至少6位~"); 40 | return; 41 | } 42 | if (password.isEmpty || password.length < 6) { 43 | Util.showSnackBar(context, password.isEmpty ? "请输入密码~" : "密码至少6位~"); 44 | return; 45 | } 46 | if (passwordRe.isEmpty || passwordRe.length < 6) { 47 | Util.showSnackBar(context, passwordRe.isEmpty ? "请确认输入密码~" : "密码至少6位~"); 48 | return; 49 | } 50 | if (password != passwordRe) { 51 | Util.showSnackBar(context, "密码不一致~"); 52 | return; 53 | } 54 | 55 | RegisterReq req = new RegisterReq(username, password, passwordRe); 56 | userRepository.register(req).then((UserModel model) { 57 | LogUtil.e("LoginResp: ${model.toString()}"); 58 | Util.showSnackBar(context, "注册成功~"); 59 | Observable.just(1).delay(new Duration(milliseconds: 500)).listen((_) { 60 | Event.sendAppEvent(context, Constant.type_refresh_all); 61 | RouteUtil.goMain(context); 62 | }); 63 | }).catchError((error) { 64 | LogUtil.e("LoginResp error: ${error.toString()}"); 65 | Util.showSnackBar(context, error.toString()); 66 | }); 67 | } 68 | 69 | return new Column( 70 | children: [ 71 | new Expanded(child: new Container()), 72 | new Expanded( 73 | child: new Container( 74 | margin: EdgeInsets.only(left: 20, top: 15, right: 20), 75 | child: new Column( 76 | children: [ 77 | LoginItem( 78 | controller: _controllerName, 79 | prefixIcon: Icons.person, 80 | hintText: IntlUtil.getString(context, Ids.user_name), 81 | ), 82 | Gaps.vGap10, 83 | LoginItem( 84 | controller: _controllerPwd, 85 | prefixIcon: Icons.lock, 86 | hintText: IntlUtil.getString(context, Ids.user_pwd), 87 | ), 88 | Gaps.vGap10, 89 | LoginItem( 90 | controller: _controllerRePwd, 91 | prefixIcon: Icons.lock, 92 | hintText: IntlUtil.getString(context, Ids.user_re_pwd), 93 | ), 94 | new RoundButton( 95 | text: IntlUtil.getString(context, Ids.user_register), 96 | margin: EdgeInsets.only(top: 20), 97 | onPressed: () { 98 | _userRegister(); 99 | }, 100 | ), 101 | ], 102 | ), 103 | )), 104 | ], 105 | ); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib/ui/widgets/article_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | 4 | class ArticleItem extends StatelessWidget { 5 | const ArticleItem( 6 | this.model, { 7 | Key key, 8 | this.labelId, 9 | this.isHome, 10 | }) : super(key: key); 11 | final ReposModel model; 12 | final String labelId; 13 | final bool isHome; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return new InkWell( 18 | onTap: () { 19 | //LogUtil.e("ReposModel: " + model.toString()); 20 | NavigatorUtil.pushWeb(context, 21 | title: model.title, url: model.link, isHome: isHome); 22 | }, 23 | child: new Container( 24 | padding: EdgeInsets.only(left: 16, top: 16, right: 16, bottom: 10), 25 | child: new Row( 26 | children: [ 27 | new Expanded( 28 | child: new Column( 29 | crossAxisAlignment: CrossAxisAlignment.start, 30 | children: [ 31 | new Text( 32 | model.title, 33 | maxLines: 1, 34 | overflow: TextOverflow.ellipsis, 35 | style: TextStyles.listTitle, 36 | ), 37 | Gaps.vGap10, 38 | new Text( 39 | model.desc, 40 | maxLines: 3, 41 | overflow: TextOverflow.ellipsis, 42 | style: TextStyles.listContent, 43 | ), 44 | Gaps.vGap5, 45 | new Row( 46 | children: [ 47 | new LikeBtn( 48 | labelId: labelId, 49 | id: model.originId ?? model.id, 50 | isLike: model.collect, 51 | ), 52 | Gaps.hGap10, 53 | new Text( 54 | model.author, 55 | style: TextStyles.listExtra, 56 | ), 57 | Gaps.hGap10, 58 | new Text( 59 | Utils.getTimeLine(context, model.publishTime), 60 | style: TextStyles.listExtra, 61 | ), 62 | ], 63 | ) 64 | ], 65 | )), 66 | new Container( 67 | alignment: Alignment.center, 68 | margin: EdgeInsets.only(left: 12.0), 69 | child: new CircleAvatar( 70 | radius: 28.0, 71 | backgroundColor: 72 | Utils.getCircleBg(model.superChapterName ?? "公众号"), 73 | child: new Padding( 74 | padding: EdgeInsets.all(5.0), 75 | child: new Text( 76 | model.superChapterName ?? "文章", 77 | textAlign: TextAlign.center, 78 | style: TextStyle(color: Colors.white, fontSize: 11.0), 79 | ), 80 | ), 81 | ), 82 | ) 83 | ], 84 | ), 85 | decoration: new BoxDecoration( 86 | color: Colors.white, 87 | border: new Border.all(width: 0.33, color: Colours.divider))), 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /lib/ui/widgets/com_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | 4 | class ComArrowItem extends StatelessWidget { 5 | const ComArrowItem(this.model, {Key key}) : super(key: key); 6 | final ComModel model; 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return new Container( 11 | child: new Material( 12 | color: Colors.white, 13 | child: new ListTile( 14 | onTap: () { 15 | if (model.page == null) { 16 | NavigatorUtil.pushWeb(context, 17 | title: model.title, url: model.url, isHome: true); 18 | } else { 19 | NavigatorUtil.pushPage(context, model.page, 20 | pageName: model.title); 21 | } 22 | }, 23 | title: new Text(model.title == null ? "" : model.title), 24 | trailing: new Row( 25 | mainAxisSize: MainAxisSize.min, 26 | children: [ 27 | new Text( 28 | model.extra == null ? "" : model.extra, 29 | style: TextStyle(color: Colors.grey, fontSize: 14.0), 30 | ), 31 | new Icon( 32 | Icons.navigate_next, 33 | color: Colors.grey, 34 | ), 35 | ], 36 | ), 37 | ), 38 | ), 39 | decoration: Decorations.bottom, 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/ui/widgets/com_list_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | 4 | class ComListPage extends StatelessWidget { 5 | const ComListPage({Key key, this.labelId, this.cid}) : super(key: key); 6 | final String labelId; 7 | final int cid; 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | LogUtil.e("ComListPage build......"); 12 | RefreshController _controller = new RefreshController(); 13 | final ComListBloc bloc = BlocProvider.of(context); 14 | bloc.comListEventStream.listen((event) { 15 | if (cid == event.cid) { 16 | _controller.sendBack(false, event.status); 17 | } 18 | }); 19 | 20 | return new StreamBuilder( 21 | stream: bloc.comListStream, 22 | builder: 23 | (BuildContext context, AsyncSnapshot> snapshot) { 24 | int loadStatus = 25 | Utils.getLoadStatus(snapshot.hasError, snapshot.data); 26 | if (loadStatus == LoadStatus.loading) { 27 | bloc.onRefresh(labelId: labelId, cid: cid); 28 | } 29 | return new RefreshScaffold( 30 | labelId: cid.toString(), 31 | loadStatus: loadStatus, 32 | controller: _controller, 33 | onRefresh: ({bool isReload}) { 34 | return bloc.onRefresh(labelId: labelId, cid: cid); 35 | }, 36 | onLoadMore: (up) { 37 | bloc.onLoadMore(labelId: labelId, cid: cid); 38 | }, 39 | itemCount: snapshot.data == null ? 0 : snapshot.data.length, 40 | itemBuilder: (BuildContext context, int index) { 41 | ReposModel model = snapshot.data[index]; 42 | return labelId == Ids.titleReposTree 43 | ? new ReposItem(model) 44 | : new ArticleItem(model); 45 | }, 46 | ); 47 | }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/ui/widgets/com_list_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/ui/widgets/widgets.dart'; 3 | 4 | class ComListView extends StatelessWidget { 5 | const ComListView( 6 | {Key key, 7 | this.isLoading: false, 8 | this.child, 9 | this.itemCount, 10 | this.itemBuilder}) 11 | : super(key: key); 12 | 13 | final bool isLoading; 14 | final Widget child; 15 | final int itemCount; 16 | final IndexedWidgetBuilder itemBuilder; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return isLoading 21 | ? new ListView( 22 | children: [ProgressView()], 23 | ) 24 | : child ?? 25 | ListView.builder(itemCount: itemCount, itemBuilder: itemBuilder); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/ui/widgets/header_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | 4 | class HeaderItem extends StatelessWidget { 5 | const HeaderItem( 6 | {this.margin, 7 | this.titleColor, 8 | this.leftIcon, 9 | this.titleId: Ids.titleRepos, 10 | this.title, 11 | this.extraId: Ids.more, 12 | this.extra, 13 | this.rightIcon, 14 | this.onTap, 15 | Key key}) 16 | : super(key: key); 17 | 18 | final EdgeInsetsGeometry margin; 19 | final Color titleColor; 20 | final IconData leftIcon; 21 | final String titleId; 22 | final String title; 23 | final String extraId; 24 | final String extra; 25 | final IconData rightIcon; 26 | final GestureTapCallback onTap; 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return new Container( 31 | height: 56.0, 32 | margin: margin ?? EdgeInsets.only(top: 0.0), 33 | child: new ListTile( 34 | onTap: onTap, 35 | title: new Row( 36 | mainAxisSize: MainAxisSize.min, 37 | children: [ 38 | new Icon( 39 | leftIcon ?? Icons.whatshot, 40 | color: titleColor ?? Colors.blueAccent, 41 | ), 42 | Gaps.hGap10, 43 | new Expanded( 44 | child: new Text( 45 | title ?? IntlUtil.getString(context, titleId), 46 | overflow: TextOverflow.ellipsis, 47 | style: TextStyle( 48 | color: titleColor ?? Colors.blueAccent, 49 | fontSize: Utils.getTitleFontSize( 50 | title ?? IntlUtil.getString(context, titleId))), 51 | )) 52 | ], 53 | ), 54 | trailing: new Row( 55 | mainAxisSize: MainAxisSize.min, 56 | children: [ 57 | new Text( 58 | extra ?? IntlUtil.getString(context, extraId), 59 | style: TextStyle(color: Colors.grey, fontSize: 14), 60 | ), 61 | new Icon( 62 | rightIcon ?? Icons.keyboard_arrow_right, 63 | color: Colors.grey, 64 | ), 65 | ], 66 | )), 67 | decoration: new BoxDecoration( 68 | //new Border.all(width: 0.33, color: Colours.divider) 69 | border: new Border( 70 | bottom: new BorderSide(width: 0.33, color: Colours.divider))), 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lib/ui/widgets/likebtn/circle_painter.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/ui/widgets/likebtn/like_button_util.dart'; 3 | 4 | class CirclePainter extends CustomPainter { 5 | Paint circlePaint = new Paint(); 6 | Paint maskPaint = new Paint(); 7 | 8 | final double outerCircleRadiusProgress; 9 | final double innerCircleRadiusProgress; 10 | final Color startColor; 11 | final Color endColor; 12 | 13 | CirclePainter({ 14 | @required this.outerCircleRadiusProgress, 15 | @required this.innerCircleRadiusProgress, 16 | this.startColor = const Color(0xFFFF5722), 17 | this.endColor = const Color(0xFFFFC107), 18 | }) { 19 | circlePaint..style = PaintingStyle.fill; 20 | maskPaint..blendMode = BlendMode.clear; 21 | } 22 | 23 | @override 24 | void paint(Canvas canvas, Size size) { 25 | double center = size.width * 0.5; 26 | _updateCircleColor(); 27 | canvas.saveLayer(Offset.zero & size, Paint()); 28 | canvas.drawCircle(Offset(center, center), 29 | outerCircleRadiusProgress * center, circlePaint); 30 | canvas.drawCircle(Offset(center, center), 31 | innerCircleRadiusProgress * center + 1, maskPaint); 32 | canvas.restore(); 33 | } 34 | 35 | void _updateCircleColor() { 36 | double colorProgress = clamp(outerCircleRadiusProgress, 0.5, 1.0); 37 | colorProgress = mapValueFromRangeToRange(colorProgress, 0.5, 1.0, 0.0, 1.0); 38 | circlePaint..color = Color.lerp(startColor, endColor, colorProgress); 39 | } 40 | 41 | @override 42 | bool shouldRepaint(CustomPainter oldDelegate) { 43 | return true; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/ui/widgets/likebtn/like_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/ui/widgets/likebtn/model.dart'; 3 | import 'dot_painter.dart'; 4 | import 'circle_painter.dart'; 5 | 6 | typedef LikeCallback = void Function(bool isLike); 7 | 8 | class LikeButton extends StatefulWidget { 9 | final double width; 10 | final LikeIcon icon; 11 | final Duration duration; 12 | final DotColor dotColor; 13 | final Color circleStartColor; 14 | final Color circleEndColor; 15 | final LikeCallback onIconClicked; 16 | 17 | const LikeButton({ 18 | Key key, 19 | @required this.width, 20 | this.icon = const LikeIcon( 21 | Icons.favorite, 22 | iconColor: Colors.pinkAccent, 23 | ), 24 | this.duration = const Duration(milliseconds: 5000), 25 | this.dotColor = const DotColor( 26 | dotPrimaryColor: const Color(0xFFFFC107), 27 | dotSecondaryColor: const Color(0xFFFF9800), 28 | dotThirdColor: const Color(0xFFFF5722), 29 | dotLastColor: const Color(0xFFF44336), 30 | ), 31 | this.circleStartColor = const Color(0xFFFF5722), 32 | this.circleEndColor = const Color(0xFFFFC107), 33 | this.onIconClicked, 34 | }) : super(key: key); 35 | 36 | @override 37 | State createState() => _LikeButtonState(); 38 | } 39 | 40 | class _LikeButtonState extends State with TickerProviderStateMixin { 41 | AnimationController _controller; 42 | Animation outerCircle; 43 | Animation innerCircle; 44 | Animation scale; 45 | Animation dots; 46 | 47 | bool isLiked = false; 48 | 49 | @override 50 | void initState() { 51 | super.initState(); 52 | _controller = 53 | new AnimationController(duration: widget.duration, vsync: this) 54 | ..addListener(() { 55 | setState(() {}); 56 | }); 57 | _initAllAmimations(); 58 | } 59 | 60 | @override 61 | Widget build(BuildContext context) { 62 | return Stack( 63 | alignment: Alignment.center, 64 | children: [ 65 | CustomPaint( 66 | size: Size(widget.width, widget.width), 67 | painter: DotPainter( 68 | currentProgress: dots.value, 69 | color1: widget.dotColor.dotPrimaryColor, 70 | color2: widget.dotColor.dotSecondaryColor, 71 | color3: widget.dotColor.dotThirdColorReal, 72 | color4: widget.dotColor.dotLastColorReal, 73 | ), 74 | ), 75 | CustomPaint( 76 | size: Size(widget.width * 0.35, widget.width * 0.35), 77 | painter: CirclePainter( 78 | innerCircleRadiusProgress: innerCircle.value, 79 | outerCircleRadiusProgress: outerCircle.value, 80 | startColor: widget.circleStartColor, 81 | endColor: widget.circleEndColor), 82 | ), 83 | Container( 84 | width: widget.width, 85 | height: widget.width, 86 | alignment: Alignment.center, 87 | child: Transform.scale( 88 | scale: isLiked ? scale.value : 1.0, 89 | child: GestureDetector( 90 | child: Icon( 91 | widget.icon.icon, 92 | color: isLiked ? widget.icon.color : Colors.grey, 93 | size: widget.width * 0.4, 94 | ), 95 | onTap: _onTap, 96 | ), 97 | ), 98 | ), 99 | ], 100 | ); 101 | } 102 | 103 | void _onTap() { 104 | if (_controller.isAnimating) return; 105 | isLiked = !isLiked; 106 | if (isLiked) { 107 | _controller.reset(); 108 | _controller.forward(); 109 | } else { 110 | setState(() {}); 111 | } 112 | if (widget.onIconClicked != null) widget.onIconClicked(isLiked); 113 | } 114 | 115 | void _initAllAmimations() { 116 | outerCircle = new Tween( 117 | begin: 0.1, 118 | end: 1.0, 119 | ).animate( 120 | new CurvedAnimation( 121 | parent: _controller, 122 | curve: new Interval( 123 | 0.0, 124 | 0.3, 125 | curve: Curves.ease, 126 | ), 127 | ), 128 | ); 129 | innerCircle = new Tween( 130 | begin: 0.2, 131 | end: 1.0, 132 | ).animate( 133 | new CurvedAnimation( 134 | parent: _controller, 135 | curve: new Interval( 136 | 0.2, 137 | 0.5, 138 | curve: Curves.ease, 139 | ), 140 | ), 141 | ); 142 | scale = new Tween( 143 | begin: 0.2, 144 | end: 1.0, 145 | ).animate( 146 | new CurvedAnimation( 147 | parent: _controller, 148 | curve: new Interval( 149 | 0.35, 150 | 0.7, 151 | curve: OvershootCurve(), 152 | ), 153 | ), 154 | ); 155 | dots = new Tween( 156 | begin: 0.0, 157 | end: 1.0, 158 | ).animate( 159 | new CurvedAnimation( 160 | parent: _controller, 161 | curve: new Interval( 162 | 0.1, 163 | 1.0, 164 | curve: Curves.decelerate, 165 | ), 166 | ), 167 | ); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /lib/ui/widgets/likebtn/like_button_util.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | 3 | num degToRad(num deg) => deg * (math.pi / 180.0); 4 | 5 | num radToDeg(num rad) => rad * (180.0 / math.pi); 6 | 7 | double mapValueFromRangeToRange(double value, double fromLow, double fromHigh, double toLow, double toHigh) { 8 | return toLow + ((value - fromLow) / (fromHigh - fromLow) * (toHigh - toLow)); 9 | } 10 | 11 | double clamp(double value, double low, double high) { 12 | return math.min(math.max(value, low), high); 13 | } -------------------------------------------------------------------------------- /lib/ui/widgets/likebtn/model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class DotColor { 4 | final Color dotPrimaryColor; 5 | final Color dotSecondaryColor; 6 | final Color dotThirdColor; 7 | final Color dotLastColor; 8 | 9 | const DotColor({ 10 | @required this.dotPrimaryColor, 11 | @required this.dotSecondaryColor, 12 | this.dotThirdColor, 13 | this.dotLastColor, 14 | }); 15 | 16 | Color get dotThirdColorReal => 17 | dotThirdColor == null ? dotPrimaryColor : dotThirdColor; 18 | 19 | Color get dotLastColorReal => 20 | dotLastColor == null ? dotSecondaryColor : dotLastColor; 21 | } 22 | 23 | class LikeIcon extends Icon { 24 | final Color iconColor; 25 | 26 | const LikeIcon( 27 | IconData icon, { 28 | this.iconColor, 29 | }) : super(icon); 30 | 31 | @override 32 | Color get color => this.iconColor; 33 | } 34 | 35 | class OvershootCurve extends Curve { 36 | const OvershootCurve([this.period = 2.5]); 37 | 38 | final double period; 39 | 40 | @override 41 | double transform(double t) { 42 | assert(t >= 0.0 && t <= 1.0); 43 | t -= 1.0; 44 | return t * t * ((period + 1) * t + period) + 1.0; 45 | } 46 | 47 | @override 48 | String toString() { 49 | return '$runtimeType($period)'; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/ui/widgets/refresh_scaffold.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | 4 | typedef void OnLoadMore(bool up); 5 | typedef OnRefreshCallback = Future Function({bool isReload}); 6 | 7 | class RefreshScaffold extends StatefulWidget { 8 | const RefreshScaffold( 9 | {Key key, 10 | this.labelId, 11 | this.loadStatus, 12 | @required this.controller, 13 | this.enablePullUp: true, 14 | this.onRefresh, 15 | this.onLoadMore, 16 | this.child, 17 | this.itemCount, 18 | this.itemBuilder}) 19 | : super(key: key); 20 | 21 | final String labelId; 22 | final int loadStatus; 23 | final RefreshController controller; 24 | final bool enablePullUp; 25 | final OnRefreshCallback onRefresh; 26 | final OnLoadMore onLoadMore; 27 | final Widget child; 28 | final int itemCount; 29 | final IndexedWidgetBuilder itemBuilder; 30 | 31 | @override 32 | State createState() { 33 | return new RefreshScaffoldState(); 34 | } 35 | } 36 | 37 | /// with AutomaticKeepAliveClientMixin 38 | class RefreshScaffoldState extends State 39 | with AutomaticKeepAliveClientMixin { 40 | bool isShowFloatBtn = false; 41 | 42 | @override 43 | void initState() { 44 | super.initState(); 45 | // LogUtil.e("RefreshScaffold initState......" + widget.labelId); 46 | WidgetsBinding.instance.addPostFrameCallback((_) { 47 | widget.controller.scrollController.addListener(() { 48 | int offset = widget.controller.scrollController.offset.toInt(); 49 | if (offset < 480 && isShowFloatBtn) { 50 | isShowFloatBtn = false; 51 | setState(() {}); 52 | } else if (offset > 480 && !isShowFloatBtn) { 53 | isShowFloatBtn = true; 54 | setState(() {}); 55 | } 56 | }); 57 | }); 58 | } 59 | 60 | Widget buildFloatingActionButton() { 61 | if (widget.controller.scrollController == null || 62 | widget.controller.scrollController.offset < 480) { 63 | return null; 64 | } 65 | 66 | return new FloatingActionButton( 67 | heroTag: widget.labelId, 68 | backgroundColor: Theme.of(context).primaryColor, 69 | child: Icon( 70 | Icons.keyboard_arrow_up, 71 | ), 72 | onPressed: () { 73 | //_controller.scrollTo(0.0); 74 | widget.controller.scrollController.animateTo(0.0, 75 | duration: new Duration(milliseconds: 300), curve: Curves.linear); 76 | }); 77 | } 78 | 79 | @override 80 | Widget build(BuildContext context) { 81 | // LogUtil.e("RefreshScaffold build...... " + widget.labelId); 82 | super.build(context); 83 | return new Scaffold( 84 | body: new Stack( 85 | children: [ 86 | new RefreshIndicator( 87 | child: new SmartRefresher( 88 | controller: widget.controller, 89 | enablePullDown: false, 90 | enablePullUp: widget.enablePullUp, 91 | enableOverScroll: false, 92 | onRefresh: widget.onLoadMore, 93 | child: widget.child ?? 94 | new ListView.builder( 95 | itemCount: widget.itemCount, 96 | itemBuilder: widget.itemBuilder, 97 | )), 98 | onRefresh: widget.onRefresh), 99 | new StatusViews( 100 | widget.loadStatus, 101 | onTap: () { 102 | LogUtil.e("ProgressViews onRefresh......"); 103 | widget.onRefresh(isReload: true); 104 | }, 105 | ), 106 | ], 107 | ), 108 | floatingActionButton: buildFloatingActionButton()); 109 | } 110 | 111 | @override 112 | bool get wantKeepAlive => true; 113 | } 114 | -------------------------------------------------------------------------------- /lib/ui/widgets/repos_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | 4 | class ReposItem extends StatelessWidget { 5 | const ReposItem( 6 | this.model, { 7 | this.labelId, 8 | Key key, 9 | this.isHome, 10 | }) : super(key: key); 11 | final String labelId; 12 | final ReposModel model; 13 | final bool isHome; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return new InkWell( 18 | onTap: () { 19 | NavigatorUtil.pushWeb(context, 20 | title: model.title, url: model.link, isHome: isHome); 21 | }, 22 | child: new Container( 23 | height: 160.0, 24 | padding: EdgeInsets.only(left: 16, top: 16, right: 16, bottom: 10), 25 | child: new Row( 26 | children: [ 27 | new Expanded( 28 | child: new Column( 29 | crossAxisAlignment: CrossAxisAlignment.start, 30 | children: [ 31 | new Text( 32 | model.title, 33 | maxLines: 1, 34 | overflow: TextOverflow.ellipsis, 35 | style: TextStyles.listTitle, 36 | ), 37 | Gaps.vGap10, 38 | new Expanded( 39 | flex: 1, 40 | child: new Text( 41 | model.desc, 42 | maxLines: 3, 43 | softWrap: true, 44 | overflow: TextOverflow.ellipsis, 45 | style: TextStyles.listContent, 46 | ), 47 | ), 48 | Gaps.vGap5, 49 | new Row( 50 | children: [ 51 | new LikeBtn( 52 | labelId: labelId, 53 | id: model.originId ?? model.id, 54 | isLike: model.collect, 55 | ), 56 | Gaps.hGap10, 57 | new Text( 58 | model.author, 59 | style: TextStyles.listExtra, 60 | ), 61 | Gaps.hGap10, 62 | new Text( 63 | Utils.getTimeLine(context, model.publishTime), 64 | style: TextStyles.listExtra, 65 | ), 66 | ], 67 | ) 68 | ], 69 | )), 70 | new Container( 71 | width: 72, 72 | alignment: Alignment.center, 73 | margin: EdgeInsets.only(left: 10.0), 74 | child: new CachedNetworkImage( 75 | width: 72, 76 | height: 128, 77 | fit: BoxFit.fill, 78 | imageUrl: model.envelopePic, 79 | placeholder: (BuildContext context, String url) { 80 | return new ProgressView(); 81 | }, 82 | errorWidget: 83 | (BuildContext context, String url, Object error) { 84 | return new Icon(Icons.error); 85 | }, 86 | ), 87 | ) 88 | ], 89 | ), 90 | decoration: new BoxDecoration( 91 | color: Colors.white, 92 | border: new Border( 93 | bottom: 94 | new BorderSide(width: 0.33, color: Colours.divider)))), 95 | ); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /lib/ui/widgets/tree_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | 4 | class TreeItem extends StatelessWidget { 5 | const TreeItem(this.model, {Key key}) : super(key: key); 6 | 7 | final TreeModel model; 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | final List chips = model.children.map((TreeModel _model) { 12 | return Chip( 13 | materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, 14 | key: ValueKey(_model.name), 15 | backgroundColor: Utils.getChipBgColor(_model.name), 16 | label: Text( 17 | _model.name, 18 | style: new TextStyle(fontSize: 14.0), 19 | ), 20 | ); 21 | }).toList(); 22 | 23 | return new InkWell( 24 | onTap: () { 25 | //LogUtil.e("ReposModel: " + model.toString()); 26 | NavigatorUtil.pushTabPage(context, 27 | labelId: Ids.titleSystemTree, title: model.name, treeModel: model); 28 | }, 29 | child: new _ChipsTile( 30 | label: model.name, 31 | children: chips, 32 | ), 33 | ); 34 | } 35 | } 36 | 37 | class _ChipsTile extends StatelessWidget { 38 | const _ChipsTile({ 39 | Key key, 40 | this.label, 41 | this.children, 42 | }) : super(key: key); 43 | 44 | final String label; 45 | final List children; 46 | 47 | // Wraps a list of chips into a ListTile for display as a section in the demo. 48 | @override 49 | Widget build(BuildContext context) { 50 | final List cardChildren = [ 51 | new Text( 52 | label, 53 | style: TextStyles.listTitle, 54 | ), 55 | Gaps.vGap10 56 | ]; 57 | cardChildren.add(Wrap( 58 | children: children.map((Widget chip) { 59 | return Padding( 60 | padding: const EdgeInsets.all(3.0), 61 | child: chip, 62 | ); 63 | }).toList())); 64 | 65 | return new Container( 66 | padding: EdgeInsets.all(16.0), 67 | child: new Column( 68 | crossAxisAlignment: CrossAxisAlignment.start, 69 | mainAxisSize: MainAxisSize.min, 70 | children: cardChildren, 71 | ), 72 | decoration: new BoxDecoration( 73 | color: Colors.white, 74 | border: new Border( 75 | bottom: new BorderSide(width: 0.33, color: Colours.divider))), 76 | ); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /lib/ui/widgets/widget_index.dart: -------------------------------------------------------------------------------- 1 | export 'article_item.dart'; 2 | export 'com_list_page.dart'; 3 | export 'com_list_view.dart'; 4 | export 'header_item.dart'; 5 | export 'refresh_scaffold.dart'; 6 | export 'repos_item.dart'; 7 | export 'tree_item.dart'; 8 | export 'web_scaffold.dart'; 9 | export 'widgets.dart'; 10 | export 'com_item.dart'; 11 | export 'likebtn/like_button.dart'; 12 | -------------------------------------------------------------------------------- /lib/ui/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | import 'package:base_library/base_library.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_wanandroid/blocs/bloc_index.dart'; 4 | import 'package:flutter_wanandroid/common/common.dart'; 5 | import 'package:flutter_wanandroid/ui/pages/user/user_login_page.dart'; 6 | import 'package:flutter_wanandroid/utils/util_index.dart'; 7 | 8 | class ProgressView extends StatelessWidget { 9 | @override 10 | Widget build(BuildContext context) { 11 | return new Center( 12 | child: new SizedBox( 13 | width: 24.0, 14 | height: 24.0, 15 | child: new CircularProgressIndicator( 16 | strokeWidth: 2.0, 17 | ), 18 | ), 19 | ); 20 | } 21 | } 22 | 23 | class LikeBtn extends StatelessWidget { 24 | const LikeBtn({ 25 | Key key, 26 | this.labelId, 27 | this.id, 28 | this.isLike, 29 | }) : super(key: key); 30 | final String labelId; 31 | final int id; 32 | final bool isLike; 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | final MainBloc bloc = BlocProvider.of(context); 37 | return new InkWell( 38 | onTap: () { 39 | if (Util.isLogin()) { 40 | bloc.doCollection(labelId, id, !isLike); 41 | } else { 42 | NavigatorUtil.pushPage(context, UserLoginPage(), 43 | pageName: "UserLoginPage"); 44 | } 45 | }, 46 | child: new Icon( 47 | Icons.favorite, 48 | color: (isLike == true && Util.isLogin()) 49 | ? Colors.redAccent 50 | : Colours.gray_99, 51 | ), 52 | ); 53 | } 54 | } 55 | 56 | class StatusViews extends StatelessWidget { 57 | const StatusViews(this.status, {Key key, this.onTap}) : super(key: key); 58 | final int status; 59 | final GestureTapCallback onTap; 60 | 61 | @override 62 | Widget build(BuildContext context) { 63 | switch (status) { 64 | case LoadStatus.fail: 65 | return new Container( 66 | width: double.infinity, 67 | child: new Material( 68 | color: Colors.white, 69 | child: new InkWell( 70 | onTap: () { 71 | onTap(); 72 | }, 73 | child: new Column( 74 | mainAxisAlignment: MainAxisAlignment.center, 75 | children: [ 76 | new Image.asset( 77 | Utils.getImgPath("ic_network_error"), 78 | package: BaseConstant.packageBase, 79 | width: 100, 80 | height: 100, 81 | ), 82 | Gaps.vGap10, 83 | new Text( 84 | "网络出问题了~ 请您查看网络设置", 85 | style: TextStyles.listContent, 86 | ), 87 | Gaps.vGap5, 88 | new Text( 89 | "点击屏幕,重新加载", 90 | style: TextStyles.listContent, 91 | ), 92 | ], 93 | ), 94 | ), 95 | ), 96 | ); 97 | break; 98 | case LoadStatus.loading: 99 | return new Container( 100 | alignment: Alignment.center, 101 | color: Colours.gray_f0, 102 | child: new ProgressView(), 103 | ); 104 | break; 105 | case LoadStatus.empty: 106 | return new Container( 107 | color: Colors.white, 108 | width: double.infinity, 109 | child: new Center( 110 | child: new Column( 111 | mainAxisAlignment: MainAxisAlignment.center, 112 | children: [ 113 | new Image.asset( 114 | Utils.getImgPath("ic_data_empty"), 115 | package: BaseConstant.packageBase, 116 | width: 60, 117 | height: 60, 118 | ), 119 | Gaps.vGap10, 120 | new Text( 121 | "空空如也~", 122 | style: TextStyles.listContent2, 123 | ), 124 | ], 125 | ), 126 | ), 127 | ); 128 | break; 129 | default: 130 | return Container(); 131 | break; 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /lib/utils/http_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:base_library/base_library.dart'; 2 | import 'package:flutter_wanandroid/common/common.dart'; 3 | import 'package:flutter_wanandroid/models/models.dart'; 4 | 5 | //模拟网络请求数据 6 | class HttpUtils { 7 | Future getSplash() { 8 | return Future.delayed(Duration(milliseconds: 500), () { 9 | return SplashModel( 10 | title: 'Flutter 常用工具类库', 11 | content: 'Flutter 常用工具类库', 12 | url: 'https://www.jianshu.com/p/425a7ff9d66e', 13 | imgUrl: 14 | 'https://raw.githubusercontent.com/Sky24n/LDocuments/master/AppImgs/flutter_common_utils_a.png', 15 | ); 16 | }); 17 | } 18 | 19 | /// 该地址可能无法下载,请自行更换可测试apk地址。 20 | Future getVersion() async { 21 | return Future.delayed(Duration(milliseconds: 500), () { 22 | return VersionModel( 23 | title: '有新版本v0.2.6,快去更新吧!', 24 | content: '1.基础库升级 | 2.修复OPPO R15详情页问题 | 3.一些优化~', 25 | url: 26 | 'https://raw.githubusercontent.com/Sky24n/Doc/master/apks/flutter_wanandroid.apk', 27 | version: '0.2.6', 28 | ); 29 | }); 30 | } 31 | 32 | Future getRecItem() async { 33 | return Future.delayed(Duration(milliseconds: 500), () { 34 | return null; 35 | }); 36 | } 37 | 38 | Future> getRecList() async { 39 | return Future.delayed(Duration(milliseconds: 500), () { 40 | return List(); 41 | }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/utils/navigator_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter_wanandroid/common/component_index.dart'; 3 | import 'package:flutter_wanandroid/ui/pages/tab_page.dart'; 4 | import 'package:flutter_wanandroid/ui/pages/user/user_login_page.dart'; 5 | import 'package:url_launcher/url_launcher.dart'; 6 | 7 | class NavigatorUtil { 8 | static void pushPage( 9 | BuildContext context, 10 | Widget page, { 11 | String pageName, 12 | bool needLogin = false, 13 | }) { 14 | if (context == null || page == null) return; 15 | if (needLogin && !Util.isLogin()) { 16 | pushPage(context, UserLoginPage()); 17 | return; 18 | } 19 | Navigator.push( 20 | context, new CupertinoPageRoute(builder: (ctx) => page)); 21 | } 22 | 23 | static void pushWeb(BuildContext context, 24 | {String title, String titleId, String url, bool isHome: false}) { 25 | if (context == null || ObjectUtil.isEmpty(url)) return; 26 | if (url.endsWith(".apk")) { 27 | launchInBrowser(url, title: title ?? titleId); 28 | } else { 29 | Navigator.push( 30 | context, 31 | new CupertinoPageRoute( 32 | builder: (ctx) => new WebScaffold( 33 | title: title, 34 | titleId: titleId, 35 | url: url, 36 | ))); 37 | } 38 | } 39 | 40 | static void pushTabPage(BuildContext context, 41 | {String labelId, String title, String titleId, TreeModel treeModel}) { 42 | if (context == null) return; 43 | Navigator.push( 44 | context, 45 | new CupertinoPageRoute( 46 | builder: (ctx) => new BlocProvider( 47 | child: new TabPage( 48 | labelId: labelId, 49 | title: title, 50 | titleId: titleId, 51 | treeModel: treeModel, 52 | ), 53 | bloc: new TabBloc(), 54 | ))); 55 | } 56 | 57 | static Future launchInBrowser(String url, {String title}) async { 58 | if (await canLaunch(url)) { 59 | await launch(url, forceSafariVC: false, forceWebView: false); 60 | } else { 61 | throw 'Could not launch $url'; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/utils/util_index.dart: -------------------------------------------------------------------------------- 1 | export 'navigator_util.dart'; 2 | export 'utils.dart'; 3 | export 'http_utils.dart'; 4 | 5 | export 'package:common_utils/common_utils.dart'; 6 | export 'package:flustars/flustars.dart'; 7 | export 'package:fluintl/fluintl.dart'; 8 | -------------------------------------------------------------------------------- /lib/utils/utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:common_utils/common_utils.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_wanandroid/common/common.dart'; 4 | import 'package:flutter_wanandroid/res/index.dart'; 5 | import 'package:lpinyin/lpinyin.dart'; 6 | 7 | class Utils { 8 | static String getImgPath(String name, {String format: 'png'}) { 9 | return 'assets/images/$name.$format'; 10 | } 11 | 12 | static String getPinyin(String str) { 13 | return PinyinHelper.getShortPinyin(str).substring(0, 1).toUpperCase(); 14 | } 15 | 16 | static Color getCircleBg(String str) { 17 | String pinyin = getPinyin(str); 18 | return getCircleAvatarBg(pinyin); 19 | } 20 | 21 | static Color getCircleAvatarBg(String key) { 22 | return circleAvatarMap[key]; 23 | } 24 | 25 | static Color getChipBgColor(String name) { 26 | String pinyin = PinyinHelper.getFirstWordPinyin(name); 27 | pinyin = pinyin.substring(0, 1).toUpperCase(); 28 | return nameToColor(pinyin); 29 | } 30 | 31 | static Color nameToColor(String name) { 32 | // assert(name.length > 1); 33 | final int hash = name.hashCode & 0xffff; 34 | final double hue = (360.0 * hash / (1 << 15)) % 360.0; 35 | return HSVColor.fromAHSV(1.0, hue, 0.4, 0.90).toColor(); 36 | } 37 | 38 | static String getTimeLine(BuildContext context, int timeMillis) { 39 | // LogUtil.e("countryCode: " + 40 | // Localizations.localeOf(context).countryCode + 41 | // " languageCode: " + 42 | // Localizations.localeOf(context).languageCode); 43 | return TimelineUtil.format(timeMillis, 44 | locale: Localizations.localeOf(context).languageCode, 45 | dayFormat: DayFormat.Common); 46 | } 47 | 48 | static double getTitleFontSize(String title) { 49 | if (ObjectUtil.isEmpty(title) || title.length < 10) { 50 | return 18.0; 51 | } 52 | int count = 0; 53 | List list = title.split(""); 54 | for (int i = 0, length = list.length; i < length; i++) { 55 | String ss = list[i]; 56 | if (RegexUtil.isZh(ss)) { 57 | count++; 58 | } 59 | } 60 | 61 | return (count >= 10 || title.length > 16) ? 14.0 : 18.0; 62 | } 63 | 64 | /// 0 不升级 65 | /// 1 升级 66 | /// > x 强升 67 | static int getUpdateStatus(String version, {String local}) { 68 | if (ObjectUtil.isEmpty(version)) return 0; 69 | String locVersion = local ?? AppConfig.version; 70 | int remote = int.tryParse(version.replaceAll('.', '')); 71 | int loc = int.tryParse(locVersion.replaceAll('.', '')); 72 | if (remote <= loc) { 73 | return 0; 74 | } else { 75 | return remote - loc; 76 | } 77 | } 78 | 79 | static bool isNeedLogin(String pageId) { 80 | if (pageId == Ids.titleCollection) { 81 | return true; 82 | } 83 | return false; 84 | } 85 | 86 | static int getLoadStatus(bool hasError, List data) { 87 | if (hasError) return LoadStatus.fail; 88 | if (data == null) { 89 | return LoadStatus.loading; 90 | } else if (data.isEmpty) { 91 | return LoadStatus.empty; 92 | } else { 93 | return LoadStatus.success; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /pkgget: -------------------------------------------------------------------------------- 1 | export PUB_HOSTED_URL=https://pub.flutter-io.cn 2 | export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn 3 | flutter packages get -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_wanandroid 2 | description: Flutter Wanandroid App. 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 | # Read more about versioning at semver.org. 10 | version: 0.2.6+26 11 | 12 | environment: 13 | sdk: ">=2.3.0 <3.0.0" 14 | 15 | dependencies: 16 | flutter: 17 | sdk: flutter 18 | flutter_localizations: 19 | sdk: flutter 20 | 21 | # The following adds the Cupertino Icons font to your application. 22 | # Use with the CupertinoIcons class for iOS style icons. 23 | cupertino_icons: ^0.1.2 24 | 25 | # base_library基础组建库,包含: 26 | # Dart 常用工具类库 common_utils, 27 | # Flutter 常用工具类库 flustars, 28 | # 网络 dio, 29 | # 国际化 fluintl。 30 | 31 | # 本地依赖 32 | # base_library: 33 | # path: ../FlutterRepos/base_library/ 34 | # git依赖 35 | base_library: 36 | git: 37 | url: git://github.com/Sky24n/FlutterRepos.git 38 | path: base_library 39 | 40 | # 汉字转拼音库 https://github.com/flutterchina/lpinyin 41 | lpinyin: ^1.1.0 42 | # Dart 常用工具类库 https://github.com/Sky24n/common_utils 43 | common_utils: ^1.2.1 44 | # Flutter 常用工具类库 https://github.com/Sky24n/flustars 45 | flustars: ^0.3.3 46 | # Flutter 国际化/多语言库 https://github.com/Sky24n/fluintl 47 | fluintl: ^0.1.3 48 | # Flutter 城市列表 https://github.com/flutterchina/azlistview 49 | azlistview: ^1.0.2 50 | 51 | 52 | # https://github.com/flutterchina/dio 53 | dio: 1.0.13 54 | # rxdart https://github.com/ReactiveX/rxdart 55 | rxdart: 0.22.6 56 | # https://github.com/renefloor/flutter_cached_network_image 57 | cached_network_image: 2.0.0 58 | # https://github.com/flutter/plugins/tree/master/packages/url_launcher 59 | url_launcher: ^5.7.10 60 | # https://github.com/peng8350/flutter_pulltorefresh 61 | pull_to_refresh: 1.1.6 62 | # device_info 63 | # device_info: ^0.4.0+1 64 | # webview_flutter 65 | webview_flutter: ^1.0.7 66 | # share 67 | share: ^0.6.5+4 68 | # https://github.com/flutterchina/flukit. 69 | flukit: ^1.0.2 70 | 71 | # 72 | # install_apk_plugin: ^1.0.1 73 | 74 | # Flutter 全局屏幕适配。https://github.com/flutterchina/auto_size 75 | # 如果有问题请尝试修改如下链接 76 | # url: https://github.com/flutterchina/auto_size.git 77 | # auto_size: 78 | # git: 79 | # url: git://github.com/flutterchina/auto_size.git 80 | 81 | dev_dependencies: 82 | flutter_test: 83 | sdk: flutter 84 | 85 | 86 | # For information on the generic Dart part of this file, see the 87 | # following page: https://www.dartlang.org/tools/pub/pubspec 88 | 89 | # The following section is specific to Flutter. 90 | flutter: 91 | 92 | # The following line ensures that the Material Icons font is 93 | # included with your application, so that you can use the icons in 94 | # the material Icons class. 95 | uses-material-design: true 96 | assets: 97 | - assets/images/ali_connors.png 98 | - assets/images/ali_connors_sml.png 99 | - assets/images/qrcode.png 100 | - assets/images/icon_wan_android.png 101 | - assets/images/ic_launcher_news.png 102 | - assets/images/guide1.png 103 | - assets/images/guide2.png 104 | - assets/images/guide3.png 105 | - assets/images/guide4.png 106 | - assets/images/splash_bg.png 107 | 108 | - packages/base_library/assets/ic_login_bg.png 109 | - packages/base_library/assets/ic_network_error.png 110 | - packages/base_library/assets/ic_data_empty.png 111 | 112 | - assets/data/china.json 113 | 114 | # To add assets to your application, add an assets section, like this: 115 | # assets: 116 | # - images/a_dot_burr.jpeg 117 | # - images/a_dot_ham.jpeg 118 | 119 | # An image asset can refer to one or more resolution-specific "variants", see 120 | # https://flutter.io/assets-and-images/#resolution-aware. 121 | 122 | # For details regarding adding assets from package dependencies, see 123 | # https://flutter.io/assets-and-images/#from-packages 124 | 125 | # To add custom fonts to your application, add a fonts section here, 126 | # in this "flutter" section. Each entry in this list should have a 127 | # "family" key with the font family name, and a "fonts" key with a 128 | # list giving the asset and other descriptors for the font. For 129 | # example: 130 | # fonts: 131 | # - family: Schyler 132 | # fonts: 133 | # - asset: fonts/Schyler-Regular.ttf 134 | # - asset: fonts/Schyler-Italic.ttf 135 | # style: italic 136 | # - family: Trajan Pro 137 | # fonts: 138 | # - asset: fonts/TrajanPro.ttf 139 | # - asset: fonts/TrajanPro_Bold.ttf 140 | # weight: 700 141 | # 142 | # For details regarding fonts from package dependencies, 143 | # see https://flutter.io/custom-fonts/#from-packages 144 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter 3 | // provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to 4 | // find child widgets in the widget tree, read text, and verify that the values of widget properties 5 | // are correct. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_test/flutter_test.dart'; 9 | 10 | import 'package:flutter_wanandroid/main.dart'; 11 | 12 | void main() { 13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(new MyApp()); 16 | 17 | // Verify that our counter starts at 0. 18 | expect(find.text('0'), findsOneWidget); 19 | expect(find.text('1'), findsNothing); 20 | 21 | // Tap the '+' icon and trigger a frame. 22 | await tester.tap(find.byIcon(Icons.add)); 23 | await tester.pump(); 24 | 25 | // Verify that our counter has incremented. 26 | expect(find.text('0'), findsNothing); 27 | expect(find.text('1'), findsOneWidget); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /uploadMaster: -------------------------------------------------------------------------------- 1 | git push origin master 2 | --------------------------------------------------------------------------------