├── res └── values │ └── strings_en.arb ├── lib ├── res │ ├── colours.dart │ ├── resources.dart │ ├── dimens.dart │ ├── gaps.dart │ ├── styles.dart │ └── colors.dart ├── config │ ├── keys.dart │ ├── config.dart │ ├── const.dart │ ├── storage_manager.dart │ ├── user_info_data.dart │ └── api.dart ├── common │ ├── functions.dart │ ├── api_exception.dart │ ├── common.dart │ ├── win_media.dart │ ├── ui.dart │ ├── date.dart │ ├── check.dart │ ├── file_util.dart │ └── shared_util.dart ├── routes │ ├── router_init.dart │ ├── application.dart │ ├── 404.dart │ ├── routers.dart │ ├── fluro_navigator.dart │ └── shop_router.dart ├── view │ ├── flutter_icon_data.dart │ ├── flutter_iconfont.dart │ ├── CustomSliverPersistentHeaderDelegate.dart │ ├── customize_appbar.dart │ ├── my_icons.dart.dart │ └── theme_ui.dart ├── utils │ ├── dialog_utils.dart │ ├── utils.dart │ ├── app_size.dart │ ├── image_utils.dart │ ├── number_text_input_formatter.dart │ ├── double_tap_back_exit_app.dart │ └── theme_utils.dart ├── http │ ├── mvvms.dart │ ├── intercept.dart │ ├── view_model.dart │ ├── base_response.dart │ ├── request_listener.dart │ ├── dio_utils.dart │ └── req_model.dart ├── provider │ └── user_provider.dart ├── widget │ ├── scroll │ │ └── my_behavior.dart │ ├── view │ │ ├── my_button.dart │ │ ├── my_card.dart │ │ ├── text_field_item.dart │ │ ├── load_state_layout.dart │ │ └── my_refresh_list.dart │ ├── load_image.dart │ ├── swiper_diy.dart │ └── root_tabbar.dart ├── api │ └── models │ │ ├── sendsmscode_model.dart │ │ ├── hot_view_model.dart │ │ ├── sendsmscode_view_model.dart │ │ ├── order_form_model.dart │ │ ├── address_view_model.dart │ │ ├── category_view_model.dart │ │ ├── shopping_address_view_model.dart │ │ ├── goods_view_model.dart │ │ ├── login_view_model.dart │ │ ├── order_view_model.dart │ │ ├── order_detail_view_model.dart │ │ ├── cart_goods_query_view_model.dart │ │ ├── goods_details_view_model.dart │ │ ├── file_upload_view_model.dart │ │ ├── topic_detail_view_model.dart │ │ ├── topic_goods_view_model.dart │ │ ├── void_view_model.dart │ │ ├── file_upload_model.dart │ │ ├── hot_modle.dart │ │ ├── topic_details_model.dart │ │ ├── order_model.dart │ │ ├── shopping_address_model.dart │ │ ├── order_detail_model.dart │ │ ├── address_model.dart │ │ ├── category_modle.dart │ │ ├── void_modle.dart │ │ ├── topic_goods_query_modle.dart │ │ └── login_modle.dart ├── receiver │ └── event_bus.dart ├── main.dart ├── page │ ├── home │ │ ├── card_goods.dart │ │ ├── details │ │ │ ├── topic_details.dart │ │ │ ├── specifica_button.dart │ │ │ └── topic_deatails_goods.dart │ │ └── topic_card_goods.dart │ ├── splash_page.dart │ ├── web_page.dart │ ├── guide_page.dart │ ├── cart │ │ ├── count_item.dart │ │ └── cart_bottom.dart │ └── root │ │ └── root_page.dart ├── base │ ├── base_page_state.dart │ └── base_page_state2.dart └── generated │ └── i18n.dart ├── ios ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Runner.xcworkspace │ └── contents.xcworkspacedata ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme └── .gitignore ├── 2222222.gif ├── linjiastudy.txt ├── android ├── app │ ├── studyshop.jks │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values │ │ │ │ │ └── styles.xml │ │ │ │ └── drawable │ │ │ │ │ └── launch_background.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── ityu │ │ │ │ │ └── studylinjiashop │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── key.properties ├── gradle.properties ├── .gitignore ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── build.gradle ├── assets ├── fonts │ ├── iconfont.ttf │ ├── iconpay.ttf │ ├── iconfont2.ttf │ ├── iconfont3.ttf │ └── MyFlutterApp.ttf └── images │ ├── banner.jpg │ ├── ic_empty.png │ ├── icon_user.png │ ├── app_start_1.png │ ├── app_start_2.png │ ├── app_start_3.png │ ├── ic_empty_shop.png │ └── load_error_view.png ├── .metadata ├── .gitignore ├── test └── widget_test.dart ├── README.md └── pubspec.yaml /res/values/strings_en.arb: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /lib/res/colours.dart: -------------------------------------------------------------------------------- 1 | 2 | class Colours2 { 3 | 4 | 5 | } -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /2222222.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/2222222.gif -------------------------------------------------------------------------------- /linjiastudy.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/linjiastudy.txt -------------------------------------------------------------------------------- /android/app/studyshop.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/android/app/studyshop.jks -------------------------------------------------------------------------------- /assets/fonts/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/assets/fonts/iconfont.ttf -------------------------------------------------------------------------------- /assets/fonts/iconpay.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/assets/fonts/iconpay.ttf -------------------------------------------------------------------------------- /assets/images/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/assets/images/banner.jpg -------------------------------------------------------------------------------- /android/key.properties: -------------------------------------------------------------------------------- 1 | storePassword=111111 2 | keyPassword=111111 3 | keyAlias=studyshop 4 | storeFile=studyshop.jks -------------------------------------------------------------------------------- /assets/fonts/iconfont2.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/assets/fonts/iconfont2.ttf -------------------------------------------------------------------------------- /assets/fonts/iconfont3.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/assets/fonts/iconfont3.ttf -------------------------------------------------------------------------------- /assets/images/ic_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/assets/images/ic_empty.png -------------------------------------------------------------------------------- /assets/images/icon_user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/assets/images/icon_user.png -------------------------------------------------------------------------------- /assets/fonts/MyFlutterApp.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/assets/fonts/MyFlutterApp.ttf -------------------------------------------------------------------------------- /assets/images/app_start_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/assets/images/app_start_1.png -------------------------------------------------------------------------------- /assets/images/app_start_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/assets/images/app_start_2.png -------------------------------------------------------------------------------- /assets/images/app_start_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/assets/images/app_start_3.png -------------------------------------------------------------------------------- /assets/images/ic_empty_shop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/assets/images/ic_empty_shop.png -------------------------------------------------------------------------------- /assets/images/load_error_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/assets/images/load_error_view.png -------------------------------------------------------------------------------- /lib/config/keys.dart: -------------------------------------------------------------------------------- 1 | class Keys { 2 | static final String account = "account"; 3 | static final String keyGuide = "keyGuide"; 4 | } 5 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /lib/common/functions.dart: -------------------------------------------------------------------------------- 1 | typedef OnChangedCallback = Future Function(); 2 | ///我的界面路由回调 3 | typedef OnGoMineCallback = Future Function(); 4 | -------------------------------------------------------------------------------- /lib/routes/router_init.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:fluro/fluro.dart'; 3 | 4 | abstract class IRouterProvider{ 5 | 6 | void initRouter(Router router); 7 | } -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /lib/common/api_exception.dart: -------------------------------------------------------------------------------- 1 | class ApiException implements Exception { 2 | final String message; 3 | 4 | final int code; 5 | 6 | const ApiException([this.code, this.message = ""]); 7 | } 8 | -------------------------------------------------------------------------------- /lib/config/config.dart: -------------------------------------------------------------------------------- 1 | /// app名字 2 | const String title = 'ncov-2019'; 3 | 4 | ///连接超时时间为5秒 5 | const int connectTimeOut = 10 * 1000; 6 | 7 | ///响应超时时间为7秒 8 | const int receiveTimeOut = 7 * 1000; 9 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubityu/studylinjiashop/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/view/flutter_icon_data.dart: -------------------------------------------------------------------------------- 1 | library flutter_iconfont; 2 | 3 | import 'package:flutter/widgets.dart' show IconData; 4 | 5 | class IconDataEx extends IconData { 6 | const IconDataEx(int codePoint) 7 | : super( 8 | codePoint, 9 | fontFamily: 'IconFont', 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /lib/config/const.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | const bool inProduction = const bool.fromEnvironment('dart.vm.product'); 4 | 5 | const topic = "topic"; 6 | const cateGory = "cateGory"; 7 | const IMAGE_JPG = "jpg"; 8 | const IMAGE_PNG = "png"; 9 | 10 | int orderIndex = 0; 11 | const limitSize = 20; 12 | -------------------------------------------------------------------------------- /lib/utils/dialog_utils.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:fluttertoast/fluttertoast.dart'; 3 | import 'app_size.dart'; 4 | class DialogUtil { 5 | static void buildToast(String str) { 6 | Fluttertoast.showToast( 7 | fontSize: AppSize.sp(13), 8 | gravity: ToastGravity.CENTER, 9 | msg: str); 10 | } 11 | 12 | 13 | } -------------------------------------------------------------------------------- /lib/http/mvvms.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | abstract class IMvvmView { 5 | BuildContext getContext(); 6 | 7 | /// 显示Progress 8 | void showProgress(); 9 | 10 | /// 关闭Progress 11 | void closeProgress(); 12 | 13 | /// 展示Toast 14 | void showToast(String string); 15 | } 16 | -------------------------------------------------------------------------------- /.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: 0b8abb4724aa590dd0f429683339b1e045a1594d 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /lib/http/intercept.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:studylinjiashop/config/user_info_data.dart'; 3 | 4 | class AuthInterceptor extends Interceptor { 5 | @override 6 | onRequest(RequestOptions options) { 7 | options.headers['Authorization'] = UserInfoData.instance.token; 8 | return super.onRequest(options); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lib/provider/user_provider.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | import 'package:studylinjiashop/config/user_info_data.dart'; 4 | 5 | 6 | class UserProvider extends ChangeNotifier { 7 | 8 | UserInfoData _user; 9 | UserInfoData get user => _user; 10 | 11 | void setUser(UserInfoData user) { 12 | _user = user; 13 | notifyListeners(); 14 | } 15 | } -------------------------------------------------------------------------------- /lib/res/resources.dart: -------------------------------------------------------------------------------- 1 | export 'colors.dart'; 2 | export 'dimens.dart'; 3 | export 'styles.dart'; 4 | export 'gaps.dart'; 5 | 6 | import 'package:flutter/widgets.dart'; 7 | import 'package:studylinjiashop/widget/load_image.dart'; 8 | 9 | 10 | class Images { 11 | static const Widget arrowRight = const LoadAssetImage('ic_arrow_right', height: 16.0, width: 16.0); 12 | 13 | } -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/config/storage_manager.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | 3 | class StorageManager { 4 | /// app全局配置 5 | static SharedPreferences sp; 6 | 7 | /// 网络连接 8 | var connect; 9 | 10 | /// 必备数据的初始化操作 11 | static init() async { 12 | // async 异步操作 13 | // sync 同步操作 14 | sp = await SharedPreferences.getInstance(); 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/utils/utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | String getSafeData(String data) { 4 | return data ?? ""; 5 | } 6 | 7 | Widget hideKeyword(Widget child, BuildContext context) { 8 | return GestureDetector( 9 | behavior: HitTestBehavior.opaque, 10 | child: child, 11 | onTap: () { 12 | FocusScope.of(context).unfocus(); 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /lib/http/view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:studylinjiashop/http/mvvms.dart'; 3 | 4 | class ViewModel { 5 | V view; 6 | CancelToken cancelToken; 7 | List dataListToModel(List data, model) { 8 | List list = new List(); 9 | 10 | data.forEach((json) => list.add(model.fromJson(json))); 11 | 12 | return list; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/http/base_response.dart: -------------------------------------------------------------------------------- 1 | ///返回对象封装 2 | class BaseResponse { 3 | BaseResponse(this.code, this.message, this.data); 4 | 5 | //后台返回的错误码 6 | int code; 7 | //返回的信息 8 | String message; 9 | //返回的数据,需要自己进行处理成自己想要的对象 10 | dynamic data; 11 | 12 | BaseResponse.fromJson(Map json) 13 | : code = json["code"], 14 | message = json["msg"], 15 | data = json["data"]; 16 | } -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /lib/widget/scroll/my_behavior.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class MyBehavior extends ScrollBehavior { 5 | @override 6 | Widget buildViewportChrome( 7 | BuildContext context, Widget child, AxisDirection axisDirection) { 8 | if (Platform.isAndroid || Platform.isFuchsia) { 9 | return child; 10 | } else { 11 | return super.buildViewportChrome(context, child, axisDirection); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/api/models/sendsmscode_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/common/common.dart'; 2 | import 'package:studylinjiashop/http/req_model.dart'; 3 | 4 | class SendSmsCodeModel extends ReqModel { 5 | String phone; 6 | 7 | @override 8 | String url() => API.sendSmsCode; 9 | 10 | @override 11 | Map params() { 12 | return {"mobile": phone}; 13 | } 14 | 15 | Future data(phone) { 16 | this.phone = phone; 17 | return post(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/ityu/studylinjiashop/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.ityu.studylinjiashop 2 | 3 | import androidx.annotation.NonNull; 4 | import io.flutter.embedding.android.FlutterActivity 5 | import io.flutter.embedding.engine.FlutterEngine 6 | import io.flutter.plugins.GeneratedPluginRegistrant 7 | 8 | class MainActivity: FlutterActivity() { 9 | override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { 10 | GeneratedPluginRegistrant.registerWith(flutterEngine); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /lib/api/models/hot_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/http/view_model.dart'; 2 | 3 | import 'hot_modle.dart'; 4 | 5 | HotViewModel hotViewModel = new HotViewModel(); 6 | 7 | class HotViewModel extends ViewModel { 8 | /* 9 | * 发送信息 10 | * */ 11 | Future getData({type = 0, key = ""}) async { 12 | final data = await HotModel().data(type, key); 13 | if (data != null && data.isNotEmpty) { 14 | return HotEntity.fromJson(data); 15 | } 16 | return Future.value(null); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/api/models/sendsmscode_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/api/models/sendsmscode_model.dart'; 2 | import 'package:studylinjiashop/http/view_model.dart'; 3 | 4 | SendSmsViewModel sendSmsViewModel = new SendSmsViewModel(); 5 | 6 | class SendSmsViewModel extends ViewModel { 7 | /* 8 | * 发送信息 9 | * */ 10 | Future getData(phone) async { 11 | final data = await SendSmsCodeModel().data(phone); 12 | if (data != null && data.isNotEmpty) { 13 | return data; 14 | } 15 | return Future.value(null); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/api/models/order_form_model.dart: -------------------------------------------------------------------------------- 1 | class OrderFormEntity { 2 | List items; 3 | 4 | OrderFormEntity(this.items); 5 | 6 | } 7 | 8 | enum OrderForm{ 9 | payment, 10 | waitsending, 11 | aftersending, 12 | finished 13 | } 14 | 15 | abstract class OrderFormListItem { 16 | int id; 17 | String storeName; 18 | String imgUrl; 19 | String status; 20 | String title; 21 | double price; 22 | int amount; 23 | String weight; 24 | double total; 25 | OrderForm type; 26 | 27 | } 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /lib/res/dimens.dart: -------------------------------------------------------------------------------- 1 | class Dimens { 2 | static const double font_sp10 = 10.0; 3 | static const double font_sp12 = 12.0; 4 | static const double font_sp14 = 14.0; 5 | static const double font_sp15 = 15.0; 6 | static const double font_sp16 = 16.0; 7 | static const double font_sp18 = 18.0; 8 | 9 | static const double dp_5 = 5; 10 | static const double dp_4 = 4; 11 | static const double dp_8 = 8; 12 | static const double dp_10 = 10; 13 | static const double dp_12 = 12; 14 | static const double dp_15 = 15; 15 | static const double dp_16 = 16; 16 | static const double dp_50 = 50; 17 | } 18 | -------------------------------------------------------------------------------- /lib/routes/application.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:fluro/fluro.dart'; 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:studylinjiashop/config/api.dart'; 5 | import 'package:studylinjiashop/http/intercept.dart'; 6 | 7 | class Application { 8 | static Router router; 9 | static GlobalKey navKey; 10 | final dio = Dio() 11 | ..options = BaseOptions(baseUrl: API.reqUrl, connectTimeout: 30, receiveTimeout: 30) 12 | ..interceptors.add(AuthInterceptor()) 13 | ..interceptors.add(LogInterceptor(responseBody: true, requestBody: true)); 14 | } 15 | -------------------------------------------------------------------------------- /lib/api/models/address_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/api/models/sendsmscode_model.dart'; 2 | import 'package:studylinjiashop/http/view_model.dart'; 3 | 4 | import 'address_model.dart'; 5 | 6 | AddressViewModel addressViewModel = new AddressViewModel(); 7 | 8 | class AddressViewModel extends ViewModel { 9 | /* 10 | * 发送信息 11 | * */ 12 | Future getData(id) async { 13 | final data = await AddressModel2().data(id); 14 | if (data != null && data.isNotEmpty) { 15 | return AddressEditEntity.fromJson(data); 16 | } 17 | return Future.value(null); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/api/models/category_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/api/models/sendsmscode_model.dart'; 2 | import 'package:studylinjiashop/http/view_model.dart'; 3 | 4 | import 'category_modle.dart'; 5 | 6 | CategoryViewModel categoryViewModel = new CategoryViewModel(); 7 | 8 | class CategoryViewModel extends ViewModel { 9 | /* 10 | * 发送信息 11 | * */ 12 | Future getData() async { 13 | final data = await CategoryModel2().data(); 14 | if (data != null && data.isNotEmpty) { 15 | return CategoryEntity.fromJson(data); 16 | } 17 | return Future.value(null); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/api/models/shopping_address_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/api/models/shopping_address_model.dart'; 2 | import 'package:studylinjiashop/http/view_model.dart'; 3 | 4 | ShoppingAddressViewModel shoppingAddressViewModel = new ShoppingAddressViewModel(); 5 | 6 | class ShoppingAddressViewModel extends ViewModel { 7 | /* 8 | * 发送信息 9 | * */ 10 | Future getData() async { 11 | final data = await ShoppingAddressModel2().get(); 12 | if (data != null) { 13 | return ShoppingAddressEntry.fromJson(data); 14 | } 15 | return Future.value(null); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/api/models/goods_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/api/models/sendsmscode_model.dart'; 2 | import 'package:studylinjiashop/http/view_model.dart'; 3 | 4 | import 'category_modle.dart'; 5 | import 'goods_modle.dart'; 6 | 7 | GoodsViewModel goodsViewModel = new GoodsViewModel(); 8 | 9 | class GoodsViewModel extends ViewModel { 10 | /* 11 | * 发送信息 12 | * */ 13 | Future getData(idCategory) async { 14 | final data = await GoodsModel2().data(idCategory); 15 | if (data != null && data.isNotEmpty) { 16 | return GoodsEntity.fromJson(data); 17 | } 18 | return Future.value(null); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/http/request_listener.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'base_response.dart'; 4 | 5 | ///网络请求监听 6 | class RequestListener { 7 | RequestListener({@required this.onSuccessListener, @required this.onErrorListener}); 8 | 9 | //请求成功 10 | final ValueChanged onSuccessListener; 11 | 12 | //请求失败 13 | final ValueChanged onErrorListener; 14 | 15 | void onSuccess(BaseResponse response) { 16 | if (onSuccessListener != null) onSuccessListener(response); 17 | } 18 | 19 | void onError(BaseResponse response) { 20 | if (onErrorListener != null) onErrorListener(response); 21 | } 22 | } -------------------------------------------------------------------------------- /lib/api/models/login_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/api/models/sendsmscode_model.dart'; 2 | import 'package:studylinjiashop/http/view_model.dart'; 3 | 4 | import 'login_modle.dart'; 5 | 6 | LoginViewModel loginViewModel = new LoginViewModel(); 7 | 8 | class LoginViewModel extends ViewModel { 9 | /* 10 | * 发送信息 11 | * */ 12 | Future getData(phone, codeOrPwd, type) async { 13 | final Map data = await LoginBeanModel(cancelToken,view).data(phone, codeOrPwd, type); 14 | if (data != null && data.isNotEmpty) { 15 | return LoginBean.fromJson(data); 16 | } 17 | return Future.value(null); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/utils/app_size.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | 5 | class AppSize{ 6 | static void init(BuildContext context){ 7 | ScreenUtil.instance = ScreenUtil(width: 1080, height: 1920)..init(context); 8 | } 9 | 10 | static double height(double value){ 11 | return ScreenUtil.getInstance().setHeight(value); 12 | } 13 | 14 | static double width(double value){ 15 | return ScreenUtil.getInstance().setWidth(value); 16 | } 17 | 18 | static double sp(double value){ 19 | return ScreenUtil.getInstance().setSp(value); 20 | } 21 | 22 | 23 | } 24 | 25 | 26 | -------------------------------------------------------------------------------- /lib/api/models/order_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/api/models/sendsmscode_model.dart'; 2 | import 'package:studylinjiashop/http/view_model.dart'; 3 | 4 | import 'login_modle.dart'; 5 | import 'order_detail_model.dart'; 6 | import 'order_model.dart'; 7 | 8 | OrderViewModel orderViewModel = new OrderViewModel(); 9 | 10 | class OrderViewModel extends ViewModel { 11 | /* 12 | * 发送信息 13 | * */ 14 | Future getData(status,page) async { 15 | final Map data = await OrderModel2().data(status,page); 16 | if (data != null && data.isNotEmpty) { 17 | return OrderEntity.fromJson(data); 18 | } 19 | return Future.value(null); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /lib/api/models/order_detail_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/api/models/sendsmscode_model.dart'; 2 | import 'package:studylinjiashop/http/view_model.dart'; 3 | 4 | import 'login_modle.dart'; 5 | import 'order_detail_model.dart'; 6 | 7 | OrderDetailViewModel orderDetailViewModel = new OrderDetailViewModel(); 8 | 9 | class OrderDetailViewModel extends ViewModel { 10 | /* 11 | * 发送信息 12 | * */ 13 | Future getData(orderSn) async { 14 | final Map data = await OrderDetailModel2().data(orderSn); 15 | if (data != null && data.isNotEmpty) { 16 | return OrderDetailEntry.fromJson(data); 17 | } 18 | return Future.value(null); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/routes/404.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:studylinjiashop/utils/app_size.dart'; 3 | import 'package:studylinjiashop/view/app_topbar.dart'; 4 | import 'package:studylinjiashop/view/customize_appbar.dart'; 5 | import 'package:studylinjiashop/widget/view/load_state_layout.dart'; 6 | 7 | class WidgetNotFound extends StatelessWidget { 8 | @override 9 | Widget build(BuildContext context) { 10 | return Scaffold( 11 | appBar: MyAppBar( 12 | preferredSize: Size.fromHeight(AppSize.height(160)), 13 | child: CommonBackTopBar(title: "页面不存在")), 14 | body: LoadStateLayout(state: LoadState.State_Error,), 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/api/models/cart_goods_query_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/api/models/sendsmscode_model.dart'; 2 | import 'package:studylinjiashop/http/view_model.dart'; 3 | 4 | import 'cart_goods_query_modle.dart'; 5 | import 'category_modle.dart'; 6 | 7 | CartGoodsQueryViewModel cartGoodsQueryViewModel = new CartGoodsQueryViewModel(); 8 | 9 | class CartGoodsQueryViewModel extends ViewModel { 10 | /* 11 | * 发送信息 12 | * */ 13 | Future getData() async { 14 | final data = await GoodsListModel2().data(); 15 | if (data != null && data.isNotEmpty) { 16 | return CartGoodsQueryEntity.fromJson(data); 17 | } 18 | return Future.value(null); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/api/models/goods_details_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/api/models/sendsmscode_model.dart'; 2 | import 'package:studylinjiashop/http/view_model.dart'; 3 | 4 | import 'goods_details_model.dart'; 5 | import 'login_modle.dart'; 6 | import 'order_detail_model.dart'; 7 | 8 | GoodsDetailViewModel goodsDetailViewModel = new GoodsDetailViewModel(); 9 | 10 | class GoodsDetailViewModel extends ViewModel { 11 | /* 12 | * 发送信息 13 | * */ 14 | Future getData(id) async { 15 | final Map data = await GoodsModelAndSkuModel().data(id); 16 | if (data != null && data.isNotEmpty) { 17 | return DetailsEntity.fromJson(data); 18 | } 19 | return Future.value(null); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/api/models/file_upload_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/api/models/sendsmscode_model.dart'; 2 | import 'package:studylinjiashop/http/view_model.dart'; 3 | 4 | import 'file_upload_model.dart'; 5 | import 'goods_details_model.dart'; 6 | import 'login_modle.dart'; 7 | import 'order_detail_model.dart'; 8 | 9 | FileUploadViewModel fileUploadViewModel = new FileUploadViewModel(); 10 | 11 | class FileUploadViewModel extends ViewModel { 12 | /* 13 | * 发送信息 14 | * */ 15 | Future getData(params) async { 16 | final Map data = await FileModel2().data(params); 17 | if (data != null) { 18 | return FileEntity.fromJson(data); 19 | } 20 | return Future.value(null); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/api/models/topic_detail_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/api/models/sendsmscode_model.dart'; 2 | import 'package:studylinjiashop/http/view_model.dart'; 3 | 4 | import 'login_modle.dart'; 5 | import 'order_detail_model.dart'; 6 | import 'topic_details_model.dart'; 7 | 8 | TopicDetailViewModel topicDetailViewModel = new TopicDetailViewModel(); 9 | 10 | class TopicDetailViewModel extends ViewModel { 11 | /* 12 | * 发送信息 13 | * */ 14 | Future getData(id) async { 15 | final Map data = await TopicDetailModel().data(id); 16 | if (data != null && data.isNotEmpty) { 17 | return TopicDetailsEntity.fromJson(data); 18 | } 19 | return Future.value(null); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /lib/api/models/topic_goods_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/api/models/sendsmscode_model.dart'; 2 | import 'package:studylinjiashop/http/view_model.dart'; 3 | 4 | import 'category_modle.dart'; 5 | import 'goods_modle.dart'; 6 | import 'hot_modle.dart'; 7 | import 'topic_goods_query_modle.dart'; 8 | 9 | TopicGoodsViewModel topicGoodsViewModel = new TopicGoodsViewModel(); 10 | 11 | class TopicGoodsViewModel extends ViewModel { 12 | /* 13 | * 发送信息 14 | * */ 15 | Future getData() async { 16 | final data = await TopicGoodsListModel2().data(); 17 | if (data != null && data.isNotEmpty) { 18 | return TopicGoodsQueryEntity.fromJson(data); 19 | } 20 | return Future.value(null); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # Web related 34 | lib/generated_plugin_registrant.dart 35 | 36 | # Exceptions to above rules. 37 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 38 | -------------------------------------------------------------------------------- /lib/utils/image_utils.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | import 'package:cached_network_image/cached_network_image.dart'; 6 | import 'package:studylinjiashop/utils/utils.dart'; 7 | 8 | 9 | class ImageUtils { 10 | 11 | static ImageProvider getAssetImage(String name, {String format: 'png'}) { 12 | return AssetImage(getImgPath(name, format: format)); 13 | } 14 | 15 | static String getImgPath(String name, {String format: 'png'}) { 16 | return 'assets/images/$name.$format'; 17 | } 18 | 19 | static ImageProvider getImageProvider(String imageUrl, {String holderImg: 'none'}) { 20 | if (getSafeData(imageUrl).isNotEmpty) { 21 | return AssetImage(getImgPath(holderImg)); 22 | } 23 | return CachedNetworkImageProvider(imageUrl, errorListener: () => print("图片加载失败!")); 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /lib/api/models/void_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:studylinjiashop/api/models/sendsmscode_model.dart'; 3 | import 'package:studylinjiashop/http/mvvms.dart'; 4 | import 'package:studylinjiashop/http/view_model.dart'; 5 | 6 | import 'void_modle.dart'; 7 | 8 | VoidViewModel voidViewModel = new VoidViewModel(); 9 | 10 | class VoidViewModel extends ViewModel { 11 | VoidViewModel() { 12 | print("======VoidViewModel()====="); 13 | } 14 | VoidViewModel.get(IMvvmView view, CancelToken cancelToken){ 15 | print("======VoidViewModel.get()====="); 16 | this.view = view; 17 | this.cancelToken = cancelToken; 18 | } 19 | 20 | Future getData({type = 0, Map params2}) async { 21 | final data = await VoidModel(cancelToken, view).data(type, params2); 22 | if (data != null) { 23 | return data; 24 | } 25 | return Future.value(null); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/view/flutter_iconfont.dart: -------------------------------------------------------------------------------- 1 | library flutter_iconfont; 2 | 3 | import 'package:flutter/widgets.dart' show IconData; 4 | 5 | import 'flutter_icon_data.dart'; 6 | 7 | 8 | class IconFonts{ 9 | IconFonts._(); 10 | 11 | static const IconData triangle_down = const IconDataEx(0xe6b4); 12 | static const IconData eye = const IconDataEx(0xe616); 13 | static const IconData scan_code = const IconDataEx(0xe640); 14 | static const IconData triangle_right = const IconDataEx(0xe615); 15 | static const IconData arrow_down = const IconDataEx(0xe62a); 16 | static const IconData arrow_left = const IconDataEx(0xe62c); 17 | static const IconData shopping_cart = const IconDataEx(0xe614); 18 | static const IconData ellipsis = const IconDataEx(0xe727); 19 | static const IconData location = const IconDataEx(0xe612); 20 | static const IconData arrow_right = const IconDataEx(0xe620); 21 | static const IconData eye_close = const IconDataEx(0xe50e); 22 | } 23 | -------------------------------------------------------------------------------- /lib/receiver/event_bus.dart: -------------------------------------------------------------------------------- 1 | import 'package:event_bus/event_bus.dart'; 2 | 3 | //Bus初始化 4 | EventBus eventBus = EventBus(); 5 | 6 | class UserLoggedInEvent { 7 | String text; 8 | UserLoggedInEvent(String text){ 9 | this.text = text; 10 | } 11 | } 12 | class UserNumInEvent{ 13 | String num; 14 | UserNumInEvent(String text){ 15 | this.num=text; 16 | } 17 | } 18 | class GoodsNumInEvent{ 19 | String event; 20 | GoodsNumInEvent(String text){ 21 | this.event=text; 22 | } 23 | } 24 | class OrderInEvent { 25 | String text; 26 | OrderInEvent(String text){ 27 | this.text = text; 28 | } 29 | } 30 | class UserInfoInEvent { 31 | String text; 32 | UserInfoInEvent(String text){ 33 | this.text = text; 34 | } 35 | } 36 | class SpecEvent{ 37 | String code; 38 | SpecEvent(String text){ 39 | this.code=text; 40 | } 41 | } 42 | 43 | class TabIndexEvent{ 44 | int num; 45 | TabIndexEvent(int text){ 46 | this.num=text; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/common/common.dart: -------------------------------------------------------------------------------- 1 | /// comMom 2 | export 'check.dart'; 3 | export '../common/ui.dart'; 4 | export 'win_media.dart'; 5 | export 'check.dart'; 6 | export 'date.dart'; 7 | export 'file_util.dart'; 8 | 9 | /// config 10 | export 'package:studylinjiashop/config/api.dart'; 11 | export 'package:studylinjiashop/config/api.dart'; 12 | export 'package:studylinjiashop/config/config.dart'; 13 | export 'package:studylinjiashop/config/const.dart'; 14 | export 'package:studylinjiashop/config/keys.dart'; 15 | 16 | /// widget 17 | //export 'package:studylinjiashop/widget/bar/commom_bar.dart'; 18 | //export 'package:studylinjiashop/widget/view/title_view.dart'; 19 | 20 | /// other 21 | //export 'package:ncov_2019/widget/view/web_view_page.dart'; 22 | export 'package:cached_network_image/cached_network_image.dart'; 23 | export 'package:connectivity/connectivity.dart'; 24 | import 'package:connectivity/connectivity.dart'; 25 | export 'package:studylinjiashop/common/shared_util.dart'; 26 | 27 | 28 | var subscription = Connectivity(); 29 | -------------------------------------------------------------------------------- /lib/utils/number_text_input_formatter.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/services.dart'; 3 | 4 | /// 只允许输入小数 5 | class UsNumberTextInputFormatter extends TextInputFormatter { 6 | static const defaultDouble = 0.001; 7 | static double strToFloat(String str, [double defaultValue = defaultDouble]) { 8 | try { 9 | return double.parse(str); 10 | } catch (e) { 11 | return defaultValue; 12 | } 13 | } 14 | 15 | @override 16 | TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { 17 | String value = newValue.text; 18 | int selectionIndex = newValue.selection.end; 19 | if (value == '.') { 20 | value = '0.'; 21 | selectionIndex++; 22 | } else if (value != '' && value != defaultDouble.toString() && strToFloat(value, defaultDouble) == defaultDouble) { 23 | value = oldValue.text; 24 | selectionIndex = oldValue.selection.end; 25 | } 26 | return new TextEditingValue( 27 | text: value, 28 | selection: new TextSelection.collapsed(offset: selectionIndex), 29 | ); 30 | } 31 | } -------------------------------------------------------------------------------- /lib/api/models/file_upload_model.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'dart:convert'; 3 | 4 | import 'package:studylinjiashop/config/api.dart'; 5 | import 'package:studylinjiashop/http/req_model.dart'; 6 | 7 | class FileModel2 extends ReqModel { 8 | 9 | Map params2; 10 | 11 | @override 12 | String encodeData() { 13 | return json.encode(params2); 14 | } 15 | 16 | @override 17 | String url() { 18 | return API.uploadBase64; 19 | } 20 | 21 | Future data(Map params2) { 22 | this.params2 = params2; 23 | return post(); 24 | } 25 | } 26 | 27 | 28 | class FileEntity { 29 | FileModel msgModel; 30 | FileEntity({this.msgModel}); 31 | FileEntity.fromJson(Map json) { 32 | msgModel =FileModel.fromJson(json,"成功"); 33 | } 34 | } 35 | class FileModel { 36 | String avatar; 37 | String nickName; 38 | String msg; 39 | 40 | FileModel({this.avatar, this.nickName,this.msg}); 41 | FileModel.fromJson(Map json,String message) { 42 | avatar = json['avatar']; 43 | nickName = json['nickName']; 44 | msg=message; 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /lib/view/CustomSliverPersistentHeaderDelegate.dart: -------------------------------------------------------------------------------- 1 | // 自定义 SliverPersistentHeaderDelegate 2 | import 'package:flutter/material.dart'; 3 | 4 | class CustomSliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate { 5 | final double max; // 最大高度 6 | final double min; // 最小高度 7 | final Widget child; // 需要展示的内容 8 | 9 | CustomSliverPersistentHeaderDelegate({@required this.max, @required this.min, @required this.child}) 10 | // 如果 assert 内部条件不成立,会报错 11 | : assert(max != null), 12 | assert(min != null), 13 | assert(child != null), 14 | assert(min <= max), 15 | super(); 16 | 17 | // 返回展示的内容,如果内容固定可以直接在这定义,如果需要可扩展,这边通过传入值来定义 18 | @override 19 | Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) => child; 20 | 21 | @override 22 | double get maxExtent => max; // 返回最大高度 23 | 24 | @override 25 | double get minExtent => min; // 返回最小高度 26 | 27 | @override 28 | bool shouldRebuild(CustomSliverPersistentHeaderDelegate oldDelegate) { 29 | // 是否需要更新,这里我们定义当高度范围和展示内容被替换的时候进行刷新界面 30 | return max != oldDelegate.max || min != oldDelegate.min || child != oldDelegate.child; 31 | } 32 | } -------------------------------------------------------------------------------- /lib/utils/double_tap_back_exit_app.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'dialog_utils.dart'; 4 | 5 | 6 | class DoubleTapBackExitApp extends StatefulWidget { 7 | 8 | const DoubleTapBackExitApp({ 9 | Key key, 10 | @required this.child, 11 | this.duration: const Duration(milliseconds: 2500), 12 | }) : super(key: key); 13 | 14 | final Widget child; 15 | 16 | /// 两次点击返回按钮的时间间隔 17 | final Duration duration; 18 | 19 | @override 20 | _DoubleTapBackExitAppState createState() => _DoubleTapBackExitAppState(); 21 | } 22 | 23 | class _DoubleTapBackExitAppState extends State { 24 | 25 | DateTime _lastTime; 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return WillPopScope( 30 | onWillPop: _isExit, 31 | child: widget.child, 32 | ); 33 | } 34 | 35 | Future _isExit() { 36 | if (_lastTime == null || 37 | DateTime.now().difference(_lastTime) > widget.duration) { 38 | _lastTime = DateTime.now(); 39 | DialogUtil.buildToast('再次点击退出应用'); 40 | return Future.value(false); 41 | } 42 | return Future.value(true); 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /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:studylinjiashop/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 | -------------------------------------------------------------------------------- /lib/common/win_media.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | double winWidth(BuildContext context) { 6 | return MediaQuery.of(context).size.width; 7 | } 8 | 9 | double winHeight(BuildContext context) { 10 | return MediaQuery.of(context).size.height; 11 | } 12 | 13 | double winTop(BuildContext context) { 14 | return MediaQuery.of(context).padding.top; 15 | } 16 | 17 | double winBottom(BuildContext context) { 18 | return MediaQuery.of(context).padding.bottom; 19 | } 20 | 21 | double winLeft(BuildContext context) { 22 | return MediaQuery.of(context).padding.left; 23 | } 24 | 25 | double winRight(BuildContext context) { 26 | return MediaQuery.of(context).padding.right; 27 | } 28 | 29 | double winKeyHeight(BuildContext context) { 30 | return MediaQuery.of(context).viewInsets.bottom; 31 | } 32 | 33 | double statusBarHeight(BuildContext context) { 34 | return MediaQueryData.fromWindow(window).padding.top; 35 | } 36 | 37 | double navigationBarHeight(BuildContext context) { 38 | return kToolbarHeight; 39 | } 40 | 41 | double topBarHeight(BuildContext context) { 42 | return kToolbarHeight + MediaQueryData.fromWindow(window).padding.top; 43 | } 44 | -------------------------------------------------------------------------------- /lib/http/dio_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:studylinjiashop/config/api.dart'; 3 | import 'package:studylinjiashop/config/config.dart'; 4 | 5 | import 'intercept.dart'; 6 | 7 | class DioUtils { 8 | static final DioUtils _singleton = DioUtils._internal(); 9 | 10 | static DioUtils get instance => DioUtils(); 11 | 12 | factory DioUtils() { 13 | return _singleton; 14 | } 15 | 16 | static Dio _dio; 17 | 18 | Dio getDio() { 19 | return _dio; 20 | } 21 | 22 | DioUtils._internal() { 23 | var options = BaseOptions( 24 | connectTimeout: connectTimeOut, 25 | receiveTimeout: receiveTimeOut, 26 | //responseType: ResponseType.plain, 27 | headers: { 28 | 'content-type': 'application/json', 29 | }, 30 | validateStatus: (status) { 31 | // 不使用http状态码判断状态,使用AdapterInterceptor来处理(适用于标准REST风格) 32 | return true; 33 | }, 34 | baseUrl: API.reqUrl, 35 | // contentType: ContentType('application', 'x-www-form-urlencoded', charset: 'utf-8'), 36 | ); 37 | _dio = new Dio(options); 38 | _dio.interceptors.add(AuthInterceptor()); 39 | /// Fiddler抓包代理配置 https://www.jianshu.com/p/d831b1f7c45b 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/api/models/hot_modle.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/config/api.dart'; 2 | import 'package:studylinjiashop/http/req_model.dart'; 3 | 4 | import 'goods_modle.dart'; 5 | 6 | class HotModel extends ReqModel { 7 | int type; 8 | String key; 9 | 10 | @override 11 | String url() { 12 | String url = ""; 13 | switch (type) { 14 | case 2: 15 | url = API.search; 16 | break; 17 | case 1: 18 | url = API.goodsSearchNew; 19 | break; 20 | default: 21 | url = API.goodsSearchHot; 22 | break; 23 | } 24 | return url; 25 | } 26 | 27 | @override 28 | Map params() { 29 | if (key.isNotEmpty) { 30 | return {"key": key}; 31 | } else { 32 | return {}; 33 | } 34 | } 35 | 36 | Future data(type, key) { 37 | this.type = type; 38 | this.key = key; 39 | return get(); 40 | } 41 | } 42 | 43 | class HotEntity { 44 | List goods; 45 | 46 | HotEntity({this.goods}); 47 | 48 | HotEntity.fromJson(List json) { 49 | goods = new List(); 50 | List dataList = json.cast(); 51 | dataList.forEach((v) { 52 | goods.add(new GoodsModel.fromJson(v)); 53 | // print(goods.length); 54 | }); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/widget/view/my_button.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | import 'package:studylinjiashop/res/colors.dart'; 4 | import 'package:studylinjiashop/res/dimens.dart'; 5 | import 'package:studylinjiashop/utils/theme_utils.dart'; 6 | 7 | 8 | class MyButton extends StatelessWidget { 9 | 10 | const MyButton({ 11 | Key key, 12 | this.text: '', 13 | @required this.onPressed, 14 | }): super(key: key); 15 | 16 | final String text; 17 | final VoidCallback onPressed; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | bool isDark = ThemeUtils.isDark(context); 22 | return FlatButton( 23 | onPressed: onPressed, 24 | textColor: isDark ? Colours.dark_button_text : Colors.white, 25 | color: isDark ? Colours.dark_app_main : Colours.app_main, 26 | disabledTextColor: isDark ? Colours.dark_text_disabled : Colours.text_disabled, 27 | disabledColor: isDark ? Colours.dark_button_disabled : Colours.button_disabled, 28 | //shape:RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)), 29 | child: Container( 30 | height: 48, 31 | width: double.infinity, 32 | alignment: Alignment.center, 33 | child: Text(text, style: TextStyle(fontSize: Dimens.font_sp18),), 34 | ), 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [TOC] 2 | # With flutter- MVVM to achieve a very simple mall shopping project 3 | #### Project directory interface 4 | 5 | ![image](https://github.com/githubityu/studylinjiashop/blob/master/2222222.gif) 6 | 7 | 8 | ├─api 9 | 10 | │ └─models model和viewmodel代码 11 | 12 | ├─base BasePageState 实现MVVMVIews,提供view和CancelToken 13 | 14 | ├─common 15 | 16 | ├─config 接口的url,用户信息 17 | 18 | ├─generated 19 | 20 | ├─http 网络请求数据相关,返回基类数据信息,异常处理,弹框显示和隐藏 21 | 22 | ├─page 项目页面 23 | 24 | │ ├─cart 25 | 26 | │ ├─home 27 | 28 | │ │ └─details 29 | 30 | │ ├─member 31 | 32 | │ ├─order 33 | 34 | │ ├─root 35 | 36 | │ └─search 37 | 38 | ├─provider 39 | 40 | ├─receiver 41 | 42 | ├─res 43 | 44 | ├─routes 路由操作相关 45 | 46 | ├─utils 47 | 48 | ├─view 49 | 50 | └─widget 51 | ├─dialog 52 | ├─scroll 53 | └─view 54 | 55 | 56 | 57 | 58 | 59 | ##### API code 60 | [linjiashopstudy](https://github.com/githubityu/linjiashopstudy.git) 61 | 62 | 63 | ##### 用gradle,springboot,jwt security 编写 64 | 65 | 66 | ##### Reference Item 67 | [ncov_2019](https://github.com/fluttercandies/ncov_2019) 68 | 69 | [linjiashop-flutter](https://github.com/microapp-store/linjiashop-flutter) 70 | 71 | [flutter_deer](https://github.com/simplezhli/flutter_deer.git) 72 | 73 | [linjiashop](https://gitee.com/microapp/linjiashop) 74 | -------------------------------------------------------------------------------- /lib/view/customize_appbar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:flutter_statusbarcolor/flutter_statusbarcolor.dart'; 4 | 5 | 6 | class MyAppBar extends StatelessWidget implements PreferredSizeWidget { 7 | final Widget child; 8 | 9 | @override 10 | final Size preferredSize; 11 | 12 | const MyAppBar({ 13 | Key key, 14 | @required this.child, 15 | this.preferredSize, 16 | }) : super(key: key); 17 | 18 | void initStateBar() async{ 19 | FlutterStatusbarcolor.setStatusBarWhiteForeground(true); 20 | } 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | ScreenUtil.instance = ScreenUtil(width: 1080, height: 1920)..init(context); 25 | double _statusHeight = ScreenUtil.statusBarHeight; 26 | 27 | return Container( 28 | padding: EdgeInsets.only(left: 6, right: 6,top: _statusHeight), 29 | decoration: BoxDecoration( 30 | gradient: LinearGradient( 31 | colors: [Color(0xff00C2FD), Color(0xff00C2FD)], 32 | begin: Alignment.topCenter, 33 | end: Alignment.bottomCenter), 34 | ), 35 | height: preferredSize.height+_statusHeight, 36 | // color: Theme.of(context).primaryColor, 37 | child: this.child, 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/api/models/topic_details_model.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:studylinjiashop/config/api.dart'; 3 | import 'package:studylinjiashop/http/req_model.dart'; 4 | 5 | /// 推荐详情页面 6 | 7 | class TopicDetailModel extends ReqModel { 8 | String id; 9 | 10 | @override 11 | String url() => "${API.topicDetail}$id"; 12 | 13 | 14 | Future data(id) { 15 | this.id = id; 16 | return get(); 17 | } 18 | 19 | } 20 | 21 | 22 | class TopicDetailsEntity { 23 | ArticleModel articleModel; 24 | List goodsList; 25 | TopicDetailsEntity({this.articleModel,this.goodsList}); 26 | TopicDetailsEntity.fromJson(Map json) { 27 | goodsList = new List(); 28 | if(json['article'] !=null) { 29 | articleModel = ArticleModel.fromJson(json['article']); 30 | } 31 | List dataList= (json['goodsList'] as List).cast(); 32 | dataList.forEach((v){ 33 | goodsList.add(new TopGoods.fromJson(v)); 34 | }); 35 | } 36 | 37 | } 38 | class ArticleModel{ 39 | String title; 40 | String content; 41 | ArticleModel.fromJson(Map json){ 42 | title=json['title']; 43 | content=json['content']; 44 | } 45 | 46 | } 47 | class TopGoods { 48 | String name; 49 | String pic; 50 | String id; 51 | TopGoods.fromJson(Map json){ 52 | name = json['name']; 53 | pic = json['pic']; 54 | id = json['id']; 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /lib/utils/theme_utils.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:studylinjiashop/res/colors.dart'; 5 | import 'package:studylinjiashop/res/colours.dart'; 6 | 7 | 8 | class ThemeUtils { 9 | 10 | static bool isDark(BuildContext context) { 11 | return Theme.of(context).brightness == Brightness.dark; 12 | } 13 | 14 | static Color getDarkColor(BuildContext context, Color darkColor) { 15 | return isDark(context) ? darkColor : null; 16 | } 17 | 18 | static Color getIconColor(BuildContext context) { 19 | return isDark(context) ? Colours.text_dark : null; 20 | } 21 | 22 | static Color getBackgroundColor(BuildContext context) { 23 | return Theme.of(context).scaffoldBackgroundColor; 24 | } 25 | 26 | static Color getDialogBackgroundColor(BuildContext context) { 27 | return Theme.of(context).canvasColor; 28 | } 29 | 30 | static Color getStickyHeaderColor(BuildContext context) { 31 | return isDark(context) ? Colours.dark_bg_gray_ : Colours.bg_gray_; 32 | } 33 | 34 | static Color getDialogTextFieldColor(BuildContext context) { 35 | return isDark(context) ? Colours.dark_bg_gray_ : Colours.bg_gray; 36 | } 37 | 38 | static Color getKeyboardActionsColor(BuildContext context) { 39 | return isDark(context) ? Colours.dark_bg_color : Colors.grey[200]; 40 | 41 | } 42 | } -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter_statusbarcolor/flutter_statusbarcolor.dart'; 6 | import 'package:studylinjiashop/config/storage_manager.dart'; 7 | import 'package:studylinjiashop/page/splash_page.dart'; 8 | import 'package:studylinjiashop/routes/fluro_navigator.dart'; 9 | 10 | import 'routes/application.dart'; 11 | 12 | void main() async { 13 | WidgetsFlutterBinding.ensureInitialized(); 14 | await StorageManager.init(); 15 | SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]) 16 | .then((_) { 17 | runApp(MyApp()); 18 | }); 19 | if (Platform.isAndroid) { 20 | SystemChrome.setSystemUIOverlayStyle( 21 | SystemUiOverlayStyle(statusBarColor: Colors.transparent)); 22 | } 23 | FlutterStatusbarcolor.setStatusBarWhiteForeground(false); 24 | } 25 | 26 | class MyApp extends StatelessWidget { 27 | final Widget home; 28 | 29 | MyApp({this.home}) { 30 | NavigatorUtils.initRouter(); 31 | } 32 | @override 33 | Widget build(BuildContext context) { 34 | return MaterialApp( 35 | // debugShowCheckedModeBanner: false, 36 | home: home ?? SplashPage(), 37 | navigatorKey: Application.navKey, 38 | onGenerateRoute: Application.router.generator, 39 | theme: ThemeData(primarySwatch: Colors.blue), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/widget/view/my_card.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | import 'package:studylinjiashop/res/colors.dart'; 4 | import 'package:studylinjiashop/utils/theme_utils.dart'; 5 | 6 | 7 | class MyCard extends StatelessWidget { 8 | 9 | const MyCard({ 10 | Key key, 11 | @required this.child, 12 | this.color, 13 | this.shadowColor 14 | }): super(key: key); 15 | 16 | final Widget child; 17 | final Color color; 18 | final Color shadowColor; 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | Color _backgroundColor; 23 | Color _shadowColor; 24 | bool isDark = ThemeUtils.isDark(context); 25 | if (color == null) { 26 | _backgroundColor = isDark ? Colours.dark_bg_gray_ : Colors.white; 27 | } else { 28 | _backgroundColor = color; 29 | } 30 | 31 | if (shadowColor == null) { 32 | _shadowColor = isDark ? Colors.transparent : const Color(0x80DCE7FA); 33 | } else { 34 | _shadowColor = isDark ? Colors.transparent : shadowColor; 35 | } 36 | 37 | return DecoratedBox( 38 | decoration: BoxDecoration( 39 | color: _backgroundColor, 40 | borderRadius: BorderRadius.circular(8.0), 41 | boxShadow: [ 42 | BoxShadow(color: _shadowColor, offset: Offset(0.0, 2.0), blurRadius: 8.0, spreadRadius: 0.0), 43 | ] 44 | ), 45 | child: child, 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/common/ui.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class HorizontalLine extends StatelessWidget { 4 | final double height; 5 | final Color color; 6 | final double horizontal; 7 | 8 | HorizontalLine({ 9 | this.height = 0.5, 10 | this.color = const Color(0xFFEEEEEE), 11 | this.horizontal = 0.0, 12 | }); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return new Container( 17 | height: height, 18 | color: color, 19 | margin: new EdgeInsets.symmetric(horizontal: horizontal), 20 | ); 21 | } 22 | } 23 | 24 | class VerticalLine extends StatelessWidget { 25 | final double width; 26 | final double height; 27 | final Color color; 28 | final double vertical; 29 | 30 | VerticalLine({ 31 | this.width = 1.0, 32 | this.height = 25, 33 | this.color = const Color.fromRGBO(209, 209, 209, 0.5), 34 | this.vertical = 0.0, 35 | }); 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | return new Container( 40 | width: width, 41 | color: Color(0xffDCE0E5), 42 | margin: new EdgeInsets.symmetric(vertical: vertical), 43 | height: height, 44 | ); 45 | } 46 | } 47 | 48 | class Space extends StatelessWidget { 49 | final double width; 50 | final double height; 51 | 52 | Space({this.width = 10.0, this.height = 10.0}); 53 | 54 | @override 55 | Widget build(BuildContext context) { 56 | return new Container(width: width, height: height); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/routes/routers.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:fluro/fluro.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:studylinjiashop/page/root/root_page.dart'; 5 | import 'package:studylinjiashop/page/splash_page.dart'; 6 | import 'package:studylinjiashop/page/web_page.dart'; 7 | import 'package:studylinjiashop/routes/router_init.dart'; 8 | 9 | import '404.dart'; 10 | import 'shop_router.dart'; 11 | 12 | 13 | class Routes { 14 | 15 | static String home = '/'; 16 | static String webViewPage = '/webview'; 17 | 18 | static List _listRouter = []; 19 | 20 | static void configureRoutes(Router router) { 21 | /// 指定路由跳转错误返回页 22 | router.notFoundHandler = Handler( 23 | handlerFunc: (BuildContext context, Map> params) { 24 | debugPrint('未找到目标页'); 25 | return WidgetNotFound(); 26 | }); 27 | 28 | router.define(home, handler: Handler( 29 | handlerFunc: (BuildContext context, Map> params) => SplashPage())); 30 | 31 | router.define(webViewPage, handler: Handler(handlerFunc: (_, params) { 32 | String title = params['title']?.first; 33 | String url = params['url']?.first; 34 | return WebViewPage(title: title, url: url); 35 | })); 36 | 37 | _listRouter.clear(); 38 | /// 各自路由由各自模块管理,统一在此添加初始化 39 | _listRouter.add(ShopRouter()); 40 | 41 | /// 初始化路由 42 | _listRouter.forEach((routerProvider) { 43 | routerProvider.initRouter(router); 44 | }); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/config/user_info_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/common/common.dart'; 2 | import 'package:studylinjiashop/utils/utils.dart'; 3 | 4 | class UserInfoData { 5 | //用户相关信息 6 | String _token = ""; 7 | static UserInfoData _instance; 8 | bool isUser = false; 9 | int orderIndex = 0; 10 | String gender = ''; 11 | String avatar = ''; 12 | String mobile = ''; 13 | String nickName = ''; 14 | 15 | factory UserInfoData() => _getInstance(); 16 | 17 | static UserInfoData get instance => _getInstance(); 18 | 19 | UserInfoData._internal() { 20 | // 初始化 21 | } 22 | 23 | static UserInfoData _getInstance() { 24 | if (_instance == null) { 25 | _instance = new UserInfoData._internal(); 26 | } 27 | return _instance; 28 | } 29 | 30 | void setToken(token) { 31 | SharedUtil.instance.saveString("token", token); 32 | this._token = token; 33 | } 34 | 35 | get isLogin { 36 | return _token != null && _token.isNotEmpty; 37 | } 38 | 39 | setNickName(String name) { 40 | nickName = name; 41 | SharedUtil.instance.saveString("nickName", name); 42 | } 43 | 44 | setAvatar(String _avatar) { 45 | avatar = _avatar; 46 | SharedUtil.instance.saveString("nickName", _avatar); 47 | } 48 | 49 | setGender(String _gender) { 50 | gender = _gender; 51 | SharedUtil.instance.saveString("nickName", _gender); 52 | } 53 | 54 | String get token { 55 | _token = getSafeData(SharedUtil.instance.getString("token")); 56 | return _token; 57 | } //访问属性 58 | 59 | } 60 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/config/api.dart: -------------------------------------------------------------------------------- 1 | class API { 2 | // 请求的url 3 | //static const reqUrl = 'http://10.0.2.2:8086'; 4 | 5 | //static const reqUrl = 'http://47.94.169.13:8086'; 6 | static const reqUrl = 'http://192.168.1.8:8086'; 7 | 8 | // 9 | static const sendSmsCode = '/login/sendSmsCode'; 10 | static const loginOrReg = '/login/loginOrReg'; 11 | static const loginByPass = '/login/loginByPass'; 12 | static const categoryList = '/category/list'; 13 | static const topicList = '/topic/list'; 14 | static const goodsSearchHot = '/goods/searchHot'; 15 | static const goodsSearchNew = '/goods/searchNew'; 16 | static const goodsQueryGoods = '/goods/queryGoods'; 17 | static const search = '/goods/search'; 18 | static const queryCartByUser = '/user/cart/queryByUser'; 19 | static const userCartDeleteAll = '/user/cart/delete'; 20 | static const userCartUpdate = '/user/cart/update/'; 21 | static const userCartAdd = '/user/cart/add'; 22 | static const userOrderDetail = '/user/order/'; 23 | static const goodsDetail = '/goods/'; 24 | static const favoriteLike = '/user/favorite/ifLike/'; 25 | static const favoriteAdd = '/user/favorite/add/'; 26 | static const topicDetail = '/topic/'; 27 | static const getOrders = '/user/order/getOrders'; 28 | static const queryAddressByUser = '/user/address/queryByUser'; 29 | static const saveAddressByUser = '/user/address/save'; 30 | static const addressByUser = '/user/address/'; 31 | static const updateGender = '/user/updateGender/'; 32 | static const uploadBase64 = '/file/upload/base64'; 33 | static const updatePassword = '/user/updatePassword/'; 34 | static const updateUserName = '/user/updateUserName/'; 35 | //static const loginOrReg = '/user/getInfo'; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | studylinjiashop 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /lib/res/gaps.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | 4 | import 'dimens.dart'; 5 | 6 | 7 | /// 间隔 8 | class Gaps { 9 | /// 水平间隔 10 | static const Widget hGap4 = const SizedBox(width: Dimens.dp_4); 11 | static const Widget hGap5 = const SizedBox(width: Dimens.dp_5); 12 | static const Widget hGap8 = const SizedBox(width: Dimens.dp_8); 13 | static const Widget hGap10 = const SizedBox(width: Dimens.dp_10); 14 | static const Widget hGap12 = const SizedBox(width: Dimens.dp_12); 15 | static const Widget hGap15 = const SizedBox(width: Dimens.dp_15); 16 | static const Widget hGap16 = const SizedBox(width: Dimens.dp_16); 17 | /// 垂直间隔 18 | static const Widget vGap4 = const SizedBox(height: Dimens.dp_4); 19 | static const Widget vGap5 = const SizedBox(height: Dimens.dp_5); 20 | static const Widget vGap8 = const SizedBox(height: Dimens.dp_8); 21 | static const Widget vGap10 = const SizedBox(height: Dimens.dp_10); 22 | static const Widget vGap12 = const SizedBox(height: Dimens.dp_12); 23 | static const Widget vGap15 = const SizedBox(height: Dimens.dp_15); 24 | static const Widget vGap16 = const SizedBox(height: Dimens.dp_16); 25 | static const Widget vGap50 = const SizedBox(height: Dimens.dp_50); 26 | 27 | // static Widget line = const SizedBox( 28 | // height: 0.6, 29 | // width: double.infinity, 30 | // child: const DecoratedBox(decoration: BoxDecoration(color: Colours.line)), 31 | // ); 32 | 33 | static Widget line = const Divider(); 34 | 35 | static Widget vLine = const SizedBox( 36 | width: 0.6, 37 | height: 24.0, 38 | child: const VerticalDivider(), 39 | ); 40 | 41 | static const Widget empty = const SizedBox.shrink(); 42 | } 43 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 9 | 13 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /lib/api/models/order_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/config/api.dart'; 2 | import 'package:studylinjiashop/config/const.dart'; 3 | import 'package:studylinjiashop/http/req_model.dart'; 4 | 5 | import 'cart_goods_query_modle.dart'; 6 | 7 | class OrderModel2 extends ReqModel { 8 | int status; 9 | int page; 10 | 11 | @override 12 | String url() => API.getOrders; 13 | 14 | @override 15 | Map params() { 16 | return {"status": status, "page": page, "limit": limitSize}; 17 | } 18 | 19 | Future data(status, page) { 20 | this.status = status; 21 | this.page = page; 22 | return get(); 23 | } 24 | } 25 | 26 | class OrderEntity { 27 | List orderModel; 28 | 29 | OrderEntity({this.orderModel}); 30 | 31 | OrderEntity.fromJson(Map json) { 32 | orderModel = new List(); 33 | (json['records'] as List).forEach((v) { 34 | orderModel.add(new OrderModel.fromJson(v)); 35 | }); 36 | } 37 | } 38 | 39 | class OrderModel { 40 | String orderSn; 41 | int realPrice; 42 | int totalPrice; 43 | String statusName; 44 | int status; 45 | List goods; 46 | 47 | OrderModel( 48 | {this.orderSn, 49 | this.realPrice, 50 | this.totalPrice, 51 | this.statusName, 52 | this.status, 53 | this.goods}); 54 | 55 | OrderModel.fromJson(Map json) { 56 | orderSn = json['orderSn']; 57 | realPrice = json['realPrice']; 58 | totalPrice = json['totalPrice']; 59 | statusName = json['statusName']; 60 | status = json['status']; 61 | goods = List(); 62 | (json['items'] as List).forEach((v) { 63 | goods.add(new GoodsListModel.fromJson(v)); 64 | }); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/page/home/card_goods.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:studylinjiashop/api/models/goods_modle.dart'; 3 | import 'package:studylinjiashop/routes/fluro_navigator.dart'; 4 | import 'package:studylinjiashop/routes/shop_router.dart'; 5 | import 'package:studylinjiashop/view/custom_view.dart'; 6 | 7 | class CardGoods extends StatelessWidget { 8 | final List goodsModleDataList; 9 | String imgUrl = "http://linjiashop-mobile-api.microapp.store/file/getImgStream?idFile="; 10 | CardGoods({Key key, this.goodsModleDataList}) :super(key: key); 11 | @override 12 | Widget build(BuildContext context) { 13 | return Container( 14 | color:Colors.white, 15 | margin: EdgeInsets.only(top: 5.0), 16 | padding:EdgeInsets.all(3.0), 17 | child: _buildWidget(context) 18 | ); 19 | } 20 | Widget _buildWidget(BuildContext context) { 21 | List mGoodsCard = []; 22 | Widget content; 23 | for (int i = 0; i < goodsModleDataList.length; i++) { 24 | double priceDouble = goodsModleDataList[i].price/100; 25 | mGoodsCard.add(InkWell( 26 | onTap: () { 27 | onItemClick(context,i); 28 | }, 29 | child: ThemeCard( 30 | title: goodsModleDataList[i].name, 31 | price:"¥"+priceDouble.toStringAsFixed(2) , 32 | imgUrl:imgUrl+goodsModleDataList[i].pic, 33 | descript: goodsModleDataList[i].descript, 34 | number: "x"+goodsModleDataList[i].stock.toString(), 35 | ) 36 | ) 37 | ); 38 | } 39 | content = Column( 40 | children: mGoodsCard, 41 | ); 42 | return content; 43 | } 44 | 45 | void onItemClick(BuildContext context,int i){ 46 | String id = goodsModleDataList[i].id; 47 | Map p={"id":id}; 48 | NavigatorUtils.push(context,ShopRouter.PRODUCT_DETAILS,params: p); 49 | } 50 | } -------------------------------------------------------------------------------- /lib/api/models/shopping_address_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/config/api.dart'; 2 | import 'package:studylinjiashop/http/req_model.dart'; 3 | 4 | class ShoppingAddressModel2 extends ReqModel { 5 | @override 6 | String url() => API.queryAddressByUser; 7 | } 8 | 9 | class ShoppingAddressEntry { 10 | List shippingAddressModels; 11 | 12 | ShoppingAddressEntry({this.shippingAddressModels}); 13 | 14 | ShoppingAddressEntry.fromJson(List json) { 15 | shippingAddressModels = new List(); 16 | (json).forEach((v) { 17 | shippingAddressModels.add(new ShoppingAddressModel.fromJson(v)); 18 | }); 19 | } 20 | } 21 | 22 | class ShoppingAddressModel { 23 | // "addressDetail":"人民路12号", 24 | // "areaCode":"110101", 25 | // "city":"北京市", 26 | // "createTime":"", 27 | // "district":"东城区", 28 | // "id":"1", 29 | // "idUser":"1", 30 | // "isDefault":true, 31 | // "isDelete":false, 32 | // "modifyTime":"", 33 | // "name":"路飞", 34 | // "postCode":"", 35 | // "province":"北京市", 36 | // "tel":"15011113333" 37 | String addressDetail; 38 | String areaCode; 39 | String city; 40 | String district; 41 | String name; 42 | String province; 43 | String tel; 44 | String id; 45 | bool isDefault; 46 | 47 | ShoppingAddressModel( 48 | {this.addressDetail, 49 | this.areaCode, 50 | this.city, 51 | this.district, 52 | this.name, 53 | this.province, 54 | this.tel, 55 | this.id, 56 | this.isDefault}); 57 | 58 | ShoppingAddressModel.fromJson(Map json) { 59 | addressDetail = json['addressDetail']; 60 | areaCode = json['areaCode']; 61 | district = json['district']; 62 | name = json['name']; 63 | province = json['province']; 64 | tel = json['tel']; 65 | id = json['id']; 66 | city = json['city']; 67 | isDefault = json['isDefault']; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/page/splash_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:rxdart/rxdart.dart'; 6 | import 'package:studylinjiashop/common/shared_util.dart'; 7 | import 'package:studylinjiashop/config/keys.dart'; 8 | import 'package:studylinjiashop/routes/fluro_navigator.dart'; 9 | import 'package:studylinjiashop/routes/shop_router.dart'; 10 | import 'package:studylinjiashop/utils/image_utils.dart'; 11 | import 'package:studylinjiashop/widget/load_image.dart'; 12 | 13 | class SplashPage extends StatefulWidget { 14 | SplashPage({Key key, this.title}) : super(key: key); 15 | 16 | final String title; 17 | 18 | 19 | @override 20 | State createState() { 21 | return _SplashPageState(); 22 | } 23 | } 24 | 25 | class _SplashPageState extends State { 26 | StreamSubscription splashSubscription; 27 | @override 28 | void initState() { 29 | // TODO: do something to init 30 | super.initState(); 31 | WidgetsBinding.instance.addPostFrameCallback((_) async { 32 | precacheImage(ImageUtils.getAssetImage('app_start_1'), context); 33 | goHome(); 34 | }); 35 | } 36 | 37 | goHome() { 38 | splashSubscription = Observable.just(1).delay(Duration(milliseconds: 3000)).listen((_) { 39 | if (SharedUtil.instance.getBoolean(Keys.keyGuide, defValue: true)) { 40 | NavigatorUtils.push(context, ShopRouter.guide_page, replace: true); 41 | } else { 42 | NavigatorUtils.goRootPage(context); 43 | } 44 | }); 45 | } 46 | @override 47 | void dispose() { 48 | splashSubscription?.cancel(); 49 | super.dispose(); 50 | } 51 | @override 52 | Widget build(BuildContext context) { 53 | return Builder(builder: (context) { 54 | return Container( 55 | child: LoadAssetImage( 56 | 'app_start_1', 57 | fit: BoxFit.fill, 58 | ), 59 | ); 60 | }); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/api/models/order_detail_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/config/api.dart'; 2 | import 'package:studylinjiashop/http/req_model.dart'; 3 | 4 | import 'cart_goods_query_modle.dart'; 5 | 6 | 7 | class OrderDetailModel2 extends ReqModel { 8 | String orderSn; 9 | 10 | @override 11 | String url() => "${API.userOrderDetail}$orderSn"; 12 | 13 | 14 | Future data(orderSn) { 15 | this.orderSn = orderSn; 16 | return get(); 17 | } 18 | 19 | } 20 | 21 | 22 | 23 | class OrderDetailEntry { 24 | OrderDetailModel orderDetailModel; 25 | OrderDetailEntry({this.orderDetailModel}); 26 | 27 | OrderDetailEntry.fromJson(Map json) { 28 | orderDetailModel= new OrderDetailModel.fromJson(json); 29 | } 30 | } 31 | class OrderDetailModel { 32 | String orderSn; 33 | int realPrice; 34 | int totalPrice; 35 | String addressDetail; 36 | String areaCode; 37 | String city; 38 | String district; 39 | String name; 40 | String tel; 41 | int status; 42 | String statusName; 43 | String province; 44 | String createTime; 45 | List goods; 46 | OrderDetailModel({this.orderSn, this.realPrice,this.totalPrice,this.statusName, 47 | this.status,this.goods}); 48 | OrderDetailModel.fromJson(Map json) { 49 | orderSn = json['orderSn']; 50 | realPrice = json['realPrice']; 51 | totalPrice = json['totalPrice']; 52 | statusName = json['statusName']; 53 | status =json['status']; 54 | addressDetail =json['address']['addressDetail']; 55 | areaCode =json['address']['areaCode']; 56 | city =json['address']['city']; 57 | district =json['address']['district']; 58 | name =json['address']['name']; 59 | tel =json['address']['tel']; 60 | province =json['address']['province']; 61 | status =json['status']; 62 | createTime =json['createTime']; 63 | goods=List (); 64 | List dataList= (json['items'] as List).cast(); 65 | dataList.forEach((v) { 66 | goods.add(GoodsListModel.fromJson(v)); 67 | }); 68 | 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /lib/api/models/address_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/config/api.dart'; 2 | import 'package:studylinjiashop/http/req_model.dart'; 3 | 4 | class AddressModel2 extends ReqModel { 5 | String id; 6 | 7 | @override 8 | String url() => "${API.addressByUser}$id"; 9 | 10 | Future data(id) { 11 | this.id = id; 12 | return get(); 13 | } 14 | } 15 | 16 | class AddressEditEntity { 17 | AddressModel addressModel; 18 | 19 | AddressEditEntity({this.addressModel}); 20 | 21 | AddressEditEntity.fromJson(Map json) { 22 | addressModel = AddressModel.fromJson(json); 23 | } 24 | } 25 | 26 | class AddressModel { 27 | // "addressDetail":"人民路12号", 28 | // "areaCode":"110101", 29 | // "city":"北京市", 30 | // "createTime":"", 31 | // "district":"东城区", 32 | // "id":"1", 33 | // "idUser":"1", 34 | // "isDefault":true, 35 | // "isDelete":false, 36 | // "modifyTime":"", 37 | // "name":"路飞", 38 | // "postCode":"", 39 | // "province":"北京市", 40 | // "tel":"15011113333" 41 | String addressDetail; 42 | String areaCode; 43 | String city; 44 | String district; 45 | bool isDefault; 46 | String name; 47 | String province; 48 | String tel; 49 | String id; 50 | String idUser; 51 | bool isDelete; 52 | 53 | AddressModel( 54 | {this.addressDetail, 55 | this.areaCode, 56 | this.city, 57 | this.district, 58 | this.isDefault, 59 | this.name, 60 | this.province, 61 | this.tel, 62 | this.id, 63 | this.idUser, 64 | this.isDelete}); 65 | 66 | AddressModel.fromJson(Map json) { 67 | addressDetail = json['addressDetail'].toString(); 68 | areaCode = json['areaCode'].toString(); 69 | city = json['city'].toString(); 70 | district = json['district'].toString(); 71 | isDefault = json['isDefault']; 72 | name = json['name'].toString(); 73 | province = json['province'].toString(); 74 | tel = json['tel'].toString(); 75 | id = json['id'].toString(); 76 | idUser = json['idUser'].toString(); 77 | isDelete = json['isDelete']; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/view/my_icons.dart.dart: -------------------------------------------------------------------------------- 1 | /// Flutter icons MyFlutterApp 2 | /// Copyright (C) 2019 by original authors @ fluttericon.com, fontello.com 3 | /// This font was generated by FlutterIcon.com, which is derived from Fontello. 4 | /// 5 | /// To use this font, place it in your fonts/ directory and include the 6 | /// following in your pubspec.yaml 7 | /// 8 | /// flutter: 9 | /// fonts: 10 | /// - family: MyFlutterApp 11 | /// fonts: 12 | /// - asset: fonts/MyFlutterApp.ttf 13 | /// 14 | /// 15 | /// 16 | import 'package:flutter/widgets.dart'; 17 | 18 | class MyIcons { 19 | MyIcons._(); 20 | 21 | static const _kFontFam = 'MyIcons'; 22 | 23 | static const IconData orderForm = const IconData(0xe800, fontFamily: _kFontFam); 24 | static const IconData findings = const IconData(0xe801, fontFamily: _kFontFam); 25 | static const IconData home = const IconData(0xe802, fontFamily: _kFontFam); 26 | static const IconData personal = const IconData(0xe803, fontFamily: _kFontFam); 27 | static const IconData location = const IconData(0xe615, fontFamily: 'MyIcons2'); 28 | static const IconData placeholder = const IconData(0xe627, fontFamily: 'MyIcons2'); 29 | static const IconData addressholder = const IconData(0xe60d, fontFamily: 'MyIcons3'); 30 | static const IconData youhuiquanholder = const IconData(0xe645, fontFamily: 'MyIcons3'); 31 | static const IconData jifenholder = const IconData(0xe624, fontFamily: 'MyIcons3'); 32 | static const IconData liwuholder = const IconData(0xe60b, fontFamily: 'MyIcons3'); 33 | static const IconData daifukuan = const IconData(0xe632, fontFamily: 'MyIcons3'); 34 | static const IconData daifahuo = const IconData(0xe63e, fontFamily: 'MyIcons3'); 35 | static const IconData yifahuo = const IconData(0xe620, fontFamily: 'MyIcons3'); 36 | static const IconData yiwancheng = const IconData(0xe6d7, fontFamily: 'MyIcons3'); 37 | static const IconData zhifubpay = const IconData(0xe68a, fontFamily: 'MyIcons4'); 38 | static const IconData weixinpay = const IconData(0xe61b, fontFamily: 'MyIcons4'); 39 | static const IconData sexicon = const IconData(0xe611, fontFamily: 'MyIcons4'); 40 | } 41 | -------------------------------------------------------------------------------- /lib/widget/view/text_field_item.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:studylinjiashop/res/gaps.dart'; 6 | import 'package:studylinjiashop/utils/number_text_input_formatter.dart'; 7 | 8 | 9 | /// 封装输入框 10 | class TextFieldItem extends StatelessWidget { 11 | 12 | const TextFieldItem({ 13 | Key key, 14 | this.controller, 15 | @required this.title, 16 | this.keyboardType: TextInputType.text, 17 | this.hintText: '', 18 | this.focusNode, 19 | }): super(key: key); 20 | 21 | final TextEditingController controller; 22 | final String title; 23 | final String hintText; 24 | final TextInputType keyboardType; 25 | final FocusNode focusNode; 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return Container( 30 | height: 50.0, 31 | margin: const EdgeInsets.only(left: 16.0), 32 | width: double.infinity, 33 | decoration: BoxDecoration( 34 | border: Border( 35 | bottom: Divider.createBorderSide(context, width: 0.6), 36 | ) 37 | ), 38 | child: Row( 39 | children: [ 40 | Text(title), 41 | Gaps.hGap16, 42 | Expanded( 43 | child: TextField( 44 | focusNode: focusNode, 45 | keyboardType: keyboardType, 46 | inputFormatters: _getInputFormatters(), 47 | controller: controller, 48 | //style: TextStyles.textDark14, 49 | decoration: InputDecoration( 50 | hintText: hintText, 51 | border: InputBorder.none, //去掉下划线 52 | //hintStyle: TextStyles.textGrayC14 53 | ) 54 | ), 55 | ), 56 | Gaps.hGap16 57 | ], 58 | ), 59 | ); 60 | } 61 | 62 | _getInputFormatters() { 63 | if (keyboardType == TextInputType.numberWithOptions(decimal: true)) { 64 | return [UsNumberTextInputFormatter()]; 65 | } 66 | if (keyboardType == TextInputType.number || keyboardType == TextInputType.phone) { 67 | return [WhitelistingTextInputFormatter.digitsOnly]; 68 | } 69 | return null; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/common/date.dart: -------------------------------------------------------------------------------- 1 | import 'package:intl/intl.dart'; 2 | import 'package:studylinjiashop/common/check.dart'; 3 | 4 | class DateTimeForMater { 5 | static String full = "yyyy-MM-dd HH:mm:ss"; 6 | 7 | static String formatDateV(DateTime dateTime, {bool isUtc, String format}) { 8 | if (dateTime == null) return ""; 9 | format = format ?? full; 10 | if (format.contains("yy")) { 11 | String year = dateTime.year.toString(); 12 | if (format.contains("yyyy")) { 13 | format = format.replaceAll("yyyy", year); 14 | } else { 15 | format = format.replaceAll( 16 | "yy", year.substring(year.length - 2, year.length)); 17 | } 18 | } 19 | 20 | format = _comFormat(dateTime.month, format, 'M', 'MM'); 21 | format = _comFormat(dateTime.day, format, 'd', 'dd'); 22 | format = _comFormat(dateTime.hour, format, 'H', 'HH'); 23 | format = _comFormat(dateTime.minute, format, 'm', 'mm'); 24 | format = _comFormat(dateTime.second, format, 's', 'ss'); 25 | format = _comFormat(dateTime.millisecond, format, 'S', 'SSS'); 26 | 27 | return format; 28 | } 29 | 30 | static String _comFormat( 31 | int value, String format, String single, String full) { 32 | if (format.contains(single)) { 33 | if (format.contains(full)) { 34 | format = 35 | format.replaceAll(full, value < 10 ? '0$value' : value.toString()); 36 | } else { 37 | format = format.replaceAll(single, value.toString()); 38 | } 39 | } 40 | return format; 41 | } 42 | } 43 | 44 | String formatTimeStampToString(timestamp, [format]) { 45 | assert(timestamp != null); 46 | 47 | int time = 0; 48 | 49 | if (timestamp is int) { 50 | time = timestamp; 51 | } else { 52 | time = int.parse(timestamp.toString()); 53 | } 54 | 55 | if (format == null) { 56 | format = 'yyyy-MM-dd HH:mm:ss'; 57 | } 58 | 59 | DateFormat dateFormat = new DateFormat(format); 60 | 61 | var date = new DateTime.fromMillisecondsSinceEpoch(time * 1000); 62 | 63 | return dateFormat.format(date); 64 | } 65 | 66 | String timeHandle(int time) { 67 | double createTimeDouble = strNoEmpty('$time') ? time / 1000 : 0; 68 | int createTime = int.parse('${stringDisposeWithDouble(createTimeDouble)}'); 69 | return '${formatTimeStampToString(createTime) ?? '未知'}'; 70 | } 71 | -------------------------------------------------------------------------------- /lib/page/web_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:studylinjiashop/utils/app_size.dart'; 3 | import 'package:studylinjiashop/view/app_topbar.dart'; 4 | import 'package:studylinjiashop/view/customize_appbar.dart'; 5 | 6 | import 'package:webview_flutter/webview_flutter.dart'; 7 | 8 | class WebViewPage extends StatefulWidget { 9 | const WebViewPage({ 10 | Key key, 11 | @required this.title, 12 | @required this.url, 13 | }) : super(key: key); 14 | 15 | final String title; 16 | final String url; 17 | 18 | @override 19 | _WebViewState createState() => _WebViewState(); 20 | } 21 | 22 | class _WebViewState extends State { 23 | bool _isLoading = true; 24 | 25 | @override 26 | void initState() { 27 | super.initState(); 28 | } 29 | 30 | @override 31 | void dispose() { 32 | super.dispose(); 33 | } 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | AppSize.init(context); 38 | 39 | return Scaffold( 40 | appBar: MyAppBar( 41 | preferredSize: Size.fromHeight(AppSize.height(160)), 42 | child: 43 | CommonBackTopBar(title: widget.title), 44 | ), 45 | body: Stack( 46 | children: [ 47 | WebView( 48 | initialUrl: widget.url, 49 | onWebViewCreated: (WebViewController web) { 50 | web.canGoBack().then((res) { 51 | // print(res); // 是否能返回上一级 52 | }); 53 | web.currentUrl().then((url) { 54 | // print(url); // 返回当前url 55 | }); 56 | web.canGoForward().then((res) { 57 | // print(res); //是否能前进 58 | }); 59 | }, 60 | onPageFinished: (String value) { 61 | // 返回当前url 62 | // print(value); 63 | setState(() { 64 | _isLoading = false; 65 | }); 66 | }, 67 | ), 68 | _loading() 69 | ], 70 | ), 71 | ); 72 | } 73 | 74 | _loading() { 75 | return _isLoading == true 76 | ? Container( 77 | decoration: BoxDecoration(color: Colors.white), 78 | child: Center( 79 | child: CircularProgressIndicator(), 80 | ), 81 | ) 82 | : Text(''); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/res/styles.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | 4 | import 'colors.dart'; 5 | import 'dimens.dart'; 6 | 7 | class TextStyles { 8 | 9 | static const TextStyle textSize12 = const TextStyle( 10 | fontSize: Dimens.font_sp12, 11 | ); 12 | static const TextStyle textSize16 = const TextStyle( 13 | fontSize: Dimens.font_sp16, 14 | ); 15 | static const TextStyle textBold14 = const TextStyle( 16 | fontSize: Dimens.font_sp14, 17 | fontWeight: FontWeight.bold 18 | ); 19 | static const TextStyle textBold16 = const TextStyle( 20 | fontSize: Dimens.font_sp16, 21 | fontWeight: FontWeight.bold 22 | ); 23 | static const TextStyle textBold18 = const TextStyle( 24 | fontSize: Dimens.font_sp18, 25 | fontWeight: FontWeight.bold 26 | ); 27 | static const TextStyle textBold24 = const TextStyle( 28 | fontSize: 24.0, 29 | fontWeight: FontWeight.bold 30 | ); 31 | static const TextStyle textBold26 = const TextStyle( 32 | fontSize: 26.0, 33 | fontWeight: FontWeight.bold 34 | ); 35 | 36 | static const TextStyle textGray14 = const TextStyle( 37 | fontSize: Dimens.font_sp14, 38 | color: Colours.text_gray, 39 | ); 40 | static const TextStyle textDarkGray14 = const TextStyle( 41 | fontSize: Dimens.font_sp14, 42 | color: Colours.dark_text_gray, 43 | ); 44 | 45 | static const TextStyle textWhite14 = const TextStyle( 46 | fontSize: Dimens.font_sp14, 47 | color: Colors.white, 48 | ); 49 | 50 | static const TextStyle text = const TextStyle( 51 | fontSize: Dimens.font_sp14, 52 | color: Colours.text, 53 | // https://github.com/flutter/flutter/issues/40248 54 | textBaseline: TextBaseline.alphabetic 55 | ); 56 | static const TextStyle textDark = const TextStyle( 57 | fontSize: Dimens.font_sp14, 58 | color: Colours.dark_text, 59 | textBaseline: TextBaseline.alphabetic 60 | ); 61 | 62 | static const TextStyle textGray12 = const TextStyle( 63 | fontSize: Dimens.font_sp12, 64 | color: Colours.text_gray, 65 | fontWeight: FontWeight.normal 66 | ); 67 | static const TextStyle textDarkGray12 = const TextStyle( 68 | fontSize: Dimens.font_sp12, 69 | color: Colours.dark_text_gray, 70 | fontWeight: FontWeight.normal 71 | ); 72 | 73 | static const TextStyle textHint14 = const TextStyle( 74 | fontSize: Dimens.font_sp14, 75 | color: Colours.dark_unselected_item_color 76 | ); 77 | } 78 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/widget/load_image.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:cached_network_image/cached_network_image.dart'; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:studylinjiashop/config/const.dart'; 6 | import 'package:studylinjiashop/utils/image_utils.dart'; 7 | import 'package:studylinjiashop/utils/utils.dart'; 8 | 9 | 10 | /// 图片加载(支持本地与网络图片) 11 | class LoadImage extends StatelessWidget { 12 | 13 | const LoadImage(this.image, { 14 | Key key, 15 | this.width, 16 | this.height, 17 | this.fit: BoxFit.cover, 18 | this.format: 'png', 19 | this.holderImg: 'none' 20 | }): super(key: key); 21 | 22 | final String image; 23 | final double width; 24 | final double height; 25 | final BoxFit fit; 26 | final String format; 27 | final String holderImg; 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | if (getSafeData(image).isEmpty) { 32 | return LoadAssetImage(holderImg, 33 | height: height, 34 | width: width, 35 | fit: fit, 36 | format: format 37 | ); 38 | } else { 39 | if (image.startsWith('http')) { 40 | return CachedNetworkImage( 41 | imageUrl: image, 42 | placeholder: (context, url) => LoadAssetImage(holderImg, height: height, width: width, fit: fit), 43 | errorWidget: (context, url, error) => LoadAssetImage(holderImg, height: height, width: width, fit: fit), 44 | width: width, 45 | height: height, 46 | fit: fit, 47 | ); 48 | } else { 49 | return LoadAssetImage(image, 50 | height: height, 51 | width: width, 52 | fit: fit, 53 | format: format 54 | ); 55 | } 56 | } 57 | } 58 | } 59 | 60 | /// 加载本地资源图片 61 | class LoadAssetImage extends StatelessWidget { 62 | 63 | const LoadAssetImage(this.image, { 64 | Key key, 65 | this.width, 66 | this.height, 67 | this.fit, 68 | this.format: IMAGE_PNG, 69 | this.color 70 | }): super(key: key); 71 | 72 | final String image; 73 | final double width; 74 | final double height; 75 | final BoxFit fit; 76 | final String format; 77 | final Color color; 78 | 79 | @override 80 | Widget build(BuildContext context) { 81 | 82 | return Image.asset( 83 | ImageUtils.getImgPath(image, format: format), 84 | height: height, 85 | width: width, 86 | fit: fit, 87 | color: color, 88 | /// 忽略图片语义 89 | excludeFromSemantics: true, 90 | ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/widget/swiper_diy.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'dart:convert'; 4 | import 'package:flutter_swiper/flutter_swiper.dart'; 5 | import 'package:studylinjiashop/api/models/category_modle.dart'; 6 | import 'package:studylinjiashop/routes/fluro_navigator.dart'; 7 | 8 | import 'package:studylinjiashop/routes/shop_router.dart'; 9 | 10 | /** 11 | * 轮播组件 12 | */ 13 | class SwiperDiy extends StatelessWidget { 14 | final List swiperDataList; 15 | final double height; 16 | final double width; 17 | 18 | String imgUrl = 19 | "http://linjiashop-mobile-api.microapp.store/file/getImgStream?idFile="; 20 | 21 | SwiperDiy({Key key, this.swiperDataList, this.height, this.width}) 22 | : super(key: key); 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return Container( 27 | height: height, 28 | width: width, 29 | child: Swiper( 30 | itemBuilder: (BuildContext context, int index) { 31 | return ClipRRect( 32 | borderRadius: BorderRadius.circular(8.0), 33 | child: InkWell( 34 | onTap: () { 35 | if (swiperDataList[index].page.isNotEmpty) { 36 | if (swiperDataList[index].page.startsWith("http") || 37 | swiperDataList[index].page.startsWith("https")) { 38 | _goWeb(context, swiperDataList[index].page,swiperDataList[index].title); 39 | } else if ("goods" == swiperDataList[index].page) { 40 | Map result = 41 | jsonDecode(swiperDataList[index].param); 42 | if (result.containsKey("id")) { 43 | _goDetail(context, result['id'].toString()); 44 | } 45 | } 46 | } 47 | }, 48 | child: Image.network( 49 | imgUrl + "${swiperDataList[index].idFile}", 50 | fit: BoxFit.cover, 51 | ), 52 | )); 53 | }, 54 | itemCount: swiperDataList.length, 55 | pagination: SwiperPagination(margin: EdgeInsets.all(1.0)), 56 | autoplay: true, 57 | ), 58 | ); 59 | } 60 | 61 | void _goWeb(BuildContext context, String url, String title) { 62 | NavigatorUtils.goWebViewPage(context, title, url); 63 | } 64 | 65 | void _goDetail(BuildContext context, String id) { 66 | NavigatorUtils.push(context, ShopRouter.PRODUCT_DETAILS, params:{"id": id}); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/api/models/category_modle.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/config/api.dart'; 2 | import 'package:studylinjiashop/http/req_model.dart'; 3 | 4 | class CategoryModel2 extends ReqModel { 5 | @override 6 | String url() => API.categoryList; 7 | 8 | Future data() => get(); 9 | } 10 | 11 | class CategoryEntity { 12 | List category; 13 | 14 | CategoryEntity({this.category}); 15 | 16 | CategoryEntity.fromJson(List json) { 17 | category = new List(); 18 | json.forEach((v) { 19 | category.add(new CategoryModel.fromJson(v)); 20 | }); 21 | } 22 | } 23 | 24 | class CategoryModel { 25 | String name; 26 | String id; 27 | List categoryInfoModels; 28 | 29 | CategoryModel({this.name, this.id, this.categoryInfoModels}); 30 | 31 | factory CategoryModel.fromJson(Map parsedJson) { 32 | var list = parsedJson['bannerList'] as List; 33 | 34 | List categoryInfoList = 35 | list.map((i) => CategoryInfoModel.fromJson(i)).toList(); 36 | return CategoryModel( 37 | id: parsedJson['id'], 38 | name: parsedJson['name'], 39 | categoryInfoModels: categoryInfoList); 40 | } 41 | } 42 | 43 | class CategoryInfoModel { 44 | String createBy; 45 | String createTime; 46 | String idFile; 47 | String modifyBy; 48 | String modifyTime; 49 | String title; 50 | String type; 51 | String url; 52 | String id; 53 | String page; 54 | String param; 55 | 56 | CategoryInfoModel( 57 | {this.createBy, 58 | this.createTime, 59 | this.idFile, 60 | this.modifyBy, 61 | this.modifyTime, 62 | this.title, 63 | this.type, 64 | this.url, 65 | this.id, 66 | this.page}); 67 | 68 | CategoryInfoModel.fromJson(Map json) { 69 | createBy = json['createBy']; 70 | createTime = json['createTime']; 71 | idFile = json['idFile']; 72 | modifyBy = json['modifyBy']; 73 | modifyTime = json['modifyTime']; 74 | title = json['title']; 75 | type = json['type']; 76 | url = json['url']; 77 | id = json['id']; 78 | page = json['page']; 79 | param = json['param']; 80 | } 81 | 82 | Map toJson() { 83 | final Map data = new Map(); 84 | data['createBy'] = this.createBy; 85 | data['createTime'] = this.createTime; 86 | data['idFile'] = this.idFile; 87 | data['modifyBy'] = this.modifyBy; 88 | data['modifyTime'] = this.modifyTime; 89 | data['title'] = this.title; 90 | data['type'] = this.type; 91 | data['url'] = this.url; 92 | data['id'] = this.id; 93 | data['page'] = this.page; 94 | return data; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /lib/base/base_page_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:studylinjiashop/http/mvvms.dart'; 4 | import 'package:studylinjiashop/routes/fluro_navigator.dart'; 5 | import 'package:studylinjiashop/utils/dialog_utils.dart'; 6 | import 'package:studylinjiashop/view/custom_view.dart'; 7 | 8 | abstract class BasePageState extends State 9 | implements IMvvmView { 10 | CancelToken _cancelToken; 11 | 12 | BasePageState() { 13 | _cancelToken = CancelToken(); 14 | injectViewModelView(); 15 | } 16 | 17 | 18 | BuildContext getContext() { 19 | return context; 20 | } 21 | 22 | @override 23 | void closeProgress() { 24 | if (mounted && _isShowDialog) { 25 | _isShowDialog = false; 26 | NavigatorUtils.goBack(context); 27 | } 28 | } 29 | 30 | bool _isShowDialog = false; 31 | 32 | @override 33 | void showProgress() { 34 | /// 避免重复弹出 35 | if (mounted && !_isShowDialog) { 36 | _isShowDialog = true; 37 | try { 38 | showDialog( 39 | context: context, 40 | barrierDismissible: false, 41 | builder: (BuildContext context) { 42 | return WillPopScope( 43 | onWillPop: () async { 44 | // 拦截到返回键,证明dialog被手动关闭 45 | _isShowDialog = false; 46 | return Future.value(true); 47 | }, 48 | child: const LoadingDialog( 49 | text: "加载中…", 50 | ), 51 | ); 52 | }); 53 | } catch (e) { 54 | /// 异常原因主要是页面没有build完成就调用Progress。 55 | print(e); 56 | } 57 | } 58 | } 59 | 60 | @override 61 | void showToast(String string) { 62 | DialogUtil.buildToast(string); 63 | } 64 | 65 | @override 66 | void didChangeDependencies() { 67 | super.didChangeDependencies(); 68 | } 69 | 70 | @override 71 | void dispose() { 72 | super.dispose(); 73 | if (_cancelToken != null && !_cancelToken.isCancelled) { 74 | _cancelToken.cancel(); 75 | } 76 | } 77 | 78 | @override 79 | void deactivate() { 80 | super.deactivate(); 81 | } 82 | 83 | @override 84 | void didUpdateWidget(T oldWidget) { 85 | super.didUpdateWidget(oldWidget); 86 | didUpdateWidgets(oldWidget); 87 | } 88 | 89 | @override 90 | void initState() { 91 | super.initState(); 92 | } 93 | 94 | void didUpdateWidgets(W oldWidget) {} 95 | 96 | CancelToken getCancelToken() { 97 | return _cancelToken; 98 | } 99 | //如果要用model与view交互就需要给viewmodle 设置view 100 | void injectViewModelView() {} 101 | } 102 | -------------------------------------------------------------------------------- /lib/api/models/void_modle.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:studylinjiashop/config/api.dart'; 5 | import 'package:studylinjiashop/http/mvvms.dart'; 6 | import 'package:studylinjiashop/http/req_model.dart'; 7 | 8 | class VoidModel extends ReqModel { 9 | static const CART_UPDATE = 1; 10 | static const CART_ADD = 2; 11 | static const CART_DELETE_ALL = 0; 12 | static const GOODS_FAVORITE_LIKE = 3; 13 | static const GOODS_FAVORITE_ADD = 4; 14 | static const ADDRESS_SAVE_ADD = 5; 15 | static const UPDATE_GENDER = 6; 16 | static const UPDATE_PWD = 7; 17 | static const UPDATE_NICKNAME = 8; 18 | 19 | 20 | 21 | int type; 22 | Map params2; 23 | 24 | 25 | VoidModel(CancelToken cancelToken,IMvvmView view) { 26 | this.view = view; 27 | this.cancelToken = cancelToken; 28 | } 29 | 30 | 31 | get isEncode { 32 | return type == CART_ADD || 33 | type == ADDRESS_SAVE_ADD; 34 | } 35 | 36 | @override 37 | Map params() { 38 | return isEncode ? null : params2; 39 | } 40 | 41 | @override 42 | String encodeData() { 43 | return isEncode ? json.encode(params2) : null; 44 | } 45 | 46 | @override 47 | String url() { 48 | String url = ""; 49 | switch (type) { 50 | case CART_UPDATE: 51 | url = "${API.userCartUpdate}${params2["id"]}/${params2["count"]}"; 52 | break; 53 | case CART_ADD: 54 | url = API.userCartAdd; 55 | break; 56 | case GOODS_FAVORITE_LIKE: 57 | url = "${API.favoriteLike}${params2["id"]}"; 58 | break; 59 | case GOODS_FAVORITE_ADD: 60 | url = "${API.favoriteAdd}${params2["id"]}"; 61 | break; 62 | case ADDRESS_SAVE_ADD: 63 | url = API.saveAddressByUser; 64 | break; 65 | case UPDATE_GENDER: 66 | url = "${API.updateGender}${params2["gender"]}"; 67 | break; 68 | case UPDATE_NICKNAME: 69 | url = "${API.updateUserName}${params2["name"]}"; 70 | break; 71 | case UPDATE_PWD: 72 | url = "${API.updatePassword}${params2["oldPwd"]}/${params2["pwd"]}/${params2["rePassword"]}"; 73 | break; 74 | default: 75 | url = API.userCartDeleteAll; 76 | break; 77 | } 78 | return url; 79 | } 80 | 81 | Future data(type, Map params2) { 82 | this.type = type; 83 | this.params2 = params2; 84 | switch (type) { 85 | case CART_UPDATE: 86 | case CART_ADD: 87 | case GOODS_FAVORITE_ADD: 88 | case CART_DELETE_ALL: 89 | case ADDRESS_SAVE_ADD: 90 | case UPDATE_GENDER: 91 | case UPDATE_NICKNAME: 92 | case UPDATE_PWD: 93 | return post(); 94 | break; 95 | } 96 | return get(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /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 plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | def keystorePropertiesFile=rootProject.file('key.properties') 29 | def keystoreProperties = new Properties() 30 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 31 | 32 | android { 33 | compileSdkVersion 28 34 | 35 | sourceSets { 36 | main.java.srcDirs += 'src/main/kotlin' 37 | } 38 | 39 | lintOptions { 40 | disable 'InvalidPackage' 41 | } 42 | 43 | defaultConfig { 44 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 45 | applicationId "com.ityu.studylinjiashop" 46 | minSdkVersion 16 47 | targetSdkVersion 28 48 | versionCode flutterVersionCode.toInteger() 49 | versionName flutterVersionName 50 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 51 | } 52 | 53 | signingConfigs { 54 | release { 55 | keyAlias keystoreProperties['keyAlias'] 56 | keyPassword keystoreProperties['keyPassword'] 57 | storeFile file(keystoreProperties['storeFile']) 58 | storePassword keystoreProperties['storePassword'] 59 | } 60 | } 61 | 62 | buildTypes { 63 | release { 64 | // TODO: Add your own signing config for the release build. 65 | // Signing with the debug keys for now, so `flutter run --release` works. 66 | signingConfig signingConfigs.release 67 | } 68 | } 69 | } 70 | 71 | flutter { 72 | source '../..' 73 | } 74 | 75 | dependencies { 76 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 77 | testImplementation 'junit:junit:4.12' 78 | androidTestImplementation 'androidx.test:runner:1.1.1' 79 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 80 | } 81 | -------------------------------------------------------------------------------- /lib/api/models/topic_goods_query_modle.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/config/api.dart'; 2 | import 'package:studylinjiashop/http/req_model.dart'; 3 | 4 | class TopicGoodsListModel2 extends ReqModel { 5 | @override 6 | String url() => API.topicList; 7 | 8 | Future data() => get(); 9 | } 10 | 11 | class TopicGoodsQueryEntity { 12 | List topicGoods; 13 | 14 | TopicGoodsQueryEntity({this.topicGoods}); 15 | 16 | TopicGoodsQueryEntity.fromJson(List json) { 17 | topicGoods = new List(); 18 | // print(goods.runtimeType); 19 | json.forEach((v) { 20 | topicGoods.add(new TopicGoodsListModel.fromJson(v)); 21 | // print(goods.length); 22 | }); 23 | } 24 | } 25 | 26 | class TopicGoodsListModel { 27 | TopicGoodsModel topicGoodsModel; 28 | 29 | String createBy; 30 | String createTime; 31 | bool disabled; 32 | String id; 33 | String idArticle; 34 | String idGoodsList; 35 | String modifyBy; 36 | String modifyTime; 37 | String pv; 38 | String title; 39 | 40 | TopicGoodsListModel( 41 | {this.createBy, 42 | this.createTime, 43 | this.disabled, 44 | this.id, 45 | this.idArticle, 46 | this.idGoodsList, 47 | this.modifyBy, 48 | this.modifyTime, 49 | this.pv, 50 | this.title, 51 | this.topicGoodsModel}); 52 | 53 | TopicGoodsListModel.fromJson(Map json) { 54 | createBy = json['createBy']; 55 | createTime = json['createTime']; 56 | disabled = json['disabled']; 57 | id = json['id']; 58 | idArticle = json['idArticle']; 59 | idGoodsList = json['idGoodsList']; 60 | modifyBy = json['modifyBy']; 61 | modifyTime = json['modifyTime']; 62 | pv = json['pv']; 63 | title = json['title']; 64 | 65 | if (json['article'] != null) { 66 | topicGoodsModel = new TopicGoodsModel.fromJson(json['article']); 67 | } 68 | } 69 | } 70 | 71 | class TopicGoodsModel { 72 | String author; 73 | String content; 74 | String createBy; 75 | String id; 76 | String idChannel; 77 | String img; 78 | String createTime; 79 | String modifyBy; 80 | String modifyTime; 81 | String title; 82 | 83 | TopicGoodsModel( 84 | {this.author, 85 | this.content, 86 | this.createTime, 87 | this.id, 88 | this.idChannel, 89 | this.img, 90 | this.modifyBy, 91 | this.modifyTime, 92 | this.title}); 93 | 94 | TopicGoodsModel.fromJson(Map json) { 95 | author = json['author']; 96 | content = json['content']; 97 | createBy = json['createBy']; 98 | id = json['id']; 99 | idChannel = json['idChannel']; 100 | img = json['img']; 101 | createTime = json['createTime']; 102 | modifyBy = json['modifyBy']; 103 | modifyTime = json['modifyTime']; 104 | title = json['title']; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/res/colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class Colours { 4 | static const Color app_main = Color(0xFF4688FA); 5 | static const Color dark_app_main = Color(0xFF3F7AE0); 6 | 7 | static const Color bg_color = Color(0xfff1f1f1); 8 | static const Color dark_bg_color = Color(0xFF18191A); 9 | 10 | static const Color material_bg = Color(0xFFFFFFFF); 11 | static const Color dark_material_bg = Color(0xFF303233); 12 | 13 | static const Color text = Color(0xFF333333); 14 | static const Color dark_text = Color(0xFFB8B8B8); 15 | 16 | static const Color text_gray = Color(0xFF999999); 17 | static const Color dark_text_gray = Color(0xFF666666); 18 | 19 | static const Color text_gray_c = Color(0xFFcccccc); 20 | static const Color dark_button_text = Color(0xFFF2F2F2); 21 | 22 | static const Color bg_gray = Color(0xFFF6F6F6); 23 | static const Color dark_bg_gray = Color(0xFF1F1F1F); 24 | 25 | static const Color line = Color(0xFFEEEEEE); 26 | static const Color dark_line = Color(0xFF3A3C3D); 27 | 28 | static const Color red = Color(0xFFFF4759); 29 | static const Color dark_red = Color(0xFFE03E4E); 30 | 31 | static const Color text_disabled = Color(0xFFD4E2FA); 32 | static const Color dark_text_disabled = Color(0xFFCEDBF2); 33 | 34 | static const Color button_disabled = Color(0xFF96BBFA); 35 | static const Color dark_button_disabled = Color(0xFF83A5E0); 36 | 37 | static const Color unselected_item_color = Color(0xffbfbfbf); 38 | static const Color dark_unselected_item_color = Color(0xFF4D4D4D); 39 | 40 | static const Color bg_gray_ = Color(0xFFFAFAFA); 41 | static const Color dark_bg_gray_ = Color(0xFF242526); 42 | 43 | static const Color fixedColor = Colors.blue; 44 | 45 | 46 | 47 | static const Color transparent_80 = Color(0x80000000); // 48 | 49 | static const Color text_dark = Color(0xFF333333); 50 | static const Color text_normal = Color(0xFF666666); 51 | 52 | static const Color divider = Color(0xffe5e5e5); 53 | 54 | static const Color gray_33 = Color(0xFF333333); //51 55 | static const Color gray_66 = Color(0xFF666666); //102 56 | static const Color gray_99 = Color(0xFF999999); //153 57 | static const Color common_orange = Color(0XFFFC9153); //252 145 83 58 | static const Color gray_ef = Color(0XFFEFEFEF); //153 59 | 60 | static const Color gray_f0 = Color(0xfff0f0f0); // 61 | static const Color white = Color(0xffffffff); // 62 | static const Color gray_f5 = Color(0xfff5f5f5); // 63 | static const Color gray_cc = Color(0xffcccccc); // 64 | static const Color gray_ce = Color(0xffcecece); // 65 | static const Color green_1 = Color(0xff2ed184); // 66 | static const Color green_62 = Color(0xff626262); // 67 | static const Color green_e5 = Color(0xffe5e5e5); // 68 | static const Color blue_1 = Color(0xff00C2FD); // 69 | static const Color lable_clour= Color(0xffff0000); // 70 | 71 | static const mainTextColor = Color.fromRGBO(115, 115, 115, 1.0); 72 | 73 | static const lineColor = Colors.grey; 74 | 75 | 76 | 77 | } 78 | 79 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/page/guide_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_slidable/flutter_slidable.dart'; 5 | 6 | import 'package:flutter_swiper/flutter_swiper.dart'; 7 | import 'package:rxdart/rxdart.dart'; 8 | import 'package:studylinjiashop/common/shared_util.dart'; 9 | import 'package:studylinjiashop/config/const.dart'; 10 | import 'package:studylinjiashop/config/user_info_data.dart'; 11 | import 'package:studylinjiashop/routes/fluro_navigator.dart'; 12 | import 'package:studylinjiashop/utils/image_utils.dart'; 13 | import 'package:studylinjiashop/utils/theme_utils.dart'; 14 | import 'package:studylinjiashop/widget/load_image.dart'; 15 | 16 | class GuidePage extends StatefulWidget { 17 | @override 18 | _GuidePageState createState() => _GuidePageState(); 19 | } 20 | 21 | class _GuidePageState extends State { 22 | int _status = 0; 23 | List _guideList = ['app_start_1', 'app_start_2', 'app_start_3']; 24 | StreamSubscription _subscription; 25 | 26 | @override 27 | void initState() { 28 | super.initState(); 29 | WidgetsBinding.instance.addPostFrameCallback((_) async { 30 | // 由于SpUtil未初始化,所以MaterialApp获取的为默认主题配置,这里同步一下。 31 | if (SharedUtil.instance.getBoolean(Keys.keyGuide, defValue: true)) { 32 | /// 预先缓存图片,避免直接使用时因为首次加载造成闪动 33 | _guideList.forEach((image) { 34 | precacheImage(ImageUtils.getAssetImage(image), context); 35 | }); 36 | } 37 | _initSplash(); 38 | }); 39 | } 40 | 41 | @override 42 | void dispose() { 43 | _subscription?.cancel(); 44 | super.dispose(); 45 | } 46 | 47 | void _initGuide() { 48 | setState(() { 49 | _status = 1; 50 | }); 51 | } 52 | 53 | void _initSplash() { 54 | _subscription = 55 | Observable.just(1).delay(Duration(milliseconds: 1500)).listen((_) { 56 | SharedUtil.instance.saveBoolean(Keys.keyGuide, false); 57 | _initGuide(); 58 | }); 59 | } 60 | 61 | _goLogin() { 62 | NavigatorUtils.goRootPage(context); 63 | } 64 | 65 | @override 66 | Widget build(BuildContext context) { 67 | return Material( 68 | color: ThemeUtils.getBackgroundColor(context), 69 | child: _status == 0 70 | ? FractionallyAlignedSizedBox( 71 | heightFactor: 0.3, 72 | widthFactor: 0.33, 73 | leftFactor: 0.33, 74 | bottomFactor: 0, 75 | child: const LoadAssetImage('banner',format: IMAGE_JPG,)) 76 | : Swiper( 77 | key: const Key('swiper'), 78 | itemCount: _guideList.length, 79 | loop: false, 80 | itemBuilder: (_, index) { 81 | return LoadAssetImage( 82 | _guideList[index], 83 | key: Key(_guideList[index]), 84 | fit: BoxFit.cover, 85 | width: double.infinity, 86 | height: double.infinity, 87 | ); 88 | }, 89 | onTap: (index) { 90 | if (index == _guideList.length - 1) { 91 | _goLogin(); 92 | } 93 | }, 94 | )); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /lib/api/models/login_modle.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:studylinjiashop/config/api.dart'; 3 | import 'package:studylinjiashop/http/mvvms.dart'; 4 | import 'package:studylinjiashop/http/req_model.dart'; 5 | import 'package:studylinjiashop/utils/dialog_utils.dart'; 6 | 7 | class LoginBeanModel extends ReqModel { 8 | String phone; 9 | String codeOrPwd; 10 | 11 | /// 0 验证码 1 密码 12 | int type; 13 | 14 | LoginBeanModel(CancelToken cancelToken,IMvvmView view) { 15 | this.view = view; 16 | this.cancelToken = cancelToken; 17 | } 18 | 19 | @override 20 | String url() => type == 0 ? API.loginOrReg : API.loginByPass; 21 | 22 | @override 23 | Map params() { 24 | return {"mobile": phone, type == 0 ? "smsCode" : "password": codeOrPwd}; 25 | } 26 | 27 | Future data(phone, codeOrPwd, type) { 28 | this.phone = phone; 29 | this.codeOrPwd = codeOrPwd; 30 | this.type = type; 31 | return post(); 32 | } 33 | 34 | // @override 35 | // handError(int statusCode, {msg}) { 36 | // DialogUtil.buildToast("重写了报错方法"); 37 | // } 38 | } 39 | 40 | class LoginBean { 41 | String initPassword; 42 | User user; 43 | String token; 44 | 45 | LoginBean({this.initPassword, this.user, this.token}); 46 | 47 | LoginBean.fromJson(Map json) { 48 | initPassword = json['initPassword']; 49 | user = json['user'] != null ? new User.fromJson(json['user']) : null; 50 | token = json['token']; 51 | } 52 | 53 | Map toJson() { 54 | final Map data = new Map(); 55 | data['initPassword'] = this.initPassword; 56 | if (this.user != null) { 57 | data['user'] = this.user.toJson(); 58 | } 59 | data['token'] = this.token; 60 | return data; 61 | } 62 | } 63 | 64 | class User { 65 | String avatar; 66 | String createTime; 67 | String gender; 68 | String id; 69 | String lastLoginTime; 70 | String mobile; 71 | String nickName; 72 | String password; 73 | String salt; 74 | 75 | User( 76 | {this.avatar, 77 | this.createTime, 78 | this.gender, 79 | this.id, 80 | this.lastLoginTime, 81 | this.mobile, 82 | this.nickName, 83 | this.password, 84 | this.salt}); 85 | 86 | User.fromJson(Map json) { 87 | avatar = json['avatar']; 88 | createTime = json['createTime']; 89 | gender = json['gender']; 90 | id = json['id']; 91 | lastLoginTime = json['lastLoginTime']; 92 | mobile = json['mobile']; 93 | nickName = json['nickName']; 94 | password = json['password']; 95 | salt = json['salt']; 96 | } 97 | 98 | Map toJson() { 99 | final Map data = new Map(); 100 | data['avatar'] = this.avatar; 101 | data['createTime'] = this.createTime; 102 | data['gender'] = this.gender; 103 | data['id'] = this.id; 104 | data['lastLoginTime'] = this.lastLoginTime; 105 | data['mobile'] = this.mobile; 106 | data['nickName'] = this.nickName; 107 | data['password'] = this.password; 108 | data['salt'] = this.salt; 109 | return data; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /lib/common/check.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 手机号正则表达式->true匹配 4 | bool isMobilePhoneNumber(String value) { 5 | RegExp mobile = new RegExp(r"(0|86|17951)?(1[0-9][0-9])[0-9]{8}"); 6 | 7 | return mobile.hasMatch(value); 8 | } 9 | 10 | ///验证网页URl 11 | bool isUrl(String value) { 12 | RegExp url = new RegExp(r"^((https|http|ftp|rtsp|mms)?:\/\/)[^\s]+"); 13 | 14 | return url.hasMatch(value); 15 | } 16 | 17 | ///校验身份证 18 | bool isIdCard(String value) { 19 | RegExp identity = new RegExp(r"\d{17}[\d|x]|\d{15}"); 20 | 21 | return identity.hasMatch(value); 22 | } 23 | 24 | ///正浮点数 25 | bool isMoney(String value) { 26 | RegExp identity = new RegExp( 27 | r"^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$"); 28 | return identity.hasMatch(value); 29 | } 30 | 31 | ///校验中文 32 | bool isChinese(String value) { 33 | RegExp identity = new RegExp(r"[\u4e00-\u9fa5]"); 34 | 35 | return identity.hasMatch(value); 36 | } 37 | 38 | ///校验支付宝名称 39 | bool isAliPayName(String value) { 40 | RegExp identity = new RegExp(r"[\u4e00-\u9fa5_a-zA-Z]"); 41 | 42 | return identity.hasMatch(value); 43 | } 44 | 45 | /// 字符串不为空 46 | bool strNoEmpty(String value) { 47 | if (value == null) return false; 48 | 49 | return value.trim().isNotEmpty; 50 | } 51 | 52 | /// 字符串不为空 53 | bool mapNoEmpty(Map value) { 54 | if (value == null) return false; 55 | return value.isNotEmpty; 56 | } 57 | 58 | ///判断List是否为空 59 | bool listNoEmpty(List list) { 60 | if (list == null) return false; 61 | 62 | if (list.length == 0) return false; 63 | 64 | return true; 65 | } 66 | 67 | /// 判断是否网络 68 | bool isNetWorkImg(String img) { 69 | return img.startsWith('http') || img.startsWith('https'); 70 | } 71 | 72 | /// 判断是否资源图片 73 | bool isAssetsImg(String img) { 74 | return img.startsWith('asset') || img.startsWith('assets'); 75 | } 76 | 77 | double getMemoryImageCashe() { 78 | return PaintingBinding.instance.imageCache.maximumSize / 1000; 79 | } 80 | 81 | void clearMemoryImageCache() { 82 | PaintingBinding.instance.imageCache.clear(); 83 | } 84 | 85 | String stringAsFixed(value, num) { 86 | double v = double.parse(value.toString()); 87 | String str = ((v * 100).floor() / 100).toStringAsFixed(2); 88 | return str; 89 | } 90 | 91 | String hiddenPhone(String phone){ 92 | String result = ''; 93 | 94 | if(phone != null && phone.length >= 11){ 95 | String sub = phone.substring(0,3); 96 | String end = phone.substring(8,11); 97 | result = '$sub****$end'; 98 | } 99 | 100 | return result; 101 | } 102 | 103 | ///去除后面的0 104 | String stringDisposeWithDouble(v, [fix = 2]) { 105 | double b = double.parse(v.toString()); 106 | String vStr = b.toStringAsFixed(fix); 107 | int len = vStr.length; 108 | for (int i = 0; i < len; i++) { 109 | if (vStr.contains('.') && vStr.endsWith('0')) { 110 | vStr = vStr.substring(0, vStr.length - 1); 111 | } else { 112 | break; 113 | } 114 | } 115 | 116 | if (vStr.endsWith('.')) { 117 | vStr = vStr.substring(0, vStr.length - 1); 118 | } 119 | 120 | return vStr; 121 | } 122 | 123 | ///去除小数点 124 | String removeDot(v) { 125 | String vStr = v.toString().replaceAll('.', ''); 126 | 127 | return vStr; 128 | } -------------------------------------------------------------------------------- /lib/base/base_page_state2.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:studylinjiashop/http/mvvms.dart'; 4 | import 'package:studylinjiashop/routes/fluro_navigator.dart'; 5 | import 'package:studylinjiashop/utils/app_size.dart'; 6 | import 'package:studylinjiashop/utils/dialog_utils.dart'; 7 | import 'package:studylinjiashop/utils/utils.dart'; 8 | import 'package:studylinjiashop/view/custom_view.dart'; 9 | 10 | abstract class BasePageState2 extends State 11 | implements IMvvmView { 12 | CancelToken _cancelToken; 13 | 14 | BasePageState2() { 15 | _cancelToken = CancelToken(); 16 | injectViewModelView(); 17 | } 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | AppSize.init(context); 22 | return Scaffold( 23 | appBar: getAppBar(context), 24 | body: isAutoCloseKeyboard() 25 | ? hideKeyword(getBody(context), context) 26 | : getBody(context), 27 | ); 28 | } 29 | 30 | BuildContext getContext() { 31 | return context; 32 | } 33 | 34 | @override 35 | void closeProgress() { 36 | if (mounted && _isShowDialog) { 37 | _isShowDialog = false; 38 | NavigatorUtils.goBack(context); 39 | } 40 | } 41 | 42 | bool _isShowDialog = false; 43 | 44 | @override 45 | void showProgress() { 46 | /// 避免重复弹出 47 | if (mounted && !_isShowDialog) { 48 | _isShowDialog = true; 49 | try { 50 | showDialog( 51 | context: context, 52 | barrierDismissible: false, 53 | builder: (BuildContext context) { 54 | return WillPopScope( 55 | onWillPop: () async { 56 | // 拦截到返回键,证明dialog被手动关闭 57 | _isShowDialog = false; 58 | return Future.value(true); 59 | }, 60 | child: const LoadingDialog( 61 | text: "加载中…", 62 | ), 63 | ); 64 | }); 65 | } catch (e) { 66 | /// 异常原因主要是页面没有build完成就调用Progress。 67 | print(e); 68 | } 69 | } 70 | } 71 | 72 | @override 73 | void showToast(String string) { 74 | DialogUtil.buildToast(string); 75 | } 76 | 77 | @override 78 | void didChangeDependencies() { 79 | super.didChangeDependencies(); 80 | } 81 | 82 | bool isAutoCloseKeyboard() { 83 | return true; 84 | } 85 | 86 | @override 87 | void dispose() { 88 | super.dispose(); 89 | if (_cancelToken != null && !_cancelToken.isCancelled) { 90 | _cancelToken.cancel(); 91 | } 92 | } 93 | 94 | @override 95 | void deactivate() { 96 | super.deactivate(); 97 | } 98 | 99 | @override 100 | void didUpdateWidget(T oldWidget) { 101 | super.didUpdateWidget(oldWidget); 102 | didUpdateWidgets(oldWidget); 103 | } 104 | 105 | @override 106 | void initState() { 107 | super.initState(); 108 | } 109 | 110 | void didUpdateWidgets(W oldWidget) {} 111 | 112 | CancelToken getCancelToken() { 113 | return _cancelToken; 114 | } 115 | 116 | Widget getAppBar(BuildContext context); 117 | 118 | Widget getBody(BuildContext context); 119 | 120 | //如果要用model与view交互就需要给viewmodle 设置view 121 | void injectViewModelView() {} 122 | } 123 | -------------------------------------------------------------------------------- /lib/routes/fluro_navigator.dart: -------------------------------------------------------------------------------- 1 | import 'package:fluro/fluro.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:studylinjiashop/config/user_info_data.dart'; 4 | import 'package:studylinjiashop/routes/shop_router.dart'; 5 | 6 | import 'application.dart'; 7 | import 'routers.dart'; 8 | 9 | /// fluro的路由跳转工具类 10 | class NavigatorUtils { 11 | static initRouter() { 12 | final router = Router(); 13 | Routes.configureRoutes(router); 14 | Application.router = router; 15 | } 16 | 17 | static push(BuildContext context, String path, 18 | {bool replace = false, 19 | bool clearStack = false, 20 | Map params}) { 21 | FocusScope.of(context).unfocus(); 22 | Application.router.navigateTo( 23 | context, navigateToReplace(path, params: params), 24 | replace: replace, 25 | clearStack: clearStack, 26 | transition: TransitionType.native); 27 | } 28 | 29 | static pushResult( 30 | BuildContext context, String path, Function(Object) function, 31 | {bool replace = false, 32 | bool clearStack = false, 33 | Map params}) { 34 | FocusScope.of(context).unfocus(); 35 | Application.router 36 | .navigateTo(context, navigateToReplace(path, params: params), 37 | replace: replace, 38 | clearStack: clearStack, 39 | transition: TransitionType.native) 40 | .then((result) { 41 | // 页面返回result为null 42 | if (result == null) { 43 | return; 44 | } 45 | function(result); 46 | }).catchError((error) { 47 | print('$error'); 48 | }); 49 | } 50 | 51 | /// 返回 52 | static void goBack(BuildContext context) { 53 | FocusScope.of(context).unfocus(); 54 | Navigator.maybePop(context); 55 | } 56 | 57 | /// 带参数返回 58 | static void goBackWithParams(BuildContext context, result) { 59 | FocusScope.of(context).unfocus(); 60 | Navigator.pop(context, result); 61 | } 62 | 63 | static void pop(BuildContext context, {isPop = true}) { 64 | FocusScope.of(context).unfocus(); 65 | if (isPop == true) { 66 | Navigator.pop(context); 67 | } else { 68 | Navigator.maybePop(context); 69 | } 70 | } 71 | 72 | /// 跳到WebView页 73 | static goWebViewPage(BuildContext context, String title, String url) { 74 | //fluro 不支持传中文,需转换 75 | push(context, Routes.webViewPage, params: {"title": title, "url": url}); 76 | } 77 | 78 | static goRootPage(BuildContext context) { 79 | //fluro 不支持传中文,需转换 80 | push(context, ShopRouter.root_page, replace: true); 81 | } 82 | 83 | static bool isLogin(context) { 84 | if (!UserInfoData.instance.isLogin) { 85 | push(context, ShopRouter.login_page); 86 | return false; 87 | } 88 | return true; 89 | } 90 | 91 | static String navigateToReplace(String path, {Map params}) { 92 | String query = ""; 93 | if (params != null) { 94 | int index = 0; 95 | for (var key in params.keys) { 96 | var value = Uri.encodeComponent(params[key]); 97 | if (index == 0) { 98 | query = "?"; 99 | } else { 100 | query = query + "\&"; 101 | } 102 | query += "$key=$value"; 103 | index++; 104 | } 105 | } 106 | // print('我是navigateTo传递的参数:$query'); 107 | return path = path + query; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /lib/page/cart/count_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:studylinjiashop/api/models/cart_goods_query_modle.dart'; 4 | import 'package:studylinjiashop/api/models/void_modle.dart'; 5 | import 'package:studylinjiashop/api/models/void_view_model.dart'; 6 | import 'package:studylinjiashop/http/mvvms.dart'; 7 | import 'package:studylinjiashop/receiver/event_bus.dart'; 8 | import 'package:studylinjiashop/utils/app_size.dart'; 9 | 10 | class CartCount extends StatelessWidget { 11 | GoodsModel item; 12 | IMvvmView view; 13 | CancelToken cancelToken; 14 | CartCount(this.item, this.view, this.cancelToken); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Container( 19 | width: AppSize.width(190.0), 20 | height: AppSize.width(65), 21 | margin: EdgeInsets.only(top: 5.0), 22 | decoration: 23 | BoxDecoration(border: Border.all(width: 1, color: Colors.black12)), 24 | child: Row( 25 | children: [ 26 | _reduceBtn(context), 27 | _countArea(), 28 | _addBtn(context), 29 | ], 30 | ), 31 | ); 32 | } 33 | 34 | // 减少按钮 35 | Widget _reduceBtn(BuildContext context) { 36 | return InkWell( 37 | onTap: () { 38 | loadReduce(context, item.orderId, item.countNum - 1); 39 | }, 40 | child: Container( 41 | width: AppSize.width(55), 42 | height: AppSize.width(55), 43 | alignment: Alignment.center, 44 | decoration: BoxDecoration( 45 | color: item.countNum > 1 ? Colors.white : Colors.black12, 46 | border: Border(right: BorderSide(width: 1, color: Colors.black12))), 47 | child: item.countNum > 1 ? Text('-') : Text(' '), 48 | ), 49 | ); 50 | } 51 | 52 | void loadReduce( 53 | BuildContext context, String orderId, int count) async { 54 | VoidViewModel.get(view, cancelToken).getData( 55 | type: VoidModel.CART_UPDATE, params2: {"id": orderId, "count": count}).then((onValue) { 56 | item.countNum--; 57 | eventBus.fire(new GoodsNumInEvent("sub")); 58 | }); 59 | } 60 | 61 | //添加按钮 62 | Widget _addBtn(BuildContext context) { 63 | return InkWell( 64 | onTap: () { 65 | addCart(context, item.id, 1, item.idSku); 66 | }, 67 | child: Container( 68 | width: AppSize.width(55), 69 | height: AppSize.width(55), 70 | alignment: Alignment.center, 71 | decoration: BoxDecoration( 72 | color: Colors.white, 73 | border: Border(left: BorderSide(width: 1, color: Colors.black12))), 74 | child: Text('+'), 75 | ), 76 | ); 77 | } 78 | 79 | void addCart(BuildContext context, String idGoods, int count, String idSku) async { 80 | VoidViewModel.get(view, cancelToken).getData(type: VoidModel.CART_ADD, params2: { 81 | "idGoods": idGoods, 82 | "count": count, 83 | "idSku": idSku 84 | }).then((onValue) { 85 | item.countNum++; 86 | eventBus.fire(GoodsNumInEvent("add")); 87 | }); 88 | } 89 | 90 | //中间数量显示区域 91 | Widget _countArea() { 92 | return Container( 93 | width: AppSize.width(70), 94 | height: AppSize.width(45), 95 | alignment: Alignment.center, 96 | color: Colors.white, 97 | child: Text('${item.countNum}'), 98 | ); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /lib/common/file_util.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:studylinjiashop/config/api.dart'; 6 | import 'package:studylinjiashop/config/config.dart'; 7 | import 'package:path_provider/path_provider.dart'; 8 | 9 | class FileUtil { 10 | static FileUtil _instance; 11 | 12 | static FileUtil getInstance() { 13 | if (_instance == null) { 14 | _instance = FileUtil._internal(); 15 | } 16 | return _instance; 17 | } 18 | 19 | FileUtil._internal(); 20 | 21 | Future getSavePath(String endPath) async { 22 | Directory tempDir = await getApplicationDocumentsDirectory(); 23 | String path = tempDir.path + endPath; 24 | Directory directory = Directory(path); 25 | if (!directory.existsSync()) { 26 | directory.createSync(recursive: true); 27 | } 28 | return path; 29 | } 30 | 31 | void copyFile(String oldPath, String newPath) { 32 | File file = File(oldPath); 33 | if (file.existsSync()) { 34 | file.copy(newPath); 35 | } 36 | } 37 | 38 | Future> getDirChildren(String path) async { 39 | Directory directory = Directory(path); 40 | final childrenDir = directory.listSync(); 41 | List pathList = []; 42 | for (var o in childrenDir) { 43 | final filename = o.path.split("/").last; 44 | if (filename.contains(".")) { 45 | pathList.add(o.path); 46 | } 47 | } 48 | return pathList; 49 | } 50 | 51 | ///[assetPath] 例子 'images/' 52 | ///[assetName] 例子 '1.jpg' 53 | ///[filePath] 例子:'/myFile/' 54 | ///[fileName] 例子 'girl.jpg' 55 | Future copyAssetToFile(String assetPath, String assetName, 56 | String filePath, String fileName) async { 57 | String newPath = await FileUtil.getInstance().getSavePath(filePath); 58 | String name = fileName; 59 | bool exists = await new File(newPath + name).exists(); 60 | if (!exists) { 61 | var data = await rootBundle.load(assetPath + assetName); 62 | List bytes = 63 | data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); 64 | await File(newPath + name).writeAsBytes(bytes); 65 | return newPath + name; 66 | } else 67 | return newPath + name; 68 | } 69 | 70 | void downloadFile( 71 | {String url, 72 | String filePath, 73 | String fileName, 74 | Function onComplete}) async { 75 | final path = await FileUtil.getInstance().getSavePath(filePath); 76 | String name = fileName ?? url.split("/").last; 77 | 78 | Dio _client; 79 | if (_client == null) { 80 | BaseOptions options = new BaseOptions(); 81 | options.connectTimeout = connectTimeOut; 82 | options.receiveTimeout = receiveTimeOut; 83 | options.headers = const {'Content-Type': 'application/json'}; 84 | options.baseUrl = API.reqUrl; 85 | _client = new Dio(options); 86 | } 87 | 88 | if (_client != null) 89 | _client.download( 90 | url, 91 | path + name, 92 | onReceiveProgress: (int count, int total) { 93 | final downloadProgress = ((count / total) * 100).toInt(); 94 | if (downloadProgress == 100) { 95 | if (onComplete != null) onComplete(path + name); 96 | } 97 | }, 98 | options: RequestOptions(connectTimeout: 15 * 1000, receiveTimeout: 360 * 1000), 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/page/home/details/topic_details.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | 7 | import 'package:flutter_html/flutter_html.dart'; 8 | import 'package:studylinjiashop/api/models/topic_detail_view_model.dart'; 9 | import 'package:studylinjiashop/api/models/topic_details_model.dart'; 10 | import 'package:studylinjiashop/utils/app_size.dart'; 11 | import 'package:studylinjiashop/view/app_topbar.dart'; 12 | import 'package:studylinjiashop/view/customize_appbar.dart'; 13 | import 'package:studylinjiashop/view/theme_ui.dart'; 14 | import 'package:studylinjiashop/widget/view/load_state_layout.dart'; 15 | 16 | import 'topic_deatails_goods.dart'; 17 | /// 18 | /// 推荐详情页 19 | /// 20 | class TopicDetails extends StatefulWidget { 21 | final String id; 22 | 23 | TopicDetails({this.id}); 24 | 25 | @override 26 | _TopicDetailsState createState() => _TopicDetailsState(); 27 | } 28 | 29 | class _TopicDetailsState extends State { 30 | final String imgUrl = 31 | "http://linjiashop-mobile-api.microapp.store/file/getImgStream?idFile="; 32 | LoadState _loadStateDetails = LoadState.State_Loading; 33 | TopicDetailsEntity topicDetailsEntity; 34 | 35 | @override 36 | void initState() { 37 | if (mounted) loadData(); 38 | super.initState(); 39 | } 40 | 41 | @override 42 | void dispose() { 43 | super.dispose(); 44 | } 45 | 46 | void loadData() async { 47 | topicDetailViewModel.getData(widget.id.toString()).then((entity){ 48 | if (entity != null) { 49 | setState(() { 50 | topicDetailsEntity = entity; 51 | _loadStateDetails = LoadState.State_Success; 52 | }); 53 | } else { 54 | setState(() { 55 | _loadStateDetails = LoadState.State_Error; 56 | }); 57 | } 58 | }); 59 | } 60 | 61 | @override 62 | Widget build(BuildContext context) { 63 | AppSize.init(context); 64 | return Scaffold( 65 | appBar: MyAppBar( 66 | preferredSize: Size.fromHeight(AppSize.height(160)), 67 | child: _getHeader(), 68 | ), 69 | body: LoadStateLayout( 70 | state: _loadStateDetails, 71 | errorRetry: () { 72 | setState(() { 73 | _loadStateDetails = LoadState.State_Loading; 74 | }); 75 | loadData(); 76 | }, 77 | successWidget: _getContent())); 78 | } 79 | 80 | ///返回不同头部 81 | Widget _getHeader() { 82 | if (null == topicDetailsEntity) { 83 | return CommonBackTopBar( 84 | title: "详情"); 85 | } else { 86 | return CommonBackTopBar( 87 | title: topicDetailsEntity.articleModel.title); 88 | } 89 | } 90 | 91 | ///返回内容 92 | Widget _getContent() { 93 | if (null == topicDetailsEntity || topicDetailsEntity.goodsList == null) { 94 | return Center( 95 | child: CircularProgressIndicator(), 96 | ); 97 | } else { 98 | return ListView( 99 | children: [ 100 | Container( 101 | width: double.infinity, 102 | height: AppSize.height(94), 103 | child: Text( 104 | topicDetailsEntity.articleModel.title, 105 | textAlign: TextAlign.center, 106 | style: ThemeTextStyle.personalShopNameStyle, 107 | ), 108 | ), 109 | Html(data: topicDetailsEntity.articleModel.content), 110 | TopicDeatilsCardGoods(topicGoods: topicDetailsEntity.goodsList) 111 | ], 112 | ); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /lib/widget/view/load_state_layout.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:studylinjiashop/widget/load_image.dart'; 3 | 4 | ///四种视图状态 5 | enum LoadState { State_Success, State_Error, State_Loading, State_Empty } 6 | 7 | ///根据不同状态来展示不同的视图 8 | class LoadStateLayout extends StatefulWidget { 9 | final LoadState state; //页面状态 10 | final Widget successWidget; //成功视图 11 | final VoidCallback errorRetry; //错误事件处理 12 | 13 | LoadStateLayout( 14 | {Key key, 15 | this.state = LoadState.State_Loading, //默认为加载状态 16 | this.successWidget, 17 | this.errorRetry}) 18 | : super(key: key); 19 | 20 | @override 21 | _LoadStateLayoutState createState() => _LoadStateLayoutState(); 22 | } 23 | 24 | class _LoadStateLayoutState extends State { 25 | @override 26 | Widget build(BuildContext context) { 27 | return Container( 28 | //宽高都充满屏幕剩余空间 29 | width: double.infinity, 30 | height: double.infinity, 31 | child: _buildWidget, 32 | ); 33 | } 34 | 35 | ///根据不同状态来显示不同的视图 36 | Widget get _buildWidget { 37 | switch (widget.state) { 38 | case LoadState.State_Success: 39 | return widget.successWidget; 40 | break; 41 | case LoadState.State_Error: 42 | return _errorView; 43 | break; 44 | case LoadState.State_Loading: 45 | return _loadingView; 46 | break; 47 | case LoadState.State_Empty: 48 | return _emptyView; 49 | break; 50 | default: 51 | return null; 52 | } 53 | } 54 | 55 | ///加载中视图 56 | Widget get _loadingView { 57 | return Container( 58 | width: double.infinity, 59 | height: double.infinity, 60 | decoration: BoxDecoration(color: Colors.transparent), 61 | alignment: Alignment.center, 62 | child: Container( 63 | height: 80, 64 | padding: EdgeInsets.all(10), 65 | decoration: BoxDecoration( 66 | color: Color(0x88000000), borderRadius: BorderRadius.circular(6)), 67 | child: Column( 68 | mainAxisAlignment: MainAxisAlignment.spaceAround, 69 | children: [CircularProgressIndicator(), Text('正在加载')], 70 | ), 71 | ), 72 | ); 73 | } 74 | 75 | ///错误视图 76 | Widget get _errorView { 77 | return Container( 78 | width: double.infinity, 79 | height: double.infinity, 80 | child: Column( 81 | mainAxisAlignment: MainAxisAlignment.center, 82 | children: [ 83 | LoadAssetImage( 84 | "load_error_view", 85 | height: 80, 86 | width: 100, 87 | ), 88 | Text("加载失败,请重试"), 89 | RaisedButton( 90 | color: Color(0xffbc2929), 91 | onPressed: widget.errorRetry, 92 | child: Text( 93 | '重新加载', 94 | style: TextStyle(color: Colors.white), 95 | ), 96 | ) 97 | ], 98 | ), 99 | ); 100 | } 101 | 102 | ///数据为空的视图 103 | Widget get _emptyView { 104 | return Container( 105 | width: double.infinity, 106 | height: double.infinity, 107 | child: Column( 108 | crossAxisAlignment: CrossAxisAlignment.center, 109 | mainAxisAlignment: MainAxisAlignment.center, 110 | children: [ 111 | LoadAssetImage( 112 | "ic_empty", 113 | width: 100, 114 | height: 100, 115 | ), 116 | Padding( 117 | padding: EdgeInsets.only(top: 10), 118 | child: Text('暂无数据'), 119 | ) 120 | ], 121 | ), 122 | ); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /lib/view/theme_ui.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:studylinjiashop/utils/app_size.dart'; 3 | /// 4 | /// 封装app整体主题风格控件 5 | /// 6 | class ThemeView{ 7 | 8 | 9 | static Widget divider({Orient orient = Orient.horizontal}){ 10 | if (orient == Orient.horizontal){ 11 | return Divider(height: 0.0,color: Color(0xFFDEDEDE)); 12 | }else{ 13 | return VerticalDivider(width: 0.0,color: Color(0xFFDEDEDE)); 14 | } 15 | } 16 | 17 | 18 | } 19 | 20 | enum Orient{ 21 | horizontal, 22 | vertical 23 | } 24 | 25 | class ThemeStyle{ 26 | } 27 | 28 | class ThemeColor{ 29 | static const Color appBg = const Color(0xfff5f6f7); 30 | static const Color appBarTopBg = const Color(0xff00C2FD); 31 | static const Color appBarBottomBg = const Color(0xff00C2FD); 32 | static const Color hintTextColor = const Color(0xff333333); 33 | static const Color subTextColor = const Color(0xff999999); 34 | static const Color starColor = const Color(0xFFFFA516); 35 | static const Color loignColor = const Color(0xFFFE976A); 36 | } 37 | 38 | class ThemeTextStyle{ 39 | static final primaryStyle = TextStyle(fontSize: AppSize.sp(44),color: Color(0xff333333)); 40 | static final primaryStyle2 = TextStyle(fontSize: AppSize.sp(44),color: Color(0xff656565)); 41 | static final menuStyle = TextStyle(fontSize: AppSize.sp(36),color: Color(0xff666666)); 42 | static final menuStyle2 = TextStyle(fontSize: AppSize.sp(36),color: Color(0xff656565)); 43 | static final menuStyle3 = TextStyle(fontSize: AppSize.sp(36),color: Color(0xff333333)); 44 | static final priceStyle = TextStyle(fontSize: AppSize.sp(32),color: Color(0xffee4646)); 45 | 46 | 47 | static final cardTitleStyle = TextStyle(fontSize: AppSize.sp(40),color: Color(0xff333333)); 48 | static final cardPriceStyle = TextStyle(fontSize: AppSize.sp(35),color: Color(0xffee4646)); 49 | static final cardNumStyle = TextStyle(fontSize: AppSize.sp(32),color: Color(0xff999999)); 50 | 51 | 52 | 53 | static final orderFormStatusStyle = TextStyle(fontSize: AppSize.sp(38),color: Color(0xff999999)); 54 | static final orderFormTitleStyle = TextStyle(fontSize: AppSize.sp(38),color: Color(0xff333333)); 55 | static final orderFormBtnStyle = TextStyle(fontSize: AppSize.sp(44),color: ThemeColor.appBarTopBg); 56 | static final orderCancelBtnStyle = TextStyle(fontSize: AppSize.sp(44),color: Color(0xff999999)); 57 | static final orderContentStyle = TextStyle(fontSize: AppSize.sp(36),color: Color(0xff999999)); 58 | 59 | 60 | static final personalShopNameStyle = TextStyle(fontSize: AppSize.sp(52),color: Color(0xff333333)); 61 | static final detailStyle = TextStyle(fontSize: AppSize.sp(44),color: Color(0xff999999)); 62 | static final detailStylePrice = TextStyle(fontSize: AppSize.sp(44),color: Color(0xffee4646)); 63 | static final orderStylePrice = TextStyle(fontSize: AppSize.sp(44),color: Color(0xffffffff)); 64 | static final personalNumStyle = TextStyle( 65 | fontSize: AppSize.sp(44), 66 | color: ThemeColor.appBarTopBg, 67 | fontWeight: FontWeight.w700); 68 | 69 | static final primaryBoldStyle = TextStyle( 70 | fontSize: AppSize.sp(44), 71 | color: Color(0xff333333), 72 | fontWeight: FontWeight.w700); 73 | 74 | 75 | } 76 | 77 | class ThemeDecoration{ 78 | static final card = BoxDecoration( 79 | color: Colors.white, 80 | borderRadius: BorderRadius.circular(6) 81 | ); 82 | 83 | static final card2 = BoxDecoration( 84 | color: Colors.white, 85 | borderRadius: BorderRadius.circular(8) 86 | ); 87 | 88 | static final outlineBtn = BoxDecoration( 89 | border: Border.all(color: ThemeColor.appBarTopBg), 90 | borderRadius:BorderRadius.circular(20), 91 | ); 92 | 93 | static final outlineCancelBtn = BoxDecoration( 94 | border: Border.all(color: Color(0xffcccccc)), 95 | borderRadius:BorderRadius.circular(20), 96 | ); 97 | } 98 | 99 | -------------------------------------------------------------------------------- /lib/page/home/details/specifica_button.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | import 'package:studylinjiashop/api/models/goods_details_model.dart'; 4 | import 'package:studylinjiashop/receiver/event_bus.dart'; 5 | 6 | class SpecificaButton extends StatefulWidget { 7 | final List treeModel; 8 | SpecificaButton({this.treeModel}); 9 | @override 10 | _SpecificaButtonState createState() { 11 | return _SpecificaButtonState(); 12 | } 13 | 14 | } 15 | 16 | class _SpecificaButtonState extends State { 17 | Map hmSpecifica = Map(); 18 | @override 19 | void initState() { 20 | super.initState(); 21 | if (mounted) { 22 | widget.treeModel.forEach((e){ 23 | hmSpecifica[e.k_s] = 0; 24 | }); 25 | specPost(); 26 | } 27 | 28 | } 29 | 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | return Container( 34 | padding: EdgeInsets.fromLTRB(16, 0, 16, 0), 35 | child: Column( 36 | mainAxisSize: MainAxisSize.min, 37 | crossAxisAlignment: CrossAxisAlignment.start, 38 | children: List.generate(widget.treeModel.length, (i) { 39 | return _BoxSpecifications(widget.treeModel[i].k, 40 | widget.treeModel[i].k_s, widget.treeModel[i].vModels); 41 | })), 42 | ); 43 | 44 | 45 | } 46 | Widget _BoxSpecifications(String name, String k_s, List vModel) { 47 | 48 | return Container( 49 | padding: EdgeInsets.fromLTRB(16, 0, 16, 0), 50 | child: Column( 51 | mainAxisSize: MainAxisSize.min, 52 | crossAxisAlignment: CrossAxisAlignment.start, 53 | children: [ 54 | Container( 55 | margin: EdgeInsets.only(top: 16, bottom: 14), 56 | child: Text(name, style: TextStyle(fontSize: 14))), 57 | Wrap( 58 | alignment: WrapAlignment.start, 59 | spacing: 16, 60 | runSpacing: 12, 61 | children: List.generate(vModel.length, (index) { 62 | return Container( 63 | margin: EdgeInsets.only(top: 8), 64 | child: FlatButton( 65 | child: Text(vModel[index].name, style: TextStyle(fontSize: 12)), 66 | onPressed: () { 67 | setState(() { 68 | hmSpecifica[k_s]=index; 69 | specPost(); 70 | }); 71 | }, 72 | color: hmSpecifica[k_s]== index 73 | ? Color(0x1AFF6631) 74 | : Color(0xFFF7F7F7), 75 | textColor: hmSpecifica[k_s]== index 76 | ? Color(0xFFFF6732) 77 | : Color(0xff333333), 78 | disabledTextColor: Color(0xff999999), 79 | 80 | shape: StadiumBorder( 81 | side: BorderSide( 82 | style: BorderStyle.solid, 83 | color: hmSpecifica[k_s]== index 84 | ? Color(0xFFFF6732) 85 | : Color(0xFFF7F7F7))), 86 | ), 87 | ); 88 | 89 | }) 90 | ), 91 | ], 92 | ), 93 | ); 94 | } 95 | 96 | void specPost() { 97 | String code=""; 98 | for(int i=0;i< widget.treeModel.length;i++){ 99 | if(i== widget.treeModel.length-1){ 100 | code= code+widget.treeModel[i].vModels[hmSpecifica[widget.treeModel[i].k_s]].id; 101 | }else{ 102 | code= code+widget.treeModel[i].vModels[hmSpecifica[widget.treeModel[i].k_s]].id+","; 103 | } 104 | } 105 | eventBus.fire(SpecEvent(code)); 106 | } 107 | } -------------------------------------------------------------------------------- /lib/page/home/details/topic_deatails_goods.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:studylinjiashop/api/models/topic_details_model.dart'; 5 | import 'package:studylinjiashop/res/colors.dart'; 6 | import 'package:studylinjiashop/res/colours.dart'; 7 | import 'package:studylinjiashop/routes/fluro_navigator.dart'; 8 | 9 | import 'package:studylinjiashop/routes/shop_router.dart'; 10 | import 'package:studylinjiashop/utils/app_size.dart'; 11 | 12 | /// 主题详情推荐 13 | class TopicDeatilsCardGoods extends StatelessWidget { 14 | final List topicGoods; 15 | String imgUrl = 16 | "http://linjiashop-mobile-api.microapp.store/file/getImgStream?idFile="; 17 | 18 | TopicDeatilsCardGoods({Key key, @required this.topicGoods}) 19 | : super(key: key); 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | AppSize.init(context); 24 | 25 | return Container( 26 | color: Colors.white, 27 | margin: EdgeInsets.only(top: 5.0), 28 | padding: EdgeInsets.all(3.0), 29 | child: _buildWidget(context)); 30 | } 31 | 32 | Row _buildRow(BuildContext context, 33 | List sub) { 34 | List mSubGoodsCard = []; 35 | Widget content; 36 | final screenWidth = ScreenUtil.screenWidth; 37 | 38 | for (int i = 0; i < sub.length; i++) { 39 | String pic = sub[i].pic; 40 | String name = sub[i].name; 41 | String id = sub[i].id; 42 | mSubGoodsCard.add( 43 | InkWell( 44 | onTap: () { 45 | onItemClick(context,id); 46 | }, 47 | child: Column( 48 | children: [ 49 | Container( 50 | width: AppSize.width(screenWidth / 2 - 28), 51 | height: AppSize.height(360), 52 | margin: EdgeInsets.all(3.0), 53 | child: Image.network( 54 | imgUrl +pic, 55 | fit: BoxFit.fill, 56 | ), 57 | ), 58 | Container( 59 | width: AppSize.width(screenWidth / 2 - 28), 60 | height: AppSize.height(48), 61 | color: Colours.gray_99, 62 | child: Center(child: Text(name, 63 | softWrap: true, 64 | textAlign: TextAlign.center, 65 | overflow: TextOverflow.ellipsis, 66 | maxLines: 1, 67 | style: TextStyle(color: Colors.white,fontSize: AppSize.sp(42)))) , 68 | ) 69 | 70 | ], 71 | ) 72 | ), 73 | ); 74 | 75 | } 76 | 77 | content = Row( 78 | children: mSubGoodsCard, 79 | ); 80 | return content; 81 | } 82 | 83 | void onItemClick(BuildContext context, String id) { 84 | 85 | Map p = {"id": id}; 86 | NavigatorUtils.push(context, ShopRouter.PRODUCT_DETAILS, params: p); 87 | } 88 | 89 | 90 | Widget _buildWidget(BuildContext context) { 91 | List mTopCard = []; 92 | Widget content; 93 | List sub = List(); 94 | for (int i = 0; i < topicGoods.length; i++) { 95 | if ((i + 1) % 2 == 1) { 96 | sub.add(topicGoods[i]); 97 | if (i == topicGoods.length - 1) { 98 | mTopCard.add(_buildRow(context, sub)); 99 | sub.clear(); 100 | } 101 | } 102 | if ((i + 1) % 2 == 0) { 103 | sub.add(topicGoods[i]); 104 | mTopCard.add(_buildRow(context, sub)); 105 | sub.clear(); 106 | } 107 | 108 | } 109 | 110 | content = Column( 111 | children: mTopCard, 112 | ); 113 | return content; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /lib/page/home/topic_card_goods.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:studylinjiashop/api/models/topic_goods_query_modle.dart'; 5 | import 'package:studylinjiashop/routes/fluro_navigator.dart'; 6 | import 'package:studylinjiashop/routes/shop_router.dart'; 7 | import 'package:studylinjiashop/utils/app_size.dart'; 8 | 9 | /** 10 | * 主题推荐 11 | */ 12 | class TopicCardGoods extends StatelessWidget { 13 | final List topicGoodsModleDataList; 14 | String imgUrl = 15 | "http://linjiashop-mobile-api.microapp.store/file/getImgStream?idFile="; 16 | 17 | TopicCardGoods({Key key, @required this.topicGoodsModleDataList}) 18 | : super(key: key); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | AppSize.init(context); 23 | 24 | return Container( 25 | color: Colors.white, 26 | margin: EdgeInsets.only(top: 5.0), 27 | padding: EdgeInsets.all(3.0), 28 | child: _buildWidget(context)); 29 | } 30 | 31 | Row _buildRow(BuildContext context, 32 | List subTopicGoodsModleDataList) { 33 | List mSubGoodsCard = []; 34 | Widget content; 35 | final screenWidth = ScreenUtil.screenWidth; 36 | 37 | for (int i = 0; i < subTopicGoodsModleDataList.length; i++) { 38 | String id=subTopicGoodsModleDataList[i].id; 39 | if (i == 0) { 40 | mSubGoodsCard.add( 41 | 42 | InkWell( 43 | onTap: () { 44 | onItemClick(context, i,id); 45 | }, 46 | child: Container( 47 | width: AppSize.width(screenWidth / 2 - 21), 48 | height: AppSize.height(230), 49 | child: Image.network( 50 | imgUrl + 51 | "${subTopicGoodsModleDataList[0].topicGoodsModel.img}", 52 | fit: BoxFit.fill, 53 | ), 54 | )), 55 | ); 56 | } else { 57 | mSubGoodsCard.add(InkWell( 58 | onTap: () { 59 | onItemClick(context, i,id); 60 | }, 61 | child: Container( 62 | width: AppSize.width(screenWidth / 4 - 30), 63 | height: AppSize.height(230), 64 | child: Image.network( 65 | imgUrl + "${subTopicGoodsModleDataList[i].topicGoodsModel.img}", 66 | fit: BoxFit.fill, 67 | ), 68 | ), 69 | )); 70 | } 71 | } 72 | 73 | content = Row( 74 | children: mSubGoodsCard, 75 | ); 76 | return content; 77 | } 78 | 79 | void onItemClick(BuildContext context, int i,String id) { 80 | 81 | Map p = {"id": id}; 82 | NavigatorUtils.push(context, ShopRouter.topic_page, params: p); 83 | } 84 | 85 | Widget _buildWidget(BuildContext context) { 86 | List mTopCard = []; 87 | Widget content; 88 | List sub = List(); 89 | for (int i = 0; i < topicGoodsModleDataList.length; i++) { 90 | if ((i + 1) % 3 == 1) { 91 | sub.add(topicGoodsModleDataList[i]); 92 | if (i == topicGoodsModleDataList.length - 1) { 93 | mTopCard.add(_buildRow(context, sub)); 94 | sub.clear(); 95 | } 96 | } 97 | if ((i + 1) % 3 == 2) { 98 | sub.add(topicGoodsModleDataList[i]); 99 | if (i == topicGoodsModleDataList.length - 1) { 100 | mTopCard.add(_buildRow(context, sub)); 101 | sub.clear(); 102 | } 103 | } 104 | if ((i + 1) % 3 == 0) { 105 | sub.add(topicGoodsModleDataList[i]); 106 | mTopCard.add(_buildRow(context, sub)); 107 | sub.clear(); 108 | } 109 | } 110 | 111 | content = Column( 112 | children: mTopCard, 113 | ); 114 | return content; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /lib/page/root/root_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/services.dart'; 6 | 7 | import 'package:package_info/package_info.dart'; 8 | import 'package:studylinjiashop/common/common.dart'; 9 | import 'package:studylinjiashop/page/cart/cart_page.dart'; 10 | import 'package:studylinjiashop/page/home/home_shop_page.dart'; 11 | import 'package:studylinjiashop/page/member/member_page.dart'; 12 | import 'package:studylinjiashop/page/search/search_page.dart'; 13 | import 'package:studylinjiashop/page/web_page.dart'; 14 | import 'package:studylinjiashop/res/colors.dart'; 15 | import 'package:studylinjiashop/utils/dialog_utils.dart'; 16 | import 'package:studylinjiashop/utils/double_tap_back_exit_app.dart'; 17 | import 'package:studylinjiashop/widget/dialog/update_dialog.dart'; 18 | import 'package:studylinjiashop/widget/root_tabbar.dart'; 19 | 20 | import '../reg_and_login.dart'; 21 | 22 | class RootPage extends StatefulWidget { 23 | @override 24 | _RootPageState createState() => _RootPageState(); 25 | } 26 | 27 | class _RootPageState extends State { 28 | DateTime lastPopTime; 29 | 30 | @override 31 | void initState() { 32 | super.initState(); 33 | checkVersion(); 34 | } 35 | 36 | /// 检查更新 [check update] 37 | checkVersion() async { 38 | // if (Platform.isIOS) return; 39 | // 40 | // final packageInfo = await PackageInfo.fromPlatform(); 41 | // 42 | // VersionModel model = await versionViewModel.getData(); 43 | // 44 | // int currentVersion = int.parse(removeDot(packageInfo.version)); 45 | // 46 | // int netVersion = int.parse(removeDot(model.appVersion)); 47 | // 48 | // if (currentVersion >= netVersion) { 49 | // debugPrint('当前版本是最新版本'); 50 | // return; 51 | // } 52 | // 53 | // showDialog( 54 | // context: context, 55 | // builder: (ctx2) { 56 | // return UpdateDialog( 57 | // version: model.appVersion, 58 | // updateUrl: model.downloadUrl, 59 | // updateInfo: model.updateInfo, 60 | // isForce: model.force, 61 | // ); 62 | // }, 63 | // ); 64 | } 65 | 66 | @override 67 | Widget build(BuildContext context) { 68 | List pages = [ 69 | new TabBarModel( 70 | title: '小铺', 71 | icon: Icon(CupertinoIcons.home), 72 | selectIcon: Icon(CupertinoIcons.home, color: Colours.fixedColor), 73 | page: HomePage(), 74 | ), 75 | new TabBarModel( 76 | title: '发现', 77 | icon: Icon(CupertinoIcons.search), 78 | selectIcon: Icon(CupertinoIcons.search, color: Colours.fixedColor), 79 | page: SearchPage(), 80 | ), 81 | new TabBarModel( 82 | title: '购物车', 83 | icon: Icon(CupertinoIcons.shopping_cart), 84 | selectIcon: Icon(CupertinoIcons.shopping_cart, color: Colours.fixedColor), 85 | page: CartPage(), 86 | ), 87 | new TabBarModel( 88 | title: '我的', 89 | icon: Icon(CupertinoIcons.profile_circled), 90 | selectIcon: Icon(CupertinoIcons.profile_circled, color: Colours.fixedColor), 91 | page: MemberPage(), 92 | ), 93 | ]; 94 | return DoubleTapBackExitApp( 95 | child: RootTabBar(pages: pages, currentIndex: 0), 96 | ); 97 | return new RootTabBar(pages: pages, currentIndex: 0); 98 | } 99 | } 100 | 101 | class LoadImage extends StatelessWidget { 102 | final String img; 103 | final bool isSelect; 104 | 105 | LoadImage(this.img, [this.isSelect = false]); 106 | 107 | @override 108 | Widget build(BuildContext context) { 109 | return new Container( 110 | margin: EdgeInsets.only(bottom: 2.0), 111 | width: 30.0, 112 | height: 30.0, 113 | child: new Image.asset( 114 | img, 115 | fit: BoxFit.cover, 116 | gaplessPlayback: true, 117 | color: isSelect ? Colours.fixedColor : Colours.mainTextColor, 118 | ), 119 | ); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /lib/routes/shop_router.dart: -------------------------------------------------------------------------------- 1 | import 'package:fluro/fluro.dart'; 2 | import 'package:studylinjiashop/page/guide_page.dart'; 3 | import 'package:studylinjiashop/page/home/details/order_details.dart'; 4 | import 'package:studylinjiashop/page/home/details/product_details.dart'; 5 | import 'package:studylinjiashop/page/home/details/topic_details.dart'; 6 | import 'package:studylinjiashop/page/member/modify_name_page.dart'; 7 | import 'package:studylinjiashop/page/member/modify_pwd_page.dart'; 8 | import 'package:studylinjiashop/page/member/setting_page.dart'; 9 | import 'package:studylinjiashop/page/member/shipping_address.dart'; 10 | import 'package:studylinjiashop/page/member/shipping_edit_address.dart'; 11 | import 'package:studylinjiashop/page/member/shipping_save_address.dart'; 12 | import 'package:studylinjiashop/page/order/orderform_page.dart'; 13 | import 'package:studylinjiashop/page/order/pay_page.dart'; 14 | import 'package:studylinjiashop/page/reg_and_login.dart'; 15 | import 'package:studylinjiashop/page/root/root_page.dart'; 16 | import 'package:studylinjiashop/routes/router_init.dart'; 17 | 18 | class ShopRouter implements IRouterProvider { 19 | // details 20 | static const ORDER_DETAILS = '/order_details'; 21 | static const PRODUCT_DETAILS = '/product_details'; 22 | 23 | static const login_page = '/login_page'; 24 | static const root_page = '/root_page'; 25 | static const guide_page = '/guide_page'; 26 | static const order_page = '/order_page'; 27 | static const pay_page = '/pay_page'; 28 | static const address_page = '/address_page'; 29 | static const save_address_page = '/save_address_page'; 30 | static const new_address_page = '/new_address_page'; 31 | static const topic_page = '/topic'; 32 | static const setting_page = '/setting'; 33 | static const modify_name_page = '/modify_name'; 34 | static const modify_pwd_page = '/modify_pwd'; 35 | 36 | @override 37 | void initRouter(Router router) { 38 | router.define(login_page, 39 | handler: 40 | Handler(handlerFunc: (context, params) => RegPageAndLoginPage())); 41 | router.define(root_page, 42 | handler: Handler(handlerFunc: (context, params) => RootPage())); 43 | router.define(guide_page, 44 | handler: Handler(handlerFunc: (context, params) => GuidePage())); 45 | router.define(PRODUCT_DETAILS, 46 | handler: Handler( 47 | handlerFunc: (context, params) => 48 | ProductDetails(id: params['id'].first))); 49 | router.define(ORDER_DETAILS, 50 | handler: Handler( 51 | handlerFunc: (context, params) => 52 | OrderDetails(orderSn: params['orderSn'].first))); 53 | router.define(topic_page, 54 | handler: Handler( 55 | handlerFunc: (context, params) => 56 | TopicDetails(id: params['id'].first))); 57 | router.define(order_page, 58 | handler: Handler(handlerFunc: (context, params) => OrderFormPage())); 59 | router.define(pay_page, 60 | handler: Handler( 61 | handlerFunc: (context, params) => PayPage( 62 | orderSn: params['orderSn'].first, 63 | totalPrice: params['totalPrice'].first))); 64 | 65 | router.define(address_page, 66 | handler: 67 | Handler(handlerFunc: (context, params) => ShippingAddressPage())); 68 | router.define(save_address_page, 69 | handler: Handler( 70 | handlerFunc: (context, params) => 71 | ShippingEditAddressPage(id: params['id'].first))); 72 | 73 | router.define(new_address_page, 74 | handler: Handler( 75 | handlerFunc: (context, params) => ShippingSaveAddressPage())); 76 | router.define(setting_page, 77 | handler: Handler(handlerFunc: (context, params) => SettingPage())); 78 | router.define(modify_name_page, 79 | handler: Handler( 80 | handlerFunc: (context, params) => ModifyNamePage( 81 | name: params['name'].first, 82 | ))); 83 | router.define(modify_pwd_page, 84 | handler: Handler(handlerFunc: (context, params) => ModifyPwdPage())); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/widget/view/my_refresh_list.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:studylinjiashop/res/gaps.dart'; 5 | import 'package:studylinjiashop/res/styles.dart'; 6 | import 'package:studylinjiashop/utils/theme_utils.dart'; 7 | 8 | import 'load_state_layout.dart'; 9 | 10 | 11 | /// 封装下拉刷新与加载更多 12 | class DeerListView extends StatefulWidget { 13 | 14 | const DeerListView({ 15 | Key key, 16 | @required this.itemCount, 17 | @required this.itemBuilder, 18 | @required this.onRefresh, 19 | this.loadMore, 20 | this.hasMore : false, 21 | this.stateType : LoadState.State_Empty, 22 | this.pageSize : 10, 23 | this.padding, 24 | this.itemExtent, 25 | }): super(key: key); 26 | 27 | final RefreshCallback onRefresh; 28 | final LoadMoreCallback loadMore; 29 | final int itemCount; 30 | final bool hasMore; 31 | final IndexedWidgetBuilder itemBuilder; 32 | final LoadState stateType; 33 | /// 一页的数量,默认为10 34 | final int pageSize; 35 | /// padding属性使用时注意会破坏原有的SafeArea,需要自行计算bottom大小 36 | final EdgeInsetsGeometry padding; 37 | final double itemExtent; 38 | 39 | @override 40 | _DeerListViewState createState() => _DeerListViewState(); 41 | } 42 | 43 | typedef RefreshCallback = Future Function(); 44 | typedef LoadMoreCallback = Future Function(); 45 | 46 | class _DeerListViewState extends State { 47 | 48 | /// 是否正在加载数据 49 | bool _isLoading = false; 50 | 51 | @override 52 | Widget build(BuildContext context) { 53 | return SafeArea( 54 | child: NotificationListener( 55 | onNotification: (ScrollNotification note) { 56 | /// 确保是垂直方向滚动,且滑动至底部 57 | if (note.metrics.pixels == note.metrics.maxScrollExtent && note.metrics.axis == Axis.vertical) { 58 | _loadMore(); 59 | } 60 | return true; 61 | }, 62 | child: RefreshIndicator( 63 | onRefresh: widget.onRefresh, 64 | child: widget.itemCount == 0 ? LoadStateLayout(state: widget.stateType) : ListView.builder( 65 | itemCount: widget.loadMore == null ? widget.itemCount : widget.itemCount + 1, 66 | padding: widget.padding, 67 | itemExtent: widget.itemExtent, 68 | itemBuilder: (BuildContext context, int index) { 69 | /// 不需要加载更多则不需要添加FootView 70 | if (widget.loadMore == null) { 71 | return widget.itemBuilder(context, index); 72 | } else { 73 | return index < widget.itemCount ? widget.itemBuilder(context, index) : MoreWidget(widget.itemCount, widget.hasMore, widget.pageSize); 74 | } 75 | } 76 | ) 77 | ), 78 | ), 79 | ); 80 | } 81 | 82 | Future _loadMore() async { 83 | if (widget.loadMore == null) { 84 | return; 85 | } 86 | if (_isLoading) { 87 | return; 88 | } 89 | if (!widget.hasMore) { 90 | return; 91 | } 92 | _isLoading = true; 93 | await widget.loadMore(); 94 | _isLoading = false; 95 | } 96 | 97 | } 98 | 99 | class MoreWidget extends StatelessWidget { 100 | 101 | const MoreWidget(this.itemCount, this.hasMore, this.pageSize); 102 | 103 | final int itemCount; 104 | final bool hasMore; 105 | final int pageSize; 106 | 107 | @override 108 | Widget build(BuildContext context) { 109 | final style = ThemeUtils.isDark(context) ? TextStyles.textGray14 : const TextStyle(color: Color(0x8A000000)); 110 | return Padding( 111 | padding: const EdgeInsets.symmetric(vertical: 10.0), 112 | child: Row( 113 | mainAxisAlignment: MainAxisAlignment.center, 114 | crossAxisAlignment: CrossAxisAlignment.center, 115 | children: [ 116 | hasMore ? const CupertinoActivityIndicator() : Gaps.empty, 117 | hasMore ? Gaps.hGap5 : Gaps.empty, 118 | /// 只有一页的时候,就不显示FooterView了 119 | Text(hasMore ? '正在加载中...' : (itemCount < pageSize ? '' : '没有了呦~'), style: style), 120 | ], 121 | ), 122 | ); 123 | } 124 | } 125 | 126 | -------------------------------------------------------------------------------- /lib/generated/i18n.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | // ignore_for_file: non_constant_identifier_names 7 | // ignore_for_file: camel_case_types 8 | // ignore_for_file: prefer_single_quotes 9 | 10 | // This file is automatically generated. DO NOT EDIT, all your changes would be lost. 11 | class S implements WidgetsLocalizations { 12 | const S(); 13 | 14 | static S current; 15 | 16 | static const GeneratedLocalizationsDelegate delegate = 17 | GeneratedLocalizationsDelegate(); 18 | 19 | static S of(BuildContext context) => Localizations.of(context, S); 20 | 21 | @override 22 | TextDirection get textDirection => TextDirection.ltr; 23 | 24 | } 25 | 26 | class $en extends S { 27 | const $en(); 28 | } 29 | 30 | class GeneratedLocalizationsDelegate extends LocalizationsDelegate { 31 | const GeneratedLocalizationsDelegate(); 32 | 33 | List get supportedLocales { 34 | return const [ 35 | Locale("en", ""), 36 | ]; 37 | } 38 | 39 | LocaleListResolutionCallback listResolution({Locale fallback, bool withCountry = true}) { 40 | return (List locales, Iterable supported) { 41 | if (locales == null || locales.isEmpty) { 42 | return fallback ?? supported.first; 43 | } else { 44 | return _resolve(locales.first, fallback, supported, withCountry); 45 | } 46 | }; 47 | } 48 | 49 | LocaleResolutionCallback resolution({Locale fallback, bool withCountry = true}) { 50 | return (Locale locale, Iterable supported) { 51 | return _resolve(locale, fallback, supported, withCountry); 52 | }; 53 | } 54 | 55 | @override 56 | Future load(Locale locale) { 57 | final String lang = getLang(locale); 58 | if (lang != null) { 59 | switch (lang) { 60 | case "en": 61 | S.current = const $en(); 62 | return SynchronousFuture(S.current); 63 | default: 64 | // NO-OP. 65 | } 66 | } 67 | S.current = const S(); 68 | return SynchronousFuture(S.current); 69 | } 70 | 71 | @override 72 | bool isSupported(Locale locale) => _isSupported(locale, true); 73 | 74 | @override 75 | bool shouldReload(GeneratedLocalizationsDelegate old) => false; 76 | 77 | /// 78 | /// Internal method to resolve a locale from a list of locales. 79 | /// 80 | Locale _resolve(Locale locale, Locale fallback, Iterable supported, bool withCountry) { 81 | if (locale == null || !_isSupported(locale, withCountry)) { 82 | return fallback ?? supported.first; 83 | } 84 | 85 | final Locale languageLocale = Locale(locale.languageCode, ""); 86 | if (supported.contains(locale)) { 87 | return locale; 88 | } else if (supported.contains(languageLocale)) { 89 | return languageLocale; 90 | } else { 91 | final Locale fallbackLocale = fallback ?? supported.first; 92 | return fallbackLocale; 93 | } 94 | } 95 | 96 | /// 97 | /// Returns true if the specified locale is supported, false otherwise. 98 | /// 99 | bool _isSupported(Locale locale, bool withCountry) { 100 | if (locale != null) { 101 | for (Locale supportedLocale in supportedLocales) { 102 | // Language must always match both locales. 103 | if (supportedLocale.languageCode != locale.languageCode) { 104 | continue; 105 | } 106 | 107 | // If country code matches, return this locale. 108 | if (supportedLocale.countryCode == locale.countryCode) { 109 | return true; 110 | } 111 | 112 | // If no country requirement is requested, check if this locale has no country. 113 | if (true != withCountry && (supportedLocale.countryCode == null || supportedLocale.countryCode.isEmpty)) { 114 | return true; 115 | } 116 | } 117 | } 118 | return false; 119 | } 120 | } 121 | 122 | String getLang(Locale l) => l == null 123 | ? null 124 | : l.countryCode != null && l.countryCode.isEmpty 125 | ? l.languageCode 126 | : l.toString(); 127 | -------------------------------------------------------------------------------- /lib/page/cart/cart_bottom.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:studylinjiashop/api/models/cart_goods_query_modle.dart'; 3 | import 'package:studylinjiashop/receiver/event_bus.dart'; 4 | import 'package:studylinjiashop/routes/fluro_navigator.dart'; 5 | import 'package:studylinjiashop/routes/shop_router.dart'; 6 | import 'package:studylinjiashop/utils/app_size.dart'; 7 | 8 | class CartBottom extends StatelessWidget { 9 | 10 | List list; 11 | bool isAllCheck; 12 | CartBottom(this.list,this.isAllCheck); 13 | @override 14 | Widget build(BuildContext context) { 15 | return Container( 16 | margin: EdgeInsets.all(5.0), 17 | height:AppSize.height(140.0) , 18 | color: Colors.white, 19 | width:AppSize.width(1080), 20 | child: Row( 21 | children: [ 22 | selectAllBtn(), 23 | allPriceArea(), 24 | goButton(context) 25 | ], 26 | ) 27 | 28 | ); 29 | 30 | } 31 | 32 | //全选按钮 33 | Widget selectAllBtn(){ 34 | return Container( 35 | child: Row( 36 | children: [ 37 | Checkbox( 38 | value: isAllCheck, 39 | activeColor: Colors.pink, 40 | onChanged: (bool val){ 41 | list.forEach((el){ 42 | el.isCheck= val; 43 | }); 44 | eventBus.fire(new GoodsNumInEvent("All")); 45 | }, 46 | ), 47 | Text('全选') 48 | ], 49 | ), 50 | ); 51 | } 52 | double allPrice=0; 53 | // 合计区域 54 | Widget allPriceArea(){ 55 | 56 | list.forEach((el){ 57 | if(el.isCheck){ 58 | allPrice=allPrice+ el.countNum*(el.price/100); 59 | } 60 | }); 61 | return Container( 62 | alignment: Alignment.center, 63 | child: Column( 64 | children: [ 65 | Row( 66 | children: [ 67 | Container( 68 | alignment: Alignment.centerRight, 69 | width: AppSize.width(220), 70 | height:AppSize.height(140) , 71 | child: Text( 72 | '合计:', 73 | style:TextStyle( 74 | fontSize: AppSize.sp(36) 75 | ) 76 | ), 77 | ), 78 | Container( 79 | alignment: Alignment.centerLeft, 80 | width: AppSize.width(240), 81 | child: Text( 82 | '¥${allPrice.toStringAsFixed(2)}', 83 | style:TextStyle( 84 | fontSize:AppSize.sp(36), 85 | color: Colors.red, 86 | ) 87 | ), 88 | ) 89 | ], 90 | ), 91 | ], 92 | ), 93 | ); 94 | 95 | } 96 | 97 | //结算按钮 98 | Widget goButton(BuildContext context){ 99 | int allGoodsCount=0; 100 | int isAll=0; 101 | list.forEach((el){ 102 | if(el.isCheck){ 103 | isAll++; 104 | allGoodsCount=allGoodsCount+ el.countNum; 105 | 106 | } 107 | }); 108 | if(isAll==list.length){ 109 | eventBus.fire(new GoodsNumInEvent("All")); 110 | } 111 | return Container( 112 | width:AppSize.width(360), 113 | padding: EdgeInsets.only(left: 30), 114 | child:InkWell( 115 | onTap: (){ 116 | Map p={"orderSn":"","totalPrice":allPrice.toStringAsFixed(2)}; 117 | NavigatorUtils.push(context,ShopRouter.pay_page,params: p); 118 | }, 119 | child: Container( 120 | padding: EdgeInsets.all(10.0), 121 | alignment: Alignment.center, 122 | decoration: BoxDecoration( 123 | color: Colors.red, 124 | borderRadius: BorderRadius.circular(3.0) 125 | ), 126 | child: Text( 127 | '结算(${allGoodsCount})', 128 | style: TextStyle( 129 | color: Colors.white 130 | ), 131 | ), 132 | ), 133 | ) , 134 | ); 135 | 136 | 137 | } 138 | 139 | 140 | } -------------------------------------------------------------------------------- /lib/common/shared_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:studylinjiashop/config/keys.dart'; 2 | export 'package:studylinjiashop/config/keys.dart'; 3 | import 'package:studylinjiashop/config/storage_manager.dart'; 4 | 5 | class SharedUtil { 6 | factory SharedUtil() => _getInstance(); 7 | 8 | static SharedUtil get instance => _getInstance(); 9 | static SharedUtil _instance; 10 | 11 | SharedUtil._internal() { 12 | //初始化 13 | //init 14 | } 15 | 16 | static SharedUtil _getInstance() { 17 | if (_instance == null) { 18 | _instance = new SharedUtil._internal(); 19 | } 20 | return _instance; 21 | } 22 | 23 | /// save 24 | Future saveString(String key, String value) async { 25 | if (key == Keys.account) { 26 | await StorageManager.sp.setString(key, value); 27 | return; 28 | } 29 | String account = StorageManager.sp.getString(Keys.account) ?? "default"; 30 | await StorageManager.sp.setString(key + account, value); 31 | } 32 | 33 | Future saveInt(String key, int value) async { 34 | String account = StorageManager.sp.getString(Keys.account) ?? "default"; 35 | await StorageManager.sp.setInt(key + account, value); 36 | } 37 | 38 | Future saveDouble(String key, double value) async { 39 | String account = StorageManager.sp.getString(Keys.account) ?? "default"; 40 | await StorageManager.sp.setDouble(key + account, value); 41 | } 42 | 43 | Future saveBoolean(String key, bool value) async { 44 | String account = StorageManager.sp.getString(Keys.account) ?? "default"; 45 | await StorageManager.sp.setBool(key + account, value); 46 | } 47 | 48 | Future saveStringList(String key, List list) async { 49 | String account = StorageManager.sp.getString(Keys.account) ?? "default"; 50 | await StorageManager.sp.setStringList(key + account, list); 51 | } 52 | 53 | Future readAndSaveList(String key, String data) async { 54 | String account = StorageManager.sp.getString(Keys.account) ?? "default"; 55 | List strings = StorageManager.sp.getStringList(key + account) ?? []; 56 | if (strings.length >= 10) return false; 57 | strings.add(data); 58 | await StorageManager.sp.setStringList(key + account, strings); 59 | return true; 60 | } 61 | 62 | void readAndExchangeList(String key, String data, int index) async { 63 | String account = StorageManager.sp.getString(Keys.account) ?? "default"; 64 | List strings = StorageManager.sp.getStringList(key + account) ?? []; 65 | strings[index] = data; 66 | await StorageManager.sp.setStringList(key + account, strings); 67 | } 68 | 69 | void readAndRemoveList(String key, int index) async { 70 | String account = StorageManager.sp.getString(Keys.account) ?? "default"; 71 | List strings = StorageManager.sp.getStringList(key + account) ?? []; 72 | strings.removeAt(index); 73 | await StorageManager.sp.setStringList(key + account, strings); 74 | } 75 | 76 | /// get 77 | String getString(String key) { 78 | if (key == Keys.account) { 79 | return StorageManager.sp.getString(key); 80 | } 81 | String account = StorageManager.sp.getString(Keys.account) ?? "default"; 82 | return StorageManager.sp.getString(key + account); 83 | } 84 | 85 | int getInt(String key) { 86 | String account = StorageManager.sp.getString(Keys.account) ?? "default"; 87 | return StorageManager.sp.getInt(key + account); 88 | } 89 | 90 | double getDouble(String key) { 91 | String account = StorageManager.sp.getString(Keys.account) ?? "default"; 92 | return StorageManager.sp.getDouble(key + account); 93 | } 94 | 95 | bool getBoolean(String key, {defValue = false}) { 96 | String account = StorageManager.sp.getString(Keys.account) ?? "default"; 97 | return StorageManager.sp.getBool(key + account) ?? defValue; 98 | } 99 | 100 | List getStringList(String key) { 101 | String account = StorageManager.sp.getString(Keys.account) ?? "default"; 102 | return StorageManager.sp.getStringList(key + account); 103 | } 104 | 105 | List readList(String key) { 106 | String account = StorageManager.sp.getString(Keys.account) ?? "default"; 107 | List strings = StorageManager.sp.getStringList(key + account) ?? []; 108 | return strings; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /lib/widget/root_tabbar.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:studylinjiashop/common/common.dart'; 7 | import 'package:studylinjiashop/receiver/event_bus.dart'; 8 | import 'package:studylinjiashop/res/colors.dart'; 9 | import 'package:studylinjiashop/utils/app_size.dart'; 10 | 11 | import 'scroll/my_behavior.dart'; 12 | 13 | class RootTabBar extends StatefulWidget { 14 | RootTabBar({this.pages, this.currentIndex = 0}); 15 | 16 | final List pages; 17 | final int currentIndex; 18 | 19 | @override 20 | State createState() => new RootTabBarState(); 21 | } 22 | 23 | class RootTabBarState extends State { 24 | var pages = new List(); 25 | int currentIndex; 26 | var contents = new List(); 27 | PageController pageController; 28 | 29 | StreamSubscription _indexSubscription; 30 | 31 | void _listen() { 32 | _indexSubscription = eventBus.on().listen((event) { 33 | int index = event.num; 34 | this.currentIndex = index; 35 | pageController.jumpToPage(index); 36 | }); 37 | } 38 | 39 | @override 40 | void initState() { 41 | super.initState(); 42 | currentIndex = widget.currentIndex; 43 | pageController = PageController(initialPage: currentIndex); 44 | for (int i = 0; i < widget.pages.length; i++) { 45 | TabBarModel model = widget.pages[i]; 46 | pages.add( 47 | new BottomNavigationBarItem( 48 | icon: model.icon, 49 | activeIcon: model.selectIcon, 50 | title: new Text(model.title, style: new TextStyle(fontSize: 12.0)), 51 | ), 52 | ); 53 | } 54 | } 55 | 56 | @override 57 | Widget build(BuildContext context) { 58 | AppSize.init(context); 59 | _listen(); 60 | final BottomNavigationBar bottomNavigationBar = new BottomNavigationBar( 61 | items: pages, 62 | type: BottomNavigationBarType.fixed, 63 | currentIndex: currentIndex, 64 | unselectedItemColor: Colours.mainTextColor, 65 | fixedColor: Colours.fixedColor, 66 | onTap: (int index) { 67 | setState(() => currentIndex = index); 68 | // pageController.jumpToPage(currentIndex); 69 | eventBus.fire(TabIndexEvent(index)); 70 | }, 71 | unselectedFontSize: 18.0, 72 | selectedFontSize: 18.0, 73 | elevation: 0, 74 | ); 75 | 76 | String title() { 77 | if (currentIndex == 0) { 78 | return '小铺'; 79 | } else if (currentIndex == 1) { 80 | return '发现'; 81 | } else if (currentIndex == 2) { 82 | return '购物车'; 83 | } else { 84 | return '我的'; 85 | } 86 | } 87 | 88 | return new Scaffold( 89 | bottomNavigationBar: new Theme( 90 | data: new ThemeData( 91 | canvasColor: Colors.grey[50], 92 | highlightColor: Colors.transparent, 93 | splashColor: Colors.transparent, 94 | ), 95 | child: new Container( 96 | decoration: BoxDecoration( 97 | border: Border(top: BorderSide(color: Colours.lineColor, width: 0.2))), 98 | child: bottomNavigationBar, 99 | ), 100 | ), 101 | body: new ScrollConfiguration( 102 | behavior: MyBehavior(), 103 | child: new PageView.builder( 104 | itemBuilder: (BuildContext context, int index) => 105 | widget.pages[index].page, 106 | controller: pageController, 107 | itemCount: pages.length, 108 | physics: Platform.isAndroid 109 | ? new ClampingScrollPhysics() 110 | : new NeverScrollableScrollPhysics(), 111 | onPageChanged: (int index) { 112 | setState(() => currentIndex = index); 113 | }, 114 | ), 115 | ), 116 | ); 117 | } 118 | 119 | @override 120 | void dispose() { 121 | // TODO: implement dispose 122 | super.dispose(); 123 | _indexSubscription.cancel(); 124 | } 125 | } 126 | 127 | class TabBarModel { 128 | const TabBarModel({this.title, this.page, this.icon, this.selectIcon}); 129 | 130 | final String title; 131 | final Widget icon; 132 | final Widget selectIcon; 133 | final Widget page; 134 | } 135 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: studylinjiashop 2 | description: A new Flutter application. 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # In Android, build-name is used as versionName while build-number used as versionCode. 10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 12 | # Read more about iOS versioning at 13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 14 | version: 1.0.0+1 15 | 16 | environment: 17 | sdk: ">=2.1.0 <3.0.0" 18 | 19 | dependencies: 20 | flutter: 21 | sdk: flutter 22 | 23 | # The following adds the Cupertino Icons font to your application. 24 | # Use with the CupertinoIcons class for iOS style icons. 25 | cupertino_icons: ^0.1.2 26 | flutter_swiper: ^1.1.6 27 | 28 | #需要申请权限(IOS) 29 | image_picker: ^0.6.2+3 30 | 31 | path_provider: ^1.5.1 32 | flutter_luban: ^0.1.7 33 | flutter_screenutil: ^0.7.0 34 | flutter_staggered_grid_view: ^0.3.0 35 | fluro: ^1.5.1 36 | flutter_statusbarcolor: ^0.2.3 37 | dio: ^3.0.9 38 | flutter_easyrefresh: ^2.0.8 39 | flutter_html: ^0.11.1 40 | fluttertoast: ^3.0.4 41 | shared_preferences: ^0.5.6 42 | event_bus: ^1.0.1 43 | flutter_slidable: ^0.5.4 44 | url_launcher: ^5.1.2 45 | city_pickers: ^0.1.29 46 | webview_flutter: ^0.3.18+1 47 | cached_network_image: 2.0.0 # 2.0.0左右flutter版本直接用这个 48 | intl: 0.16.1 49 | open_file: 2.1.1 50 | connectivity: 0.4.6 51 | device_info: 0.4.0+1 52 | package_info: 0.4.0+6 53 | rxdart: ^0.22.5 54 | sprintf: ^4.0.2 55 | provider: ^4.0.4 56 | 57 | dev_dependencies: 58 | flutter_test: 59 | sdk: flutter 60 | 61 | 62 | # For information on the generic Dart part of this file, see t he 63 | # following page: https://dart.dev/tools/pub/pubspec 64 | 65 | # The following section is specific to Flutter. 66 | flutter: 67 | 68 | # The following line ensures that the Material Icons font is 69 | # included with your application, so that you can use the icons in 70 | # the material Icons class. 71 | uses-material-design: true 72 | assets: 73 | - assets/images/ 74 | fonts: 75 | - family: MyIcons 76 | fonts: 77 | - asset: assets/fonts/MyFlutterApp.ttf 78 | - family: MyIcons2 79 | fonts: 80 | - asset: assets/fonts/iconfont2.ttf 81 | - family: MyIcons3 82 | fonts: 83 | - asset: assets/fonts/iconfont3.ttf 84 | - family: IconFont 85 | fonts: 86 | - asset: assets/fonts/iconfont.ttf 87 | - family: MyIcons4 88 | fonts: 89 | - asset: assets/fonts/iconpay.ttf 90 | # To add assets to your application, add an assets section, like this: 91 | # assets: 92 | # - images/a_dot_burr.jpeg 93 | # - images/a_dot_ham.jpeg 94 | 95 | # An image asset can refer to one or more resolution-specific "variants", see 96 | # https://flutter.dev/assets-and-images/#resolution-aware. 97 | 98 | # For details regarding adding assets from package dependencies, see 99 | # https://flutter.dev/assets-and-images/#from-packages 100 | 101 | # To add custom fonts to your application, add a fonts section here, 102 | # in this "flutter" section. Each entry in this list should have a 103 | # "family" key with the font family name, and a "fonts" key with a 104 | # list giving the asset and other descriptors for the font. For 105 | # example: 106 | # fonts: 107 | # - family: Schyler 108 | # fonts: 109 | # - asset: fonts/Schyler-Regular.ttf 110 | # - asset: fonts/Schyler-Italic.ttf 111 | # style: italic 112 | # - family: Trajan Pro 113 | # fonts: 114 | # - asset: fonts/TrajanPro.ttf 115 | # - asset: fonts/TrajanPro_Bold.ttf 116 | # weight: 700 117 | # 118 | # For details regarding fonts from package dependencies, 119 | # see https://flutter.dev/custom-fonts/#from-packages 120 | -------------------------------------------------------------------------------- /lib/http/req_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:studylinjiashop/common/api_exception.dart'; 6 | import 'package:studylinjiashop/common/common.dart'; 7 | import 'package:studylinjiashop/config/api.dart'; 8 | import 'package:studylinjiashop/config/user_info_data.dart'; 9 | import 'package:studylinjiashop/http/base_response.dart'; 10 | import 'package:studylinjiashop/http/mvvms.dart'; 11 | import 'package:studylinjiashop/page/reg_and_login.dart'; 12 | import 'package:studylinjiashop/routes/application.dart'; 13 | import 'package:studylinjiashop/utils/dialog_utils.dart'; 14 | 15 | import 'dio_utils.dart'; 16 | 17 | // 请求计数 18 | var _id = 0; 19 | 20 | /* 21 | * 请求类型枚举 22 | * */ 23 | enum RequestType { GET, POST } 24 | 25 | class ReqModel { 26 | // 请求url路径 27 | String url() => null; 28 | 29 | // 请求参数 30 | Map params() => {}; 31 | 32 | String encodeData() => null; 33 | 34 | IMvvmView view; 35 | CancelToken cancelToken; 36 | 37 | /* 38 | * get请求 39 | * */ 40 | Future get() async { 41 | return this._request( 42 | url: url(), 43 | method: RequestType.GET, 44 | params: params(), 45 | ); 46 | } 47 | 48 | /* 49 | * post请求 50 | * */ 51 | Future post() async { 52 | return this 53 | ._request(url: url(), method: RequestType.POST, params: params()); 54 | } 55 | 56 | /* 57 | * post请求-文件上传方式 58 | * */ 59 | Future postUpload( 60 | ProgressCallback progressCallBack, { 61 | FormData formData, 62 | }) async { 63 | return this._request( 64 | url: url(), 65 | method: RequestType.POST, 66 | formData: formData, 67 | progressCallBack: progressCallBack, 68 | params: params(), 69 | ); 70 | } 71 | 72 | /* 73 | * 请求方法 74 | * */ 75 | Future _request( 76 | {String url, 77 | RequestType method, 78 | Map params, 79 | FormData formData, 80 | ProgressCallback progressCallBack}) async { 81 | final httpUrl = '${API.reqUrl}$url'; 82 | print('HTTP_REQUEST_URL::$httpUrl'); 83 | 84 | final id = _id++; 85 | int statusCode; 86 | try { 87 | Response response; 88 | view?.showProgress(); 89 | if (method == RequestType.GET) { 90 | ///组合GET请求的参数 91 | if (mapNoEmpty(params)) { 92 | response = await DioUtils.instance 93 | .getDio() 94 | .get(url, queryParameters: params, cancelToken: cancelToken); 95 | } else { 96 | response = await DioUtils.instance 97 | .getDio() 98 | .get(url, cancelToken: cancelToken); 99 | } 100 | } else { 101 | if (mapNoEmpty(params)) { 102 | response = await DioUtils.instance.getDio().post(url, 103 | queryParameters: params, 104 | data: formData, 105 | onSendProgress: progressCallBack, 106 | cancelToken: cancelToken); 107 | } else { 108 | response = await DioUtils.instance 109 | .getDio() 110 | .post(url, data: encodeData(), cancelToken: cancelToken); 111 | } 112 | } 113 | statusCode = response.statusCode; 114 | if (response != null && statusCode == 200) { 115 | if (mapNoEmpty(params)) print('HTTP_REQUEST_BODY::[$id]::$params'); 116 | print('HTTP_RESPONSE_BODY::[$id]::${json.encode(response.data)}'); 117 | BaseResponse baseResponse = BaseResponse.fromJson(response.data); 118 | if (baseResponse.code == 0) { 119 | view?.closeProgress(); 120 | return baseResponse.data; 121 | } else { 122 | handError(baseResponse.code, msg: baseResponse.message); 123 | } 124 | } 125 | 126 | ///处理错误部分 127 | if (statusCode < 0) { 128 | handError(statusCode); 129 | } 130 | } catch (e) { 131 | handError(statusCode, msg: "$e"); 132 | } 133 | } 134 | 135 | ///处理异常 136 | handError(int statusCode, {msg}) { 137 | DialogUtil.buildToast(msg ?? "$statusCode"); 138 | view?.closeProgress(); 139 | if (statusCode == -101) { 140 | Application.navKey.currentState.push( 141 | MaterialPageRoute(builder: (context) => RegPageAndLoginPage()), 142 | ); 143 | UserInfoData.instance.setToken(""); 144 | DialogUtil.buildToast("请求失败~"); 145 | } 146 | throw ApiException(statusCode, msg); 147 | } 148 | } 149 | --------------------------------------------------------------------------------