├── .gitignore ├── .metadata ├── LICENSE ├── README.md ├── android ├── app │ ├── build.gradle │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── itresourceexchangeapp │ │ │ └── MainActivity.java │ │ └── res │ │ ├── drawable │ │ └── launch_background.xml │ │ ├── mipmap-hdpi │ │ ├── app_icon.png │ │ └── launch_image.png │ │ ├── mipmap-mdpi │ │ ├── app_icon.png │ │ └── launch_image.png │ │ ├── mipmap-xhdpi │ │ ├── app_icon.png │ │ └── launch_image.png │ │ ├── mipmap-xxhdpi │ │ ├── app_icon.png │ │ └── launch_image.png │ │ ├── mipmap-xxxhdpi │ │ ├── app_icon.png │ │ └── launch_image.png │ │ └── values │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── fonts │ └── iconfont.ttf └── imgs │ ├── app_icon.png │ ├── ic_cards_wallet.png │ ├── ic_collections.png │ ├── ic_comment.png │ ├── ic_settings.png │ └── img_default.png ├── ios ├── ExportOptions.plist ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── flutter_export_environment.sh ├── Podfile ├── Runner-Bridging-Header.h ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── app_icon.png │ │ ├── app_iconiPadApp_76pt.png │ │ ├── app_iconiPadApp_76pt@2x.png │ │ ├── app_iconiPadNotifications_20pt.png │ │ ├── app_iconiPadNotifications_20pt@2x.png │ │ ├── app_iconiPadProApp_83.5pt@2x.png │ │ ├── app_iconiPadSpootlight5_29pt.png │ │ ├── app_iconiPadSpootlight5_29pt@2x.png │ │ ├── app_iconiPadSpootlight7_40pt.png │ │ ├── app_iconiPadSpootlight7_40pt@2x.png │ │ ├── app_iconiPhoneApp_60pt@2x.png │ │ ├── app_iconiPhoneApp_60pt@3x.png │ │ ├── app_iconiPhoneNotification_20pt@2x.png │ │ ├── app_iconiPhoneNotification_20pt@3x.png │ │ ├── app_iconiPhoneSpootlight5_29pt@2x.png │ │ ├── app_iconiPhoneSpootlight5_29pt@3x.png │ │ ├── app_iconiPhoneSpootlight7_40pt@2x.png │ │ └── app_iconiPhoneSpootlight7_40pt@3x.png │ ├── Contents.json │ └── LaunchImage.launchimage │ │ ├── Contents.json │ │ ├── launchimage-1125*2436@2x.png │ │ ├── launchimage-1242*2208@2x.png │ │ ├── launchimage-1242*2688@2x.png │ │ ├── launchimage-1536*2048@2x.png │ │ ├── launchimage-640*1136@2x.png │ │ ├── launchimage-640*960@2x.png │ │ ├── launchimage-750*1334@2x.png │ │ ├── launchimage-768*1024@2x.png │ │ └── launchimage-828*1792@2x.png │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── main.m ├── lib ├── common │ └── constant.dart ├── main.dart ├── model │ ├── base_result.dart │ ├── base_result.g.dart │ ├── cate_info.dart │ ├── cate_info.g.dart │ ├── collect_product_info.dart │ ├── collect_product_info.g.dart │ ├── comment_model.dart │ ├── comment_model.g.dart │ ├── home_info.dart │ ├── home_info.g.dart │ ├── movie_info.dart │ ├── movie_info.g.dart │ ├── page_result.dart │ ├── page_result.g.dart │ ├── product_detail.dart │ ├── product_detail.g.dart │ ├── upload_info.dart │ ├── upload_info.g.dart │ ├── user_info.dart │ └── user_info.g.dart ├── net │ ├── code.dart │ ├── http_manager.dart │ ├── interceptors │ │ ├── error_interceptor.dart │ │ ├── logs_interceptor.dart │ │ └── response_Interceptor.dart │ └── network_utils.dart ├── pages │ ├── application_page.dart │ ├── classify │ │ ├── classify_item_view.dart │ │ ├── classify_list_view.dart │ │ └── classify_page.dart │ ├── collection │ │ ├── my_collection_item_view.dart │ │ └── my_collection_list_page.dart │ ├── create │ │ ├── new_goods_page.dart │ │ ├── new_goods_preview_widget.dart │ │ └── new_goods_text_field.dart │ ├── detail │ │ ├── comment_view │ │ │ ├── goods_comment_content_view.dart │ │ │ ├── goods_comment_header_view.dart │ │ │ ├── goods_comment_item_view.dart │ │ │ └── goods_comment_reply_view.dart │ │ ├── goods_detail_bottom_bar.dart │ │ ├── goods_detail_content_view.dart │ │ ├── goods_detail_page.dart │ │ └── input_dialog │ │ │ ├── bottom_input_dialog.dart │ │ │ └── pop_bottom_input_dialog_route.dart │ ├── home │ │ ├── goods_item_view.dart │ │ └── home_page.dart │ ├── login │ │ ├── login_page.dart │ │ ├── perfect_info_page.dart │ │ ├── register_page.dart │ │ ├── reset_password_page.dart │ │ └── user_verify_code_page.dart │ ├── movie │ │ ├── movie_cate_list_page.dart │ │ ├── movie_cate_list_view.dart │ │ ├── movie_item_view.dart │ │ ├── movie_list_item_view.dart │ │ ├── movie_search_page.dart │ │ └── rating_bar.dart │ ├── my_product_list │ │ ├── my_product_item_view.dart │ │ └── my_product_list_page.dart │ ├── player │ │ ├── video_player_page.dart │ │ └── video_player_widget.dart │ ├── profile │ │ ├── full_width_button.dart │ │ ├── profile_header_info.dart │ │ └── profile_page.dart │ └── web │ │ └── webview_page.dart ├── routes │ ├── it_router.dart │ ├── route_handlers.dart │ └── routes.dart ├── utils │ ├── local_storage_utils.dart │ ├── regex_utils.dart │ └── user_utils.dart ├── vo │ ├── comment_vo.dart │ └── new_product_vo.dart └── widgets │ ├── choose_img_modal_sheet.dart │ ├── custom_alert_dialog.dart │ ├── indicator_factory.dart │ ├── load_state_layout_widget.dart │ └── loading_dialog.dart ├── pubspec.yaml ├── screenshots ├── 1.jpg ├── 2.jpg ├── 3.jpg ├── 4.png └── apk_download.png └── test └── widget_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | release.sh 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .vscode/ 21 | 22 | # Flutter/Dart/Pub related 23 | **/doc/api/ 24 | .dart_tool/ 25 | .flutter-plugins 26 | .packages 27 | .pub-cache/ 28 | .pub/ 29 | build/ 30 | 31 | # Android related 32 | **/android/**/gradle-wrapper.jar 33 | **/android/.gradle 34 | **/android/captures/ 35 | **/android/gradlew 36 | **/android/gradlew.bat 37 | **/android/local.properties 38 | **/android/**/GeneratedPluginRegistrant.java 39 | 40 | # iOS/XCode related 41 | **/ios/**/*.mode1v3 42 | **/ios/**/*.mode2v3 43 | **/ios/**/*.moved-aside 44 | **/ios/**/*.pbxuser 45 | **/ios/**/*.perspectivev3 46 | **/ios/**/*sync/ 47 | **/ios/**/.sconsign.dblite 48 | **/ios/**/.tags* 49 | **/ios/**/.vagrant/ 50 | **/ios/**/DerivedData/ 51 | **/ios/**/Icon? 52 | **/ios/**/Pods/ 53 | **/ios/**/.symlinks/ 54 | **/ios/**/profile 55 | **/ios/**/xcuserdata 56 | **/ios/.generated/ 57 | **/ios/Flutter/App.framework 58 | **/ios/Flutter/Flutter.framework 59 | **/ios/Flutter/Generated.xcconfig 60 | **/ios/Flutter/app.flx 61 | **/ios/Flutter/app.zip 62 | **/ios/Flutter/flutter_assets/ 63 | **/ios/ServiceDefinitions.json 64 | **/ios/Runner/GeneratedPluginRegistrant.* 65 | **/ios/IPADir/ 66 | 67 | # Exceptions to above rules. 68 | !**/ios/**/default.mode1v3 69 | !**/ios/**/default.mode2v3 70 | !**/ios/**/default.pbxuser 71 | !**/ios/**/default.perspectivev3 72 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 73 | -------------------------------------------------------------------------------- /.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: 5391447fae6209bb21a89e6a5a6583cac1af9b4b 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD License 2 | 3 | Copyright (c) 2019-present, IT Resource Exchange Group Holding Limited. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | * Neither the name of the copyright holder nor the names of its contributors may be used to 16 | endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 《IT换换》基于Flutter开发,是一款完全开源、跨平台的一个资源共享社区APP,目前项目还是初期阶段,项目搜集全网的精品资源教程,进行筛选分类,让用户可以快速找到自己中意的资源(可以快速导出android版本、iOS版本),如果你喜欢或项目对你有帮助,欢迎给个star鼓励一波~~~ 2 | 3 | ## 下载 4 | ![android 下载二维码](screenshots/apk_download.png) 5 | 6 | * 因为苹果审核不可抗力的原因,暂不上线 7 | 8 | ## 版本更新记录 9 | **1.** [注册增加验证码和忘记密码流程](https://www.jianshu.com/p/84393a45935f) 10 | 11 | **2.** [新增收藏和评论功能](https://www.jianshu.com/p/d2768f0d20b8) 12 | 13 | ## 先上图 14 | ![1.png](screenshots/1.jpg) 15 | 16 | ![2.png](screenshots/2.jpg) 17 | 18 | ![3.png](screenshots/3.jpg) 19 | 20 | ## 项目结构 21 | ``` 22 | ├── common - 一些通用的常量定义 23 | ├── model - 项目中使用的后台返回的数据模型 24 | ├── net - 基于dio的网络请求封装 25 | │   └── interceptors - 网络请求的拦截器 26 | ├── pages - 页面 27 | │   ├── classify - 分类页面 28 | │   ├── create - 资源创建编辑页面 29 | │   ├── detail - 资源详情页面 30 | │   ├── home - 首页 31 | │   ├── login - 登录注册页面 32 | │   ├── my_product_list - 我发布资源列表页面 33 | │   ├── profile - 个人中心页面 34 | │   └── web - web页面 35 | ├── route - 基于fluro的路由 36 | ├── utils - 项目中使用的工具类 37 | ├── vo - 前端页面组合的VO 38 | └── widgets - 一些通用Widget 39 | ``` 40 | 41 | ## JSON 数据解析 42 | 项目开发过程中,使用json_serializable,但在编写模型的时候依然较为繁琐,索性在之前[JSONConverter](https://github.com/iosyaowei/JSONConverter)添加了对Flutter的支持。 43 | JSONConverter 是MAC上iOS开发的辅助小工具,可以快速的把JSON数据转换生成对应的模型类属性,目前支持Objective-C、Swift、Flutter以及目前流行的Swift第三方库: [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON)、[HandyJSON](https://github.com/alibaba/HandyJSON),[ObjectMapper](https://github.com/Hearst-DD/ObjectMapper),可以灵活选择构建class/struct,并支持配置类名前缀等,省去手敲模型的麻烦,借此提高我们的开发效率。 44 | 45 | ![4.png](screenshots/4.png) 46 | 47 | ## 待完成事项 48 | 1. 注册邮箱验证码功能 (已完成) 49 | 2. 资源评论功能(已完成) 50 | 3. 基于微信的资源详情H5分享 51 | 4. 收藏功能(已完成) 52 | 5. 搜索功能 53 | 6. 。。。 54 | 55 | ## 第三方声明 56 | * cached_network_image: ^1.1.0 57 | * pull_to_refresh: ^1.3.3 58 | * dio: ^2.1.1 59 | * shared_preferences: ^0.4.2 60 | * connectivity: ^0.4.3+6 61 | * event_bus: ^1.1.0 62 | * json_annotation: ^2.0.0 63 | * intl: ^0.15.8 64 | * flutter_spinkit: "^3.1.0" 65 | * oktoast: ^2.1.9 66 | * image_picker: ^0.6.0+10 67 | * multi_image_picker: ^4.5.1 68 | * flutter_webview_plugin: ^0.3.5 69 | * flutter_swiper : ^1.1.6 70 | * fluro: "^1.5.1" 71 | 72 | ## 注意 73 | * 源码仅作学习,接口进行次数限制. 74 | 75 | ## 开源协议 76 | ``` 77 | BSD License 78 | 79 | Copyright (c) 2019-present, IT Resource Exchange Group Holding Limited. All rights reserved. 80 | 81 | Redistribution and use in source and binary forms, with or without modification, 82 | are permitted provided that the following conditions are met: 83 | 84 | * Redistributions of source code must retain the above copyright notice, this 85 | list of conditions and the following disclaimer. 86 | 87 | * Redistributions in binary form must reproduce the above copyright notice, 88 | this list of conditions and the following disclaimer in the documentation 89 | and/or other materials provided with the distribution. 90 | 91 | * Neither the name of the copyright holder nor the names of its contributors may be used to 92 | endorse or promote products derived from this software without specific 93 | prior written permission. 94 | 95 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 96 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 97 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 98 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 99 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 100 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 101 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 102 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 103 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 104 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE 105 | ``` 106 | -------------------------------------------------------------------------------- /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 28 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.example.itresourceexchangeapp" 37 | minSdkVersion 19 38 | targetSdkVersion 28 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | testInstrumentationRunner "androidx.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 | } 56 | 57 | dependencies { 58 | testImplementation 'junit:junit:4.12' 59 | androidTestImplementation 'androidx.test:runner:1.3.0-alpha02' 60 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0-alpha02' 61 | } 62 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 17 | 21 | 28 | 32 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/example/itresourceexchangeapp/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.itresourceexchangeapp; 2 | 3 | import android.os.Bundle; 4 | import io.flutter.app.FlutterActivity; 5 | import io.flutter.plugins.GeneratedPluginRegistrant; 6 | 7 | public class MainActivity extends FlutterActivity { 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | GeneratedPluginRegistrant.registerWith(this); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/android/app/src/main/res/mipmap-hdpi/app_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/launch_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/android/app/src/main/res/mipmap-hdpi/launch_image.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/android/app/src/main/res/mipmap-mdpi/app_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/launch_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/android/app/src/main/res/mipmap-mdpi/launch_image.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/android/app/src/main/res/mipmap-xhdpi/app_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/launch_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/android/app/src/main/res/mipmap-xhdpi/launch_image.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/android/app/src/main/res/mipmap-xxhdpi/app_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/launch_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/android/app/src/main/res/mipmap-xxhdpi/launch_image.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/android/app/src/main/res/mipmap-xxxhdpi/app_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/launch_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/android/app/src/main/res/mipmap-xxxhdpi/launch_image.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.2.1' 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 | android.enableJetifier=true 2 | android.useAndroidX=true 3 | org.gradle.jvmargs=-Xmx1536M 4 | -------------------------------------------------------------------------------- /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-4.10.2-all.zip 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /assets/fonts/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/assets/fonts/iconfont.ttf -------------------------------------------------------------------------------- /assets/imgs/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/assets/imgs/app_icon.png -------------------------------------------------------------------------------- /assets/imgs/ic_cards_wallet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/assets/imgs/ic_cards_wallet.png -------------------------------------------------------------------------------- /assets/imgs/ic_collections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/assets/imgs/ic_collections.png -------------------------------------------------------------------------------- /assets/imgs/ic_comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/assets/imgs/ic_comment.png -------------------------------------------------------------------------------- /assets/imgs/ic_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/assets/imgs/ic_settings.png -------------------------------------------------------------------------------- /assets/imgs/img_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/assets/imgs/img_default.png -------------------------------------------------------------------------------- /ios/ExportOptions.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | destination 6 | export 7 | method 8 | app-store 9 | provisioningProfiles 10 | 11 | com.devyao.itresourceexchange 12 | it_resource_exchange_appstore_profile 13 | 14 | signingCertificate 15 | iPhone Distribution 16 | signingStyle 17 | manual 18 | stripSwiftSymbols 19 | 20 | teamID 21 | 7DDW7FQJ8R 22 | uploadSymbols 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /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/yaowei/Documents/flutter" 4 | export "FLUTTER_APPLICATION_PATH=/Users/yaowei/Documents/GitPro/it_resource_exchange_app" 5 | export "FLUTTER_TARGET=/Users/yaowei/Documents/GitPro/it_resource_exchange_app/lib/main.dart" 6 | export "FLUTTER_BUILD_DIR=build" 7 | export "SYMROOT=${SOURCE_ROOT}/../build/ios" 8 | export "FLUTTER_FRAMEWORK_DIR=/Users/yaowei/Documents/flutter/bin/cache/artifacts/engine/ios" 9 | export "FLUTTER_BUILD_NAME=1.2.0" 10 | export "FLUTTER_BUILD_NUMBER=0" 11 | export "TRACK_WIDGET_CREATION=true" 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 | use_frameworks! 37 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 38 | # referring to absolute paths on developers' machines. 39 | system('rm -rf .symlinks') 40 | system('mkdir -p .symlinks/plugins') 41 | 42 | # Flutter Pods 43 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') 44 | if generated_xcode_build_settings.empty? 45 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." 46 | end 47 | generated_xcode_build_settings.map { |p| 48 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR' 49 | symlink = File.join('.symlinks', 'flutter') 50 | File.symlink(File.dirname(p[:path]), symlink) 51 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) 52 | end 53 | } 54 | 55 | # Plugin Pods 56 | plugin_pods = parse_KV_file('../.flutter-plugins') 57 | plugin_pods.map { |p| 58 | symlink = File.join('.symlinks', 'plugins', p[:name]) 59 | File.symlink(p[:path], symlink) 60 | pod p[:name], :path => File.join(symlink, 'ios') 61 | } 62 | end 63 | 64 | post_install do |installer| 65 | installer.pods_project.targets.each do |target| 66 | target.build_configurations.each do |config| 67 | config.build_settings['ENABLE_BITCODE'] = 'NO' 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /ios/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import 6 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /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" : "app_iconiPhoneNotification_20pt@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "app_iconiPhoneNotification_20pt@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "app_iconiPhoneSpootlight5_29pt@2x.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "app_iconiPhoneSpootlight5_29pt@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "app_iconiPhoneSpootlight7_40pt@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "app_iconiPhoneSpootlight7_40pt@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "app_iconiPhoneApp_60pt@2x.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "app_iconiPhoneApp_60pt@3x.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "20x20", 53 | "idiom" : "ipad", 54 | "filename" : "app_iconiPadNotifications_20pt.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "app_iconiPadNotifications_20pt@2x.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "29x29", 65 | "idiom" : "ipad", 66 | "filename" : "app_iconiPadSpootlight5_29pt.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "app_iconiPadSpootlight5_29pt@2x.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "40x40", 77 | "idiom" : "ipad", 78 | "filename" : "app_iconiPadSpootlight7_40pt.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "app_iconiPadSpootlight7_40pt@2x.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "76x76", 89 | "idiom" : "ipad", 90 | "filename" : "app_iconiPadApp_76pt.png", 91 | "scale" : "1x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "app_iconiPadApp_76pt@2x.png", 97 | "scale" : "2x" 98 | }, 99 | { 100 | "size" : "83.5x83.5", 101 | "idiom" : "ipad", 102 | "filename" : "app_iconiPadProApp_83.5pt@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "1024x1024", 107 | "idiom" : "ios-marketing", 108 | "filename" : "app_icon.png", 109 | "scale" : "1x" 110 | } 111 | ], 112 | "info" : { 113 | "version" : 1, 114 | "author" : "xcode" 115 | } 116 | } -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPadApp_76pt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPadApp_76pt.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPadApp_76pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPadApp_76pt@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPadNotifications_20pt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPadNotifications_20pt.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPadNotifications_20pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPadNotifications_20pt@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPadProApp_83.5pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPadProApp_83.5pt@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPadSpootlight5_29pt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPadSpootlight5_29pt.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPadSpootlight5_29pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPadSpootlight5_29pt@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPadSpootlight7_40pt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPadSpootlight7_40pt.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPadSpootlight7_40pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPadSpootlight7_40pt@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPhoneApp_60pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPhoneApp_60pt@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPhoneApp_60pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPhoneApp_60pt@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPhoneNotification_20pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPhoneNotification_20pt@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPhoneNotification_20pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPhoneNotification_20pt@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPhoneSpootlight5_29pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPhoneSpootlight5_29pt@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPhoneSpootlight5_29pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPhoneSpootlight5_29pt@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPhoneSpootlight7_40pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPhoneSpootlight7_40pt@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPhoneSpootlight7_40pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/AppIcon.appiconset/app_iconiPhoneSpootlight7_40pt@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "extent" : "full-screen", 5 | "idiom" : "iphone", 6 | "subtype" : "2688h", 7 | "filename" : "launchimage-1242*2688@2x.png", 8 | "minimum-system-version" : "12.0", 9 | "orientation" : "portrait", 10 | "scale" : "3x" 11 | }, 12 | { 13 | "extent" : "full-screen", 14 | "idiom" : "iphone", 15 | "subtype" : "1792h", 16 | "filename" : "launchimage-828*1792@2x.png", 17 | "minimum-system-version" : "12.0", 18 | "orientation" : "portrait", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "extent" : "full-screen", 23 | "idiom" : "iphone", 24 | "subtype" : "2436h", 25 | "filename" : "launchimage-1125*2436@2x.png", 26 | "minimum-system-version" : "11.0", 27 | "orientation" : "portrait", 28 | "scale" : "3x" 29 | }, 30 | { 31 | "extent" : "full-screen", 32 | "idiom" : "iphone", 33 | "subtype" : "736h", 34 | "filename" : "launchimage-1242*2208@2x.png", 35 | "minimum-system-version" : "8.0", 36 | "orientation" : "portrait", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "extent" : "full-screen", 41 | "idiom" : "iphone", 42 | "subtype" : "667h", 43 | "filename" : "launchimage-750*1334@2x.png", 44 | "minimum-system-version" : "8.0", 45 | "orientation" : "portrait", 46 | "scale" : "2x" 47 | }, 48 | { 49 | "orientation" : "portrait", 50 | "idiom" : "iphone", 51 | "filename" : "launchimage-640*960@2x.png", 52 | "extent" : "full-screen", 53 | "minimum-system-version" : "7.0", 54 | "scale" : "2x" 55 | }, 56 | { 57 | "extent" : "full-screen", 58 | "idiom" : "iphone", 59 | "subtype" : "retina4", 60 | "filename" : "launchimage-640*1136@2x.png", 61 | "minimum-system-version" : "7.0", 62 | "orientation" : "portrait", 63 | "scale" : "2x" 64 | }, 65 | { 66 | "orientation" : "portrait", 67 | "idiom" : "ipad", 68 | "filename" : "launchimage-768*1024@2x.png", 69 | "extent" : "full-screen", 70 | "minimum-system-version" : "7.0", 71 | "scale" : "1x" 72 | }, 73 | { 74 | "orientation" : "portrait", 75 | "idiom" : "ipad", 76 | "filename" : "launchimage-1536*2048@2x.png", 77 | "extent" : "full-screen", 78 | "minimum-system-version" : "7.0", 79 | "scale" : "2x" 80 | } 81 | ], 82 | "info" : { 83 | "version" : 1, 84 | "author" : "xcode" 85 | } 86 | } -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.launchimage/launchimage-1125*2436@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/LaunchImage.launchimage/launchimage-1125*2436@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.launchimage/launchimage-1242*2208@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/LaunchImage.launchimage/launchimage-1242*2208@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.launchimage/launchimage-1242*2688@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/LaunchImage.launchimage/launchimage-1242*2688@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.launchimage/launchimage-1536*2048@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/LaunchImage.launchimage/launchimage-1536*2048@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.launchimage/launchimage-640*1136@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/LaunchImage.launchimage/launchimage-640*1136@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.launchimage/launchimage-640*960@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/LaunchImage.launchimage/launchimage-640*960@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.launchimage/launchimage-750*1334@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/LaunchImage.launchimage/launchimage-750*1334@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.launchimage/launchimage-768*1024@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/LaunchImage.launchimage/launchimage-768*1024@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.launchimage/launchimage-828*1792@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/ios/Runner/Assets.xcassets/LaunchImage.launchimage/launchimage-828*1792@2x.png -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /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 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ITSAppUsesNonExemptEncryption 6 | 7 | CFBundleDevelopmentRegion 8 | en 9 | CFBundleDisplayName 10 | IT换换 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | it_resource_exchange_app 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | $(FLUTTER_BUILD_NAME) 23 | CFBundleSignature 24 | ???? 25 | CFBundleVersion 26 | $(FLUTTER_BUILD_NUMBER) 27 | LSRequiresIPhoneOS 28 | 29 | NSAppTransportSecurity 30 | 31 | NSAllowsArbitraryLoadsInWebContent 32 | 33 | NSAllowsArbitraryLoads 34 | 35 | 36 | NSCameraUsageDescription 37 | 创建新产品的时候需要使用相机拍摄封面和预览图 38 | NSMicrophoneUsageDescription 39 | 创建新产品的时候需要使用相机拍摄封面和预览图 40 | NSPhotoLibraryUsageDescription 41 | 创建新产品的时候需要从相册选择封面和预览图 42 | UIMainStoryboardFile 43 | Main 44 | UIStatusBarStyle 45 | UIStatusBarStyleLightContent 46 | UIRequiresFullScreen 47 | 48 | UISupportedInterfaceOrientations 49 | 50 | UIInterfaceOrientationPortrait 51 | UIInterfaceOrientationLandscapeLeft 52 | UIInterfaceOrientationLandscapeRight 53 | 54 | UISupportedInterfaceOrientations~ipad 55 | 56 | UIInterfaceOrientationPortrait 57 | UIInterfaceOrientationPortraitUpsideDown 58 | UIInterfaceOrientationLandscapeLeft 59 | UIInterfaceOrientationLandscapeRight 60 | 61 | UIViewControllerBasedStatusBarAppearance 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/common/constant.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:it_resource_exchange_app/model/cate_info.dart'; 3 | import 'dart:math'; 4 | 5 | /// 颜色 6 | class AppColors { 7 | static const PrimaryColor = Color(0xff2944CC); 8 | static const DividerColor = Color(0xffd9d9d9); 9 | static const ArrowNormalColor = Color(0xff999999); 10 | static const BackgroundColor = Color(0xffebebeb); 11 | static const DarkTextColor = Color(0xFF333333); 12 | static const MidTextColor = Color(0xFF666666); 13 | static const LightTextColor = Color(0xFF999999); 14 | static const DisableTextColor = Color(0xFFDCDCDC); 15 | static randomColor() { 16 | return Color.fromARGB(255, Random.secure().nextInt(255), 17 | Random.secure().nextInt(255), Random.secure().nextInt(255)); 18 | } 19 | } 20 | 21 | class AppSize { 22 | static const DividerWidth = 0.5; 23 | } 24 | 25 | class Constant { 26 | static const IconFontFamily = "appIconFont"; 27 | static final movieCateInfoList = [ 28 | CateInfo(1, 0, '动作', 0), 29 | CateInfo(2, 0, '喜剧', 0), 30 | CateInfo(3, 0, '爱情', 0), 31 | CateInfo(4, 0, '科幻', 0), 32 | CateInfo(5, 0, '恐怖', 0), 33 | CateInfo(6, 0, '剧情', 0), 34 | CateInfo(7, 0, '战争', 0), 35 | CateInfo(8, 0, '伦理', 0), 36 | CateInfo(9, 0, '奇幻', 0) 37 | ]; 38 | } 39 | 40 | class APPConfig { 41 | static const DEBUG = false; 42 | static const Server = "http://47.107.231.54:8090"; 43 | // static const Server = "http://localhost:8090"; 44 | } 45 | 46 | class APPIcons { 47 | static const PlaceHolderAvatar = Icon( 48 | IconData( 49 | 0xe642, 50 | fontFamily: Constant.IconFontFamily, 51 | ), 52 | size: 60.0, 53 | color: AppColors.ArrowNormalColor, 54 | ); 55 | 56 | static const AvatarData = IconData( 57 | 0xe642, 58 | fontFamily: Constant.IconFontFamily, 59 | ); 60 | 61 | static const AddImgData = IconData( 62 | 0xe70a, 63 | fontFamily: Constant.IconFontFamily, 64 | ); 65 | 66 | static const ProfileListImgData = IconData( 67 | 0xe64d, 68 | fontFamily: Constant.IconFontFamily, 69 | ); 70 | 71 | static const ProfileAddImgData = IconData( 72 | 0xe60c, 73 | fontFamily: Constant.IconFontFamily, 74 | ); 75 | 76 | static const ProfileSettingImgData = IconData( 77 | 0xe615, 78 | fontFamily: Constant.IconFontFamily, 79 | ); 80 | 81 | static const EmptyData = IconData( 82 | 0xe643, 83 | fontFamily: Constant.IconFontFamily, 84 | ); 85 | 86 | static const NetworkErrorData = IconData( 87 | 0xe86e, 88 | fontFamily: Constant.IconFontFamily, 89 | ); 90 | 91 | static const CollectionData = IconData( 92 | 0xe616, 93 | fontFamily: Constant.IconFontFamily, 94 | ); 95 | 96 | static const CollectSelectData = IconData( 97 | 0xe600, 98 | fontFamily: Constant.IconFontFamily, 99 | ); 100 | 101 | static const VideoData = IconData( 102 | 0xe60d, 103 | fontFamily: Constant.IconFontFamily, 104 | ); 105 | } 106 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:it_resource_exchange_app/common/constant.dart' show AppColors; 3 | import 'package:it_resource_exchange_app/routes/it_router.dart'; 4 | import './utils/local_storage_utils.dart'; 5 | import 'package:oktoast/oktoast.dart'; 6 | import 'package:flutter/services.dart'; 7 | import 'package:fluro/fluro.dart'; 8 | import './routes/routes.dart'; 9 | 10 | void main() async { 11 | await LocalStorage.getInstance(); 12 | runApp(MyApp()); 13 | } 14 | 15 | class MyApp extends StatelessWidget { 16 | MyApp() { 17 | SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light); 18 | final router = new Router(); 19 | Routes.configureRoutes(router); 20 | ITRouter.initWithRouter(router); 21 | } 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return OKToast( 26 | textStyle: TextStyle(fontSize: 16.0, color: Colors.white), 27 | backgroundColor: Colors.black87, 28 | radius: 6.0, 29 | child: MaterialApp( 30 | title: '换换', 31 | theme: ThemeData( 32 | primaryColor: AppColors.PrimaryColor, 33 | backgroundColor: Colors.white), 34 | onGenerateRoute: ITRouter.router().generator, 35 | ), 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/model/base_result.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | part 'base_result.g.dart'; 3 | 4 | @JsonSerializable() 5 | class BaseResult { 6 | var data; 7 | int status; 8 | String message; 9 | 10 | BaseResult(this.data, this.status, this.message); 11 | 12 | factory BaseResult.fromJson(Map json) => _$BaseResultFromJson(json); 13 | Map toJson() => _$BaseResultToJson(this); 14 | } -------------------------------------------------------------------------------- /lib/model/base_result.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'base_result.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | BaseResult _$BaseResultFromJson(Map json) { 10 | return BaseResult( 11 | json['data'], json['status'] as int, json['message'] as String); 12 | } 13 | 14 | Map _$BaseResultToJson(BaseResult instance) => 15 | { 16 | 'data': instance.data, 17 | 'status': instance.status, 18 | 'message': instance.message 19 | }; 20 | -------------------------------------------------------------------------------- /lib/model/cate_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'cate_info.g.dart'; 4 | 5 | 6 | @JsonSerializable() 7 | class CateInfo extends Object { 8 | 9 | @JsonKey(name: 'cateId') 10 | int cateId; 11 | 12 | @JsonKey(name: 'orderIndex') 13 | int orderIndex; 14 | 15 | @JsonKey(name: 'cateTitle') 16 | String cateTitle; 17 | 18 | @JsonKey(name: 'cateStatus') 19 | int cateStatus; 20 | 21 | 22 | CateInfo(this.cateId,this.orderIndex,this.cateTitle,this.cateStatus,); 23 | 24 | factory CateInfo.fromJson(Map srcJson) => _$CateInfoFromJson(srcJson); 25 | 26 | Map toJson() => _$CateInfoToJson(this); 27 | 28 | } -------------------------------------------------------------------------------- /lib/model/cate_info.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'cate_info.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | CateInfo _$CateInfoFromJson(Map json) { 10 | return CateInfo(json['cateId'] as int, json['orderIndex'] as int, 11 | json['cateTitle'] as String, json['cateStatus'] as int); 12 | } 13 | 14 | Map _$CateInfoToJson(CateInfo instance) => { 15 | 'cateId': instance.cateId, 16 | 'orderIndex': instance.orderIndex, 17 | 'cateTitle': instance.cateTitle, 18 | 'cateStatus': instance.cateStatus 19 | }; 20 | -------------------------------------------------------------------------------- /lib/model/collect_product_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'collect_product_info.g.dart'; 4 | 5 | @JsonSerializable() 6 | class CollectProductInfo extends Object { 7 | 8 | @JsonKey(name: 'price') 9 | double price; 10 | 11 | @JsonKey(name: 'cateId') 12 | String cateId; 13 | 14 | @JsonKey(name: 'coverUrl') 15 | String coverUrl; 16 | 17 | @JsonKey(name: 'updateTime') 18 | int updateTime; 19 | 20 | @JsonKey(name: 'productId') 21 | int productId; 22 | 23 | @JsonKey(name: 'productStatus') 24 | int productStatus; 25 | 26 | @JsonKey(name: 'collectId') 27 | int collectId; 28 | 29 | @JsonKey(name: 'createdBy') 30 | String createdBy; 31 | 32 | @JsonKey(name: 'productTitle') 33 | String productTitle; 34 | 35 | @JsonKey(name: 'imgUrls') 36 | String imgUrls; 37 | 38 | @JsonKey(name: 'productAddressUrl') 39 | String productAddressUrl; 40 | 41 | @JsonKey(name: 'keywords') 42 | String keywords; 43 | 44 | @JsonKey(name: 'createdTime') 45 | int createdTime; 46 | 47 | @JsonKey(name: 'productDesc') 48 | String productDesc; 49 | 50 | @JsonKey(name: 'tradeCount') 51 | int tradeCount; 52 | 53 | @JsonKey(name: 'isDelete') 54 | bool isDelete; 55 | 56 | @JsonKey(name: 'cateTitle') 57 | String cateTitle; 58 | 59 | CollectProductInfo(this.price,this.cateId,this.coverUrl,this.updateTime,this.productId,this.productStatus,this.collectId,this.createdBy,this.productTitle,this.imgUrls,this.productAddressUrl,this.keywords,this.createdTime,this.productDesc,this.tradeCount,this.isDelete,this.cateTitle,); 60 | 61 | factory CollectProductInfo.fromJson(Map srcJson) => _$CollectProductInfoFromJson(srcJson); 62 | 63 | Map toJson() => _$CollectProductInfoToJson(this); 64 | 65 | } 66 | -------------------------------------------------------------------------------- /lib/model/collect_product_info.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'collect_product_info.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | CollectProductInfo _$CollectProductInfoFromJson(Map json) { 10 | return CollectProductInfo( 11 | (json['price'] as num)?.toDouble(), 12 | json['cateId'] as String, 13 | json['coverUrl'] as String, 14 | json['updateTime'] as int, 15 | json['productId'] as int, 16 | json['productStatus'] as int, 17 | json['collectId'] as int, 18 | json['createdBy'] as String, 19 | json['productTitle'] as String, 20 | json['imgUrls'] as String, 21 | json['productAddressUrl'] as String, 22 | json['keywords'] as String, 23 | json['createdTime'] as int, 24 | json['productDesc'] as String, 25 | json['tradeCount'] as int, 26 | json['isDelete'] as bool, 27 | json['cateTitle'] as String); 28 | } 29 | 30 | Map _$CollectProductInfoToJson(CollectProductInfo instance) => 31 | { 32 | 'price': instance.price, 33 | 'cateId': instance.cateId, 34 | 'coverUrl': instance.coverUrl, 35 | 'updateTime': instance.updateTime, 36 | 'productId': instance.productId, 37 | 'productStatus': instance.productStatus, 38 | 'collectId': instance.collectId, 39 | 'createdBy': instance.createdBy, 40 | 'productTitle': instance.productTitle, 41 | 'imgUrls': instance.imgUrls, 42 | 'productAddressUrl': instance.productAddressUrl, 43 | 'keywords': instance.keywords, 44 | 'createdTime': instance.createdTime, 45 | 'productDesc': instance.productDesc, 46 | 'tradeCount': instance.tradeCount, 47 | 'isDelete': instance.isDelete, 48 | 'cateTitle': instance.cateTitle 49 | }; 50 | -------------------------------------------------------------------------------- /lib/model/comment_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'comment_model.g.dart'; 4 | 5 | @JsonSerializable() 6 | class CommentModel extends Object { 7 | 8 | @JsonKey(name: 'content') 9 | String content; 10 | 11 | @JsonKey(name: 'createdTime') 12 | int createdTime; 13 | 14 | @JsonKey(name: 'createUserId') 15 | int createUserId; 16 | 17 | @JsonKey(name: 'createUserName') 18 | String createUserName; 19 | 20 | @JsonKey(name: 'createUserAvatar') 21 | String createUserAvatar; 22 | 23 | @JsonKey(name: 'parentUserId') 24 | int parentUserId; 25 | 26 | @JsonKey(name: 'parentUserName') 27 | String parentUserName; 28 | 29 | @JsonKey(name: 'parentUserAvatar') 30 | String parentUserAvatar; 31 | 32 | @JsonKey(name: 'productId') 33 | int productId; 34 | 35 | @JsonKey(name: 'commentList') 36 | List commentList; 37 | 38 | @JsonKey(name: 'commentId') 39 | int commentId; 40 | 41 | @JsonKey(name: 'parentCommentId') 42 | int parentCommentId; 43 | 44 | CommentModel(this.content,this.createdTime,this.createUserId,this.createUserName,this.createUserAvatar,this.parentUserId,this.parentUserName,this.parentUserAvatar,this.productId,this.commentList,this.commentId,this.parentCommentId,); 45 | 46 | factory CommentModel.fromJson(Map srcJson) => _$CommentModelFromJson(srcJson); 47 | 48 | Map toJson() => _$CommentModelToJson(this); 49 | 50 | } -------------------------------------------------------------------------------- /lib/model/comment_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'comment_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | CommentModel _$CommentModelFromJson(Map json) { 10 | return CommentModel( 11 | json['content'] as String, 12 | json['createdTime'] as int, 13 | json['createUserId'] as int, 14 | json['createUserName'] as String, 15 | json['createUserAvatar'] as String, 16 | json['parentUserId'] as int, 17 | json['parentUserName'] as String, 18 | json['parentUserAvatar'] as String, 19 | json['productId'] as int, 20 | (json['commentList'] as List) 21 | ?.map((e) => e == null 22 | ? null 23 | : CommentModel.fromJson(e as Map)) 24 | ?.toList(), 25 | json['commentId'] as int, 26 | json['parentCommentId'] as int); 27 | } 28 | 29 | Map _$CommentModelToJson(CommentModel instance) => 30 | { 31 | 'content': instance.content, 32 | 'createdTime': instance.createdTime, 33 | 'createUserId': instance.createUserId, 34 | 'createUserName': instance.createUserName, 35 | 'createUserAvatar': instance.createUserAvatar, 36 | 'parentUserId': instance.parentUserId, 37 | 'parentUserName': instance.parentUserName, 38 | 'parentUserAvatar': instance.parentUserAvatar, 39 | 'productId': instance.productId, 40 | 'commentList': instance.commentList, 41 | 'commentId': instance.commentId, 42 | 'parentCommentId': instance.parentCommentId 43 | }; 44 | -------------------------------------------------------------------------------- /lib/model/home_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'home_info.g.dart'; 4 | 5 | @JsonSerializable() 6 | class HomeInfo extends Object { 7 | 8 | @JsonKey(name: 'advertiseList') 9 | List advertiseList; 10 | 11 | @JsonKey(name: 'recommendProductList') 12 | List recommendProductList; 13 | 14 | HomeInfo(this.advertiseList,this.recommendProductList,); 15 | 16 | factory HomeInfo.fromJson(Map srcJson) => _$HomeInfoFromJson(srcJson); 17 | 18 | Map toJson() => _$HomeInfoToJson(this); 19 | 20 | } 21 | 22 | @JsonSerializable() 23 | class AdvertiseList extends Object { 24 | 25 | @JsonKey(name: 'adId') 26 | int adId; 27 | 28 | @JsonKey(name: 'adTitle') 29 | String adTitle; 30 | 31 | @JsonKey(name: 'adDetailUrl') 32 | String adDetailUrl; 33 | 34 | @JsonKey(name: 'adProductId') 35 | String adProductId; 36 | 37 | @JsonKey(name: 'adType') 38 | int adType; 39 | 40 | @JsonKey(name: 'adCoverUrl') 41 | String adCoverUrl; 42 | 43 | AdvertiseList(this.adId,this.adTitle,this.adDetailUrl,this.adProductId,this.adType,this.adCoverUrl,); 44 | 45 | factory AdvertiseList.fromJson(Map srcJson) => _$AdvertiseListFromJson(srcJson); 46 | 47 | Map toJson() => _$AdvertiseListToJson(this); 48 | 49 | } 50 | 51 | @JsonSerializable() 52 | class RecommendProductList extends Object { 53 | 54 | @JsonKey(name: 'coverUrl') 55 | String coverUrl; 56 | 57 | @JsonKey(name: 'cateId') 58 | String cateId; 59 | 60 | @JsonKey(name: 'productAddressPassword') 61 | String productAddressPassword; 62 | 63 | @JsonKey(name: 'imgUrls') 64 | String imgUrls; 65 | 66 | @JsonKey(name: 'keywords') 67 | String keywords; 68 | 69 | @JsonKey(name: 'price') 70 | double price; 71 | 72 | @JsonKey(name: 'productTitle') 73 | String productTitle; 74 | 75 | @JsonKey(name: 'revision') 76 | String revision; 77 | 78 | @JsonKey(name: 'productStatus') 79 | int productStatus; 80 | 81 | @JsonKey(name: 'productDesc') 82 | String productDesc; 83 | 84 | @JsonKey(name: 'recommendId') 85 | int recommendId; 86 | 87 | @JsonKey(name: 'cateTitle') 88 | String cateTitle; 89 | 90 | @JsonKey(name: 'isDelete') 91 | bool isDelete; 92 | 93 | @JsonKey(name: 'productId') 94 | int productId; 95 | 96 | @JsonKey(name: 'productAddressUrl') 97 | String productAddressUrl; 98 | 99 | @JsonKey(name: 'updateBy') 100 | String updateBy; 101 | 102 | @JsonKey(name: 'updateTime') 103 | int updateTime; 104 | 105 | @JsonKey(name: 'tradeCount') 106 | int tradeCount; 107 | 108 | @JsonKey(name: 'createdBy') 109 | String createdBy; 110 | 111 | @JsonKey(name: 'createdTime') 112 | int createdTime; 113 | 114 | RecommendProductList(this.coverUrl,this.cateId,this.productAddressPassword,this.imgUrls,this.keywords,this.price,this.productTitle,this.revision,this.productStatus,this.productDesc,this.recommendId,this.cateTitle,this.isDelete,this.productId,this.productAddressUrl,this.updateBy,this.updateTime,this.tradeCount,this.createdBy,this.createdTime,); 115 | 116 | factory RecommendProductList.fromJson(Map srcJson) => _$RecommendProductListFromJson(srcJson); 117 | 118 | Map toJson() => _$RecommendProductListToJson(this); 119 | 120 | } 121 | -------------------------------------------------------------------------------- /lib/model/home_info.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'home_info.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | HomeInfo _$HomeInfoFromJson(Map json) { 10 | return HomeInfo( 11 | (json['advertiseList'] as List) 12 | ?.map((e) => e == null 13 | ? null 14 | : AdvertiseList.fromJson(e as Map)) 15 | ?.toList(), 16 | (json['recommendProductList'] as List) 17 | ?.map((e) => e == null 18 | ? null 19 | : RecommendProductList.fromJson(e as Map)) 20 | ?.toList()); 21 | } 22 | 23 | Map _$HomeInfoToJson(HomeInfo instance) => { 24 | 'advertiseList': instance.advertiseList, 25 | 'recommendProductList': instance.recommendProductList 26 | }; 27 | 28 | AdvertiseList _$AdvertiseListFromJson(Map json) { 29 | return AdvertiseList( 30 | json['adId'] as int, 31 | json['adTitle'] as String, 32 | json['adDetailUrl'] as String, 33 | json['adProductId'] as String, 34 | json['adType'] as int, 35 | json['adCoverUrl'] as String); 36 | } 37 | 38 | Map _$AdvertiseListToJson(AdvertiseList instance) => 39 | { 40 | 'adId': instance.adId, 41 | 'adTitle': instance.adTitle, 42 | 'adDetailUrl': instance.adDetailUrl, 43 | 'adProductId': instance.adProductId, 44 | 'adType': instance.adType, 45 | 'adCoverUrl': instance.adCoverUrl 46 | }; 47 | 48 | RecommendProductList _$RecommendProductListFromJson(Map json) { 49 | return RecommendProductList( 50 | json['coverUrl'] as String, 51 | json['cateId'] as String, 52 | json['productAddressPassword'] as String, 53 | json['imgUrls'] as String, 54 | json['keywords'] as String, 55 | (json['price'] as num)?.toDouble(), 56 | json['productTitle'] as String, 57 | json['revision'] as String, 58 | json['productStatus'] as int, 59 | json['productDesc'] as String, 60 | json['recommendId'] as int, 61 | json['cateTitle'] as String, 62 | json['isDelete'] as bool, 63 | json['productId'] as int, 64 | json['productAddressUrl'] as String, 65 | json['updateBy'] as String, 66 | json['updateTime'] as int, 67 | json['tradeCount'] as int, 68 | json['createdBy'] as String, 69 | json['createdTime'] as int); 70 | } 71 | 72 | Map _$RecommendProductListToJson( 73 | RecommendProductList instance) => 74 | { 75 | 'coverUrl': instance.coverUrl, 76 | 'cateId': instance.cateId, 77 | 'productAddressPassword': instance.productAddressPassword, 78 | 'imgUrls': instance.imgUrls, 79 | 'keywords': instance.keywords, 80 | 'price': instance.price, 81 | 'productTitle': instance.productTitle, 82 | 'revision': instance.revision, 83 | 'productStatus': instance.productStatus, 84 | 'productDesc': instance.productDesc, 85 | 'recommendId': instance.recommendId, 86 | 'cateTitle': instance.cateTitle, 87 | 'isDelete': instance.isDelete, 88 | 'productId': instance.productId, 89 | 'productAddressUrl': instance.productAddressUrl, 90 | 'updateBy': instance.updateBy, 91 | 'updateTime': instance.updateTime, 92 | 'tradeCount': instance.tradeCount, 93 | 'createdBy': instance.createdBy, 94 | 'createdTime': instance.createdTime 95 | }; 96 | -------------------------------------------------------------------------------- /lib/model/movie_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'movie_info.g.dart'; 4 | 5 | @JsonSerializable() 6 | class MovieInfo extends Object { 7 | 8 | @JsonKey(name: 'director') 9 | String director; 10 | 11 | @JsonKey(name: 'movieId') 12 | int movieId; 13 | 14 | @JsonKey(name: 'movieArea') 15 | int movieArea; 16 | 17 | @JsonKey(name: 'score') 18 | int score; 19 | 20 | @JsonKey(name: 'status') 21 | int status; 22 | 23 | @JsonKey(name: 'tradeCount') 24 | int tradeCount; 25 | 26 | @JsonKey(name: 'movieName') 27 | String movieName; 28 | 29 | @JsonKey(name: 'language') 30 | int language; 31 | 32 | @JsonKey(name: 'quality') 33 | int quality; 34 | 35 | @JsonKey(name: 'coverUrl') 36 | String coverUrl; 37 | 38 | @JsonKey(name: 'isDelete') 39 | bool isDelete; 40 | 41 | @JsonKey(name: 'desc') 42 | String desc; 43 | 44 | @JsonKey(name: 'rolesNames') 45 | String rolesNames; 46 | 47 | @JsonKey(name: 'releaseYear') 48 | String releaseYear; 49 | 50 | @JsonKey(name: 'playUrl') 51 | String playUrl; 52 | 53 | @JsonKey(name: 'cateId') 54 | String cateId; 55 | 56 | @JsonKey(name: 'price') 57 | double price; 58 | 59 | MovieInfo(this.director,this.movieId,this.movieArea,this.score,this.status,this.tradeCount,this.movieName,this.language,this.quality,this.coverUrl,this.isDelete,this.desc,this.rolesNames,this.releaseYear,this.playUrl,this.cateId,this.price,); 60 | 61 | factory MovieInfo.fromJson(Map srcJson) => _$MovieInfoFromJson(srcJson); 62 | 63 | Map toJson() => _$MovieInfoToJson(this); 64 | 65 | } 66 | -------------------------------------------------------------------------------- /lib/model/movie_info.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'movie_info.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | MovieInfo _$MovieInfoFromJson(Map json) { 10 | return MovieInfo( 11 | json['director'] as String, 12 | json['movieId'] as int, 13 | json['movieArea'] as int, 14 | json['score'] as int, 15 | json['status'] as int, 16 | json['tradeCount'] as int, 17 | json['movieName'] as String, 18 | json['language'] as int, 19 | json['quality'] as int, 20 | json['coverUrl'] as String, 21 | json['isDelete'] as bool, 22 | json['desc'] as String, 23 | json['rolesNames'] as String, 24 | json['releaseYear'] as String, 25 | json['playUrl'] as String, 26 | json['cateId'] as String, 27 | json['price'] as double); 28 | } 29 | 30 | Map _$MovieInfoToJson(MovieInfo instance) => { 31 | 'director': instance.director, 32 | 'movieId': instance.movieId, 33 | 'movieArea': instance.movieArea, 34 | 'score': instance.score, 35 | 'status': instance.status, 36 | 'tradeCount': instance.tradeCount, 37 | 'movieName': instance.movieName, 38 | 'language': instance.language, 39 | 'quality': instance.quality, 40 | 'coverUrl': instance.coverUrl, 41 | 'isDelete': instance.isDelete, 42 | 'desc': instance.desc, 43 | 'rolesNames': instance.rolesNames, 44 | 'releaseYear': instance.releaseYear, 45 | 'playUrl': instance.playUrl, 46 | 'cateId': instance.cateId, 47 | 'price': instance.price 48 | }; 49 | -------------------------------------------------------------------------------- /lib/model/page_result.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'page_result.g.dart'; 4 | 5 | @JsonSerializable() 6 | class PageResult extends Object { 7 | 8 | @JsonKey(name: 'currentPage') 9 | int currentPage; 10 | 11 | @JsonKey(name: 'pageSize') 12 | int pageSize; 13 | 14 | @JsonKey(name: 'totalNum') 15 | int totalNum; 16 | 17 | @JsonKey(name: 'isMore') 18 | int isMore; 19 | 20 | @JsonKey(name: 'totalPage') 21 | int totalPage; 22 | 23 | @JsonKey(name: 'startIndex') 24 | int startIndex; 25 | 26 | @JsonKey(name: 'items') 27 | List items; 28 | 29 | PageResult(this.currentPage,this.pageSize,this.totalNum,this.isMore,this.totalPage,this.startIndex,this.items,); 30 | 31 | factory PageResult.fromJson(Map srcJson) => _$PageResultFromJson(srcJson); 32 | 33 | Map toJson() => _$PageResultToJson(this); 34 | 35 | } -------------------------------------------------------------------------------- /lib/model/page_result.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'page_result.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | PageResult _$PageResultFromJson(Map json) { 10 | return PageResult( 11 | json['currentPage'] as int, 12 | json['pageSize'] as int, 13 | json['totalNum'] as int, 14 | json['isMore'] as int, 15 | json['totalPage'] as int, 16 | json['startIndex'] as int, 17 | (json['items'] as List)?.map((e) => e as Map)?.toList()); 18 | } 19 | 20 | Map _$PageResultToJson(PageResult instance) => 21 | { 22 | 'currentPage': instance.currentPage, 23 | 'pageSize': instance.pageSize, 24 | 'totalNum': instance.totalNum, 25 | 'isMore': instance.isMore, 26 | 'totalPage': instance.totalPage, 27 | 'startIndex': instance.startIndex, 28 | 'items': instance.items 29 | }; 30 | -------------------------------------------------------------------------------- /lib/model/product_detail.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'product_detail.g.dart'; 4 | 5 | @JsonSerializable() 6 | class ProductDetail extends Object { 7 | 8 | @JsonKey(name: 'commentCount') 9 | int commentCount; 10 | 11 | @JsonKey(name: 'productDesc') 12 | String productDesc; 13 | 14 | @JsonKey(name: 'tradeCount') 15 | int tradeCount; 16 | 17 | @JsonKey(name: 'productTitle') 18 | String productTitle; 19 | 20 | @JsonKey(name: 'productStatus') 21 | int productStatus; 22 | 23 | @JsonKey(name: 'coverUrl') 24 | String coverUrl; 25 | 26 | @JsonKey(name: 'productAddressUrl') 27 | String productAddressUrl; 28 | 29 | @JsonKey(name: 'updateTime') 30 | int updateTime; 31 | 32 | @JsonKey(name: 'productAddressPassword') 33 | String productAddressPassword; 34 | 35 | @JsonKey(name: 'keywords') 36 | String keywords; 37 | 38 | @JsonKey(name: 'price') 39 | double price; 40 | 41 | @JsonKey(name: 'cateTitle') 42 | String cateTitle; 43 | 44 | @JsonKey(name: 'isDelete') 45 | bool isDelete; 46 | 47 | @JsonKey(name: 'imgUrls') 48 | String imgUrls; 49 | 50 | @JsonKey(name: 'createdTime') 51 | int createdTime; 52 | 53 | @JsonKey(name: 'createdBy') 54 | String createdBy; 55 | 56 | @JsonKey(name: 'productId') 57 | int productId; 58 | 59 | @JsonKey(name: 'cateId') 60 | String cateId; 61 | 62 | @JsonKey(name: 'collectId') 63 | int collectId; 64 | 65 | 66 | ProductDetail(this.commentCount,this.productDesc,this.tradeCount,this.productTitle,this.productStatus,this.coverUrl,this.productAddressUrl,this.updateTime,this.productAddressPassword,this.keywords,this.price,this.cateTitle,this.isDelete,this.imgUrls,this.createdTime,this.createdBy,this.productId,this.cateId,this.collectId); 67 | 68 | factory ProductDetail.fromJson(Map srcJson) => _$ProductDetailFromJson(srcJson); 69 | 70 | Map toJson() => _$ProductDetailToJson(this); 71 | 72 | } -------------------------------------------------------------------------------- /lib/model/product_detail.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'product_detail.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | ProductDetail _$ProductDetailFromJson(Map json) { 10 | return ProductDetail( 11 | json['commentCount'] as int, 12 | json['productDesc'] as String, 13 | json['tradeCount'] as int, 14 | json['productTitle'] as String, 15 | json['productStatus'] as int, 16 | json['coverUrl'] as String, 17 | json['productAddressUrl'] as String, 18 | json['updateTime'] as int, 19 | json['productAddressPassword'] as String, 20 | json['keywords'] as String, 21 | (json['price'] as num)?.toDouble(), 22 | json['cateTitle'] as String, 23 | json['isDelete'] as bool, 24 | json['imgUrls'] as String, 25 | json['createdTime'] as int, 26 | json['createdBy'] as String, 27 | json['productId'] as int, 28 | json['cateId'] as String, 29 | json['collectId'] as int); 30 | } 31 | 32 | Map _$ProductDetailToJson(ProductDetail instance) => 33 | { 34 | 'commentCount': instance.commentCount, 35 | 'productDesc': instance.productDesc, 36 | 'tradeCount': instance.tradeCount, 37 | 'productTitle': instance.productTitle, 38 | 'productStatus': instance.productStatus, 39 | 'coverUrl': instance.coverUrl, 40 | 'productAddressUrl': instance.productAddressUrl, 41 | 'updateTime': instance.updateTime, 42 | 'productAddressPassword': instance.productAddressPassword, 43 | 'keywords': instance.keywords, 44 | 'price': instance.price, 45 | 'cateTitle': instance.cateTitle, 46 | 'isDelete': instance.isDelete, 47 | 'imgUrls': instance.imgUrls, 48 | 'createdTime': instance.createdTime, 49 | 'createdBy': instance.createdBy, 50 | 'productId': instance.productId, 51 | 'cateId': instance.cateId, 52 | 'collectId': instance.collectId 53 | }; 54 | -------------------------------------------------------------------------------- /lib/model/upload_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'upload_info.g.dart'; 4 | 5 | 6 | @JsonSerializable() 7 | class UploadInfo extends Object { 8 | 9 | @JsonKey(name: 'fileName') 10 | String fileName; 11 | 12 | @JsonKey(name: 'downloadUrl') 13 | String downloadUrl; 14 | 15 | @JsonKey(name: 'token') 16 | String token; 17 | 18 | UploadInfo(this.fileName,this.downloadUrl,this.token,); 19 | 20 | factory UploadInfo.fromJson(Map srcJson) => _$UploadInfoFromJson(srcJson); 21 | 22 | Map toJson() => _$UploadInfoToJson(this); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /lib/model/upload_info.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'upload_info.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | UploadInfo _$UploadInfoFromJson(Map json) { 10 | return UploadInfo(json['fileName'] as String, json['downloadUrl'] as String, 11 | json['token'] as String); 12 | } 13 | 14 | Map _$UploadInfoToJson(UploadInfo instance) => 15 | { 16 | 'fileName': instance.fileName, 17 | 'downloadUrl': instance.downloadUrl, 18 | 'token': instance.token 19 | }; 20 | -------------------------------------------------------------------------------- /lib/model/user_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'user_info.g.dart'; 4 | 5 | 6 | @JsonSerializable() 7 | class UserInfo extends Object { 8 | 9 | @JsonKey(name: 'userId') 10 | int userId; 11 | 12 | @JsonKey(name: 'nickname') 13 | String nickname; 14 | 15 | @JsonKey(name: 'account') 16 | String account; 17 | 18 | @JsonKey(name: 'intro') 19 | String intro; 20 | 21 | @JsonKey(name: 'token') 22 | String token; 23 | 24 | @JsonKey(name: 'expireTime') 25 | int expireTime; 26 | 27 | @JsonKey(name: 'userRole') 28 | int userRole; 29 | 30 | @JsonKey(name: 'avatar') 31 | String avatar; 32 | 33 | UserInfo(this.userId,this.nickname,this.account,this.intro,this.token,this.expireTime,this.userRole,this.avatar,); 34 | 35 | factory UserInfo.fromJson(Map srcJson) => _$UserInfoFromJson(srcJson); 36 | 37 | Map toJson() => _$UserInfoToJson(this); 38 | 39 | } 40 | 41 | 42 | -------------------------------------------------------------------------------- /lib/model/user_info.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'user_info.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | UserInfo _$UserInfoFromJson(Map json) { 10 | return UserInfo( 11 | json['userId'] as int, 12 | json['nickname'] as String, 13 | json['account'] as String, 14 | json['intro'] as String, 15 | json['token'] as String, 16 | json['expireTime'] as int, 17 | json['userRole'] as int, 18 | json['avatar'] as String); 19 | } 20 | 21 | Map _$UserInfoToJson(UserInfo instance) => { 22 | 'userId': instance.userId, 23 | 'nickname': instance.nickname, 24 | 'account': instance.account, 25 | 'intro': instance.intro, 26 | 'token': instance.token, 27 | 'expireTime': instance.expireTime, 28 | 'userRole': instance.userRole, 29 | 'avatar': instance.avatar 30 | }; 31 | -------------------------------------------------------------------------------- /lib/net/code.dart: -------------------------------------------------------------------------------- 1 | 2 | class Code { 3 | //网络错误 4 | static const NETWORK_ERROR = -1; 5 | 6 | //请求超时 7 | static const NETWOEK_TIMEROUT = -2; 8 | 9 | //网络返回数据格式化一次 10 | static const NETWORK_JSON_EXCEPTION = -3; 11 | 12 | static const SUCCESS = 200; 13 | } -------------------------------------------------------------------------------- /lib/net/http_manager.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:it_resource_exchange_app/model/base_result.dart'; 5 | import 'package:it_resource_exchange_app/utils/user_utils.dart'; 6 | import 'interceptors/logs_interceptor.dart'; 7 | import 'interceptors/error_interceptor.dart'; 8 | import 'interceptors/response_Interceptor.dart'; 9 | import './code.dart'; 10 | import 'dart:typed_data'; 11 | 12 | enum HttpMethod { GET, POST } 13 | 14 | const HTTPMethodValues = ['GET', 'POST']; 15 | const ContentTypeURLEncoded = 'application/x-www-form-urlencoded'; 16 | 17 | class HttpManager { 18 | Dio _dio = Dio(); 19 | 20 | HttpManager() { 21 | _dio.interceptors.add(LogsInterceptor()); 22 | _dio.interceptors.add(ErrorInterceptor(_dio)); 23 | _dio.interceptors.add(ResponseInterceptor()); 24 | } 25 | 26 | request(HttpMethod method, String url, Map params, 27 | {ContentType contentType}) async { 28 | Options _options; 29 | Map header; 30 | 31 | var type = contentType == null 32 | ? ContentType.parse(ContentTypeURLEncoded) 33 | : contentType; 34 | if (UserUtils.isLogin()) { 35 | header = {'token': UserUtils.getUserInfo().token ?? ""}; 36 | } 37 | 38 | if (method == HttpMethod.GET) { 39 | _options = Options( 40 | method: HTTPMethodValues[method.index], 41 | contentType: type, 42 | headers: header); 43 | } else { 44 | _options = Options( 45 | method: HTTPMethodValues[method.index], 46 | contentType: type, 47 | headers: header); 48 | } 49 | 50 | Response response; 51 | try { 52 | if (method == HttpMethod.GET) { 53 | response = 54 | await _dio.get(url, queryParameters: params, options: _options); 55 | } else { 56 | response = await _dio.post(url, data: params, options: _options); 57 | } 58 | } on DioError catch (e) { 59 | if (e.response != null) { 60 | response = e.response; 61 | } else { 62 | response = Response(statusCode: 999, statusMessage: "请求失败,稍后再试!"); 63 | } 64 | 65 | if (e.type == DioErrorType.CONNECT_TIMEOUT || 66 | e.type == DioErrorType.RECEIVE_TIMEOUT) { 67 | response.statusCode = Code.NETWOEK_TIMEROUT; 68 | response.statusMessage = "请求超时,请稍后再试!"; 69 | } 70 | response.data = 71 | BaseResult(null, response.statusCode, response.statusMessage); 72 | } 73 | 74 | return response.data; 75 | } 76 | 77 | upload(String url, Uint8List data) async { 78 | UploadFileInfo file = UploadFileInfo.fromBytes(data, 'fileName'); 79 | FormData formData = FormData.from({'file': file}); 80 | 81 | Map header = { 82 | 'token': UserUtils.getUserInfo().token ?? "" 83 | }; 84 | 85 | Response response; 86 | try { 87 | response = await _dio.put(url, 88 | data: formData, options: Options(headers: header)); 89 | } on DioError catch (e) { 90 | if (e.response != null) { 91 | response = e.response; 92 | } else { 93 | response = Response(statusCode: 999, statusMessage: "请求失败,稍后再试!"); 94 | } 95 | 96 | if (e.type == DioErrorType.CONNECT_TIMEOUT || 97 | e.type == DioErrorType.RECEIVE_TIMEOUT) { 98 | response.statusCode = Code.NETWOEK_TIMEROUT; 99 | response.statusMessage = "请求超时,请稍后再试!"; 100 | } 101 | response.data = 102 | BaseResult(null, response.statusCode, response.data.message); 103 | } 104 | 105 | return response.data; 106 | } 107 | } 108 | 109 | final HttpManager httpManager = HttpManager(); 110 | -------------------------------------------------------------------------------- /lib/net/interceptors/error_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:connectivity/connectivity.dart'; 3 | import 'package:it_resource_exchange_app/model/base_result.dart'; 4 | import '../code.dart'; 5 | class ErrorInterceptor extends InterceptorsWrapper { 6 | final Dio _dio; 7 | ErrorInterceptor(this._dio); 8 | 9 | @override 10 | onRequest(RequestOptions options) async { 11 | var connectivityResult = await (Connectivity().checkConnectivity()); 12 | if (connectivityResult == ConnectivityResult.none) { 13 | return _dio.resolve(BaseResult(null, Code.NETWORK_ERROR, "网络错误")); 14 | } 15 | return super.onRequest(options); 16 | } 17 | } -------------------------------------------------------------------------------- /lib/net/interceptors/logs_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:it_resource_exchange_app/common/constant.dart' show APPConfig; 3 | class LogsInterceptor extends InterceptorsWrapper { 4 | @override 5 | onRequest(RequestOptions options) { 6 | if (APPConfig.DEBUG) { 7 | print("请求url:${options.path}"); 8 | print('请求头: ' + options.headers.toString()); 9 | if (options.data != null) { 10 | print('请求参数: ' + options.data.toString()); 11 | } 12 | } 13 | return options; 14 | } 15 | 16 | @override 17 | onResponse(Response response) { 18 | if (APPConfig.DEBUG) { 19 | if (response != null) { 20 | print('返回参数: ' + response.toString()); 21 | } 22 | } 23 | return response; // continue 24 | } 25 | 26 | @override 27 | onError(DioError err) { 28 | if (APPConfig.DEBUG) { 29 | print('请求异常: ' + err.toString()); 30 | print('请求异常信息: ' + err.response?.toString() ?? ""); 31 | } 32 | return err; 33 | } 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /lib/net/interceptors/response_Interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:it_resource_exchange_app/model/base_result.dart'; 3 | import 'package:it_resource_exchange_app/common/constant.dart' show APPConfig; 4 | 5 | class ResponseInterceptor extends InterceptorsWrapper { 6 | @override 7 | onResponse(Response response) { 8 | // RequestOptions options = response.request; 9 | try { 10 | if (response.statusCode == 200 || response.statusCode == 201) { //http code 11 | BaseResult result = BaseResult.fromJson(response.data); 12 | return result; 13 | }else { 14 | if (APPConfig.DEBUG) { 15 | print("ResponseInterceptor: $response.statusCode"); 16 | } 17 | } 18 | } catch(e) { 19 | if (APPConfig.DEBUG) { 20 | print("ResponseInterceptor: $e.toString() + options.path"); 21 | } 22 | return BaseResult(response.data, response.statusCode, e.toString()); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /lib/pages/application_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:it_resource_exchange_app/common/constant.dart' show AppColors; 3 | import 'package:it_resource_exchange_app/routes/it_router.dart'; 4 | import 'package:it_resource_exchange_app/routes/routes.dart'; 5 | import 'classify/classify_page.dart'; 6 | import 'home/home_page.dart'; 7 | import 'profile/profile_page.dart'; 8 | import 'movie/movie_cate_list_page.dart'; 9 | 10 | class ApplicationPage extends StatefulWidget { 11 | @override 12 | _ApplicationPageState createState() => _ApplicationPageState(); 13 | } 14 | 15 | class _ApplicationPageState extends State 16 | with SingleTickerProviderStateMixin { 17 | int page = 0; 18 | String title = '首页'; 19 | PageController pageController; 20 | IconButton _searchBtn; 21 | 22 | //定义底部导航项目 23 | final List _bottomTabs = [ 24 | BottomNavigationBarItem( 25 | icon: Icon(Icons.home), 26 | title: Text('首页'), 27 | backgroundColor: AppColors.PrimaryColor, 28 | ), 29 | BottomNavigationBarItem( 30 | icon: Icon(Icons.video_library), 31 | title: Text('娱乐'), 32 | backgroundColor: AppColors.PrimaryColor, 33 | ), 34 | BottomNavigationBarItem( 35 | icon: Icon(Icons.tune), 36 | title: Text('学习'), 37 | backgroundColor: AppColors.PrimaryColor, 38 | ), 39 | BottomNavigationBarItem( 40 | icon: Icon(Icons.person), 41 | title: Text('我的'), 42 | backgroundColor: AppColors.PrimaryColor, 43 | ), 44 | ]; 45 | 46 | @override 47 | void initState() { 48 | super.initState(); 49 | _searchBtn = IconButton( 50 | icon: Icon(Icons.search), 51 | onPressed: () { 52 | ITRouter.push(context, Routes.movieSearchPage, {}); 53 | }, 54 | ); 55 | pageController = PageController(initialPage: this.page); 56 | } 57 | 58 | @override 59 | void dispose() { 60 | pageController.dispose(); 61 | super.dispose(); 62 | } 63 | 64 | @override 65 | Widget build(BuildContext context) { 66 | List actions = this.page != 1 ? [] : [_searchBtn]; 67 | return Scaffold( 68 | appBar: AppBar( 69 | title: Text( 70 | this.title, 71 | style: TextStyle(color: Colors.white), 72 | ), 73 | elevation: 0.0, 74 | actions: actions, 75 | ), 76 | body: PageView( 77 | physics: NeverScrollableScrollPhysics(), 78 | 79 | /// 去除滑动手势 80 | children: [ 81 | HomePage(), 82 | MovieCateListPage(), 83 | ClassifyPage(), 84 | ProfilePage() 85 | ], 86 | controller: pageController, 87 | onPageChanged: (int index) { 88 | onPageChanged(index); 89 | }, 90 | ), 91 | bottomNavigationBar: BottomNavigationBar( 92 | items: _bottomTabs, 93 | currentIndex: page, 94 | fixedColor: AppColors.PrimaryColor, 95 | type: BottomNavigationBarType.fixed, 96 | onTap: (int index) { 97 | onTap(index); 98 | }, 99 | ), 100 | ); 101 | } 102 | 103 | void onTap(int index) { 104 | pageController.jumpToPage(index); 105 | } 106 | 107 | void onPageChanged(int page) { 108 | setState(() { 109 | this.page = page; 110 | switch (page) { 111 | case 0: 112 | title = "首页"; 113 | break; 114 | case 1: 115 | title = "娱乐"; 116 | break; 117 | case 2: 118 | title = "学习"; 119 | break; 120 | case 3: 121 | title = "我的"; 122 | break; 123 | } 124 | }); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /lib/pages/classify/classify_item_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:it_resource_exchange_app/common/constant.dart' 3 | show AppSize, AppColors; 4 | import 'package:cached_network_image/cached_network_image.dart'; 5 | import '../../model/home_info.dart'; 6 | import 'package:intl/intl.dart'; 7 | 8 | class ClassifyItemView extends StatelessWidget { 9 | const ClassifyItemView({Key key, this.recomendProduct, this.onPressed}) 10 | : assert(recomendProduct != null), 11 | super(key: key); 12 | 13 | final RecommendProductList recomendProduct; 14 | 15 | final VoidCallback onPressed; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | var format = new DateFormat('yyyy-MM-dd HH:mm'); 20 | var date = DateTime.fromMillisecondsSinceEpoch(recomendProduct.createdTime); 21 | var createDateStr = format.format(date); 22 | 23 | return GestureDetector( 24 | onTap: this.onPressed, 25 | child: Container( 26 | height: 100.0, 27 | padding: EdgeInsets.only(left: 10.0, right: 10.0), 28 | decoration: BoxDecoration( 29 | border: Border( 30 | bottom: BorderSide( 31 | width: AppSize.DividerWidth, 32 | color: AppColors.DividerColor, 33 | ))), 34 | child: Row( 35 | children: [ 36 | Expanded( 37 | child: Column( 38 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 39 | crossAxisAlignment: CrossAxisAlignment.start, 40 | children: [ 41 | Container( 42 | padding: EdgeInsets.only(top: 10.0), 43 | child: Text( 44 | recomendProduct.productTitle, 45 | style: TextStyle(fontSize: 15.0, color: AppColors.DarkTextColor), 46 | ), 47 | ), 48 | Container( 49 | padding: EdgeInsets.only(bottom: 10.0), 50 | child: Row( 51 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 52 | children: [ 53 | Text(createDateStr, 54 | style: TextStyle( 55 | color: AppColors.LightTextColor, 56 | fontSize: 12.0)), 57 | Row( 58 | mainAxisAlignment: MainAxisAlignment.end, 59 | children: [ 60 | // Text( 61 | // '0', 62 | // style: TextStyle( 63 | // color: AppColors.LightTextColor, 64 | // fontSize: 12.0), 65 | // ), 66 | // SizedBox(width: 5.0), 67 | Image.asset('./assets/imgs/ic_comment.png', 68 | width: 16.0, height: 16.0), 69 | ], 70 | ), 71 | ], 72 | ), 73 | ) 74 | ], 75 | ), 76 | ), 77 | Container( 78 | padding: EdgeInsets.only(left: 6.0), 79 | width: 120.0, 80 | height: 80.0, 81 | child: CachedNetworkImage( 82 | imageUrl: recomendProduct.coverUrl, 83 | placeholder: (context, url) => Image.asset('./assets/imgs/img_default.png'), 84 | fit: BoxFit.cover, 85 | ), 86 | ), 87 | ], 88 | )), 89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/pages/classify/classify_list_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:it_resource_exchange_app/routes/it_router.dart'; 4 | import 'package:it_resource_exchange_app/routes/routes.dart'; 5 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 6 | import 'package:it_resource_exchange_app/widgets/indicator_factory.dart'; 7 | import 'package:it_resource_exchange_app/pages/classify/classify_item_view.dart'; 8 | import 'package:it_resource_exchange_app/model/cate_info.dart'; 9 | import 'package:it_resource_exchange_app/net/network_utils.dart'; 10 | import 'package:it_resource_exchange_app/model/page_result.dart'; 11 | import "package:it_resource_exchange_app/model/home_info.dart"; 12 | import 'package:it_resource_exchange_app/widgets/load_state_layout_widget.dart'; 13 | 14 | class ClassifyListView extends StatefulWidget { 15 | final CateInfo cate; 16 | 17 | ClassifyListView(this.cate); 18 | 19 | @override 20 | _ClassifyListViewState createState() => _ClassifyListViewState(); 21 | } 22 | 23 | class _ClassifyListViewState extends State 24 | with AutomaticKeepAliveClientMixin { 25 | //页面加载状态,默认为加载中 26 | LoadState _layoutState = LoadState.State_Loading; 27 | 28 | RefreshController _refreshController; 29 | 30 | PageResult pageResult; 31 | List productList = []; 32 | 33 | @override 34 | void initState() { 35 | super.initState(); 36 | _refreshController = RefreshController(); 37 | loadData(); 38 | } 39 | 40 | @override 41 | bool get wantKeepAlive => true; 42 | 43 | SmartRefresher _buildRefreshListView() { 44 | return SmartRefresher( 45 | controller: _refreshController, 46 | enablePullUp: true, 47 | enablePullDown: true, 48 | header: buildDefaultHeader(), 49 | footer: buildDefaultFooter(), 50 | onRefresh: () { 51 | loadData(loadMore: false); 52 | }, 53 | onLoading: () { 54 | loadData(loadMore: true); 55 | }, 56 | child: ListView.builder( 57 | itemCount: productList.length, 58 | itemBuilder: (context, index) { 59 | return ClassifyItemView( 60 | recomendProduct: productList[index], 61 | onPressed: () { 62 | int productId = productList[index].productId; 63 | ITRouter.push(context, Routes.productDetailPage, {'productId': productId}); 64 | }, 65 | ); 66 | }, 67 | ), 68 | ); 69 | } 70 | 71 | @override 72 | Widget build(BuildContext context) { 73 | super.build(context); 74 | return Container( 75 | color: Colors.white, 76 | child: LoadStateLayout( 77 | state: _layoutState, 78 | errorRetry: () { 79 | setState(() { 80 | _layoutState = LoadState.State_Loading; 81 | }); 82 | this.loadData(); 83 | }, 84 | successWidget: _buildRefreshListView(), 85 | ), 86 | ); 87 | } 88 | 89 | void loadData({bool loadMore = false}) { 90 | int page = (pageResult == null || loadMore == false) 91 | ? 1 92 | : pageResult.currentPage + 1; 93 | NetworkUtils.requestProductListByCateId(this.widget.cate.cateId, page) 94 | .then((res) { 95 | if (res.status == 200) { 96 | pageResult = PageResult.fromJson(res.data); 97 | if (!this.mounted) { 98 | return; 99 | } 100 | if (loadMore) { 101 | if (pageResult.items.length > 0) { 102 | var tempList = pageResult.items 103 | .map((m) => RecommendProductList.fromJson(m)) 104 | .toList(); 105 | productList.addAll(tempList); 106 | _refreshController.loadComplete(); 107 | } else { 108 | _refreshController.loadNoData(); 109 | } 110 | setState(() {}); 111 | } else { 112 | if (pageResult.items.length == 0) { 113 | setState(() { 114 | _layoutState = LoadState.State_Empty; 115 | }); 116 | } else { 117 | productList = pageResult.items 118 | .map((m) => RecommendProductList.fromJson(m)) 119 | .toList(); 120 | _refreshController.refreshCompleted(); 121 | setState(() { 122 | _layoutState = LoadState.State_Success; 123 | }); 124 | } 125 | } 126 | } else { 127 | //请求失败 128 | if (loadMore) { 129 | _refreshController.loadComplete(); 130 | setState(() {}); 131 | } else { 132 | _refreshController.refreshFailed(); 133 | setState(() { 134 | _layoutState = loadStateByErrorCode(res.status); 135 | }); 136 | } 137 | } 138 | }); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /lib/pages/classify/classify_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'classify_list_view.dart'; 3 | import 'package:it_resource_exchange_app/net/network_utils.dart'; 4 | import 'package:it_resource_exchange_app/model/cate_info.dart'; 5 | import 'package:it_resource_exchange_app/common/constant.dart' show AppColors; 6 | import 'package:it_resource_exchange_app/widgets/load_state_layout_widget.dart'; 7 | 8 | class ClassifyPage extends StatefulWidget { 9 | @override 10 | _ClassifyPageState createState() => _ClassifyPageState(); 11 | } 12 | 13 | class _ClassifyPageState extends State with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin{ 14 | 15 | TabController _tabController; 16 | 17 | List cateList = []; 18 | 19 | //页面加载状态,默认为加载中 20 | LoadState _layoutState = LoadState.State_Loading; 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | loadData(); 26 | } 27 | 28 | @override 29 | void dispose() { 30 | _tabController.dispose(); 31 | super.dispose(); 32 | } 33 | 34 | @override 35 | bool get wantKeepAlive => true; 36 | 37 | loadData() { 38 | NetworkUtils.requestCategoryListData().then((res) { 39 | if (res.status == 200) { 40 | cateList = (res.data as List).map((m) =>CateInfo.fromJson(m)).toList(); 41 | //添加全部分类 42 | cateList.insert(0, CateInfo(null, 0, "全部", 0)); 43 | _tabController = TabController(vsync: this, length: cateList.length); 44 | setState(() { 45 | _layoutState = LoadState.State_Success; 46 | }); 47 | }else { 48 | setState(() { 49 | _layoutState = loadStateByErrorCode(res.status); 50 | }); 51 | } 52 | }); 53 | } 54 | 55 | Widget _buildTabPageView() { 56 | return Column( 57 | children: [ 58 | Container( 59 | decoration: BoxDecoration( 60 | color: Theme.of(context).canvasColor, 61 | border: Border( 62 | bottom: BorderSide( 63 | width: 0.0, 64 | color: AppColors.DividerColor, 65 | ) 66 | ) 67 | ), 68 | child: TabBar( 69 | controller: _tabController, 70 | isScrollable: true, 71 | labelColor: AppColors.PrimaryColor, 72 | indicatorColor: AppColors.PrimaryColor, 73 | tabs: cateList.map((cate){ 74 | return Tab(text: cate.cateTitle); 75 | }).toList() 76 | ), 77 | ), 78 | Expanded( 79 | child: TabBarView( 80 | controller: _tabController, 81 | children: cateList.map((CateInfo cate) { 82 | return ClassifyListView(cate); 83 | }).toList() 84 | ), 85 | ) 86 | ], 87 | ); 88 | } 89 | 90 | @override 91 | Widget build(BuildContext context) { 92 | super.build(context); 93 | return LoadStateLayout( 94 | state: _layoutState, 95 | errorRetry: () { 96 | setState(() { 97 | _layoutState = LoadState.State_Loading; 98 | }); 99 | this.loadData(); 100 | }, 101 | successWidget: _buildTabPageView(), 102 | ); 103 | } 104 | } -------------------------------------------------------------------------------- /lib/pages/collection/my_collection_item_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:it_resource_exchange_app/common/constant.dart' 3 | show AppSize, AppColors; 4 | import 'package:cached_network_image/cached_network_image.dart'; 5 | import '../../model/collect_product_info.dart'; 6 | import 'package:intl/intl.dart'; 7 | 8 | class MyCollectionItemView extends StatelessWidget { 9 | const MyCollectionItemView({Key key, this.product, this.onPressed}) 10 | : assert(product != null), 11 | super(key: key); 12 | 13 | final CollectProductInfo product; 14 | 15 | final VoidCallback onPressed; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | var format = new DateFormat('yyyy-MM-dd HH:mm'); 20 | var date = DateTime.fromMillisecondsSinceEpoch(product.createdTime); 21 | var createDateStr = format.format(date); 22 | 23 | return GestureDetector( 24 | onTap: this.onPressed, 25 | child: Container( 26 | height: 100.0, 27 | padding: EdgeInsets.only(left: 10.0, right: 10.0), 28 | decoration: BoxDecoration( 29 | border: Border( 30 | bottom: BorderSide( 31 | width: AppSize.DividerWidth, 32 | color: AppColors.DividerColor, 33 | ))), 34 | child: Row( 35 | children: [ 36 | Expanded( 37 | child: Column( 38 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 39 | crossAxisAlignment: CrossAxisAlignment.start, 40 | children: [ 41 | Container( 42 | padding: EdgeInsets.only(top: 10.0), 43 | child: Text( 44 | product.productTitle, 45 | style: TextStyle( 46 | fontSize: 15.0, color: AppColors.DarkTextColor), 47 | ), 48 | ), 49 | Container( 50 | padding: EdgeInsets.only(bottom: 10.0), 51 | child: Row( 52 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 53 | children: [ 54 | Text(product?.cateTitle ?? '', 55 | style: TextStyle( 56 | color: AppColors.LightTextColor, 57 | fontSize: 12.0)), 58 | Text(createDateStr, 59 | style: TextStyle( 60 | color: AppColors.LightTextColor, 61 | fontSize: 12.0)), 62 | ], 63 | ), 64 | ) 65 | ], 66 | ), 67 | ), 68 | Container( 69 | padding: EdgeInsets.only(left: 6.0), 70 | width: 120.0, 71 | height: 80.0, 72 | child: CachedNetworkImage( 73 | imageUrl: product.coverUrl, 74 | placeholder: (context, url) => 75 | Image.asset('./assets/imgs/img_default.png'), 76 | fit: BoxFit.cover, 77 | ), 78 | ), 79 | ], 80 | )), 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/pages/collection/my_collection_list_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:it_resource_exchange_app/model/collect_product_info.dart'; 3 | import 'package:it_resource_exchange_app/net/network_utils.dart'; 4 | import 'package:it_resource_exchange_app/routes/it_router.dart'; 5 | import 'package:it_resource_exchange_app/routes/routes.dart'; 6 | import 'package:it_resource_exchange_app/model/product_detail.dart'; 7 | import '../my_product_list/my_product_item_view.dart'; 8 | import 'package:it_resource_exchange_app/widgets/load_state_layout_widget.dart'; 9 | 10 | import 'my_collection_item_view.dart'; 11 | 12 | class MyCollectionListPage extends StatefulWidget { 13 | @override 14 | _MyCollectionListPageState createState() => _MyCollectionListPageState(); 15 | } 16 | 17 | class _MyCollectionListPageState extends State { 18 | //页面加载状态,默认为加载中 19 | LoadState _layoutState = LoadState.State_Loading; 20 | List productList = []; 21 | 22 | void initState() { 23 | super.initState(); 24 | loadData(); 25 | } 26 | 27 | loadData() { 28 | NetworkUtils.requestMyCollectionListData().then((res) { 29 | if (res.status == 200) { 30 | productList = (res.data as List) 31 | .map((m) => CollectProductInfo.fromJson(m)) 32 | .toList(); 33 | setState(() { 34 | _layoutState = LoadState.State_Success; 35 | }); 36 | } else { 37 | setState(() { 38 | _layoutState = loadStateByErrorCode(res.status); 39 | }); 40 | } 41 | }); 42 | } 43 | 44 | _buildListView() { 45 | return ListView.builder( 46 | itemCount: productList.length, 47 | itemBuilder: (context, index) { 48 | return MyCollectionItemView( 49 | product: productList[index], 50 | onPressed: () { 51 | CollectProductInfo product = productList[index]; 52 | int productId = product.productId; 53 | ITRouter.push( 54 | context, Routes.productDetailPage, {'productId': productId}); 55 | }, 56 | ); 57 | }, 58 | ); 59 | } 60 | 61 | @override 62 | Widget build(BuildContext context) { 63 | return Scaffold( 64 | backgroundColor: Colors.white, 65 | appBar: AppBar( 66 | title: Text( 67 | "我的收藏", 68 | style: TextStyle(color: Colors.white), 69 | ), 70 | elevation: 0.0, 71 | iconTheme: IconThemeData( 72 | color: Colors.white, 73 | ), 74 | ), 75 | body: LoadStateLayout( 76 | state: _layoutState, 77 | errorRetry: () { 78 | setState(() { 79 | _layoutState = LoadState.State_Loading; 80 | }); 81 | this.loadData(); 82 | }, 83 | successWidget: _buildListView(), 84 | ), 85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lib/pages/create/new_goods_preview_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:multi_image_picker/multi_image_picker.dart'; 4 | import 'package:it_resource_exchange_app/common/constant.dart' 5 | show AppSize, AppColors, APPIcons, Constant; 6 | import 'package:it_resource_exchange_app/vo/new_product_vo.dart' 7 | show NewProductImgVo; 8 | import 'package:cached_network_image/cached_network_image.dart'; 9 | 10 | class NewGoodsPreviewWidget extends StatefulWidget { 11 | const NewGoodsPreviewWidget( 12 | {Key key, 13 | this.imgVoList, 14 | this.onPressed, 15 | this.removePressd, 16 | this.addPressd}) 17 | : super(key: key); 18 | 19 | final List imgVoList; 20 | final VoidCallback onPressed; 21 | final ValueChanged removePressd; 22 | final VoidCallback addPressd; 23 | 24 | @override 25 | _NewGoodsPreviewWidgetState createState() => _NewGoodsPreviewWidgetState(); 26 | } 27 | 28 | class _NewGoodsPreviewWidgetState extends State { 29 | @override 30 | Widget build(BuildContext context) { 31 | final screenWidth = MediaQuery.of(context).size.width; 32 | 33 | var itemSpace = 8.0; 34 | 35 | var itemWidth = ((screenWidth - 2 * 30.0) - 4 * itemSpace) / 3.0; 36 | 37 | Widget addWidget = GestureDetector( 38 | child: Container( 39 | decoration: BoxDecoration( 40 | border: Border.all( 41 | color: AppColors.DividerColor, width: AppSize.DividerWidth), 42 | borderRadius: BorderRadius.circular(itemSpace), 43 | ), 44 | child: Icon( 45 | APPIcons.AddImgData, 46 | size: itemWidth.toInt().toDouble() - 2 * AppSize.DividerWidth, 47 | color: AppColors.PrimaryColor, 48 | ), 49 | ), 50 | onTap: () { 51 | this.widget.addPressd(); 52 | }, 53 | ); 54 | 55 | List previewList = []; 56 | if (this.widget.imgVoList.length < 6) { 57 | previewList.add(addWidget); 58 | } 59 | 60 | var itemList = this.widget.imgVoList.map((imgVo) { 61 | // 删除按钮 62 | Positioned removeBtn = Positioned( 63 | right: -5, 64 | top: -5, 65 | child: IconButton( 66 | icon: Icon(IconData( 67 | 0xe622, 68 | fontFamily: Constant.IconFontFamily, 69 | )), 70 | onPressed: () { 71 | int index = this.widget.imgVoList.indexOf(imgVo); 72 | this.widget.removePressd(index); 73 | }), 74 | ); 75 | 76 | Widget imgWidget; 77 | if (imgVo.url == null) { 78 | imgWidget = AssetThumb( 79 | asset: imgVo.asset, 80 | width: itemWidth.toInt(), 81 | height: itemWidth.toInt()); 82 | } else { 83 | imgWidget = CachedNetworkImage( 84 | imageUrl: imgVo.url, 85 | placeholder: (context, url) { 86 | return Container( 87 | decoration: BoxDecoration( 88 | border: Border.all( 89 | color: AppColors.BackgroundColor, 90 | width: AppSize.DividerWidth), 91 | borderRadius: BorderRadius.circular(2), 92 | ), 93 | width: itemWidth.toInt().toDouble(), 94 | height: itemWidth.toInt().toDouble(), 95 | child: Center( 96 | child: Icon( 97 | APPIcons.AddImgData, 98 | color: AppColors.PrimaryColor, 99 | size: 25, 100 | ), 101 | ), 102 | ); 103 | }, 104 | fit: BoxFit.cover, 105 | height: itemWidth.toInt().toDouble(), 106 | width: itemWidth.toInt().toDouble(), 107 | errorWidget: (context, url, error) => new Icon(Icons.error), 108 | ); 109 | } 110 | 111 | return Stack( 112 | overflow: Overflow.clip, 113 | children: [imgWidget, removeBtn], 114 | ); 115 | }).toList(); 116 | 117 | previewList.insertAll(0, itemList); 118 | 119 | return Container( 120 | decoration: BoxDecoration( 121 | border: Border.all( 122 | color: AppColors.DividerColor, width: AppSize.DividerWidth), 123 | borderRadius: BorderRadius.circular(itemSpace), 124 | ), 125 | padding: EdgeInsets.symmetric(horizontal: itemSpace, vertical: itemSpace), 126 | width: double.infinity, 127 | height: 3 * itemSpace + itemWidth * 2, 128 | child: Wrap( 129 | spacing: itemSpace, 130 | runSpacing: itemSpace, 131 | children: previewList, 132 | ), 133 | ); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /lib/pages/create/new_goods_text_field.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:it_resource_exchange_app/common/constant.dart' 3 | show AppSize, AppColors; 4 | import 'package:flutter/services.dart'; 5 | 6 | class NewGoodsTextField extends StatelessWidget { 7 | const NewGoodsTextField({Key key, this.controller, this.inputFormatters, this.keyboardType, this.hintText, this.onChanged}) 8 | : super(key: key); 9 | 10 | final TextEditingController controller; 11 | 12 | final String hintText; 13 | 14 | final ValueChanged onChanged; 15 | 16 | final List inputFormatters; 17 | 18 | final TextInputType keyboardType; 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Container( 23 | decoration: BoxDecoration( 24 | border: Border( 25 | bottom: BorderSide( 26 | width: AppSize.DividerWidth, 27 | color: AppColors.DividerColor, 28 | ), 29 | ), 30 | ), 31 | child: Column( 32 | children: [ 33 | SizedBox(height: 5), 34 | TextField( 35 | controller: controller, 36 | inputFormatters: inputFormatters, 37 | keyboardType: keyboardType, 38 | decoration: 39 | InputDecoration(hintText: hintText, border: InputBorder.none), 40 | onChanged: onChanged, 41 | ) 42 | ], 43 | ), 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/pages/detail/comment_view/goods_comment_content_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:it_resource_exchange_app/model/comment_model.dart'; 3 | import 'package:it_resource_exchange_app/utils/user_utils.dart'; 4 | import 'package:it_resource_exchange_app/vo/comment_vo.dart'; 5 | 6 | import 'goods_comment_item_view.dart'; 7 | import 'goods_comment_reply_view.dart'; 8 | 9 | class GoodsCommentContentView extends StatelessWidget { 10 | final CommentModel commentModel; 11 | 12 | final ValueChanged tapCallback; 13 | 14 | const GoodsCommentContentView({Key key, this.commentModel, this.tapCallback}) 15 | : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return CustomScrollView( 20 | shrinkWrap: true, 21 | physics: NeverScrollableScrollPhysics(), 22 | slivers: [ 23 | SliverToBoxAdapter( 24 | child: GestureDetector( 25 | child: GoodsCommentItemView( 26 | commentModel: commentModel, 27 | ), 28 | onTap: UserUtils.getUserInfo().userId != this.commentModel.createUserId ? () { 29 | this.tapCallback(CommentVO(index: -1, commentModel: commentModel)); 30 | } : null, 31 | ), 32 | ), 33 | SliverList( 34 | delegate: SliverChildBuilderDelegate( 35 | (context, int index) { 36 | return GestureDetector( 37 | child: GoodsCommentReplyView(commentModel: this.commentModel.commentList[index],), 38 | onTap: UserUtils.getUserInfo().userId != this.commentModel.commentList[index].createUserId ? () { 39 | this.tapCallback(CommentVO(index: index, commentModel: commentModel)); 40 | } : null, 41 | ); 42 | }, 43 | childCount: commentModel.commentList?.length ?? 0, 44 | ), 45 | ) 46 | ], 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/pages/detail/comment_view/goods_comment_header_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:it_resource_exchange_app/common/constant.dart'; 3 | 4 | class GoodsCommentHeaderView extends StatelessWidget { 5 | 6 | final int commentCount; 7 | 8 | const GoodsCommentHeaderView({Key key, this.commentCount}) : super(key: key); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Container( 13 | height: 45, 14 | child: Padding( 15 | padding: EdgeInsets.only(left: 15), 16 | child: Column( 17 | crossAxisAlignment: CrossAxisAlignment.start, 18 | mainAxisAlignment: MainAxisAlignment.center, 19 | children: [ 20 | Text( 21 | '共 $commentCount 条评论', 22 | style: TextStyle( 23 | color: AppColors.DarkTextColor, fontWeight: FontWeight.w700), 24 | ) 25 | ], 26 | ), 27 | ), 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/pages/detail/comment_view/goods_comment_item_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:cached_network_image/cached_network_image.dart'; 3 | import 'package:intl/intl.dart'; 4 | import 'package:it_resource_exchange_app/common/constant.dart'; 5 | import 'package:it_resource_exchange_app/model/comment_model.dart'; 6 | 7 | class GoodsCommentItemView extends StatelessWidget { 8 | 9 | final CommentModel commentModel; 10 | 11 | const GoodsCommentItemView({Key key, this.commentModel}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | String avatarUrl; 16 | 17 | Widget avatar; 18 | 19 | if (avatarUrl != null) { 20 | avatar = CachedNetworkImage( 21 | imageUrl: avatarUrl, 22 | placeholder: (context, url) => APPIcons.PlaceHolderAvatar, 23 | fit: BoxFit.cover, 24 | height: 35.0, 25 | width: 35.0, 26 | errorWidget: (context, url, error) => new Icon(Icons.error), 27 | ); 28 | } else { 29 | avatar = Icon( 30 | APPIcons.AvatarData, 31 | size: 35.0, 32 | color: AppColors.ArrowNormalColor, 33 | ); 34 | } 35 | 36 | var createDateStr = "未知"; 37 | if (commentModel?.createdTime != null) { 38 | var format = new DateFormat('yyyy-MM-dd HH:mm'); 39 | var date = DateTime.fromMillisecondsSinceEpoch(commentModel.createdTime); 40 | createDateStr = format.format(date); 41 | } 42 | 43 | 44 | return Container( 45 | decoration: BoxDecoration( 46 | border: Border( 47 | top: BorderSide( 48 | width: AppSize.DividerWidth, 49 | color: AppColors.DividerColor, 50 | ), 51 | ), 52 | ), 53 | child: Row( 54 | crossAxisAlignment: CrossAxisAlignment.start, 55 | children: [ 56 | Padding(padding: const EdgeInsets.all(10.0), child: avatar), 57 | Expanded( 58 | child: Container( 59 | margin: EdgeInsets.symmetric(vertical: 10.0), 60 | child: Column( 61 | crossAxisAlignment: CrossAxisAlignment.start, 62 | children: [ 63 | Row( 64 | children: [ 65 | Expanded( 66 | child: Text( 67 | commentModel.createUserName ?? "未知", 68 | style: TextStyle(color: const Color(0xFF63CA6C)), 69 | ), 70 | ), 71 | Padding( 72 | padding: const EdgeInsets.fromLTRB(0.0, 0.0, 10.0, 0.0), 73 | child: Text( 74 | createDateStr, 75 | style: TextStyle( 76 | fontSize: 12.0, color: const Color(0xFFB5BDC0)), 77 | ), 78 | ), 79 | ], 80 | ), 81 | SizedBox( 82 | height: 5, 83 | ), 84 | Padding( 85 | padding: const EdgeInsets.only(right: 10.0), 86 | child: Text( 87 | commentModel.content ?? '', 88 | style: TextStyle(fontSize: 15.0, color: Colors.black), 89 | ), 90 | ), 91 | ], 92 | ), 93 | )) 94 | ], 95 | ), 96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/pages/detail/comment_view/goods_comment_reply_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:cached_network_image/cached_network_image.dart'; 3 | import 'package:intl/intl.dart'; 4 | import 'package:it_resource_exchange_app/common/constant.dart'; 5 | import 'package:it_resource_exchange_app/model/comment_model.dart'; 6 | 7 | class GoodsCommentReplyView extends StatelessWidget { 8 | final CommentModel commentModel; 9 | 10 | const GoodsCommentReplyView({Key key, this.commentModel}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | String avatarUrl; 15 | 16 | Widget avatar; 17 | if (avatarUrl != null) { 18 | avatar = CachedNetworkImage( 19 | imageUrl: avatarUrl, 20 | placeholder: (context, url) => APPIcons.PlaceHolderAvatar, 21 | fit: BoxFit.cover, 22 | height: 20.0, 23 | width: 20.0, 24 | errorWidget: (context, url, error) => new Icon(Icons.error), 25 | ); 26 | } else { 27 | avatar = Icon( 28 | APPIcons.AvatarData, 29 | size: 20.0, 30 | color: AppColors.ArrowNormalColor, 31 | ); 32 | } 33 | 34 | var createDateStr = "未知"; 35 | if (commentModel?.createdTime != null) { 36 | var format = new DateFormat('yyyy-MM-dd HH:mm'); 37 | var date = DateTime.fromMillisecondsSinceEpoch(commentModel.createdTime); 38 | createDateStr = format.format(date); 39 | } 40 | 41 | return Container( 42 | margin: EdgeInsets.fromLTRB(55.0, 0, 0, 10), 43 | decoration: BoxDecoration( 44 | border: Border( 45 | top: BorderSide( 46 | width: AppSize.DividerWidth, 47 | color: AppColors.DividerColor, 48 | ), 49 | ), 50 | ), 51 | child: Column( 52 | crossAxisAlignment: CrossAxisAlignment.start, 53 | children: [ 54 | SizedBox( 55 | height: 10, 56 | ), 57 | Row( 58 | children: [ 59 | avatar, 60 | SizedBox( 61 | width: 5, 62 | ), 63 | Expanded( 64 | child: Text( 65 | this.commentModel?.createUserName ?? "未知", 66 | style: TextStyle(color: const Color(0xFF63CA6C)), 67 | ), 68 | ), 69 | Padding( 70 | padding: EdgeInsets.only(right: 10.0), 71 | child: Text( 72 | createDateStr, 73 | style: 74 | TextStyle(fontSize: 12.0, color: const Color(0xFFB5BDC0)), 75 | ), 76 | ) 77 | ], 78 | ), 79 | Padding( 80 | padding: const EdgeInsets.fromLTRB(25.0, 5.0, 10.0, 0.0), 81 | child: Text( 82 | this.commentModel.content ?? '', 83 | style: TextStyle(fontSize: 15.0, color: Colors.black), 84 | ), 85 | ) 86 | ], 87 | ), 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /lib/pages/detail/goods_detail_bottom_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:it_resource_exchange_app/common/constant.dart'; 4 | 5 | class GoodsCommentBottomBar extends StatelessWidget { 6 | final ValueChanged btnActionCallback; 7 | final isCollect; 8 | 9 | const GoodsCommentBottomBar({Key key, this.isCollect, this.btnActionCallback}) 10 | : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | var collectIcon; 15 | if (isCollect) { 16 | collectIcon = Icon( 17 | APPIcons.CollectSelectData, 18 | color: AppColors.PrimaryColor, 19 | ); 20 | } else { 21 | collectIcon = Icon( 22 | APPIcons.CollectionData, 23 | color: AppColors.ArrowNormalColor, 24 | ); 25 | } 26 | 27 | IconButton favoriteBtn = IconButton( 28 | icon: collectIcon, 29 | onPressed: () { 30 | this.btnActionCallback(100); 31 | }, 32 | ); 33 | 34 | Widget commentBtn = GestureDetector( 35 | onTap: () { 36 | this.btnActionCallback(200); 37 | }, 38 | child: ClipRRect( 39 | borderRadius: BorderRadius.circular(20), 40 | child: Container( 41 | width: 220, 42 | height: 40, 43 | color: AppColors.DividerColor, 44 | child: Row( 45 | children: [ 46 | SizedBox( 47 | width: 20, 48 | ), 49 | Text('说点什么吧', 50 | style: 51 | TextStyle(fontSize: 13, color: AppColors.LightTextColor)), 52 | ], 53 | ), 54 | ), 55 | ), 56 | ); 57 | 58 | return Container( 59 | decoration: BoxDecoration( 60 | border: Border( 61 | top: BorderSide( 62 | width: AppSize.DividerWidth, 63 | color: AppColors.DividerColor, 64 | ), 65 | ), 66 | ), 67 | width: MediaQuery.of(context).size.width, 68 | height: MediaQuery.of(context).padding.bottom + 50, 69 | padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), 70 | child: Row( 71 | crossAxisAlignment: CrossAxisAlignment.start, 72 | children: [ 73 | favoriteBtn, 74 | Expanded(child: SizedBox()), 75 | commentBtn 76 | ], 77 | ), 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/pages/detail/input_dialog/bottom_input_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:it_resource_exchange_app/common/constant.dart'; 3 | import 'package:oktoast/oktoast.dart'; 4 | 5 | class BottomInputDialog extends StatelessWidget { 6 | TextEditingController inputController = TextEditingController(); 7 | 8 | ValueChanged callback; 9 | 10 | BottomInputDialog({this.callback}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return new Scaffold( 15 | backgroundColor: Colors.transparent, 16 | body: new Column( 17 | children: [ 18 | Expanded( 19 | child: new GestureDetector( 20 | child: new Container(color: Colors.black38), 21 | onTap: () { 22 | Navigator.pop(context); 23 | }, 24 | )), 25 | new Container( 26 | height: 80, 27 | color: AppColors.MidTextColor, 28 | child: Padding( 29 | padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10), 30 | child: Row( 31 | children: [ 32 | Expanded( 33 | child: TextField( 34 | controller: inputController, 35 | decoration: InputDecoration( 36 | filled: true, 37 | fillColor: Colors.white, 38 | hintText: '留下你的精彩吧!', 39 | border: InputBorder.none, 40 | ), 41 | maxLines: 100, 42 | autofocus: true, 43 | textInputAction: TextInputAction.newline, 44 | ), 45 | ), 46 | SizedBox(width: 10,), 47 | MaterialButton( 48 | color: AppColors.PrimaryColor, 49 | textColor: Colors.white, 50 | clipBehavior:Clip.antiAlias, 51 | child: Text('发布', style:TextStyle(fontSize:14)), 52 | onPressed: () { 53 | String content = inputController.text; 54 | if (content == null || content.isEmpty) { 55 | showToast('评论内容不能为空'); 56 | }else { 57 | this.callback(content); 58 | Navigator.of(context).pop(); 59 | } 60 | }, 61 | ) 62 | ], 63 | ), 64 | )) 65 | ], 66 | ), 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/pages/detail/input_dialog/pop_bottom_input_dialog_route.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class PopBottomInputDialogRoute extends PopupRoute{ 4 | final Duration _duration = Duration(milliseconds: 300); 5 | Widget child; 6 | 7 | PopBottomInputDialogRoute({@required this.child}); 8 | 9 | @override 10 | Color get barrierColor => null; 11 | 12 | @override 13 | bool get barrierDismissible => true; 14 | 15 | @override 16 | String get barrierLabel => null; 17 | 18 | @override 19 | Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) { 20 | return child; 21 | } 22 | 23 | @override 24 | Duration get transitionDuration => _duration; 25 | 26 | } -------------------------------------------------------------------------------- /lib/pages/home/goods_item_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:it_resource_exchange_app/common/constant.dart' 3 | show AppSize, AppColors; 4 | import 'package:cached_network_image/cached_network_image.dart'; 5 | import '../../model/home_info.dart'; 6 | import 'package:intl/intl.dart'; 7 | 8 | class GoodsItemView extends StatelessWidget { 9 | const GoodsItemView({Key key, this.recomendProduct, this.onPressed}) 10 | : assert(recomendProduct != null), 11 | super(key: key); 12 | 13 | final RecommendProductList recomendProduct; 14 | 15 | final VoidCallback onPressed; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | var format = new DateFormat('yyyy-MM-dd HH:mm'); 20 | var date = DateTime.fromMillisecondsSinceEpoch(recomendProduct.createdTime); 21 | var createDateStr = format.format(date); 22 | 23 | return GestureDetector( 24 | onTap: this.onPressed, 25 | child: Container( 26 | height: 100.0, 27 | padding: EdgeInsets.only(left: 10.0, right: 10.0), 28 | decoration: BoxDecoration( 29 | border: Border( 30 | bottom: BorderSide( 31 | width: AppSize.DividerWidth, 32 | color: AppColors.DividerColor, 33 | ))), 34 | child: Row( 35 | children: [ 36 | Expanded( 37 | child: Column( 38 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 39 | crossAxisAlignment: CrossAxisAlignment.start, 40 | children: [ 41 | Container( 42 | padding: EdgeInsets.only(top: 10.0), 43 | child: Text( 44 | recomendProduct.productTitle, 45 | style: TextStyle( 46 | fontSize: 15.0, color: AppColors.DarkTextColor), 47 | ), 48 | ), 49 | Container( 50 | padding: EdgeInsets.only(bottom: 10.0), 51 | child: Row( 52 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 53 | children: [ 54 | Text(recomendProduct?.cateTitle ?? '', 55 | style: TextStyle( 56 | color: AppColors.LightTextColor, 57 | fontSize: 12.0)), 58 | Text(createDateStr, 59 | style: TextStyle( 60 | color: AppColors.LightTextColor, 61 | fontSize: 12.0)), 62 | ], 63 | ), 64 | ) 65 | ], 66 | ), 67 | ), 68 | Container( 69 | padding: EdgeInsets.only(left: 6.0), 70 | width: 120.0, 71 | height: 80.0, 72 | child: CachedNetworkImage( 73 | imageUrl: recomendProduct.coverUrl, 74 | placeholder: (context, url) => 75 | Image.asset('./assets/imgs/img_default.png'), 76 | fit: BoxFit.cover, 77 | ), 78 | ), 79 | ], 80 | )), 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/pages/home/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_swiper/flutter_swiper.dart'; 3 | import 'package:it_resource_exchange_app/routes/it_router.dart'; 4 | import 'package:it_resource_exchange_app/routes/routes.dart'; 5 | import './goods_item_view.dart'; 6 | import 'package:it_resource_exchange_app/net/network_utils.dart'; 7 | import 'package:it_resource_exchange_app/model/home_info.dart'; 8 | import 'package:it_resource_exchange_app/common/constant.dart' 9 | show AppColors, AppSize, APPIcons; 10 | import 'package:cached_network_image/cached_network_image.dart'; 11 | import 'package:it_resource_exchange_app/widgets/load_state_layout_widget.dart'; 12 | 13 | class HomePage extends StatefulWidget { 14 | @override 15 | _HomePageState createState() => _HomePageState(); 16 | } 17 | 18 | class _HomePageState extends State 19 | with AutomaticKeepAliveClientMixin { 20 | HomeInfo homeInfo; 21 | List bannerInfoList = []; 22 | 23 | //页面加载状态,默认为加载中 24 | LoadState _layoutState = LoadState.State_Loading; 25 | 26 | void initState() { 27 | super.initState(); 28 | loadData(); 29 | } 30 | 31 | @override 32 | bool get wantKeepAlive => true; 33 | 34 | loadData() async { 35 | NetworkUtils.requestHomeAdvertisementsAndRecommendProductsData() 36 | .then((res) { 37 | if (res.status == 200) { 38 | homeInfo = HomeInfo.fromJson(res.data); 39 | setState(() { 40 | _layoutState = LoadState.State_Success; 41 | }); 42 | } else { 43 | setState(() { 44 | _layoutState = loadStateByErrorCode(res.status); 45 | }); 46 | } 47 | }); 48 | } 49 | 50 | Widget _buildListView() { 51 | return ListView.builder( 52 | physics: AlwaysScrollableScrollPhysics(), 53 | padding: EdgeInsets.all(1.0), 54 | itemCount: homeInfo == null ? 0 : (1 + homeInfo.recommendProductList.length), 55 | itemBuilder: (context, i) { 56 | if (i == 0) { 57 | return Container( 58 | height: 220, 59 | child: Swiper( 60 | itemBuilder: (BuildContext context, int index) { 61 | return CachedNetworkImage( 62 | imageUrl: homeInfo.advertiseList[index].adCoverUrl, 63 | placeholder: (context, url) { 64 | return Container( 65 | decoration: BoxDecoration( 66 | border: Border.all( 67 | color: AppColors.BackgroundColor, 68 | width: AppSize.DividerWidth), 69 | borderRadius: BorderRadius.circular(2), 70 | ), 71 | width: double.infinity, 72 | height: double.infinity, 73 | child: Center( 74 | child: Icon( 75 | APPIcons.AddImgData, 76 | color: AppColors.PrimaryColor, 77 | size: 40, 78 | ), 79 | ), 80 | ); 81 | }, 82 | fit: BoxFit.cover, 83 | ); 84 | }, 85 | autoplay: true, 86 | itemCount: homeInfo.advertiseList.length, 87 | pagination: SwiperPagination(builder: SwiperPagination.dots), 88 | control: null, 89 | onTap: (index) { 90 | AdvertiseList advertise = homeInfo.advertiseList[index]; 91 | if (advertise.adType == 1) { 92 | String productId = homeInfo.advertiseList[index].adProductId; 93 | ITRouter.push(context, Routes.productDetailPage, {'productId': productId}); 94 | } else { 95 | ITRouter.push(context, Routes.webPage, {'title': advertise.adTitle, 'url': advertise.adDetailUrl}); 96 | } 97 | }, 98 | ), 99 | ); 100 | } else { 101 | return GoodsItemView( 102 | recomendProduct: homeInfo.recommendProductList[i - 1], 103 | onPressed: () { 104 | int productId = homeInfo.recommendProductList[i - 1].productId; 105 | ITRouter.push(context, Routes.productDetailPage, {'productId': productId}); 106 | }, 107 | ); 108 | } 109 | }, 110 | ); 111 | } 112 | 113 | @override 114 | Widget build(BuildContext context) { 115 | super.build(context); 116 | return LoadStateLayout( 117 | state: _layoutState, 118 | errorRetry: () { 119 | setState(() { 120 | _layoutState = LoadState.State_Loading; 121 | }); 122 | this.loadData(); 123 | }, 124 | successWidget: _buildListView(), 125 | ); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /lib/pages/login/perfect_info_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:it_resource_exchange_app/common/constant.dart'; 4 | 5 | class PerfectInfoPage extends StatefulWidget { 6 | @override 7 | _PerfectInfoPageState createState() => _PerfectInfoPageState(); 8 | } 9 | 10 | class _PerfectInfoPageState extends State { 11 | String _avatarURL; 12 | 13 | String _nickName; 14 | 15 | _buildIconView() { 16 | Widget avatar; 17 | if (_avatarURL != null) { 18 | avatar = CachedNetworkImage( 19 | imageUrl: _avatarURL, 20 | placeholder: (context, url) => APPIcons.PlaceHolderAvatar, 21 | fit: BoxFit.cover, 22 | height: 80.0, 23 | width: 80.0, 24 | errorWidget: (context, url, error) => new Icon(Icons.error), 25 | ); 26 | } else { 27 | avatar = Icon( 28 | APPIcons.AvatarData, 29 | size: 80.0, 30 | color: AppColors.ArrowNormalColor, 31 | ); 32 | } 33 | return GestureDetector( 34 | child: ClipOval( 35 | child: avatar, 36 | ), 37 | onTap: () { 38 | 39 | }, 40 | ); 41 | } 42 | 43 | _buildNickNameFieldView() { 44 | var node = FocusNode(); 45 | return Padding( 46 | padding: EdgeInsets.symmetric(horizontal: 40.0), 47 | child: TextField( 48 | controller: TextEditingController(text: _nickName), 49 | onChanged: (value) { 50 | _nickName = value; 51 | }, 52 | decoration: InputDecoration( 53 | hintText: '请输入昵称', 54 | labelText: '昵称', 55 | hintStyle: 56 | TextStyle(fontSize: 12.0, color: AppColors.ArrowNormalColor), 57 | ), 58 | maxLines: 1, 59 | maxLength: 30, 60 | keyboardType: TextInputType.emailAddress, 61 | autofocus: true, 62 | onSubmitted: (value) { 63 | FocusScope.of(context).requestFocus(node); 64 | }, 65 | ), 66 | ); 67 | } 68 | 69 | @override 70 | Widget build(BuildContext context) { 71 | return Scaffold( 72 | backgroundColor: Colors.white, 73 | appBar: new AppBar( 74 | title: new Text('完善信息', style: TextStyle(color: Colors.white)), 75 | elevation: 0.0, 76 | iconTheme: IconThemeData( 77 | color: Colors.white, 78 | )), 79 | body: GestureDetector( 80 | behavior: HitTestBehavior.translucent, 81 | onTap: () { 82 | FocusScope.of(context).requestFocus(FocusNode()); 83 | }, 84 | child: SingleChildScrollView( 85 | child: Column( 86 | children: [ 87 | SizedBox(height: 100), 88 | _buildIconView(), 89 | SizedBox(height: 80), 90 | _buildNickNameFieldView() 91 | ], 92 | ), 93 | ), 94 | ), 95 | ); 96 | } 97 | } -------------------------------------------------------------------------------- /lib/pages/movie/movie_cate_list_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'movie_cate_list_view.dart'; 3 | import 'package:it_resource_exchange_app/model/cate_info.dart'; 4 | import 'package:it_resource_exchange_app/common/constant.dart'; 5 | 6 | class MovieCateListPage extends StatefulWidget { 7 | @override 8 | _MovieCateListPageState createState() => _MovieCateListPageState(); 9 | } 10 | 11 | class _MovieCateListPageState extends State with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin{ 12 | 13 | TabController _tabController; 14 | 15 | List cateList = Constant.movieCateInfoList; 16 | 17 | @override 18 | void initState() { 19 | super.initState(); 20 | _tabController = TabController(vsync: this, length: cateList.length); 21 | } 22 | 23 | @override 24 | void dispose() { 25 | _tabController.dispose(); 26 | super.dispose(); 27 | } 28 | 29 | @override 30 | bool get wantKeepAlive => true; 31 | 32 | Widget _buildTabPageView() { 33 | return Column( 34 | children: [ 35 | Container( 36 | decoration: BoxDecoration( 37 | color: Theme.of(context).canvasColor, 38 | border: Border( 39 | bottom: BorderSide( 40 | width: 0.0, 41 | color: AppColors.DividerColor, 42 | ) 43 | ) 44 | ), 45 | child: TabBar( 46 | controller: _tabController, 47 | isScrollable: true, 48 | labelColor: AppColors.PrimaryColor, 49 | indicatorColor: AppColors.PrimaryColor, 50 | tabs: Constant.movieCateInfoList.map((cate){ 51 | return Tab(text: cate.cateTitle); 52 | }).toList() 53 | ), 54 | ), 55 | Expanded( 56 | child: TabBarView( 57 | controller: _tabController, 58 | children: cateList.map((CateInfo cate) { 59 | return MovieCateListView(cate); 60 | }).toList() 61 | ), 62 | ) 63 | ], 64 | ); 65 | } 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | super.build(context); 70 | return _buildTabPageView(); 71 | } 72 | } -------------------------------------------------------------------------------- /lib/pages/movie/movie_cate_list_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:it_resource_exchange_app/pages/movie/movie_item_view.dart'; 4 | import 'package:it_resource_exchange_app/routes/it_router.dart'; 5 | import 'package:it_resource_exchange_app/routes/routes.dart'; 6 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 7 | import 'package:it_resource_exchange_app/widgets/indicator_factory.dart'; 8 | import 'package:it_resource_exchange_app/pages/movie/movie_list_item_view.dart'; 9 | import 'package:it_resource_exchange_app/model/cate_info.dart'; 10 | import 'package:it_resource_exchange_app/net/network_utils.dart'; 11 | import 'package:it_resource_exchange_app/model/page_result.dart'; 12 | import 'package:it_resource_exchange_app/widgets/load_state_layout_widget.dart'; 13 | import 'package:it_resource_exchange_app/model/movie_info.dart'; 14 | 15 | class MovieCateListView extends StatefulWidget { 16 | final CateInfo cate; 17 | 18 | MovieCateListView(this.cate); 19 | 20 | @override 21 | _MovieCateListViewState createState() => _MovieCateListViewState(); 22 | } 23 | 24 | class _MovieCateListViewState extends State 25 | with AutomaticKeepAliveClientMixin { 26 | //页面加载状态,默认为加载中 27 | LoadState _layoutState = LoadState.State_Loading; 28 | 29 | RefreshController _refreshController; 30 | 31 | PageResult pageResult; 32 | List movieList = []; 33 | 34 | var itemW; 35 | var childAspectRatio; 36 | 37 | @override 38 | void initState() { 39 | super.initState(); 40 | _refreshController = RefreshController(); 41 | loadData(); 42 | } 43 | 44 | @override 45 | bool get wantKeepAlive => true; 46 | 47 | SmartRefresher _buildRefreshListView() { 48 | itemW = (MediaQuery.of(context).size.width - 30.0 - 20.0) / 3; 49 | childAspectRatio = 0.60; 50 | 51 | return SmartRefresher( 52 | controller: _refreshController, 53 | enablePullUp: true, 54 | enablePullDown: true, 55 | header: buildDefaultHeader(), 56 | footer: buildDefaultFooter(), 57 | onRefresh: () { 58 | loadData(loadMore: false); 59 | }, 60 | onLoading: () { 61 | loadData(loadMore: true); 62 | }, 63 | child: GridView.builder( 64 | itemCount: this.movieList.length, 65 | gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( 66 | crossAxisCount: 3, 67 | crossAxisSpacing: 10.0, 68 | mainAxisSpacing: 0.0, 69 | childAspectRatio: childAspectRatio), 70 | itemBuilder: (BuildContext context, int index) { 71 | return MovieItemView( 72 | movieInfo: this.movieList[index], 73 | onPressed: () { 74 | int movieId = movieList[index].movieId; 75 | ITRouter.push( 76 | context, Routes.moviePlayerPage, {'movieId': movieId}); 77 | }, 78 | ); 79 | }, 80 | ), 81 | ); 82 | } 83 | 84 | @override 85 | Widget build(BuildContext context) { 86 | super.build(context); 87 | return Container( 88 | color: Colors.white, 89 | child: LoadStateLayout( 90 | state: _layoutState, 91 | errorRetry: () { 92 | setState(() { 93 | _layoutState = LoadState.State_Loading; 94 | }); 95 | this.loadData(); 96 | }, 97 | successWidget: _buildRefreshListView(), 98 | ), 99 | ); 100 | } 101 | 102 | void loadData({bool loadMore = false}) { 103 | int page = (pageResult == null || loadMore == false) 104 | ? 1 105 | : pageResult.currentPage + 1; 106 | NetworkUtils.requestMovieListByCateId(this.widget.cate.cateId, page) 107 | .then((res) { 108 | if (res.status == 200) { 109 | pageResult = PageResult.fromJson(res.data); 110 | if (!this.mounted) { 111 | return; 112 | } 113 | if (loadMore) { 114 | if (pageResult.items.length > 0) { 115 | var tempList = 116 | pageResult.items.map((m) => MovieInfo.fromJson(m)).toList(); 117 | movieList.addAll(tempList); 118 | _refreshController.loadComplete(); 119 | } else { 120 | _refreshController.loadNoData(); 121 | } 122 | setState(() {}); 123 | } else { 124 | if (pageResult.items.length == 0) { 125 | setState(() { 126 | _layoutState = LoadState.State_Empty; 127 | }); 128 | } else { 129 | movieList = 130 | pageResult.items.map((m) => MovieInfo.fromJson(m)).toList(); 131 | _refreshController.refreshCompleted(); 132 | setState(() { 133 | _layoutState = LoadState.State_Success; 134 | }); 135 | } 136 | } 137 | } else { 138 | //请求失败 139 | if (loadMore) { 140 | _refreshController.loadComplete(); 141 | setState(() {}); 142 | } else { 143 | _refreshController.refreshFailed(); 144 | setState(() { 145 | _layoutState = loadStateByErrorCode(res.status); 146 | }); 147 | } 148 | } 149 | }); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /lib/pages/movie/movie_item_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:it_resource_exchange_app/common/constant.dart'; 4 | import 'package:it_resource_exchange_app/model/movie_info.dart'; 5 | import 'package:it_resource_exchange_app/pages/movie/rating_bar.dart'; 6 | 7 | class MovieItemView extends StatelessWidget { 8 | final MovieInfo movieInfo; 9 | 10 | final VoidCallback onPressed; 11 | 12 | const MovieItemView({Key key, this.movieInfo, this.onPressed}) 13 | : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | var itemW = (MediaQuery.of(context).size.width - 30.0 - 20.0) / 3; 18 | 19 | return GestureDetector( 20 | child: Container( 21 | child: Column( 22 | crossAxisAlignment: CrossAxisAlignment.center, 23 | children: [ 24 | SizedBox(height: 8,), 25 | ClipRRect( 26 | borderRadius: BorderRadius.all(Radius.circular(5.0)), 27 | child: CachedNetworkImage( 28 | imageUrl: this.movieInfo.coverUrl, 29 | placeholder: (context, url) => Image.asset( 30 | './assets/imgs/img_default.png', 31 | width: itemW, 32 | height: itemW * 1.4, 33 | fit: BoxFit.fill, 34 | ), 35 | fadeInDuration: const Duration(milliseconds: 80), 36 | fadeOutDuration: const Duration(milliseconds: 80), 37 | fit: BoxFit.fill, 38 | width: itemW, 39 | height: itemW * 1.4, 40 | ), 41 | ), 42 | Padding( 43 | padding: EdgeInsets.only(left: 5.0, right: 5.0, top: 5.0), 44 | child: Container( 45 | // width: double.infinity, 46 | child: Text( 47 | movieInfo.movieName, 48 | ///文本只显示一行 49 | softWrap: false, 50 | ///多出的文本渐隐方式 51 | overflow: TextOverflow.ellipsis, 52 | style: TextStyle( 53 | color: AppColors.DarkTextColor, 54 | fontSize: 13, 55 | fontWeight: FontWeight.bold), 56 | ), 57 | ), 58 | ), 59 | ], 60 | ), 61 | ), 62 | onTap: onPressed, 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/pages/movie/movie_list_item_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:it_resource_exchange_app/common/constant.dart' 3 | show AppSize, AppColors; 4 | import 'package:cached_network_image/cached_network_image.dart'; 5 | import 'package:it_resource_exchange_app/model/movie_info.dart'; 6 | 7 | class MovieListItemView extends StatelessWidget { 8 | const MovieListItemView({Key key, this.moive, this.onPressed}) 9 | : assert(moive != null), 10 | super(key: key); 11 | 12 | final MovieInfo moive; 13 | 14 | final VoidCallback onPressed; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return GestureDetector( 19 | onTap: this.onPressed, 20 | child: Container( 21 | height: 100.0, 22 | padding: EdgeInsets.only(left: 10.0, right: 10.0), 23 | decoration: BoxDecoration( 24 | border: Border( 25 | bottom: BorderSide( 26 | width: AppSize.DividerWidth, 27 | color: AppColors.DividerColor, 28 | ))), 29 | child: Row( 30 | children: [ 31 | Expanded( 32 | child: Column( 33 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 34 | crossAxisAlignment: CrossAxisAlignment.start, 35 | children: [ 36 | Container( 37 | padding: EdgeInsets.only(top: 10.0), 38 | child: Text( 39 | moive.movieName ?? "", 40 | style: TextStyle(fontSize: 15.0, color: AppColors.DarkTextColor), 41 | ), 42 | ), 43 | Container( 44 | padding: EdgeInsets.only(bottom: 10.0), 45 | child: Row( 46 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 47 | children: [ 48 | Text('上映日期:${moive.releaseYear}', 49 | style: TextStyle( 50 | color: AppColors.LightTextColor, 51 | fontSize: 12.0)), 52 | Row( 53 | mainAxisAlignment: MainAxisAlignment.end, 54 | children: [ 55 | // Text( 56 | // '0', 57 | // style: TextStyle( 58 | // color: AppColors.LightTextColor, 59 | // fontSize: 12.0), 60 | // ), 61 | // SizedBox(width: 5.0), 62 | Image.asset('./assets/imgs/ic_comment.png', 63 | width: 16.0, height: 16.0), 64 | ], 65 | ), 66 | ], 67 | ), 68 | ) 69 | ], 70 | ), 71 | ), 72 | Container( 73 | padding: EdgeInsets.only(left: 6.0), 74 | width: 120.0, 75 | height: 80.0, 76 | child: CachedNetworkImage( 77 | imageUrl: moive.coverUrl, 78 | placeholder: (context, url) => Image.asset('./assets/imgs/img_default.png'), 79 | fit: BoxFit.cover, 80 | ), 81 | ), 82 | ], 83 | )), 84 | ); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/pages/movie/rating_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class RatingBar extends StatelessWidget { 4 | var stars; 5 | final double size; 6 | final double fontSize; 7 | final color = Color.fromARGB(255, 255, 170, 71); 8 | 9 | RatingBar(this.stars, {Key key, this.size = 18.0, this.fontSize = 13.0}) 10 | : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | stars = stars * 1.0; 15 | List startList = []; 16 | //实心星星 17 | var startNumber = stars ~/ 2; 18 | //半实心星星 19 | var startHalf = 0; 20 | if (stars.toString().contains('.')) { 21 | int tmp = int.parse((stars.toString().split('.')[1])); 22 | if (tmp >= 5) { 23 | startHalf = 1; 24 | } 25 | } 26 | //空心星星 27 | var startEmpty = 5 - startNumber - startHalf; 28 | 29 | for (var i = 0; i < startNumber; i++) { 30 | startList.add(Icon( 31 | Icons.star, 32 | color: color, 33 | size: size, 34 | )); 35 | } 36 | if (startHalf > 0) { 37 | startList.add(Icon( 38 | Icons.star_half, 39 | color: color, 40 | size: size, 41 | )); 42 | } 43 | for (var i = 0; i < startEmpty; i++) { 44 | startList.add(Icon( 45 | Icons.star_border, 46 | color: Colors.grey, 47 | size: size, 48 | )); 49 | } 50 | startList.add(Text( 51 | '$stars', 52 | style: TextStyle(color: Colors.grey, fontSize: fontSize), 53 | )); 54 | return Container( 55 | alignment: Alignment.topLeft, 56 | child: Row( 57 | children: startList, 58 | ), 59 | ); 60 | } 61 | } -------------------------------------------------------------------------------- /lib/pages/my_product_list/my_product_item_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:it_resource_exchange_app/common/constant.dart' 3 | show AppSize, AppColors; 4 | import 'package:cached_network_image/cached_network_image.dart'; 5 | import '../../model/product_detail.dart'; 6 | 7 | 8 | class MyProductItemView extends StatelessWidget { 9 | const MyProductItemView({Key key, this.product, this.onPressed}) 10 | : assert(product != null), 11 | super(key: key); 12 | 13 | final ProductDetail product; 14 | 15 | final VoidCallback onPressed; 16 | 17 | 18 | String productStatusDesc(int status) { 19 | var desc; 20 | switch (status) { 21 | case 1000: 22 | desc = '待审核'; 23 | break; 24 | case 1001: 25 | desc = '已拒绝'; 26 | break; 27 | case 1002: 28 | case 2000: 29 | desc = '已通过'; 30 | break; 31 | case 2001: 32 | desc = '已下架'; 33 | break; 34 | default: 35 | desc = '未知'; 36 | } 37 | return desc; 38 | } 39 | 40 | Color productStatusColor(int status) { 41 | var color; 42 | switch (status) { 43 | case 1000: 44 | color = Colors.yellow; 45 | break; 46 | case 1001: 47 | color = Colors.red; 48 | break; 49 | case 1002: 50 | case 2000: 51 | color = Colors.green; 52 | break; 53 | case 2001: 54 | color = Colors.grey; 55 | break; 56 | default: 57 | color = null; 58 | } 59 | return color; 60 | } 61 | 62 | @override 63 | Widget build(BuildContext context) { 64 | return GestureDetector( 65 | onTap: this.onPressed, 66 | child: Container( 67 | height: 100.0, 68 | padding: EdgeInsets.only(left: 10.0, right: 10.0), 69 | decoration: BoxDecoration( 70 | border: Border( 71 | bottom: BorderSide( 72 | width: AppSize.DividerWidth, 73 | color: AppColors.DividerColor, 74 | ))), 75 | child: Row( 76 | children: [ 77 | Expanded( 78 | child: Column( 79 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 80 | crossAxisAlignment: CrossAxisAlignment.start, 81 | children: [ 82 | Container( 83 | padding: EdgeInsets.only(top: 10.0), 84 | child: Text( 85 | product.productTitle, 86 | style: TextStyle(fontSize: 15.0, color: AppColors.DarkTextColor), 87 | ), 88 | ), 89 | Container( 90 | padding: EdgeInsets.only(bottom: 10.0), 91 | child: Row( 92 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 93 | children: [ 94 | Text(product.cateTitle, 95 | style: TextStyle( 96 | color: AppColors.LightTextColor, 97 | fontSize: 14.0)), 98 | Text(productStatusDesc(product.productStatus), 99 | style: TextStyle( 100 | color: productStatusColor(product.productStatus), 101 | fontSize: 14.0)), 102 | ], 103 | ), 104 | ) 105 | ], 106 | ), 107 | ), 108 | Container( 109 | padding: EdgeInsets.only(left: 6.0), 110 | width: 120.0, 111 | height: 80.0, 112 | child: CachedNetworkImage( 113 | imageUrl: product.coverUrl, 114 | placeholder: (context, url) => Image.asset('./assets/imgs/img_default.png'), 115 | fit: BoxFit.cover, 116 | ), 117 | ), 118 | ], 119 | )), 120 | ); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/pages/my_product_list/my_product_list_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:it_resource_exchange_app/net/network_utils.dart'; 3 | import 'package:it_resource_exchange_app/routes/it_router.dart'; 4 | import 'package:it_resource_exchange_app/routes/routes.dart'; 5 | import 'package:it_resource_exchange_app/utils/user_utils.dart'; 6 | import 'package:it_resource_exchange_app/model/product_detail.dart'; 7 | import './my_product_item_view.dart'; 8 | import 'package:it_resource_exchange_app/widgets/load_state_layout_widget.dart'; 9 | 10 | class MyProductListPage extends StatefulWidget { 11 | @override 12 | _MyProductListPageState createState() => _MyProductListPageState(); 13 | } 14 | 15 | class _MyProductListPageState extends State { 16 | //页面加载状态,默认为加载中 17 | LoadState _layoutState = LoadState.State_Loading; 18 | List productList = []; 19 | 20 | void initState() { 21 | super.initState(); 22 | loadData(); 23 | } 24 | 25 | loadData() { 26 | var userId = UserUtils.getUserInfo().userId; 27 | NetworkUtils.requestMyProductListData(userId).then((res) { 28 | if (res.status == 200) { 29 | productList = 30 | (res.data as List).map((m) => ProductDetail.fromJson(m)).toList(); 31 | setState(() { 32 | _layoutState = LoadState.State_Success; 33 | }); 34 | } else { 35 | setState(() { 36 | _layoutState = loadStateByErrorCode(res.status); 37 | }); 38 | } 39 | }); 40 | } 41 | 42 | _buildListView() { 43 | return ListView.builder( 44 | itemCount: productList.length, 45 | itemBuilder: (context, index) { 46 | return MyProductItemView( 47 | product: productList[index], 48 | onPressed: () { 49 | ProductDetail product = productList[index]; 50 | ITRouter.push(context, Routes.newProductPage,{'productId': product.productId}).then((res) { 51 | if (res) { 52 | this.loadData(); 53 | setState(() { 54 | _layoutState = LoadState.State_Loading; 55 | }); 56 | } 57 | }); 58 | }, 59 | ); 60 | }, 61 | ); 62 | } 63 | 64 | @override 65 | Widget build(BuildContext context) { 66 | return Scaffold( 67 | backgroundColor: Colors.white, 68 | appBar: AppBar( 69 | title: Text( 70 | "我的资源", 71 | style: TextStyle(color: Colors.white), 72 | ), 73 | elevation: 0.0, 74 | iconTheme: IconThemeData( 75 | color: Colors.white, 76 | ), 77 | ), 78 | body: LoadStateLayout( 79 | state: _layoutState, 80 | errorRetry: () { 81 | setState(() { 82 | _layoutState = LoadState.State_Loading; 83 | }); 84 | this.loadData(); 85 | }, 86 | successWidget: _buildListView(), 87 | ), 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /lib/pages/player/video_player_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:it_resource_exchange_app/common/constant.dart'; 4 | import 'package:it_resource_exchange_app/model/movie_info.dart'; 5 | import 'package:it_resource_exchange_app/net/network_utils.dart'; 6 | import 'package:it_resource_exchange_app/widgets/load_state_layout_widget.dart'; 7 | import 'package:video_player/video_player.dart'; 8 | import 'video_player_widget.dart'; 9 | class VideoPlayerPage extends StatefulWidget { 10 | 11 | final int movieId; 12 | 13 | const VideoPlayerPage({Key key, this.movieId}) : super(key: key); 14 | 15 | @override 16 | _VideoPlayerPageState createState() => _VideoPlayerPageState(); 17 | } 18 | 19 | class _VideoPlayerPageState extends State { 20 | VideoPlayerController _videoPlayerController; 21 | MovieInfo movieInfo; 22 | //页面加载状态,默认为加载中 23 | LoadState _layoutState = LoadState.State_Loading; 24 | 25 | @override 26 | void initState() { 27 | super.initState(); 28 | loadData(); 29 | } 30 | 31 | @override 32 | void dispose() { 33 | super.dispose(); 34 | } 35 | 36 | loadData() { 37 | NetworkUtils.requstMovieDetailData(this.widget.movieId).then((res) { 38 | if (res.status == 200) { 39 | movieInfo = MovieInfo.fromJson(res.data); 40 | _videoPlayerController = VideoPlayerController.network(this.movieInfo.playUrl ?? ""); 41 | setState(() { 42 | _layoutState = LoadState.State_Success; 43 | }); 44 | } else { 45 | setState(() { 46 | _layoutState = loadStateByErrorCode(res.status); 47 | }); 48 | } 49 | }); 50 | } 51 | 52 | Column _buildPlayerView() { 53 | return Column( 54 | children: [ 55 | VideoPlayerWidget(videoPlayerController: _videoPlayerController,), 56 | ListView( 57 | shrinkWrap: true, 58 | padding: EdgeInsets.fromLTRB(10, 10, 10, 10), 59 | children: [ 60 | Text('导演:${this.movieInfo?.director ?? ""}', style: TextStyle(color: AppColors.DarkTextColor, fontSize: 16)), 61 | Text('演员:${this.movieInfo?.rolesNames ?? ""}', style: TextStyle(color: AppColors.DarkTextColor, fontSize: 16)), 62 | Text('上映时间:${this.movieInfo?.releaseYear ?? ""}', style: TextStyle(color: AppColors.DarkTextColor, fontSize: 16)), 63 | SizedBox(height: 20,), 64 | Text('剧情描述:', style: TextStyle(color: AppColors.DarkTextColor, fontSize: 16)), 65 | Text(this.movieInfo?.desc ?? "", style: TextStyle(color: AppColors.MidTextColor, fontSize: 14)) , 66 | ], 67 | ), 68 | ], 69 | ); 70 | } 71 | 72 | @override 73 | Widget build(BuildContext context) { 74 | return Scaffold( 75 | appBar: AppBar( 76 | title: Text(this.movieInfo?.movieName ?? "电影详情"), 77 | ), 78 | body: LoadStateLayout( 79 | state: _layoutState, 80 | errorRetry: () { 81 | setState(() { 82 | _layoutState = LoadState.State_Loading; 83 | }); 84 | this.loadData(); 85 | }, 86 | successWidget: _buildPlayerView(), 87 | ), 88 | 89 | 90 | ); 91 | } 92 | } 93 | 94 | 95 | -------------------------------------------------------------------------------- /lib/pages/player/video_player_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_orientation/auto_orientation.dart'; 2 | import 'package:chewie/chewie.dart'; 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/services.dart'; 6 | import 'package:flutter_spinkit/flutter_spinkit.dart'; 7 | import 'package:video_player/video_player.dart'; 8 | 9 | class VideoPlayerWidget extends StatefulWidget { 10 | // This will contain the URL/asset path which we want to play 11 | final VideoPlayerController videoPlayerController; 12 | 13 | const VideoPlayerWidget({Key key, this.videoPlayerController}) 14 | : super(key: key); 15 | 16 | @override 17 | _VideoPlayerWidgetState createState() => _VideoPlayerWidgetState(); 18 | } 19 | 20 | class _VideoPlayerWidgetState extends State { 21 | Container loadingView = Container( 22 | constraints: BoxConstraints.expand(), 23 | color: Colors.black, 24 | child: Center( 25 | child: Opacity( 26 | opacity: 0.8, 27 | child: SpinKitThreeBounce( 28 | color: Colors.white, 29 | size: 20.0, 30 | ), 31 | ), 32 | ), 33 | ); 34 | 35 | ChewieController _chewieController; 36 | 37 | @override 38 | void initState() { 39 | super.initState(); 40 | _chewieController = ChewieController( 41 | videoPlayerController: widget.videoPlayerController, 42 | aspectRatio: 16 / 9, 43 | autoPlay: true, 44 | placeholder: loadingView, 45 | autoInitialize: true, 46 | errorBuilder: (BuildContext context, String errorMessage) { 47 | return Center( 48 | child: Text( 49 | errorMessage, 50 | style: TextStyle(color: Colors.white), 51 | ), 52 | ); 53 | }, 54 | routePageBuilder: (BuildContext context, Animation animation, 55 | Animation secondAnimation, provider) { 56 | return AnimatedBuilder( 57 | animation: animation, 58 | builder: (BuildContext context, Widget child) { 59 | return VideoScaffold( 60 | child: Scaffold( 61 | resizeToAvoidBottomPadding: false, 62 | body: Container( 63 | alignment: Alignment.center, 64 | color: Colors.black, 65 | child: provider, 66 | ), 67 | ), 68 | ); 69 | }, 70 | ); 71 | }, 72 | ); 73 | } 74 | 75 | @override 76 | void dispose() { 77 | super.dispose(); 78 | // IMPORTANT to dispose of all the used resources 79 | widget.videoPlayerController.dispose(); 80 | _chewieController.dispose(); 81 | } 82 | 83 | @override 84 | Widget build(BuildContext context) { 85 | return Chewie( 86 | controller: _chewieController, 87 | ); 88 | } 89 | } 90 | 91 | class VideoScaffold extends StatefulWidget { 92 | const VideoScaffold({Key key, this.child}) : super(key: key); 93 | 94 | final Widget child; 95 | 96 | @override 97 | State createState() => _VideoScaffoldState(); 98 | } 99 | 100 | class _VideoScaffoldState extends State { 101 | @override 102 | void initState() { 103 | SystemChrome.setPreferredOrientations([ 104 | DeviceOrientation.landscapeRight, 105 | DeviceOrientation.landscapeLeft, 106 | ]); 107 | AutoOrientation.landscapeMode(); 108 | super.initState(); 109 | } 110 | 111 | @override 112 | dispose() { 113 | SystemChrome.setPreferredOrientations([ 114 | DeviceOrientation.portraitUp, 115 | DeviceOrientation.portraitDown, 116 | ]); 117 | AutoOrientation.portraitMode(); 118 | super.dispose(); 119 | } 120 | 121 | @override 122 | Widget build(BuildContext context) { 123 | return widget.child; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /lib/pages/profile/full_width_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:it_resource_exchange_app/common/constant.dart' show AppColors, AppSize, Constant; 3 | 4 | class FullWidthButton extends StatelessWidget { 5 | static const HORIZONTAL_PADDING= 20.0; 6 | static const VERTICAL_PADDING = 13.0; 7 | 8 | const FullWidthButton({ 9 | @required this.title, 10 | @required this.iconData, 11 | @required this.onPressed, 12 | this.showDivider: false, 13 | }) : assert(iconData != null), 14 | assert(title != null), 15 | assert(onPressed != null); 16 | 17 | final String title; 18 | final IconData iconData; 19 | final bool showDivider; 20 | final VoidCallback onPressed; 21 | @override 22 | Widget build(BuildContext context) { 23 | final pureButton = Row( 24 | crossAxisAlignment: CrossAxisAlignment.center, 25 | children: [ 26 | Icon( 27 | iconData, 28 | size: 24.0, 29 | color: AppColors.PrimaryColor, 30 | ), 31 | SizedBox(width: HORIZONTAL_PADDING), 32 | Expanded( 33 | child: Text(title, style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.normal),), 34 | ), 35 | Icon( 36 | IconData( 37 | 0xe664, 38 | fontFamily: Constant.IconFontFamily, 39 | ), 40 | size: 22.0, 41 | color: AppColors.ArrowNormalColor, 42 | ), 43 | ], 44 | ); 45 | 46 | final borderButton = Container( 47 | decoration: BoxDecoration( 48 | border: Border( 49 | bottom: BorderSide(color: AppColors.DividerColor, width: AppSize.DividerWidth) 50 | ), 51 | ), 52 | padding: EdgeInsets.only(bottom: VERTICAL_PADDING), 53 | child: pureButton, 54 | ); 55 | 56 | return FlatButton( 57 | onPressed: this.onPressed, 58 | padding: EdgeInsets.only( 59 | left: HORIZONTAL_PADDING, 60 | right: HORIZONTAL_PADDING, 61 | top: VERTICAL_PADDING, bottom: showDivider ? 0.0 : VERTICAL_PADDING), 62 | color: Colors.white, 63 | child: showDivider ? borderButton : pureButton, 64 | ); 65 | } 66 | } -------------------------------------------------------------------------------- /lib/pages/profile/profile_header_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:it_resource_exchange_app/common/constant.dart' show APPIcons; 3 | import 'package:it_resource_exchange_app/common/constant.dart'; 4 | import 'package:it_resource_exchange_app/model/user_info.dart'; 5 | import 'package:it_resource_exchange_app/utils/user_utils.dart'; 6 | import 'package:cached_network_image/cached_network_image.dart'; 7 | 8 | class ProfileHeaderInfoView extends StatelessWidget { 9 | const ProfileHeaderInfoView({this.onPressed}); 10 | 11 | final VoidCallback onPressed; 12 | @override 13 | Widget build(BuildContext context) { 14 | UserInfo userInfo = UserUtils.getUserInfo(); 15 | 16 | Widget avatar; 17 | 18 | if (userInfo.avatar != null) { 19 | avatar = CachedNetworkImage( 20 | imageUrl: userInfo.avatar, 21 | placeholder: (context, url) => APPIcons.PlaceHolderAvatar, 22 | fit: BoxFit.cover, 23 | height: 60.0, 24 | width: 60.0, 25 | errorWidget: (context, url, error) => new Icon(Icons.error), 26 | ); 27 | } else { 28 | avatar = APPIcons.PlaceHolderAvatar; 29 | } 30 | 31 | return Container( 32 | height: 150, 33 | color: Colors.white, 34 | child: FlatButton( 35 | onPressed: this.onPressed, 36 | child: Column( 37 | mainAxisAlignment: MainAxisAlignment.start, 38 | crossAxisAlignment: CrossAxisAlignment.center, 39 | children: [ 40 | SizedBox(height: 20), 41 | Expanded( 42 | child: Column( 43 | mainAxisSize: MainAxisSize.min, 44 | children: [ 45 | ClipOval( 46 | child: avatar, 47 | ), 48 | SizedBox(height: 5,), 49 | Text( 50 | userInfo.account, 51 | style: TextStyle(color: AppColors.DarkTextColor, fontSize: 15), 52 | ), 53 | Padding( 54 | padding: EdgeInsets.only(top: 6.0), 55 | child: Text( 56 | userInfo.intro ?? '此人很懒,什么都没写~', 57 | style: Theme.of(context) 58 | .textTheme 59 | .body1 60 | .copyWith(fontSize: 12, color: Color(0xff818181)), 61 | ), 62 | ) 63 | ], 64 | ), 65 | ) 66 | ], 67 | ), 68 | ), 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/pages/profile/profile_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:fluro/fluro.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:it_resource_exchange_app/routes/it_router.dart'; 5 | import 'package:it_resource_exchange_app/routes/routes.dart'; 6 | 7 | import './full_width_button.dart'; 8 | import './profile_header_info.dart'; 9 | import 'package:it_resource_exchange_app/common/constant.dart'; 10 | import '../../net/network_utils.dart'; 11 | import '../../model/user_info.dart'; 12 | import '../../utils/user_utils.dart'; 13 | import 'package:oktoast/oktoast.dart'; 14 | import 'package:it_resource_exchange_app/pages/create/new_goods_page.dart'; 15 | import 'package:it_resource_exchange_app/common/constant.dart' show APPIcons; 16 | 17 | class ProfilePage extends StatefulWidget { 18 | @override 19 | _ProfilePageState createState() => _ProfilePageState(); 20 | } 21 | 22 | class _ProfilePageState extends State { 23 | static const SEPARATE_SIZE = 20.0; 24 | logout() { 25 | UserInfo userInfo = UserUtils.getUserInfo(); 26 | NetworkUtils.logout(userInfo.userId.toString()).then((res) { 27 | if (res.status == 200) { 28 | UserUtils.removeUserInfo(); 29 | // 保存成功,跳转到登录页面 30 | ITRouter.push(context, Routes.loginPage, {}, clearStack: true, transition: TransitionType.nativeModal); 31 | } else { 32 | showToast(res.message, duration: Duration(milliseconds: 1500)); 33 | } 34 | }); 35 | } 36 | 37 | Widget _buildLogoutBtn() { 38 | return Padding( 39 | padding: EdgeInsets.only(left: 30.0, right: 30.0), 40 | child: RaisedButton( 41 | padding: EdgeInsets.symmetric(vertical: 12), 42 | color: AppColors.PrimaryColor, 43 | textColor: Colors.white, 44 | disabledColor: AppColors.DisableTextColor, 45 | onPressed: () { 46 | showDialog( 47 | context: context, 48 | barrierDismissible: false, 49 | builder: (BuildContext context) { 50 | return CupertinoAlertDialog( 51 | title: Text('提示'), 52 | content: SingleChildScrollView( 53 | child: ListBody( 54 | children: [ 55 | Text('确定要退出登录吗?'), 56 | ], 57 | ), 58 | ), 59 | actions: [ 60 | CupertinoDialogAction( 61 | child: Text('确定'), 62 | onPressed: () { 63 | Navigator.of(context).pop(); 64 | logout(); 65 | }, 66 | ), 67 | CupertinoDialogAction( 68 | child: Text('取消'), 69 | onPressed: () { 70 | Navigator.of(context).pop(); 71 | }, 72 | ), 73 | ], 74 | ); 75 | }, 76 | ); 77 | }, 78 | child: Text( 79 | '退出登录', 80 | style: TextStyle(fontSize: 16.0, color: Colors.white), 81 | ), 82 | ), 83 | ); 84 | } 85 | 86 | @override 87 | Widget build(BuildContext context) { 88 | return ListView(children: [ 89 | ProfileHeaderInfoView(onPressed: () { 90 | //跳转到个人信息页面 91 | }), 92 | SizedBox(height: SEPARATE_SIZE), 93 | FullWidthButton( 94 | iconData: APPIcons.CollectionData, 95 | title: '我的收藏', 96 | showDivider: false, 97 | onPressed: () { 98 | ITRouter.push(context, Routes.myCollectionListPage, {}); 99 | }, 100 | ), 101 | SizedBox(height: SEPARATE_SIZE), 102 | 103 | FullWidthButton( 104 | iconData: APPIcons.ProfileListImgData, 105 | title: '资源列表', 106 | showDivider: true, 107 | onPressed: () { 108 | ITRouter.push(context, Routes.myProductListPage, {}); 109 | }, 110 | ), 111 | FullWidthButton( 112 | iconData: APPIcons.ProfileAddImgData, 113 | title: '发布资源', 114 | showDivider: false, 115 | onPressed: () { 116 | Navigator.of(context) 117 | .push(MaterialPageRoute(builder: (context) => NewGoodsPage())); 118 | }, 119 | ), 120 | // SizedBox(height: SEPARATE_SIZE), 121 | // FullWidthButton( 122 | // iconData: APPIcons.ProfileSettingImgData, 123 | // title: '设置', 124 | // showDivider: false, 125 | // onPressed: () {}, 126 | // ), 127 | SizedBox(height: 100), 128 | _buildLogoutBtn() 129 | ]); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /lib/pages/web/webview_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 3 | import 'package:flutter_spinkit/flutter_spinkit.dart'; 4 | import 'package:it_resource_exchange_app/common/constant.dart' show AppColors; 5 | 6 | class WebviewPage extends StatefulWidget { 7 | final String title; 8 | final String url; 9 | WebviewPage({Key key, @required this.title, @required this.url}) 10 | : super(key: key); 11 | 12 | @override 13 | _WebviewPageState createState() => _WebviewPageState(); 14 | } 15 | 16 | class _WebviewPageState extends State { 17 | final _loadingContainer = Container( 18 | color: Colors.white, 19 | constraints: BoxConstraints.expand(), 20 | child: Center( 21 | child: Opacity( 22 | opacity: 0.9, 23 | child: SpinKitRing( 24 | color: AppColors.PrimaryColor, 25 | size: 50.0, 26 | ), 27 | ), 28 | ), 29 | ); 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | return WebviewScaffold( 34 | url: this.widget.url, 35 | appBar: AppBar( 36 | title: Text(this.widget.title, style: TextStyle(color: Colors.white)), 37 | elevation: 0.0, 38 | iconTheme: IconThemeData( 39 | color: Colors.white, 40 | ), 41 | ), 42 | withZoom: true, 43 | withLocalStorage: true, 44 | hidden: true, 45 | initialChild: _loadingContainer, 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/routes/it_router.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:fluro/fluro.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:it_resource_exchange_app/utils/regex_utils.dart'; 6 | 7 | class ITRouter { 8 | static Router _router; 9 | 10 | static initWithRouter(Router router) { 11 | _router = router; 12 | } 13 | 14 | static Router router() { 15 | return _router; 16 | } 17 | 18 | static push(BuildContext context, String path, Map params, {bool clearStack = false, TransitionType transition = TransitionType.inFromRight}) { 19 | String query = ""; 20 | int index = 0; 21 | for (var key in params.keys) { 22 | String value = params[key].toString(); 23 | if (value.startsWith('http')) { 24 | value = Uri.encodeComponent(value); 25 | }else if (RegexUtils.isZh(value)) { 26 | value = jsonEncode(Utf8Encoder().convert(value)); 27 | } 28 | 29 | if (index == 0) { 30 | query = "?"; 31 | } else { 32 | query = query + "\&"; 33 | } 34 | 35 | query += "$key=$value"; 36 | index++; 37 | } 38 | path = path + query; 39 | return _router.navigateTo(context, path, clearStack: clearStack, transition:transition); 40 | } 41 | 42 | static pop(BuildContext context) { 43 | _router.pop(context); 44 | } 45 | } -------------------------------------------------------------------------------- /lib/routes/route_handlers.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:fluro/fluro.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:it_resource_exchange_app/pages/login/register_page.dart'; 6 | import '../pages/login/user_verify_code_page.dart'; 7 | import '../pages/login/reset_password_page.dart'; 8 | import 'package:it_resource_exchange_app/pages/web/webview_page.dart'; 9 | 10 | import '../utils/user_utils.dart'; 11 | 12 | import '../pages/login/login_page.dart'; 13 | import '../pages/application_page.dart'; 14 | import '../pages/detail/goods_detail_page.dart'; 15 | import '../pages/my_product_list/my_product_list_page.dart'; 16 | import '../pages/create/new_goods_page.dart'; 17 | import '../pages/collection/my_collection_list_page.dart'; 18 | import '../pages/login/perfect_info_page.dart'; 19 | import '../pages/player/video_player_page.dart'; 20 | import '../pages/movie/movie_search_page.dart'; 21 | 22 | var rootHandler = Handler( 23 | handlerFunc: (BuildContext context, Map> params) { 24 | return UserUtils.isLogin() ? ApplicationPage() : LoginPage(); 25 | }); 26 | 27 | var mainHandler = Handler( 28 | handlerFunc: (BuildContext context, Map> params) { 29 | return ApplicationPage(); 30 | }); 31 | 32 | var loginHandler = Handler( 33 | handlerFunc: (BuildContext context, Map> params) { 34 | return LoginPage(); 35 | }); 36 | 37 | var registerHandler = Handler( 38 | handlerFunc: (BuildContext context, Map> params) { 39 | return RegisterPage(); 40 | }); 41 | 42 | var resetPassworVerityHandler = Handler( 43 | handlerFunc: (BuildContext context, Map> params) { 44 | return UserVerifyCodePage(); 45 | }); 46 | 47 | var resetPassworHandler = Handler( 48 | handlerFunc: (BuildContext context, Map> params) { 49 | String account = params['account']?.first; 50 | String verityCode = params['verityCode']?.first; 51 | return RestPasswordPage( 52 | account: account, 53 | verityCode: verityCode, 54 | ); 55 | }); 56 | 57 | var perfectInfoHander = Handler( 58 | handlerFunc: (BuildContext context, Map> params) { 59 | return PerfectInfoPage(); 60 | }); 61 | 62 | var productDetailHandler = Handler( 63 | handlerFunc: (BuildContext context, Map> params) { 64 | String productId = params['productId']?.first; 65 | return GoodsDetailPage(productId: int.parse(productId)); 66 | }); 67 | 68 | var webHandler = Handler( 69 | handlerFunc: (BuildContext context, Map> params) { 70 | String title = params['title']?.first; 71 | var list = List(); 72 | jsonDecode(title).forEach(list.add); 73 | title = Utf8Decoder().convert(list); 74 | String url = params['url']?.first; 75 | return WebviewPage(title: title, url: url); 76 | }); 77 | 78 | var myProductListHandler = Handler( 79 | handlerFunc: (BuildContext context, Map> params) { 80 | return MyProductListPage(); 81 | }); 82 | 83 | var myCollectionListHandler = Handler( 84 | handlerFunc: (BuildContext context, Map> params) { 85 | return MyCollectionListPage(); 86 | }); 87 | 88 | var newProductHandler = Handler( 89 | handlerFunc: (BuildContext context, Map> params) { 90 | String productId = params['productId']?.first; 91 | return NewGoodsPage(productId: int.parse(productId)); 92 | }); 93 | 94 | var videoPlayerHandler = Handler( 95 | handlerFunc: (BuildContext context, Map> params) { 96 | String movieId = params['movieId']?.first; 97 | return VideoPlayerPage(movieId: int.parse(movieId),); 98 | }); 99 | 100 | var movieSearchHandler = Handler( 101 | handlerFunc: (BuildContext context, Map> params) { 102 | return MovieSearchPage(); 103 | }); 104 | -------------------------------------------------------------------------------- /lib/routes/routes.dart: -------------------------------------------------------------------------------- 1 | import 'package:fluro/fluro.dart'; 2 | import 'package:flutter/material.dart'; 3 | import './route_handlers.dart'; 4 | 5 | class Routes { 6 | 7 | static String rootPage = "/"; 8 | 9 | static String mainPage = "/main"; 10 | 11 | static String loginPage = "/login"; 12 | 13 | static String registerPage = "/registerPage"; 14 | 15 | static String resetPasswordVerityPage = "/resetPasswordVerityPage"; 16 | 17 | static String resetPasswordPage = "/resetPasswordPage"; 18 | 19 | static String perfectInfoPage = "/perfectInfoPage"; 20 | 21 | static String productDetailPage = "/productDetailPage"; 22 | 23 | static String webPage = "/webPage"; 24 | 25 | static String myProductListPage = "/myProductList"; 26 | 27 | static String myCollectionListPage = "/myCollectionListPage"; 28 | 29 | static String newProductPage = "/newProductPage"; 30 | 31 | static String moviePlayerPage = "/moviePlayerPage"; 32 | 33 | static String movieSearchPage = "/movieSearchPage"; 34 | 35 | static void configureRoutes(Router router) { 36 | router.notFoundHandler = Handler(handlerFunc: (BuildContext context, Map> params) { 37 | print("ROUTE WAS NOT FOUND !!!"); 38 | return null; 39 | }); 40 | 41 | router.define(rootPage, handler: rootHandler); 42 | router.define(mainPage, handler: mainHandler); 43 | router.define(productDetailPage, handler: productDetailHandler); 44 | router.define(webPage, handler: webHandler); 45 | router.define(myProductListPage, handler: myProductListHandler); 46 | router.define(myCollectionListPage, handler: myCollectionListHandler); 47 | router.define(newProductPage, handler: newProductHandler); 48 | router.define(loginPage, handler:loginHandler); 49 | router.define(registerPage, handler:registerHandler); 50 | router.define(perfectInfoPage, handler:perfectInfoHander); 51 | router.define(resetPasswordVerityPage, handler:resetPassworVerityHandler); 52 | router.define(resetPasswordPage, handler:resetPassworHandler); 53 | router.define(moviePlayerPage, handler:videoPlayerHandler); 54 | router.define(movieSearchPage, handler: movieSearchHandler); 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /lib/utils/local_storage_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | import 'package:synchronized/synchronized.dart'; 3 | import 'dart:convert'; 4 | import 'dart:async'; 5 | 6 | ///SharedPreferences 本地存储 7 | class LocalStorage { 8 | static LocalStorage _singleton; 9 | static SharedPreferences _prefs; 10 | static Lock _lock = Lock(); 11 | 12 | static Future getInstance() async { 13 | if (_singleton == null) { 14 | await _lock.synchronized(() async { 15 | if (_singleton == null) { 16 | // keep local instance till it is fully initialized. 17 | // 保持本地实例直到完全初始化。 18 | var singleton = LocalStorage._(); 19 | await singleton._init(); 20 | _singleton = singleton; 21 | } 22 | }); 23 | } 24 | return _singleton; 25 | } 26 | 27 | LocalStorage._(); 28 | 29 | Future _init() async { 30 | _prefs = await SharedPreferences.getInstance(); 31 | } 32 | 33 | /// put object. 34 | static Future putObject(String key, Object value) { 35 | if (_prefs == null) return null; 36 | return _prefs.setString(key, value == null ? "" : json.encode(value)); 37 | } 38 | 39 | /// get obj. 40 | static T getObj(String key, T f(Map v), {T defValue}) { 41 | Map map = getObject(key); 42 | return map == null ? defValue : f(map); 43 | } 44 | 45 | /// get object. 46 | static Map getObject(String key) { 47 | if (_prefs == null) return null; 48 | String _data = _prefs.getString(key); 49 | return (_data == null || _data.isEmpty) ? null : json.decode(_data); 50 | } 51 | 52 | /// put object list. 53 | static Future putObjectList(String key, List list) { 54 | if (_prefs == null) return null; 55 | List _dataList = list?.map((value) { 56 | return json.encode(value); 57 | })?.toList(); 58 | return _prefs.setStringList(key, _dataList); 59 | } 60 | 61 | /// get obj list. 62 | static List getObjList(String key, T f(Map v), 63 | {List defValue = const []}) { 64 | List dataList = getObjectList(key); 65 | List list = dataList?.map((value) { 66 | return f(value); 67 | })?.toList(); 68 | return list ?? defValue; 69 | } 70 | 71 | /// get object list. 72 | static List getObjectList(String key) { 73 | if (_prefs == null) return null; 74 | List dataLis = _prefs.getStringList(key); 75 | return dataLis?.map((value) { 76 | Map _dataMap = json.decode(value); 77 | return _dataMap; 78 | })?.toList(); 79 | } 80 | 81 | /// get string. 82 | static String getString(String key, {String defValue = ''}) { 83 | if (_prefs == null) return defValue; 84 | return _prefs.getString(key) ?? defValue; 85 | } 86 | 87 | /// put string. 88 | static Future putString(String key, String value) { 89 | if (_prefs == null) return null; 90 | return _prefs.setString(key, value); 91 | } 92 | 93 | /// get bool. 94 | static bool getBool(String key, {bool defValue = false}) { 95 | if (_prefs == null) return defValue; 96 | return _prefs.getBool(key) ?? defValue; 97 | } 98 | 99 | /// put bool. 100 | static Future putBool(String key, bool value) { 101 | if (_prefs == null) return null; 102 | return _prefs.setBool(key, value); 103 | } 104 | 105 | /// get int. 106 | static int getInt(String key, {int defValue = 0}) { 107 | if (_prefs == null) return defValue; 108 | return _prefs.getInt(key) ?? defValue; 109 | } 110 | 111 | /// put int. 112 | static Future putInt(String key, int value) { 113 | if (_prefs == null) return null; 114 | return _prefs.setInt(key, value); 115 | } 116 | 117 | /// get double. 118 | static double getDouble(String key, {double defValue = 0.0}) { 119 | if (_prefs == null) return defValue; 120 | return _prefs.getDouble(key) ?? defValue; 121 | } 122 | 123 | /// put double. 124 | static Future putDouble(String key, double value) { 125 | if (_prefs == null) return null; 126 | return _prefs.setDouble(key, value); 127 | } 128 | 129 | /// get string list. 130 | static List getStringList(String key, 131 | {List defValue = const []}) { 132 | if (_prefs == null) return defValue; 133 | return _prefs.getStringList(key) ?? defValue; 134 | } 135 | 136 | /// put string list. 137 | static Future putStringList(String key, List value) { 138 | if (_prefs == null) return null; 139 | return _prefs.setStringList(key, value); 140 | } 141 | 142 | /// get dynamic. 143 | static dynamic getDynamic(String key, {Object defValue}) { 144 | if (_prefs == null) return defValue; 145 | return _prefs.get(key) ?? defValue; 146 | } 147 | 148 | /// have key. 149 | static bool haveKey(String key) { 150 | if (_prefs == null) return null; 151 | return _prefs.getKeys().contains(key); 152 | } 153 | 154 | /// get keys. 155 | static Set getKeys() { 156 | if (_prefs == null) return null; 157 | return _prefs.getKeys(); 158 | } 159 | 160 | /// remove. 161 | static Future remove(String key) { 162 | if (_prefs == null) return null; 163 | return _prefs.remove(key); 164 | } 165 | 166 | /// clear. 167 | static Future clear() { 168 | if (_prefs == null) return null; 169 | return _prefs.clear(); 170 | } 171 | 172 | ///Sp is initialized. 173 | static bool isInitialized() { 174 | return _prefs != null; 175 | } 176 | } -------------------------------------------------------------------------------- /lib/utils/regex_utils.dart: -------------------------------------------------------------------------------- 1 | class RegexUtils { 2 | /// Regex of email. 3 | static final String regexEmail = 4 | "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*\$"; 5 | 6 | /// Regex of url. 7 | static final String regexUrl = "[a-zA-z]+://[^\\s]*"; 8 | 9 | /// Regex of Chinese character. 10 | static final String regexZh = "[\\u4e00-\\u9fa5]"; 11 | 12 | /// Return whether input matches regex of email. 13 | static bool isEmail(String input) { 14 | return matches(regexEmail, input); 15 | } 16 | 17 | /// Return whether input matches regex of url. 18 | static bool isURL(String input) { 19 | return matches(regexUrl, input); 20 | } 21 | 22 | /// Return whether input matches regex of Chinese character. 23 | static bool isZh(String input) { 24 | return '〇' == input || matches(regexZh, input); 25 | } 26 | 27 | static bool matches(String regex, String input) { 28 | if (input == null || input.isEmpty) return false; 29 | return new RegExp(regex).hasMatch(input); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/utils/user_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:it_resource_exchange_app/utils/local_storage_utils.dart'; 2 | import './local_storage_utils.dart'; 3 | import '../model/user_info.dart'; 4 | 5 | class UserUtils { 6 | static const USER_INFO_KEY = "USER_INFO_KEY"; 7 | 8 | static const USER_NAME_KEY = "USER_NAME_KEY"; 9 | 10 | static saveUserInfo(UserInfo userInfo) { 11 | if (userInfo != null) { 12 | LocalStorage.putObject(USER_INFO_KEY, userInfo.toJson()); 13 | } 14 | } 15 | 16 | static saveUserName(String username) { 17 | if (username != null) { 18 | LocalStorage.putString(USER_NAME_KEY, username); 19 | } 20 | } 21 | 22 | static removeUserInfo() { 23 | LocalStorage.remove(USER_INFO_KEY); 24 | } 25 | 26 | static UserInfo getUserInfo() { 27 | Map userJson = LocalStorage.getObject(USER_INFO_KEY); 28 | return userJson == null ? null : UserInfo.fromJson(userJson); 29 | } 30 | 31 | static String getUserName() { 32 | return LocalStorage.getString(USER_NAME_KEY); 33 | } 34 | 35 | static bool isLogin() { 36 | var res = getUserInfo() == null ? false : true; 37 | return res; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/vo/comment_vo.dart: -------------------------------------------------------------------------------- 1 | import 'package:it_resource_exchange_app/model/comment_model.dart'; 2 | 3 | class CommentVO extends Object { 4 | int index = -1; // -1 点击的是评论, 其他值为回复 5 | CommentModel commentModel; 6 | 7 | CommentVO({this.index, this.commentModel}); 8 | } -------------------------------------------------------------------------------- /lib/vo/new_product_vo.dart: -------------------------------------------------------------------------------- 1 | import 'package:multi_image_picker/multi_image_picker.dart'; 2 | import 'package:it_resource_exchange_app/model/cate_info.dart'; 3 | 4 | class NewProductImgVo extends Object { 5 | String url; 6 | String path; 7 | Asset asset; 8 | 9 | NewProductImgVo({this.url, this.asset}); 10 | } 11 | 12 | class NewProductVo extends Object { 13 | String productId; 14 | String title; 15 | String price; 16 | String resourceUrl; 17 | String resourcePassword; 18 | String desc; 19 | CateInfo cateInfo; 20 | 21 | List imgVoList = []; 22 | 23 | static NewProductVo init({String productId, String cateId, String title, String price, String resourceUrl, String resourcePassword, String desc, List imgUrlList}) { 24 | CateInfo info; 25 | if (null != cateId) { 26 | info = CateInfo(int.parse(cateId), null, null, null); 27 | } 28 | imgUrlList = imgUrlList == null ? [] : imgUrlList; 29 | List imgVoList = imgUrlList.map((url) { 30 | return NewProductImgVo(url: url); 31 | }).toList(); 32 | return NewProductVo._(productId: productId, title: title, price: price, resourceUrl: resourceUrl, resourcePassword: resourcePassword, desc: desc, cateInfo: info, imgVoList: imgVoList); 33 | } 34 | 35 | NewProductVo._({this.productId, this.title, this.price, this.resourceUrl, this.resourcePassword, this.desc, this.cateInfo, this.imgVoList}); 36 | } -------------------------------------------------------------------------------- /lib/widgets/choose_img_modal_sheet.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:image_picker/image_picker.dart'; 3 | import 'dart:io'; 4 | 5 | showChooseImgModalSheet(BuildContext context, ValueChanged valueChanged ) async { 6 | showModalBottomSheet( 7 | context: context, 8 | builder: (BuildContext context) { 9 | return Column( 10 | mainAxisSize: MainAxisSize.min, 11 | children: [ 12 | ListTile( 13 | leading: new Icon(Icons.photo_camera), 14 | title: new Text("相机"), 15 | onTap: () async { 16 | Navigator.of(context).pop(); 17 | File file = await ImagePicker.pickImage(source: ImageSource.camera); 18 | valueChanged(file); 19 | }, 20 | ), 21 | Divider(), 22 | ListTile( 23 | leading: new Icon(Icons.photo_library), 24 | title: new Text("相册"), 25 | onTap: () async { 26 | Navigator.of(context).pop(); 27 | File file = await ImagePicker.pickImage(source: ImageSource.gallery); 28 | valueChanged(file); 29 | }, 30 | ), 31 | Divider(), 32 | ListTile( 33 | leading: new Icon(Icons.cancel), 34 | title: new Text("取消"), 35 | onTap: () async { 36 | Navigator.of(context).pop(); 37 | valueChanged(null); 38 | }, 39 | ), 40 | Divider(), 41 | ], 42 | ); 43 | }); 44 | } -------------------------------------------------------------------------------- /lib/widgets/custom_alert_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | class CustomAlertDialog extends CupertinoAlertDialog { 4 | final String desc; 5 | final ValueChanged onChanged; 6 | 7 | CustomAlertDialog({Key key, this.desc, this.onChanged}) : super(key: key); 8 | 9 | Widget build(BuildContext context) { 10 | return CupertinoAlertDialog( 11 | title: Text('提示'), 12 | content: SingleChildScrollView( 13 | child: ListBody( 14 | children: [ 15 | Text(this.desc), 16 | ], 17 | ), 18 | ), 19 | actions: [ 20 | CupertinoDialogAction( 21 | child: Text('取消'), 22 | onPressed: () { 23 | Navigator.of(context).pop(); 24 | this.onChanged(false); 25 | }, 26 | ), 27 | CupertinoDialogAction( 28 | child: Text('确定'), 29 | onPressed: () { 30 | Navigator.of(context).pop(); 31 | this.onChanged(true); 32 | 33 | }, 34 | ), 35 | ], 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/widgets/indicator_factory.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 4 | 5 | /* 6 | * Author: Jpeng 7 | * Email: peng8350@gmail.com 8 | * Time: 2018/5/30 上午10:25 9 | */ 10 | 11 | Widget buildDefaultHeader() { 12 | return new ClassicHeader( 13 | failedText: '刷新失败!', 14 | completeText: '刷新完成!', 15 | releaseText: '释放可以刷新', 16 | idleText: '下拉刷新哦!', 17 | failedIcon: new Icon(Icons.clear, color: Colors.black), 18 | completeIcon: new Icon(Icons.done, color: Colors.black), 19 | idleIcon: new Icon(Icons.arrow_downward, color: Colors.black), 20 | releaseIcon: new Icon(Icons.arrow_upward, color: Colors.black), 21 | refreshingText: '正在刷新...', 22 | ); 23 | } 24 | 25 | Widget buildDefaultFooter() { 26 | return new ClassicFooter( 27 | idleIcon: new Icon(Icons.arrow_upward, color: Colors.black), 28 | loadingText: '正在加载中...', 29 | idleText: '上拉加载', 30 | noDataText: '没有更多数据'); 31 | } 32 | -------------------------------------------------------------------------------- /lib/widgets/loading_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_spinkit/flutter_spinkit.dart'; 3 | import 'package:it_resource_exchange_app/common/constant.dart' show AppColors; 4 | 5 | class LoadingDialog extends StatefulWidget { 6 | final Function dismissDialog; 7 | 8 | LoadingDialog({Key key, this.dismissDialog}) : super(key: key); 9 | 10 | @override 11 | _LoadingDialogState createState() => _LoadingDialogState(); 12 | } 13 | 14 | class _LoadingDialogState extends State { 15 | @override 16 | void initState() { 17 | super.initState(); 18 | if (widget.dismissDialog != null) { 19 | widget.dismissDialog(() { 20 | Navigator.of(context).pop(); 21 | }); 22 | } 23 | } 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return Container( 28 | constraints: BoxConstraints.expand(), 29 | child: Center( 30 | child: Opacity( 31 | opacity: 0.8, 32 | child: SpinKitRing( 33 | color: AppColors.PrimaryColor, 34 | size: 50.0, 35 | ), 36 | ), 37 | ),); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: it_resource_exchange_app 2 | description: A new Flutter project. 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: 1.2.1+0 11 | 12 | environment: 13 | sdk: ">=2.2.2 <3.0.0" 14 | 15 | dependencies: 16 | flutter: 17 | sdk: flutter 18 | 19 | # The following adds the Cupertino Icons font to your application. 20 | # Use with the CupertinoIcons class for iOS style icons. 21 | cupertino_icons: ^0.1.2 22 | cached_network_image: ^1.1.0 23 | pull_to_refresh: ^1.3.3 24 | dio: ^2.1.1 25 | shared_preferences: ^0.4.2 26 | connectivity: ^0.4.3+6 27 | event_bus: ^1.1.0 28 | json_annotation: ^2.0.0 29 | intl: ^0.15.8 30 | flutter_spinkit: "^3.1.0" 31 | oktoast: ^2.1.9 32 | image_picker: ^0.6.0+10 33 | multi_image_picker: ^4.5.1 34 | flutter_webview_plugin: ^0.3.5 35 | flutter_swiper : ^1.1.6 36 | fluro: ^1.5.1 37 | chewie: ^0.9.8 38 | auto_orientation: ^0.0.2 39 | 40 | 41 | 42 | dev_dependencies: 43 | build_runner: ^1.2.8 44 | json_serializable: ^2.0.0 45 | flutter_test: 46 | sdk: flutter 47 | 48 | 49 | # For information on the generic Dart part of this file, see the 50 | # following page: https://www.dartlang.org/tools/pub/pubspec 51 | 52 | # The following section is specific to Flutter. 53 | flutter: 54 | 55 | # The following line ensures that the Material Icons font is 56 | # included with your application, so that you can use the icons in 57 | # the material Icons class. 58 | uses-material-design: true 59 | 60 | # To add assets to your application, add an assets section, like this: 61 | assets: 62 | - assets/imgs/img_default.png 63 | - assets/imgs/ic_comment.png 64 | - assets/imgs/ic_cards_wallet.png 65 | - assets/imgs/ic_collections.png 66 | - assets/imgs/ic_settings.png 67 | - assets/imgs/app_icon.png 68 | 69 | # An image asset can refer to one or more resolution-specific "variants", see 70 | # https://flutter.io/assets-and-images/#resolution-aware. 71 | 72 | # For details regarding adding assets from package dependencies, see 73 | # https://flutter.io/assets-and-images/#from-packages 74 | 75 | # To add custom fonts to your application, add a fonts section here, 76 | # in this "flutter" section. Each entry in this list should have a 77 | # "family" key with the font family name, and a "fonts" key with a 78 | # list giving the asset and other descriptors for the font. For 79 | # example: 80 | # fonts: 81 | # - family: Schyler 82 | # fonts: 83 | # - asset: fonts/Schyler-Regular.ttf 84 | # - asset: fonts/Schyler-Italic.ttf 85 | # style: italic 86 | # - family: Trajan Pro 87 | # fonts: 88 | # - asset: fonts/TrajanPro.ttf 89 | # - asset: fonts/TrajanPro_Bold.ttf 90 | # weight: 700 91 | # 92 | # For details regarding fonts from package dependencies, 93 | # see https://flutter.io/custom-fonts/#from-packages 94 | fonts: 95 | - family: appIconFont 96 | fonts: 97 | - asset: assets/fonts/iconfont.ttf 98 | -------------------------------------------------------------------------------- /screenshots/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/screenshots/1.jpg -------------------------------------------------------------------------------- /screenshots/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/screenshots/2.jpg -------------------------------------------------------------------------------- /screenshots/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/screenshots/3.jpg -------------------------------------------------------------------------------- /screenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/screenshots/4.png -------------------------------------------------------------------------------- /screenshots/apk_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvkeep/it_resource_exchange_app/ba8f0ff8c7f7eb8fec3ae2fac00bddd182d579d9/screenshots/apk_download.png -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:it_resource_exchange_app/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | --------------------------------------------------------------------------------