├── .gitignore ├── .metadata ├── LICENSE ├── README.md ├── android ├── .project ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── flutter_luckin_coffee_copy │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── ios ├── Flutter │ ├── .last_build_id │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ ├── Flutter.podspec │ ├── Release.xcconfig │ └── flutter_export_environment.sh ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── assets │ └── images │ │ ├── QRCode_258.png │ │ ├── home │ │ ├── bottom_bar.png │ │ ├── swiper1.jpg │ │ ├── swiper2.jpg │ │ └── swiper3.jpg │ │ ├── logo.png │ │ ├── logo1.png │ │ ├── map.png │ │ ├── menu │ │ ├── author1.jpg │ │ ├── dialog1.jpg │ │ ├── goods.png │ │ ├── goods1.png │ │ ├── goods2.png │ │ ├── goods3.png │ │ ├── swiper1.jpg │ │ └── swiper2.png │ │ ├── mine │ │ ├── mine1.png │ │ └── mine2.jpg │ │ ├── not_data.png │ │ ├── order │ │ ├── order1.png │ │ └── order2.png │ │ └── shopping_cart_null.png ├── components │ ├── a_button │ │ ├── custom_button.dart │ │ └── index.dart │ ├── a_row │ │ └── a_row.dart │ ├── a_stepper │ │ └── a_stepper.dart │ ├── custom_swiper │ │ └── index.dart │ ├── goods_detail │ │ ├── index.dart │ │ └── select_row.dart │ └── take_out_btn │ │ └── index.dart ├── get_pages.dart ├── main.dart ├── mock │ ├── goods_category.dart │ └── goods_list.dart ├── pages │ ├── coupon │ │ ├── index.dart │ │ └── widgets │ │ │ └── coupon_row.dart │ ├── diningcode │ │ └── dining_code.dart │ ├── login │ │ ├── login_mail.dart │ │ ├── login_method.dart │ │ └── user_agreement.dart │ ├── order │ │ ├── order_confirm.dart │ │ ├── order_detail.dart │ │ ├── order_evaluation.dart │ │ ├── order_remark.dart │ │ └── widgets │ │ │ └── goods_msg_row.dart │ ├── selfstore │ │ └── index.dart │ ├── storedetail │ │ └── index.dart │ └── toolbar │ │ ├── home │ │ └── index.dart │ │ ├── index.dart │ │ ├── menu │ │ ├── category.dart │ │ ├── goods_list_row.dart │ │ ├── index.dart │ │ └── widgets │ │ │ ├── classify_desc.dart │ │ │ ├── dacai_say.dart │ │ │ └── radius_btn.dart │ │ ├── mine │ │ └── index.dart │ │ ├── order │ │ ├── index.dart │ │ └── widgets │ │ │ └── order_list_row.dart │ │ └── shopping_cart │ │ ├── index.dart │ │ └── widgets │ │ ├── recommend_goods.dart │ │ └── shopping_cart_row.dart └── utils │ ├── custom_appbar.dart │ ├── global.dart │ ├── loading.dart │ └── pull_to_refresh_style.dart ├── pubspec.lock ├── pubspec.yaml ├── qrcode.png ├── readme ├── backlog │ ├── v1.x.x.md │ └── v2.x.x.md └── images │ ├── 1.gif │ ├── 2.gif │ └── githead1.png ├── upload_android.sh └── upload_ios.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | .vscode/ 12 | build_android.sh 13 | build_ios.sh 14 | app/ 15 | 16 | # IntelliJ related 17 | *.iml 18 | *.ipr 19 | *.iws 20 | .idea/ 21 | 22 | # The .vscode folder contains launch configuration and tasks you configure in 23 | # VS Code which you may wish to be included in version control, so this line 24 | # is commented out by default. 25 | #.vscode/ 26 | 27 | # Flutter/Dart/Pub related 28 | **/doc/api/ 29 | .dart_tool/ 30 | .flutter-plugins 31 | .packages 32 | .pub-cache/ 33 | .pub/ 34 | /build/ 35 | 36 | # Android related 37 | **/android/**/gradle-wrapper.jar 38 | **/android/.gradle 39 | **/android/captures/ 40 | **/android/gradlew 41 | **/android/gradlew.bat 42 | **/android/local.properties 43 | **/android/**/GeneratedPluginRegistrant.java 44 | 45 | # iOS/XCode related 46 | **/ios/**/*.mode1v3 47 | **/ios/**/*.mode2v3 48 | **/ios/**/*.moved-aside 49 | **/ios/**/*.pbxuser 50 | **/ios/**/*.perspectivev3 51 | **/ios/**/*sync/ 52 | **/ios/**/.sconsign.dblite 53 | **/ios/**/.tags* 54 | **/ios/**/.vagrant/ 55 | **/ios/**/DerivedData/ 56 | **/ios/**/Icon? 57 | **/ios/**/Pods/ 58 | **/ios/**/.symlinks/ 59 | **/ios/**/profile 60 | **/ios/**/xcuserdata 61 | **/ios/.generated/ 62 | **/ios/Flutter/App.framework 63 | **/ios/Flutter/Flutter.framework 64 | **/ios/Flutter/Generated.xcconfig 65 | **/ios/Flutter/app.flx 66 | **/ios/Flutter/app.zip 67 | **/ios/Flutter/flutter_assets/ 68 | **/ios/Flutter/flutter_export_environment.sh 69 | **/ios/ServiceDefinitions.json 70 | **/ios/Runner/GeneratedPluginRegistrant.* 71 | 72 | # Exceptions to above rules. 73 | !**/ios/**/default.mode1v3 74 | !**/ios/**/default.mode2v3 75 | !**/ios/**/default.pbxuser 76 | !**/ios/**/default.perspectivev3 77 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 78 | 79 | .fvm -------------------------------------------------------------------------------- /.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: 68587a0916366e9512a78df22c44163d041dd5f3 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Academic Free License (“AFL”) v. 3.0 2 | 3 | This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: 4 | 5 | Licensed under the Academic Free License version 3.0 6 | 7 | 1) Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: 8 | 9 | a) to reproduce the Original Work in copies, either alone or as part of a collective work; 10 | b) to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; 11 | c) to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor’s reserved rights and remedies, in this Academic Free License; 12 | d) to perform the Original Work publicly; and 13 | e) to display the Original Work publicly. 14 | 15 | 2) Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. 16 | 17 | 3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. 18 | 19 | 4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor’s trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. 20 | 21 | 5) External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). 22 | 23 | 6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. 24 | 25 | 7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. 26 | 27 | 8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. 28 | 29 | 9) Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including “fair use” or “fair dealing”). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). 30 | 31 | 10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. 32 | 33 | 11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. 34 | 35 | 12) Attorneys’ Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. 36 | 37 | 13) Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. 38 | 39 | 14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 40 | 41 | 15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. 42 | 43 | 16) Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![app_head](./readme/images/githead1.png) 2 | 3 | # flutter_luckin_coffee 2.0 4 | 5 | > flutter luckin coffee application(仿瑞幸咖啡) 6 | 7 | 老版本使用 `api工厂` 的分支 [v1.0](https://gitee.com/meetqy/flutter_luckin_coffee/tree/v1.0/),如果有之前克隆过数据的同学,可以评论区留言或私信我**你的 ID** 8 | 9 | ## Flutter Version 10 | 11 | ``` 12 | Flutter 2.10.5 • channel stable • https://github.com/flutter/flutter.git 13 | Framework • revision 5464c5bac7 (8 weeks ago) • 2022-04-18 09:55:37 -0700 14 | Engine • revision 57d3bac3dd 15 | Tools • Dart 2.16.2 • DevTools 2.9.2 16 | ``` 17 | 18 | ## Wiki 19 | 20 | 总结,升级 2.0 过程中,遇到问题解决的思路,以及最终使用的方法,希望对你有帮助!!! 21 | 22 | [升级 flutter 2.0 总结](https://gitee.com/meetqy/flutter_luckin_coffee/wikis/%E5%8D%87%E7%BA%A72.0%E9%97%AE%E9%A2%98%E6%80%BB%E7%BB%93?sort_id=1729161) 23 | 24 | ## 升级思路 25 | 26 | 1. jsonserialize 中之前保留了一份 mockdata.json,使用直接读取 json 的方式代替 dio 请求 27 | 2. 移除 dio 相关代码 28 | 3. 移除处理请求,数据转换的特殊逻辑 29 | 4. mock.js 生成随机数据去替代 mockdata.json 30 | 5. `路由方面`的管理和 `provide` 改为`getx` 31 | 6. 尽量用原生的方式去替代 a_button, a_checkbox...类似 weight 32 | 33 | > 这里不得不说一下,为啥最近又要维护这个项目了,没办法呀公司又开了个 flutter 项目,开发也接近尾声了,有了新的理解,在这个项目上实践一下。 34 | 35 | ## 进度 36 | 37 | - [ ] 本地 mockdata.json 数据替换`api工厂` 38 | - [x] 简化 mockdata 中的数据 39 | - [x] 去掉一些跟 api 工厂强关联的逻辑 40 | - [ ] 升级 flutter 2.10.x 41 | - [ ] 升级插件 42 | - [ ] 移除多余组件,尽量使用原生组件 43 | 44 | ## 最后大致的方向 45 | 46 | 项目可能更加会趋向于一个**偏向前端的纯模板**,大家有需要的页面可以直接拷进自己的项目,不用修改很多不必要的逻辑。 47 | 48 | 类似于这样的: 49 | 50 | - [flutter 交友模板](https://github.com/meetqy/flutter_dating_template) 51 | -------------------------------------------------------------------------------- /android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | android 4 | Project android created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | -------------------------------------------------------------------------------- /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 | android { 29 | compileSdkVersion 28 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | checkReleaseBuilds false 38 | } 39 | 40 | defaultConfig { 41 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 42 | applicationId "com.flutter_luckin_coffee" 43 | minSdkVersion 16 44 | targetSdkVersion 28 45 | versionCode flutterVersionCode.toInteger() 46 | versionName flutterVersionName 47 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 48 | } 49 | 50 | buildTypes { 51 | release { 52 | // TODO: Add your own signing config for the release build. 53 | // Signing with the debug keys for now, so `flutter run --release` works. 54 | signingConfig signingConfigs.debug 55 | } 56 | } 57 | } 58 | 59 | flutter { 60 | source '../..' 61 | } 62 | 63 | dependencies { 64 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 65 | testImplementation 'junit:junit:4.12' 66 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 67 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 68 | } 69 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 12 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_luckin_coffee_copy/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.flutter_luckin_coffee 2 | 3 | import io.flutter.embedding.android.FlutterActivity; 4 | 5 | class MainActivity : FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.0.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 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip 7 | -------------------------------------------------------------------------------- /android/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 | -------------------------------------------------------------------------------- /ios/Flutter/.last_build_id: -------------------------------------------------------------------------------- 1 | b3fb6367504560810f44cba78a9eb4a2 -------------------------------------------------------------------------------- /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 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Flutter.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # NOTE: This podspec is NOT to be published. It is only used as a local source! 3 | # This is a generated file; do not edit or check into version control. 4 | # 5 | 6 | Pod::Spec.new do |s| 7 | s.name = 'Flutter' 8 | s.version = '1.0.0' 9 | s.summary = 'High-performance, high-fidelity mobile apps.' 10 | s.homepage = 'https://flutter.io' 11 | s.license = { :type => 'MIT' } 12 | s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } 13 | s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } 14 | s.ios.deployment_target = '9.0' 15 | # Framework linking is handled by Flutter tooling, not CocoaPods. 16 | # Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs. 17 | s.vendored_frameworks = 'path/to/nothing' 18 | end 19 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/flutter_export_environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a generated file; do not edit or check into version control. 3 | export "FLUTTER_ROOT=/Users/meetqy/fvm/versions/2.10.5" 4 | export "FLUTTER_APPLICATION_PATH=/Users/meetqy/Desktop/my-template/flutter_luckin_coffee" 5 | export "COCOAPODS_PARALLEL_CODE_SIGN=true" 6 | export "FLUTTER_TARGET=/Users/meetqy/Desktop/my-template/flutter_luckin_coffee/lib/main.dart" 7 | export "FLUTTER_BUILD_DIR=build" 8 | export "FLUTTER_BUILD_NAME=1.0.0" 9 | export "FLUTTER_BUILD_NUMBER=1" 10 | export "DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==" 11 | export "DART_OBFUSCATION=false" 12 | export "TRACK_WIDGET_CREATION=true" 13 | export "TREE_SHAKE_ICONS=false" 14 | export "PACKAGE_CONFIG=/Users/meetqy/Desktop/my-template/flutter_luckin_coffee/.dart_tool/package_config.json" 15 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | 4 | DEPENDENCIES: 5 | - Flutter (from `Flutter`) 6 | 7 | EXTERNAL SOURCES: 8 | Flutter: 9 | :path: Flutter 10 | 11 | SPEC CHECKSUMS: 12 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a 13 | 14 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c 15 | 16 | COCOAPODS: 1.11.3 17 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/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 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/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/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/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/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/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/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/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/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/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/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/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/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/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/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/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/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/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/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/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/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/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/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/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/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | luckin_coffee 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | flutter_luckin_coffee 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /lib/assets/images/QRCode_258.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/QRCode_258.png -------------------------------------------------------------------------------- /lib/assets/images/home/bottom_bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/home/bottom_bar.png -------------------------------------------------------------------------------- /lib/assets/images/home/swiper1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/home/swiper1.jpg -------------------------------------------------------------------------------- /lib/assets/images/home/swiper2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/home/swiper2.jpg -------------------------------------------------------------------------------- /lib/assets/images/home/swiper3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/home/swiper3.jpg -------------------------------------------------------------------------------- /lib/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/logo.png -------------------------------------------------------------------------------- /lib/assets/images/logo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/logo1.png -------------------------------------------------------------------------------- /lib/assets/images/map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/map.png -------------------------------------------------------------------------------- /lib/assets/images/menu/author1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/menu/author1.jpg -------------------------------------------------------------------------------- /lib/assets/images/menu/dialog1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/menu/dialog1.jpg -------------------------------------------------------------------------------- /lib/assets/images/menu/goods.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/menu/goods.png -------------------------------------------------------------------------------- /lib/assets/images/menu/goods1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/menu/goods1.png -------------------------------------------------------------------------------- /lib/assets/images/menu/goods2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/menu/goods2.png -------------------------------------------------------------------------------- /lib/assets/images/menu/goods3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/menu/goods3.png -------------------------------------------------------------------------------- /lib/assets/images/menu/swiper1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/menu/swiper1.jpg -------------------------------------------------------------------------------- /lib/assets/images/menu/swiper2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/menu/swiper2.png -------------------------------------------------------------------------------- /lib/assets/images/mine/mine1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/mine/mine1.png -------------------------------------------------------------------------------- /lib/assets/images/mine/mine2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/mine/mine2.jpg -------------------------------------------------------------------------------- /lib/assets/images/not_data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/not_data.png -------------------------------------------------------------------------------- /lib/assets/images/order/order1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/order/order1.png -------------------------------------------------------------------------------- /lib/assets/images/order/order2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/order/order2.png -------------------------------------------------------------------------------- /lib/assets/images/shopping_cart_null.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/lib/assets/images/shopping_cart_null.png -------------------------------------------------------------------------------- /lib/components/a_button/custom_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class CustomButton { 4 | static Map _buttonTypeConfig = { 5 | "warning": { 6 | "color": Color(0xfff), 7 | "bgColor": Color(0xffff976a), 8 | "borderColor": Color(0xffff976a), 9 | }, 10 | "danger": { 11 | "color": Color(0xfffff), 12 | "bgColor": Color(0xfff44), 13 | "borderColor": Color(0xfff44), 14 | }, 15 | "info": { 16 | "color": Color(0xfffff), 17 | "bgColor": Color(0xff1989fa), 18 | "borderColor": Color(0xff1989fa), 19 | }, 20 | "primary": { 21 | "color": Color(0xfffff), 22 | "bgColor": Color(0xff07c160), 23 | "borderColor": Color(0xff07c160), 24 | }, 25 | "default": { 26 | "color": Color(0xff323233), 27 | "bgColor": Color(0xfffff), 28 | "borderColor": Color(0xffebedf0), 29 | }, 30 | }; 31 | 32 | Widget widget; 33 | 34 | static Color _bgColor; 35 | static Color _color; 36 | static Color _borderColor; 37 | 38 | final String type; 39 | final Color color; 40 | final Color bgColor; 41 | final Color borderColor; 42 | final double width; 43 | final double height; 44 | final bool plain; 45 | final VoidCallback onPressed; 46 | final EdgeInsetsGeometry padding; 47 | final BorderRadius borderRadius; 48 | 49 | CustomButton.normal( 50 | {this.width, 51 | this.height, 52 | this.type, 53 | this.color, 54 | this.bgColor, 55 | this.borderColor, 56 | this.plain, 57 | this.onPressed, 58 | this.padding, 59 | this.borderRadius, 60 | Widget child}) { 61 | _setColor(); 62 | 63 | widget = _init(child); 64 | } 65 | 66 | CustomButton.icon({ 67 | this.width, 68 | this.height, 69 | this.type, 70 | this.color, 71 | this.bgColor, 72 | this.borderColor, 73 | this.plain, 74 | this.onPressed, 75 | this.padding, 76 | this.borderRadius, 77 | Widget textChild, 78 | Widget icon, 79 | }) { 80 | _setColor(); 81 | 82 | widget = _initIcon(textChild, icon); 83 | } 84 | 85 | CustomButton.loading({ 86 | this.width, 87 | this.height, 88 | this.type, 89 | this.color, 90 | this.bgColor, 91 | this.borderColor, 92 | this.plain, 93 | this.onPressed, 94 | this.padding, 95 | this.borderRadius, 96 | Widget textChild, 97 | Widget loadingChild, 98 | }) { 99 | _setColor(); 100 | 101 | Widget defaultLoading = Transform.scale( 102 | scale: 0.7, 103 | child: CircularProgressIndicator( 104 | strokeWidth: 2, 105 | backgroundColor: Colors.transparent, 106 | valueColor: AlwaysStoppedAnimation(_color), 107 | ), 108 | ); 109 | 110 | widget = _initIcon(textChild, defaultLoading); 111 | } 112 | 113 | _init(Widget child) { 114 | return Container( 115 | width: width, 116 | height: height, 117 | child: FlatButton( 118 | padding: padding == null ? EdgeInsets.all(0) : padding, 119 | shape: RoundedRectangleBorder( 120 | borderRadius: 121 | borderRadius == null ? BorderRadius.circular(4) : borderRadius, 122 | side: BorderSide( 123 | width: 1, color: !plain ? Colors.transparent : _borderColor)), 124 | materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, 125 | child: child, 126 | textColor: _color, 127 | color: _bgColor, 128 | splashColor: onPressed == null ? Colors.transparent : null, 129 | highlightColor: onPressed == null ? Colors.transparent : null, 130 | disabledColor: _bgColor, 131 | onPressed: onPressed == null ? () {} : onPressed, 132 | ), 133 | ); 134 | } 135 | 136 | _initIcon(Widget textChild, Widget icon) { 137 | Widget iconChild = Row( 138 | mainAxisAlignment: MainAxisAlignment.center, 139 | crossAxisAlignment: CrossAxisAlignment.center, 140 | children: [ 141 | Container( 142 | child: icon, 143 | ), 144 | Container( 145 | margin: EdgeInsets.only(left: textChild == null ? 0 : 5), 146 | child: textChild, 147 | ) 148 | ], 149 | ); 150 | 151 | return _init(iconChild); 152 | } 153 | 154 | _getType() { 155 | var buttonColor = _buttonTypeConfig["$type"]; 156 | 157 | // type不符合要求,设置为defalut 158 | if (buttonColor == null) buttonColor = _buttonTypeConfig['default']; 159 | 160 | return buttonColor; 161 | } 162 | 163 | /// 设置color 164 | /// disabled状态 color透明度设置为0.5 165 | _setColor() { 166 | Map buttonColor = _getType(); 167 | 168 | // color 传入的 169 | // buttonColor['xxx'] 默认的 170 | // $color 最终结果 171 | Color $color = color ?? buttonColor['color']; 172 | Color $bgColor = bgColor ?? buttonColor['bgColor']; 173 | Color $borderColor = borderColor ?? buttonColor['borderColor']; 174 | 175 | // 通过plain属性将按钮设置为朴素按钮,朴素按钮的文字为按钮颜色 176 | // 默认背景颜色为白色 177 | if (plain) { 178 | // 如果调用者传入的color为空,文字颜色 = 背景颜色 179 | // 默认背景颜色为白色 180 | if (color == null) { 181 | _color = onPressed == null ? $bgColor.withOpacity(.5) : $bgColor; 182 | } else { 183 | _color = onPressed == null ? $color.withOpacity(.5) : $color; 184 | } 185 | 186 | // 如果调用者传入的borderColor为空,边框颜色 = 背景颜色 187 | // 默认背景颜色为白色 188 | if (borderColor == null) { 189 | _borderColor = onPressed == null ? $bgColor.withOpacity(.5) : $bgColor; 190 | } else { 191 | _borderColor = 192 | onPressed == null ? $borderColor.withOpacity(.5) : $borderColor; 193 | } 194 | 195 | _bgColor = bgColor ?? Color(0xfffff); 196 | } else { 197 | _color = onPressed == null ? $color.withOpacity(.5) : $color; 198 | _bgColor = onPressed == null ? $bgColor.withOpacity(.5) : $bgColor; 199 | _borderColor = 200 | onPressed == null ? $borderColor.withOpacity(.5) : $borderColor; 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /lib/components/a_button/index.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: meetqy 3 | * @since: 2019-09-02 10:52:36 4 | * @lastTime: 2019-11-18 17:29:02 5 | * @LastEditors: meetqy 6 | */ 7 | import 'package:flutter/material.dart'; 8 | 9 | import 'custom_button.dart'; 10 | 11 | 12 | /// 暴露button 相当于工厂函数 13 | class AButton { 14 | /// 按钮 15 | /// 16 | /// ``` 17 | /// @param {double} width 宽度 18 | /// @param {double} height 高度 19 | /// @param {String} type 按钮类型:default,primary,info,danger,warning 20 | /// @param {Color} color 文字颜色 21 | /// @param {Color} bgColor 背景颜色 22 | /// @param {Color} borderColor 边框颜色 23 | /// @param {bool} plain 是否使用边框样式 24 | /// @param {VoidCallback} onPressed 点击回调 如果没有该参数表示不可点击状态 25 | /// @param {Widget} child 按钮内容 26 | /// @param {EdgeInsetsGeometry} padding 内边距 27 | /// @param {BorderRadius} borderRadius 圆角 28 | /// ``` 29 | static Widget normal({ 30 | double width, 31 | double height = 44, 32 | String type = 'default', 33 | Color color, 34 | Color bgColor, 35 | Color borderColor, 36 | bool plain = false, 37 | VoidCallback onPressed, 38 | Widget child, 39 | EdgeInsetsGeometry padding, 40 | BorderRadius borderRadius 41 | }) { 42 | return CustomButton.normal( 43 | width: width, 44 | height: height, 45 | type: type, 46 | color: color, 47 | bgColor: bgColor, 48 | borderColor: borderColor, 49 | plain: plain, 50 | onPressed: onPressed, 51 | child: child, 52 | padding: padding, 53 | borderRadius: borderRadius 54 | ).widget; 55 | } 56 | 57 | /// 按钮 58 | /// 59 | /// ``` 60 | /// @param {double} width 宽度 61 | /// @param {double} height 高度 62 | /// @param {String} type 按钮类型:default,primary,info,danger,warning 63 | /// @param {Color} color 文字颜色 64 | /// @param {Color} bgColor 背景颜色 65 | /// @param {Color} borderColor 边框颜色 66 | /// @param {bool} plain 是否使用边框样式 67 | /// @param {VoidCallback} onPressed 点击回调 如果没有该参数表示不可点击状态 68 | /// @param {Widget} textChild 按钮内容 69 | /// @param {Widget} icon icon 70 | /// @param {EdgeInsetsGeometry} padding 内边距 71 | /// @param {BorderRadius} borderRadius 圆角 72 | /// ``` 73 | static Widget icon({ 74 | double width, 75 | double height = 44, 76 | String type = 'default', 77 | Color color, 78 | Color bgColor, 79 | Color borderColor, 80 | bool plain = false, 81 | VoidCallback onPressed, 82 | Widget textChild, 83 | Widget icon, 84 | EdgeInsetsGeometry padding, 85 | BorderRadius borderRadius 86 | }) { 87 | return CustomButton.icon( 88 | width: width, 89 | height: height, 90 | type: type, 91 | color: color, 92 | bgColor: bgColor, 93 | borderColor: borderColor, 94 | plain: plain, 95 | onPressed: onPressed, 96 | textChild: textChild, 97 | icon: icon, 98 | padding: padding, 99 | borderRadius: borderRadius 100 | ).widget; 101 | } 102 | 103 | /// loading 按钮 104 | /// 105 | /// ``` 106 | /// @param {double} width 宽度 107 | /// @param {double} height 高度 108 | /// @param {String} type 按钮类型:default,primary,info,danger,warning 109 | /// @param {Color} color 加载动画颜色颜色 110 | /// @param {Color} bgColor 背景颜色 111 | /// @param {Color} borderColor 边框颜色 112 | /// @param {bool} plain 是否使用边框样式 113 | /// @param {VoidCallback} onPressed 点击回调 如果没有该参数表示不可点击状态 114 | /// @param {Widget} loadingChild 按钮内容 115 | /// @param {EdgeInsetsGeometry} padding 内边距 116 | /// @param {BorderRadius} borderRadius 圆角 117 | /// ``` 118 | static Widget loading({ 119 | double width, 120 | double height = 44, 121 | String type = 'default', 122 | Color color, 123 | Color bgColor, 124 | Color borderColor, 125 | bool plain = false, 126 | VoidCallback onPressed, 127 | EdgeInsetsGeometry padding, 128 | BorderRadius borderRadius, 129 | Widget loadingChild, 130 | }) { 131 | return CustomButton.loading( 132 | width: width, 133 | height: height, 134 | type: type, 135 | color: color, 136 | bgColor: bgColor, 137 | borderColor: borderColor, 138 | plain: plain, 139 | onPressed: onPressed, 140 | padding: padding, 141 | borderRadius: borderRadius, 142 | loadingChild: loadingChild, 143 | ).widget; 144 | } 145 | } -------------------------------------------------------------------------------- /lib/components/a_row/a_row.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ARow extends StatelessWidget { 4 | final double height; 5 | final Widget leftChild; 6 | final Widget centerChild; 7 | final Widget rightChild; 8 | final EdgeInsets padding; 9 | final EdgeInsets margin; 10 | final Border border; 11 | final Color color; 12 | final Function onPressed; 13 | 14 | /// ARow 行 15 | /// 16 | /// ``` 17 | /// @param {double} height 高度 18 | /// @param {Widget} leftChild 左侧内容 19 | /// @param {Widget} centerChild 中间内容 20 | /// @param {Widget} rightChild 右侧内容 21 | /// @param {EdgeInsets} padding 内边距 22 | /// @param {EdgeInsets} margin 外边距 23 | /// @param {Border} border 24 | /// @param {Color} color 25 | /// @param {Function} onPressed 点击回调 26 | /// ``` 27 | ARow( 28 | {Key key, 29 | this.height = 44, 30 | this.padding, 31 | this.leftChild, 32 | this.centerChild, 33 | this.rightChild, 34 | this.border, 35 | this.color, 36 | this.onPressed, 37 | this.margin}) 38 | : super(key: key); 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return InkWell( 43 | highlightColor: onPressed == null ? Colors.transparent : null, 44 | splashColor: onPressed == null ? Colors.transparent : null, 45 | child: Container( 46 | height: height, 47 | decoration: BoxDecoration( 48 | color: color == null ? Colors.white : color, 49 | border: border == null 50 | ? Border( 51 | bottom: BorderSide( 52 | width: 1, color: Color.fromRGBO(242, 242, 242, 1))) 53 | : border), 54 | padding: 55 | padding == null ? EdgeInsets.symmetric(horizontal: 15) : padding, 56 | margin: margin == null ? EdgeInsets.all(0) : margin, 57 | child: Row( 58 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 59 | crossAxisAlignment: CrossAxisAlignment.center, 60 | children: [ 61 | // left 62 | leftChild == null ? Container() : leftChild, 63 | 64 | // center 65 | Expanded(child: centerChild == null ? Container() : centerChild), 66 | 67 | // right 68 | rightChild == null ? Container() : rightChild 69 | ], 70 | ), 71 | ), 72 | onTap: () => onPressed == null ? () {} : onPressed(), 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/components/a_stepper/a_stepper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/components/a_button/index.dart'; 3 | 4 | class AStepper extends StatelessWidget { 5 | final num min; 6 | final num max; 7 | final Function onChange; 8 | final num value; 9 | 10 | /// 步进器 格式: - 1 + 11 | /// 12 | /// ``` 13 | /// @param {num} min - 最小值 14 | /// @param {num} max - 最大值 15 | /// @param {num} value - value 16 | /// @param {Function} onChange - 值改变的回调 返回一个num 17 | /// ``` 18 | const AStepper({ 19 | Key key, 20 | this.min = 0, 21 | this.max = 99, 22 | this.value = 1, 23 | @required this.onChange, 24 | }) : super(key: key); 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return Container( 29 | child: Row( 30 | children: [ 31 | AButton.icon( 32 | width: 30, 33 | height: 30, 34 | bgColor: Colors.transparent, 35 | icon: Icon( 36 | Icons.do_not_disturb_on_rounded, 37 | color: Color.fromRGBO(144, 192, 239, value == min ? 0.3 : 1), 38 | ), 39 | onPressed: () { 40 | if (value <= min) return; 41 | onChange(value - 1); 42 | }, 43 | ), 44 | ConstrainedBox( 45 | constraints: BoxConstraints( 46 | minWidth: 20, 47 | maxHeight: 28, 48 | ), 49 | child: Container( 50 | alignment: Alignment.center, 51 | child: SingleChildScrollView( 52 | scrollDirection: Axis.horizontal, 53 | child: Container( 54 | padding: EdgeInsets.only(top: 2), 55 | child: Text( 56 | '$value', 57 | ), 58 | ), 59 | ), 60 | ), 61 | ), 62 | AButton.icon( 63 | width: 30, 64 | height: 30, 65 | bgColor: Colors.transparent, 66 | icon: Icon( 67 | Icons.add_circle, 68 | color: Color.fromRGBO(144, 192, 239, value == max ? 0.3 : 1), 69 | ), 70 | onPressed: () { 71 | if (value >= max) return; 72 | onChange(value + 1); 73 | }, 74 | ), 75 | ], 76 | ), 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/components/custom_swiper/index.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: meetqy 3 | * @since: 2019-08-08 16:16:47 4 | * @lastTime: 2019-08-15 14:57:57 5 | * @LastEditors: meetqy 6 | */ 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_swiper/flutter_swiper.dart'; 9 | 10 | class CustomSwiper extends StatelessWidget { 11 | final List images; 12 | final int index; 13 | final double height; 14 | 15 | /// 轮播图 16 | /// ``` 17 | /// @param {List} images - 轮播图地址 18 | /// @param {int} index - 初始下标位置 19 | /// @param {double} height - 容器高度 20 | /// ``` 21 | CustomSwiper(this.images, { 22 | this.index, 23 | this.height = 288 24 | }); 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return Container( 29 | height: height, 30 | child: Swiper( 31 | index: index, 32 | itemBuilder: (BuildContext context,int index){ 33 | return Image.asset(images[index], fit: BoxFit.cover); 34 | }, 35 | itemCount: images.length, 36 | pagination: SwiperPagination( 37 | builder: DotSwiperPaginationBuilder( 38 | size: 8, 39 | activeSize: 8 40 | ) 41 | ), 42 | autoplay: true, 43 | duration: 500, 44 | autoplayDelay: 5000 45 | ), 46 | ); 47 | } 48 | } -------------------------------------------------------------------------------- /lib/components/goods_detail/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/components/a_button/index.dart'; 3 | import 'package:flutter_luckin_coffee/components/a_stepper/a_stepper.dart'; 4 | 5 | import 'select_row.dart'; 6 | 7 | class GoodsDetailDialog extends StatefulWidget { 8 | final int id; 9 | 10 | GoodsDetailDialog({Key key, this.id}) : super(key: key); 11 | 12 | _GoodsDetailDialogState createState() => _GoodsDetailDialogState(); 13 | } 14 | 15 | class _GoodsDetailDialogState extends State { 16 | @override 17 | Widget build(BuildContext context) { 18 | return AnimatedPadding( 19 | padding: EdgeInsets.zero, 20 | duration: const Duration(milliseconds: 100), 21 | curve: Curves.decelerate, 22 | child: MediaQuery.removeViewInsets( 23 | removeLeft: true, 24 | removeTop: true, 25 | removeRight: true, 26 | removeBottom: true, 27 | context: context, 28 | child: Center( 29 | child: ConstrainedBox( 30 | constraints: const BoxConstraints(minWidth: 280.0), 31 | child: Material( 32 | color: Colors.transparent, 33 | elevation: 24.0, 34 | shape: RoundedRectangleBorder( 35 | borderRadius: BorderRadius.all(Radius.circular(4.0))), 36 | type: MaterialType.card, 37 | child: _initContent(), 38 | ), 39 | ), 40 | ), 41 | ), 42 | ); 43 | } 44 | 45 | _initContent() { 46 | return Container( 47 | width: 335, 48 | height: 580, 49 | child: Column( 50 | children: [ 51 | _initHeader(), 52 | Expanded( 53 | child: Container( 54 | color: Colors.white, 55 | padding: EdgeInsets.symmetric(vertical: 12), 56 | child: SingleChildScrollView( 57 | child: Column( 58 | children: [_initOption(), _line(), _initGoodsDesc()], 59 | )), 60 | ), 61 | ), 62 | _initAccount(), 63 | _initFooter() 64 | ], 65 | ), 66 | ); 67 | } 68 | 69 | /// 分隔线 70 | _line() { 71 | return Container( 72 | padding: EdgeInsets.symmetric(horizontal: 15, vertical: 8), 73 | child: Container( 74 | decoration: BoxDecoration( 75 | border: Border( 76 | bottom: 77 | BorderSide(color: Color.fromRGBO(242, 242, 242, 1), width: 1), 78 | ), 79 | ), 80 | ), 81 | ); 82 | } 83 | 84 | /// 选项 85 | _initOption() { 86 | return Column( 87 | children: [ 88 | Column( 89 | children: [SelectRow(), SelectRow(), SelectRow()], 90 | ), 91 | ], 92 | ); 93 | } 94 | 95 | /// 商品描述 96 | Widget _initGoodsDesc() { 97 | return Container( 98 | padding: EdgeInsets.symmetric(horizontal: 15, vertical: 8), 99 | child: Column( 100 | mainAxisAlignment: MainAxisAlignment.start, 101 | children: [ 102 | Row( 103 | children: [ 104 | Expanded( 105 | child: Text( 106 | '商品描述', 107 | style: TextStyle( 108 | color: Color.fromRGBO(56, 56, 56, 1), fontSize: 13), 109 | ), 110 | ) 111 | ], 112 | ), 113 | Row( 114 | children: [ 115 | Expanded( 116 | child: Container( 117 | margin: EdgeInsets.only(top: 8), 118 | child: Text( 119 | "flutter luckin coffee application(仿瑞幸咖啡)", 120 | style: TextStyle( 121 | fontSize: 12, 122 | color: Color.fromRGBO(128, 128, 128, 1), 123 | ), 124 | ), 125 | ), 126 | ) 127 | ], 128 | ) 129 | ], 130 | ), 131 | ); 132 | } 133 | 134 | /// 结算总价 135 | Widget _initAccount() { 136 | return Container( 137 | padding: EdgeInsets.only(left: 15, right: 15, top: 10), 138 | decoration: BoxDecoration( 139 | border: Border( 140 | top: BorderSide(color: Color.fromRGBO(242, 242, 242, 1), width: 1), 141 | bottom: BorderSide(color: Color.fromRGBO(242, 242, 242, 1), width: 1), 142 | ), 143 | color: Colors.white, 144 | ), 145 | height: 60, 146 | child: Column( 147 | children: [ 148 | Row( 149 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 150 | children: [ 151 | Container( 152 | alignment: Alignment.centerLeft, 153 | child: Text( 154 | '¥ 28.0', 155 | style: TextStyle( 156 | fontSize: 18, 157 | fontWeight: FontWeight.bold, 158 | color: Color.fromRGBO(56, 56, 56, 1)), 159 | ), 160 | ), 161 | AStepper( 162 | value: 23, 163 | min: 1, 164 | onChange: (num val) {}, 165 | ) 166 | ], 167 | ), 168 | Container( 169 | alignment: Alignment.centerLeft, 170 | child: Text( 171 | '金枪鱼三明治 大,热,糖', 172 | style: 173 | TextStyle(color: Color.fromRGBO(80, 80, 80, 1), fontSize: 10), 174 | ), 175 | ) 176 | ], 177 | ), 178 | ); 179 | } 180 | 181 | /// 底部 182 | Widget _initFooter() { 183 | return Container( 184 | padding: EdgeInsets.only(left: 15, right: 15), 185 | decoration: BoxDecoration( 186 | borderRadius: BorderRadius.only( 187 | bottomLeft: Radius.circular(8), 188 | bottomRight: Radius.circular(8), 189 | ), 190 | color: Colors.white, 191 | ), 192 | height: 60, 193 | child: Row( 194 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 195 | children: [ 196 | AButton.icon( 197 | icon: Icon( 198 | Icons.card_giftcard_outlined, 199 | size: 14, 200 | ), 201 | textChild: Text( 202 | '充2赠1', 203 | style: TextStyle(fontSize: 12), 204 | ), 205 | color: Colors.white, 206 | bgColor: Color.fromRGBO(255, 129, 2, 1), 207 | height: 32, 208 | onPressed: () => {}, 209 | ), 210 | AButton.icon( 211 | height: 32, 212 | icon: Icon( 213 | Icons.favorite_border, 214 | size: 16, 215 | ), 216 | textChild: Text( 217 | '收藏口味', 218 | style: TextStyle(fontSize: 12), 219 | ), 220 | color: Color.fromRGBO(136, 175, 213, 1), 221 | bgColor: Color.fromRGBO(136, 175, 213, 0.3), 222 | onPressed: () => {}, 223 | ), 224 | AButton.icon( 225 | height: 32, 226 | icon: Icon( 227 | Icons.shopping_cart, 228 | size: 16, 229 | ), 230 | bgColor: Color.fromRGBO(136, 175, 213, 1), 231 | color: Colors.white, 232 | textChild: Text( 233 | '加入购物车', 234 | style: TextStyle(fontSize: 12), 235 | ), 236 | onPressed: () async { 237 | /// HACK: 加入购物车 238 | }, 239 | ) 240 | ], 241 | ), 242 | ); 243 | } 244 | 245 | /// 收藏 246 | Widget _circelIcon({Icon icon, Function onPress, Color bgColor}) { 247 | return InkWell( 248 | child: Container( 249 | padding: EdgeInsets.all(5), 250 | alignment: Alignment.center, 251 | decoration: BoxDecoration( 252 | color: bgColor == null ? Color.fromRGBO(0, 0, 0, 0.3) : bgColor, 253 | borderRadius: BorderRadius.all(Radius.circular(20))), 254 | child: icon), 255 | onTap: () { 256 | if (onPress != null) { 257 | onPress(); 258 | } 259 | }, 260 | ); 261 | } 262 | 263 | /// 关闭弹窗 264 | Widget _initClose() { 265 | // 关闭 266 | return Positioned( 267 | right: 10, 268 | top: 10, 269 | child: InkWell( 270 | child: Container( 271 | padding: EdgeInsets.all(5), 272 | alignment: Alignment.center, 273 | decoration: BoxDecoration( 274 | color: Color.fromRGBO(0, 0, 0, 0.3), 275 | borderRadius: BorderRadius.all(Radius.circular(20))), 276 | child: Icon( 277 | Icons.close, 278 | size: 16, 279 | color: Colors.white, 280 | ), 281 | ), 282 | onTap: () { 283 | Navigator.pop(context); 284 | }, 285 | )); 286 | } 287 | 288 | /// 头部 289 | Widget _initHeader() { 290 | return Stack( 291 | children: [ 292 | Container( 293 | width: 335, 294 | height: 150, 295 | child: ClipRRect( 296 | borderRadius: BorderRadius.only( 297 | topLeft: Radius.circular(8), topRight: Radius.circular(8)), 298 | child: Image.asset( 299 | 'lib/assets/images/menu/dialog1.jpg', 300 | height: 150, 301 | fit: BoxFit.cover, 302 | ), 303 | ), 304 | ), 305 | 306 | _initClose(), 307 | 308 | // 收藏 309 | Positioned( 310 | left: 10, 311 | top: 10, 312 | child: _circelIcon( 313 | icon: Icon( 314 | Icons.favorite_border_outlined, 315 | size: 16, 316 | color: Colors.white, 317 | ), 318 | )), 319 | 320 | // 标题 321 | Positioned( 322 | left: 15, 323 | bottom: 15, 324 | child: Column( 325 | children: [ 326 | Text( 327 | '金枪鱼三明治', 328 | style: TextStyle( 329 | fontSize: 22, 330 | fontWeight: FontWeight.bold, 331 | color: Colors.white), 332 | ), 333 | Text( 334 | 'Nice tuna SandWich', 335 | style: TextStyle(fontSize: 14, color: Colors.white), 336 | ) 337 | ], 338 | ), 339 | ) 340 | ], 341 | ); 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /lib/components/goods_detail/select_row.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/components/a_button/index.dart'; 3 | 4 | class SelectRow extends StatelessWidget { 5 | SelectRow(); 6 | 7 | Container _createRadio(bool isActive, String text) { 8 | return Container( 9 | key: Key(text), 10 | margin: EdgeInsets.only(right: 10), 11 | child: AButton.normal( 12 | width: 80, 13 | height: 30, 14 | borderRadius: BorderRadius.circular(15), 15 | child: Text( 16 | text, 17 | style: TextStyle(fontSize: 12), 18 | ), 19 | plain: true, 20 | color: isActive ? Colors.white : Color.fromRGBO(204, 192, 180, 1), 21 | bgColor: isActive ? Color.fromRGBO(204, 192, 180, 1) : Colors.white, 22 | borderColor: 23 | isActive ? Colors.transparent : Color.fromRGBO(204, 192, 180, 1), 24 | onPressed: () { 25 | // Map type = { 26 | // "typeId": data.id, 27 | // "childId": item.id, 28 | // "typeName": data.name, 29 | // "childName": item.name 30 | // }; 31 | 32 | // onChange(type); 33 | }, 34 | ), 35 | ); 36 | } 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return Container( 41 | padding: EdgeInsets.only(top: 6, bottom: 6), 42 | child: Column( 43 | children: [ 44 | Container( 45 | margin: EdgeInsets.only(bottom: 10), 46 | child: Row( 47 | crossAxisAlignment: CrossAxisAlignment.start, 48 | children: [ 49 | Container( 50 | width: 65, 51 | alignment: Alignment.center, 52 | height: 30, 53 | child: Text( 54 | '温度', 55 | style: TextStyle(color: Color.fromRGBO(56, 56, 56, 1)), 56 | ), 57 | ), 58 | Expanded( 59 | child: Container( 60 | child: Wrap( 61 | children: [ 62 | _createRadio(false, '冰'), 63 | _createRadio(true, '热') 64 | ], 65 | ), 66 | ), 67 | ) 68 | ], 69 | ), 70 | ), 71 | ], 72 | ), 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/components/take_out_btn/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class TakeOutBtn extends StatelessWidget { 4 | /// 自提或则外送按钮 5 | const TakeOutBtn({ 6 | Key key, 7 | }) : super(key: key); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Container( 12 | padding: EdgeInsets.all(3), 13 | width: 90, 14 | height: 36, 15 | decoration: BoxDecoration( 16 | border: Border.all(width: 1, color: Color.fromRGBO(136, 175, 213, 1)), 17 | borderRadius: BorderRadius.circular(18)), 18 | child: Row( 19 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 20 | children: [ 21 | Container( 22 | width: 44, 23 | height: 30, 24 | alignment: Alignment.center, 25 | decoration: BoxDecoration( 26 | color: Color.fromRGBO(136, 175, 213, 1), 27 | borderRadius: BorderRadius.circular(15)), 28 | child: Text( 29 | '自提', 30 | style: TextStyle( 31 | color: Color.fromRGBO(255, 255, 255, 1), fontSize: 12), 32 | )), 33 | Container( 34 | height: 30, 35 | margin: EdgeInsets.only(right: 9), 36 | alignment: Alignment.center, 37 | decoration: 38 | BoxDecoration(borderRadius: BorderRadius.circular(15)), 39 | child: Text( 40 | '外送', 41 | style: TextStyle( 42 | color: Color.fromRGBO(136, 175, 213, 1), fontSize: 12), 43 | )) 44 | ], 45 | ), 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/get_pages.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_luckin_coffee/pages/coupon/index.dart'; 2 | import 'package:flutter_luckin_coffee/pages/diningcode/dining_code.dart'; 3 | import 'package:flutter_luckin_coffee/pages/login/login_mail.dart'; 4 | import 'package:flutter_luckin_coffee/pages/login/login_method.dart'; 5 | import 'package:flutter_luckin_coffee/pages/login/user_agreement.dart'; 6 | import 'package:flutter_luckin_coffee/pages/order/order_confirm.dart'; 7 | import 'package:flutter_luckin_coffee/pages/order/order_detail.dart'; 8 | import 'package:flutter_luckin_coffee/pages/order/order_evaluation.dart'; 9 | import 'package:flutter_luckin_coffee/pages/order/order_remark.dart'; 10 | import 'package:flutter_luckin_coffee/pages/selfstore/index.dart'; 11 | import 'package:flutter_luckin_coffee/pages/storedetail/index.dart'; 12 | import 'package:flutter_luckin_coffee/pages/toolbar/index.dart'; 13 | import 'package:get/get.dart'; 14 | 15 | List> pages = [ 16 | // toolbar 17 | GetPage(name: '/', page: () => Toolbar()), 18 | GetPage(name: '/mine', page: () => Toolbar()), 19 | GetPage(name: '/shopping_cart', page: () => Toolbar()), 20 | GetPage(name: '/menu', page: () => Toolbar()), 21 | 22 | // other 23 | GetPage(name: '/login_method', page: () => LoginMethod()), 24 | GetPage(name: '/login_mail', page: () => LoginMail()), 25 | GetPage(name: '/user_agreement', page: () => UserAgreement()), 26 | GetPage(name: '/order_evaluation', page: () => OrderEvaluation()), 27 | GetPage(name: '/order_confirm', page: () => OrderConfirm()), 28 | GetPage(name: '/order_remark', page: () => OrderRemark()), 29 | GetPage(name: '/order_detail', page: () => OrderDetail()), 30 | GetPage(name: '/coupon', page: () => Coupon()), 31 | GetPage(name: '/self_store', page: () => SelfStore()), 32 | GetPage(name: '/store_detail', page: () => StoreDetail()), 33 | GetPage(name: '/dining_code', page: () => DiningCode()), 34 | ]; 35 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/get_pages.dart'; 3 | import 'package:get/route_manager.dart'; 4 | 5 | void main() { 6 | runApp(GetMaterialApp( 7 | title: 'Flutter Luckin Coffee', 8 | initialRoute: '/', 9 | getPages: pages, 10 | )); 11 | } 12 | -------------------------------------------------------------------------------- /lib/mock/goods_category.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_mock/dart_mock.dart' as mock; 2 | 3 | class MockGoodsCategory { 4 | final String name; 5 | final String desc; 6 | 7 | MockGoodsCategory({this.name, this.desc}); 8 | 9 | static List _data = []; 10 | 11 | static List data() { 12 | if (_data.isEmpty) { 13 | ['人气TOP', '大师咖啡', '瑞纳冰', '经典饮品', '健康轻食', '鲜榨果蔬汁'].forEach((item) { 14 | _data.add(MockGoodsCategory( 15 | name: item, 16 | desc: mock.csentence(max: 5), 17 | )); 18 | }); 19 | } 20 | 21 | return _data; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/mock/goods_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_luckin_coffee/mock/goods_category.dart'; 2 | import 'package:dart_mock/dart_mock.dart' as mock; 3 | 4 | class MockGoods { 5 | final int categoryId; 6 | final String characteristic; 7 | final int originalPrice; 8 | final String name; 9 | final String pic; 10 | 11 | const MockGoods({ 12 | this.categoryId, 13 | this.characteristic, 14 | this.originalPrice, 15 | this.name, 16 | this.pic, 17 | }); 18 | 19 | static List _data = []; 20 | 21 | static data() { 22 | var category = MockGoodsCategory.data(); 23 | if (_data.isEmpty) { 24 | category.forEach((element) { 25 | var categoryId = category.indexOf(element); 26 | 27 | List.generate(mock.integer(min: 1, max: 9), (index) { 28 | _data.add( 29 | MockGoods( 30 | categoryId: categoryId, 31 | characteristic: mock.cword(max: 36), 32 | originalPrice: mock.integer(min: 9, max: 99), 33 | name: mock.cword(max: 6), 34 | pic: 35 | 'https://cdn.wcao.cc/assets/avatar/coffee/${mock.integer(min: 1, max: 12)}.jpg'), 36 | ); 37 | }); 38 | }); 39 | } 40 | 41 | return _data; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/pages/coupon/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/components/a_button/index.dart'; 3 | import 'package:flutter_luckin_coffee/pages/coupon/widgets/coupon_row.dart'; 4 | import 'package:flutter_luckin_coffee/utils/global.dart'; 5 | import 'package:get/get.dart'; 6 | 7 | class Coupon extends StatefulWidget { 8 | Coupon({Key key}) : super(key: key); 9 | 10 | _CouponState createState() => _CouponState(); 11 | } 12 | 13 | class _CouponState extends State { 14 | double bottom = Get.bottomBarHeight; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Scaffold( 19 | appBar: customAppbar( 20 | title: '可使用优惠券', 21 | context: context, 22 | ), 23 | body: Stack( 24 | children: [ 25 | Container( 26 | padding: EdgeInsets.only(left: 25, right: 25), 27 | child: ListView( 28 | padding: EdgeInsets.only(bottom: 80 + bottom), 29 | children: [ 30 | CouponRow(), 31 | CouponRow(), 32 | CouponRow(), 33 | CouponRow(), 34 | CouponRow(), 35 | CouponRow(), 36 | CouponRow(), 37 | CouponRow(), 38 | CouponRow(), 39 | ], 40 | ), 41 | ), 42 | Positioned( 43 | bottom: 0, 44 | left: 0, 45 | child: Container( 46 | color: Colors.white, 47 | alignment: Alignment.center, 48 | padding: EdgeInsets.only(bottom: bottom, top: bottom / 2), 49 | width: Get.width, 50 | child: AButton.normal( 51 | width: 300, 52 | child: Text('保存'), 53 | color: Colors.white, 54 | bgColor: Color.fromRGBO(144, 192, 239, 1), 55 | onPressed: () => {}), 56 | ), 57 | ) 58 | ], 59 | ), 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/pages/coupon/widgets/coupon_row.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class CouponRow extends StatelessWidget { 4 | const CouponRow({ 5 | Key key, 6 | }) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Container( 11 | margin: EdgeInsets.only(top: 15), 12 | decoration: BoxDecoration( 13 | borderRadius: BorderRadius.all(Radius.circular(4)), 14 | color: Colors.white, 15 | ), 16 | height: 120, 17 | padding: EdgeInsets.all(5), 18 | child: Container( 19 | alignment: Alignment.center, 20 | decoration: BoxDecoration( 21 | border: 22 | Border.all(color: Color.fromRGBO(242, 242, 242, 1), width: 1), 23 | borderRadius: BorderRadius.all(Radius.circular(4)), 24 | ), 25 | child: Stack( 26 | children: [ 27 | Container( 28 | padding: EdgeInsets.symmetric(vertical: 25), 29 | child: Row( 30 | children: [ 31 | Container( 32 | width: 85, 33 | alignment: Alignment.center, 34 | decoration: BoxDecoration( 35 | border: Border( 36 | right: BorderSide( 37 | width: 1, 38 | color: Color.fromRGBO(242, 242, 242, 1)))), 39 | child: Row( 40 | mainAxisAlignment: MainAxisAlignment.center, 41 | children: [ 42 | Text( 43 | '4', 44 | style: TextStyle( 45 | color: Color.fromRGBO(144, 192, 239, 1), 46 | fontSize: 34), 47 | ), 48 | Text( 49 | '.8折', 50 | style: TextStyle( 51 | color: Color.fromRGBO(144, 192, 239, 1), 52 | fontSize: 18), 53 | ) 54 | ], 55 | ), 56 | ), 57 | Expanded( 58 | child: Container( 59 | padding: EdgeInsets.only(left: 20, right: 15), 60 | child: Row( 61 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 62 | children: [ 63 | // left 64 | Column( 65 | crossAxisAlignment: CrossAxisAlignment.start, 66 | mainAxisAlignment: MainAxisAlignment.center, 67 | children: [ 68 | Text( 69 | '咖啡.果汁', 70 | style: TextStyle( 71 | color: Color.fromRGBO(56, 56, 56, 1), 72 | fontSize: 16), 73 | ), 74 | Container( 75 | margin: EdgeInsets.only(top: 7), 76 | child: Text( 77 | '有效期至2019-01-14', 78 | style: TextStyle( 79 | color: Color.fromRGBO(166, 166, 166, 1), 80 | fontSize: 12), 81 | ), 82 | ) 83 | ], 84 | ), 85 | 86 | // right 87 | Container( 88 | width: 20, 89 | height: 20, 90 | child: Transform.scale( 91 | scale: .8, 92 | child: Checkbox( 93 | activeColor: Color.fromRGBO(136, 175, 213, 1), 94 | value: true, 95 | onChanged: (flag) {}, 96 | ), 97 | ), 98 | ), 99 | ], 100 | ), 101 | )) 102 | ], 103 | ), 104 | ), 105 | Positioned( 106 | right: 15, 107 | bottom: 5, 108 | child: Container( 109 | child: Row( 110 | mainAxisAlignment: MainAxisAlignment.end, 111 | children: [ 112 | Text( 113 | '使用规则', 114 | style: TextStyle( 115 | fontSize: 10, 116 | color: Color.fromRGBO(166, 166, 166, 1), 117 | ), 118 | ), 119 | Icon( 120 | Icons.keyboard_arrow_down_outlined, 121 | color: Color.fromRGBO(153, 153, 153, 1), 122 | size: 14, 123 | ) 124 | ], 125 | ), 126 | ), 127 | ) 128 | ], 129 | )), 130 | ); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /lib/pages/diningcode/dining_code.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/utils/global.dart'; 3 | 4 | class DiningCode extends StatefulWidget { 5 | DiningCode(); 6 | 7 | _DiningCodeState createState() => _DiningCodeState(); 8 | } 9 | 10 | class _DiningCodeState extends State { 11 | @override 12 | Widget build(BuildContext context) { 13 | return Scaffold( 14 | appBar: customAppbar(title: "取餐码", context: context), 15 | body: Container( 16 | child: Container( 17 | alignment: Alignment.center, 18 | margin: EdgeInsets.only(top: 10), 19 | padding: EdgeInsets.symmetric(vertical: 30), 20 | height: 395, 21 | color: Colors.white, 22 | child: Column( 23 | children: [ 24 | Row( 25 | mainAxisAlignment: MainAxisAlignment.center, 26 | children: [ 27 | Text( 28 | '取餐码:', 29 | style: TextStyle( 30 | fontSize: 18, 31 | fontWeight: FontWeight.bold, 32 | color: Color.fromRGBO(56, 56, 56, 1)), 33 | ), 34 | Text( 35 | '432', 36 | style: TextStyle( 37 | color: Color.fromRGBO(136, 175, 213, 1), 38 | fontWeight: FontWeight.bold, 39 | fontSize: 18), 40 | ) 41 | ], 42 | ), 43 | Row( 44 | mainAxisAlignment: MainAxisAlignment.center, 45 | children: [ 46 | Text( 47 | 'piu先生,请扫码取餐', 48 | style: TextStyle( 49 | color: Color.fromRGBO(56, 56, 56, 1), fontSize: 14), 50 | ) 51 | ], 52 | ), 53 | Container( 54 | margin: EdgeInsets.only(top: 40), 55 | child: Row( 56 | mainAxisAlignment: MainAxisAlignment.center, 57 | children: [ 58 | Image.asset( 59 | 'lib/assets/images/QRCode_258.png', 60 | width: 155, 61 | fit: BoxFit.cover, 62 | ) 63 | ], 64 | ), 65 | ), 66 | Container( 67 | margin: EdgeInsets.only(top: 40), 68 | child: Row( 69 | mainAxisAlignment: MainAxisAlignment.center, 70 | children: [ 71 | // Button.bigButton( 72 | // '分享餐码给好友', 73 | // bgColor: Colors.transparent, 74 | // color: Color.fromRGBO(144, 192, 239, 1), 75 | // border: Border.all(color: Color.fromRGBO(144, 192, 239, 1)), 76 | // height: 30, 77 | // fontSize: 14, 78 | // width: 150, 79 | // ) 80 | ], 81 | ), 82 | ) 83 | ], 84 | ), 85 | ), 86 | )); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /lib/pages/login/login_mail.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_luckin_coffee/components/a_button/index.dart'; 4 | import 'package:flutter_luckin_coffee/utils/global.dart'; 5 | 6 | class LoginMail extends StatefulWidget { 7 | LoginMail({Key key}) : super(key: key); 8 | 9 | _LoginMailState createState() => _LoginMailState(); 10 | } 11 | 12 | class _LoginMailState extends State { 13 | static Map email = {"value": null, "verify": true}; 14 | static Map code = {"value": null, "verify": true}; 15 | 16 | /// 开始倒计时 时间 17 | int startTime; 18 | 19 | /// 当前倒计时 时间 20 | int countDownTime = 0; 21 | 22 | /// 总倒计时时长 23 | final int speed = 60; 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return Scaffold( 28 | resizeToAvoidBottomInset: false, 29 | appBar: customAppbar(context: context, borderBottom: false), 30 | body: Container( 31 | color: Colors.white, 32 | padding: EdgeInsets.only(left: 35, right: 35, top: 87), 33 | child: Column( 34 | children: [ 35 | Container( 36 | margin: EdgeInsets.only(bottom: 20), 37 | width: 80, 38 | height: 94, 39 | child: Image.asset( 40 | 'lib/assets/images/logo1.png', 41 | fit: BoxFit.cover, 42 | ), 43 | ), 44 | 45 | /// 输入邮箱 46 | Container( 47 | height: 55, 48 | decoration: BoxDecoration(border: cuBorderBottom()), 49 | child: TextField( 50 | keyboardType: TextInputType.emailAddress, 51 | decoration: InputDecoration( 52 | counterText: "", 53 | border: InputBorder.none, 54 | hintText: '请输入邮箱', 55 | hintStyle: TextStyle( 56 | fontSize: 14, 57 | )), 58 | onChanged: (e) {}, 59 | ), 60 | ), 61 | 62 | /// 验证码 63 | Container( 64 | height: 55, 65 | decoration: BoxDecoration(border: cuBorderBottom()), 66 | child: Row( 67 | children: [ 68 | Flexible( 69 | child: TextField( 70 | maxLength: 4, 71 | keyboardType: TextInputType.number, 72 | decoration: InputDecoration( 73 | counterText: '', 74 | border: InputBorder.none, 75 | hintText: '请输入邮箱验证码', 76 | hintStyle: TextStyle( 77 | fontSize: 14, 78 | )), 79 | onChanged: (e) { 80 | setState(() { 81 | code['value'] = e; 82 | code['verify'] = e.length == 4 ? true : false; 83 | }); 84 | }, 85 | ), 86 | ), 87 | Container( 88 | height: 25, 89 | decoration: BoxDecoration( 90 | border: Border( 91 | left: BorderSide( 92 | color: Color.fromRGBO(242, 242, 242, 1)))), 93 | ), 94 | buildGetEmailCode() 95 | ], 96 | ), 97 | ), 98 | 99 | /// 确认 100 | Container( 101 | margin: EdgeInsets.only(top: 20), 102 | child: AButton.normal( 103 | width: 300, 104 | child: Text('确定'), 105 | bgColor: Color.fromRGBO(136, 175, 213, 1), 106 | color: Colors.white, 107 | onPressed: () {}, 108 | ), 109 | ), 110 | 111 | /// 协议 112 | Container( 113 | margin: EdgeInsets.only(top: 10), 114 | child: Row( 115 | mainAxisAlignment: MainAxisAlignment.center, 116 | children: [ 117 | Text( 118 | '点击确定,即表示以阅读并同意', 119 | style: TextStyle( 120 | fontSize: 12, color: Color.fromRGBO(153, 153, 153, 1)), 121 | ), 122 | InkWell( 123 | child: Text( 124 | '《注册会员服务条款》', 125 | style: TextStyle( 126 | color: Color.fromRGBO(85, 122, 157, 1), fontSize: 12), 127 | ), 128 | onTap: () => 129 | Navigator.pushNamed(context, '/user_agreement'), 130 | ) 131 | ], 132 | ), 133 | ) 134 | ], 135 | ), 136 | ), 137 | ); 138 | } 139 | 140 | /// 获取验证码 141 | Container buildGetEmailCode() { 142 | return Container( 143 | child: AButton.normal( 144 | child: Text(countDownTime <= 0 145 | ? "获取验证码" 146 | : countDownTime < 10 147 | ? '0$countDownTime' 148 | : '$countDownTime'), 149 | color: email['verify'] && email['value'] != null 150 | ? Color.fromRGBO(85, 122, 157, 1) 151 | : Color.fromRGBO(166, 166, 166, 1), 152 | onPressed: () { 153 | if (countDownTime > 0) return; 154 | 155 | /// HACK: 获取验证码 156 | }), 157 | ); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /lib/pages/login/login_method.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/components/a_button/index.dart'; 3 | import 'package:flutter_luckin_coffee/utils/global.dart'; 4 | import 'package:get/get.dart'; 5 | 6 | class LoginMethod extends StatelessWidget { 7 | const LoginMethod({Key key}) : super(key: key); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Scaffold( 12 | appBar: customAppbar(context: context, borderBottom: false), 13 | body: Container( 14 | width: Get.width, 15 | color: Colors.white, 16 | child: Column( 17 | children: [ 18 | Container( 19 | margin: EdgeInsets.only(top: 26), 20 | width: 80, 21 | height: 94, 22 | child: Image.asset( 23 | 'lib/assets/images/logo1.png', 24 | fit: BoxFit.cover, 25 | ), 26 | ), 27 | Container( 28 | margin: EdgeInsets.only(top: 116), 29 | child: AButton.normal( 30 | width: 300, 31 | child: Text('微信一键登录'), 32 | color: Colors.white, 33 | bgColor: Color.fromRGBO(73, 194, 101, 1), 34 | ), 35 | ), 36 | Container( 37 | margin: EdgeInsets.only(top: 25), 38 | child: AButton.normal( 39 | width: 300, 40 | child: Text('邮箱验证码登录'), 41 | color: Color.fromRGBO(136, 175, 213, 1), 42 | bgColor: Colors.white, 43 | borderColor: Color.fromRGBO(136, 175, 213, 1), 44 | plain: true, 45 | onPressed: () => Get.toNamed('/login_mail')), 46 | ), 47 | ], 48 | ), 49 | ), 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/pages/login/user_agreement.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/utils/global.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | class UserAgreement extends StatelessWidget { 6 | const UserAgreement({Key key}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Scaffold( 11 | appBar: customAppbar(context: context, title: "用户协议"), 12 | body: SingleChildScrollView( 13 | child: SafeArea( 14 | child: Container( 15 | padding: EdgeInsets.symmetric(vertical: 10), 16 | width: Get.width, 17 | child: Column( 18 | children: [ 19 | Text( 20 | '''尊敬的客户,在您下载luckin coffee程序( 下称“APP") 21 | 使用我们的服务前,请仔细阅读本用户协议。 22 | 用户协议 23 | 本协议是您与瑞幸咖啡(北京)有限公司(以下简称“瑞 24 | 幸咖啡”)签署的《用户协议》。您下载本APP并成功注 25 | 册后,即被视为已阅读、理解、接受本协议条款。本协 26 | 议可由我们不时作出修订,且构成您与我们之间达成的有 27 | 关APP使用、具有约束力的协议。您在修订发布后继续使 28 | 用APP,即视为您已接受了相关修订。如果您不同意本 29 | 协议或相关修订,则应立即卸载本APP。 30 | 如果您不满十八(18) 周岁,您应确保与您的父母或监 31 | 护人共同阅读本协议,以便您及您的父母或监护人理解并 32 | 同意本协议的内容。 33 | 1.用户许可 34 | 1.1.我们允许您在本协议范围内享有个人的、非排他性 35 | 的、不可转让的使用本APP的权利。但是您不得有以下行 36 | 为: 37 | 1.1.1.以任何方式出售、转让、分发、修改或传播APP或 38 | 与APP有关的文字、图片、音乐、条形码、视频、数据、 39 | 超链接、展示及其他内容(“内容") ; 40 | 1.1.4.利用服务或APP程序进行违法犯罪活动; 41 | 1.1.5.利用服务或APP程序侵犯他人合法权益; 42 | 1.1.6.利用服务或APP程序影响网络的正常运行; 43 | 1.1.7.损害APP程序数据的完整性或性能。 44 | 本用户许可同样适用于本APP的任何更新、补充或替代产 45 | 品,但相关更新、补充或替代产品中有相反规定的除外。 46 | 1.2.如果我们认为您存在任何违反本协议的行为或存在其 47 | 他损害APP的行为,我们保留在不另行通知的情况下随时 48 | 禁止您继续使用APP和相关服务的权利。 49 | 2.注册帐号、密码及安全性 50 | 2.1.注册资格您承诺:您具有完全民事权利能力和行为 51 | 能力或虽不具有完全民事权利能力和行为能力但经您的法 52 | 定代理人同意并由其代理注册APP服务。 53 | 2.2.注册流程:您同意根据APP注册页面的要求提供手机 54 | 号码并通过认证程序注册账号,或者通过微信授权快速注 55 | 册账号。您将对账户安全负全部责任。另外,每个用户 56 | 都要对以其用户名进行的所有活动和事件负全责。 57 | 2.3.您成功注册后,您同意接收我们发送的与APP管理、 58 | 运营相关的微信订阅号和/或客户端消息和/或电子邮件和/ 59 | 或短消息。 60 | 2.4.注册成功后,您有权根据APP相关页面公示的服务规 61 | 称“瑞幸咖啡标志"。与 APP相关的其他商标、服务标 62 | 志、图像和标识为其各自所有人的商标(统称“第三方标 63 | 志") 64 | 未经瑞幸咖啡或相关商标持有人的事先书面许 65 | 可,用户不得全部或部分地复制、模仿或使用瑞幸咖啡标 66 | 志或第三方标志。APP和内容受到与著作权、商标、专 67 | 利、商业秘密及其他专有权利相关的国际条约、法律、法 68 | 规、行政规章等规定的保护,您可在瑞幸咖啡或内容所有 69 | 人授权的情况下使用包含保护数字信息的安全组件。 70 | 6.合约期限及变更、终止 71 | 6.1我们和您订立的这份协议是无固定期限协议。 72 | 6.2您有权在结清全部应付费用后,随时通过永久性删除 73 | 移动设备上安装的APP程序来终止协议。 74 | 6.3我们保留随时修改或替换本协议,或者更改、暂停服 75 | 务及APP程序(包括但不限于任何功能、数据库或内容) 76 | 的权利。 77 | 7.其他 78 | 7.1本用户协议部分条款或附件无效或终止的,我们有权 79 | 根据具体情况选择是否继续履行其他条款。 80 | 7.2本协议适用中国法律。本协议履行中发生的任何争 81 | 议,由瑞幸咖啡所在地人民法院管辖。''', 82 | style: TextStyle( 83 | color: Color.fromRGBO(56, 56, 56, 1), 84 | fontSize: 14, 85 | height: 1.4), 86 | ), 87 | Container( 88 | padding: EdgeInsets.only(right: 10), 89 | child: Row( 90 | children: [ 91 | Expanded( 92 | child: Text( 93 | '瑞幸咖啡(北京)有限公司', 94 | style: TextStyle( 95 | fontSize: 14, color: Color.fromRGBO(56, 56, 56, 1)), 96 | textAlign: TextAlign.right, 97 | ), 98 | ) 99 | ], 100 | ), 101 | ) 102 | ], 103 | ), 104 | ), 105 | )), 106 | ); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /lib/pages/order/order_evaluation.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/components/a_button/index.dart'; 3 | 4 | import 'package:flutter_luckin_coffee/utils/global.dart'; 5 | import 'package:get/get.dart'; 6 | 7 | class OrderEvaluation extends StatefulWidget { 8 | OrderEvaluation({Key key}) : super(key: key); 9 | 10 | _OrderEvaluationState createState() => _OrderEvaluationState(); 11 | } 12 | 13 | class _OrderEvaluationState extends State { 14 | double bottom = Get.bottomBarHeight; // 获取屏幕的底部距离 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Scaffold( 19 | backgroundColor: Colors.white, 20 | appBar: customAppbar( 21 | context: context, 22 | title: "评价", 23 | ), 24 | body: Container( 25 | color: Color.fromRGBO(248, 248, 248, 1), 26 | child: Stack( 27 | children: [ 28 | Container( 29 | child: Column( 30 | children: [ 31 | Container( 32 | padding: EdgeInsets.only(top: 25), 33 | color: Color.fromRGBO(255, 255, 255, 1), 34 | child: Column( 35 | children: [ 36 | Row( 37 | mainAxisAlignment: MainAxisAlignment.center, 38 | children: [ 39 | Text( 40 | '“满意”', 41 | style: TextStyle( 42 | color: Color.fromRGBO(56, 56, 56, 1), 43 | fontSize: 18), 44 | ) 45 | ], 46 | ), 47 | Container( 48 | margin: EdgeInsets.only(top: 20), 49 | child: Row( 50 | mainAxisAlignment: MainAxisAlignment.center, 51 | children: [ 52 | Row( 53 | children: [ 54 | Container( 55 | margin: EdgeInsets.only(right: 15), 56 | child: Icon( 57 | Icons.sentiment_satisfied_alt_outlined, 58 | color: Color.fromRGBO(244, 236, 44, 1), 59 | size: 56, 60 | ), 61 | ), 62 | Container( 63 | margin: EdgeInsets.only(left: 15), 64 | child: Icon( 65 | Icons.sentiment_dissatisfied_outlined, 66 | color: Color.fromRGBO(188, 188, 188, 1), 67 | size: 56, 68 | ), 69 | ) 70 | ], 71 | ), 72 | ], 73 | ), 74 | ), 75 | 76 | // 输入框 77 | Container( 78 | margin: EdgeInsets.only(top: 30, bottom: 20), 79 | padding: EdgeInsets.symmetric(horizontal: 15), 80 | child: Row( 81 | children: [ 82 | Expanded( 83 | child: TextField( 84 | maxLines: 5, 85 | keyboardType: TextInputType.multiline, 86 | decoration: InputDecoration( 87 | filled: true, 88 | focusColor: 89 | Color.fromRGBO(248, 248, 248, 1), 90 | counterText: '', 91 | border: OutlineInputBorder( 92 | borderSide: BorderSide.none, 93 | borderRadius: BorderRadius.all( 94 | Radius.circular(4.0), 95 | ), 96 | ), 97 | contentPadding: EdgeInsets.all(10), 98 | hintText: '请输入评价内容(可不填)', 99 | hintStyle: TextStyle( 100 | fontSize: 14, 101 | color: 102 | Color.fromRGBO(166, 166, 166, 1)), 103 | fillColor: 104 | Color.fromRGBO(248, 248, 248, 1)), 105 | ), 106 | ) 107 | ], 108 | ), 109 | ) 110 | ], 111 | ), 112 | ), 113 | ], 114 | ), 115 | ), 116 | Positioned( 117 | left: 0, 118 | bottom: 0, 119 | child: Container( 120 | width: Get.width, 121 | padding: EdgeInsets.only(bottom: bottom, top: bottom / 2), 122 | color: Colors.white, 123 | alignment: Alignment.center, 124 | child: AButton.normal( 125 | width: 300, 126 | child: Text('提交'), 127 | bgColor: Color.fromRGBO(144, 192, 239, 1), 128 | color: Colors.white, 129 | onPressed: () => {}), 130 | ), 131 | ) 132 | ], 133 | ), 134 | ), 135 | ); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /lib/pages/order/order_remark.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/components/a_button/index.dart'; 3 | import 'package:flutter_luckin_coffee/pages/toolbar/menu/widgets/radius_btn.dart'; 4 | import 'package:flutter_luckin_coffee/utils/global.dart'; 5 | import 'package:get/get.dart'; 6 | 7 | class OrderRemark extends StatefulWidget { 8 | OrderRemark({Key key}) : super(key: key); 9 | 10 | _OrderRemarkState createState() => _OrderRemarkState(); 11 | } 12 | 13 | class _OrderRemarkState extends State { 14 | @override 15 | Widget build(BuildContext context) { 16 | return Scaffold( 17 | appBar: customAppbar(title: '订单备注', context: context), 18 | body: SingleChildScrollView( 19 | child: Container( 20 | color: Colors.white, 21 | child: Column( 22 | children: [ 23 | Container( 24 | padding: EdgeInsets.symmetric(horizontal: 15), 25 | child: Column( 26 | children: [ 27 | RemarkRow('纸巾'), 28 | RemarkRow('奶包'), 29 | RemarkRow('糖包', borderBottom: false), 30 | ], 31 | )), 32 | 33 | // 输入框 34 | Container( 35 | margin: EdgeInsets.only(top: 10, bottom: 20), 36 | padding: EdgeInsets.symmetric(horizontal: 15), 37 | child: Row( 38 | children: [ 39 | Expanded( 40 | child: TextField( 41 | maxLines: 5, 42 | keyboardType: TextInputType.multiline, 43 | decoration: InputDecoration( 44 | filled: true, 45 | focusColor: Color.fromRGBO(248, 248, 248, 1), 46 | counterText: '', 47 | border: OutlineInputBorder( 48 | borderSide: BorderSide.none, 49 | borderRadius: BorderRadius.all( 50 | Radius.circular(4.0), 51 | ), 52 | ), 53 | contentPadding: EdgeInsets.all(10), 54 | hintText: '请输入评价内容(可不填)', 55 | hintStyle: TextStyle( 56 | fontSize: 14, 57 | color: Color.fromRGBO(166, 166, 166, 1)), 58 | fillColor: Color.fromRGBO(248, 248, 248, 1)), 59 | ), 60 | ) 61 | ], 62 | ), 63 | ), 64 | 65 | Container( 66 | child: AButton.normal( 67 | width: 300, 68 | child: Text('确认'), 69 | color: Colors.white, 70 | bgColor: Color.fromRGBO(144, 192, 239, 1), 71 | onPressed: () => Get.toNamed('/order_confirm')), 72 | ) 73 | ], 74 | ), 75 | ), 76 | )); 77 | } 78 | } 79 | 80 | class RemarkRow extends StatelessWidget { 81 | final String title; 82 | final bool borderBottom; 83 | 84 | RemarkRow(this.title, {this.borderBottom = true}); 85 | 86 | @override 87 | Widget build(BuildContext context) { 88 | return Container( 89 | padding: EdgeInsets.only(top: 20, bottom: 10), 90 | decoration: BoxDecoration(border: cuBorderBottom(show: borderBottom)), 91 | child: Column( 92 | mainAxisAlignment: MainAxisAlignment.start, 93 | crossAxisAlignment: CrossAxisAlignment.start, 94 | children: [ 95 | Container( 96 | margin: EdgeInsets.only(bottom: 10), 97 | child: Text( 98 | title, 99 | style: 100 | TextStyle(color: Color.fromRGBO(56, 56, 56, 1), fontSize: 14), 101 | ), 102 | ), 103 | Row( 104 | children: [ 105 | RadiusBtn( 106 | '不需要', 107 | isActive: true, 108 | ), 109 | RadiusBtn('需要'), 110 | ], 111 | ) 112 | ], 113 | ), 114 | ); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /lib/pages/order/widgets/goods_msg_row.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/utils/global.dart'; 3 | 4 | class GoodsMsgRow extends StatelessWidget { 5 | GoodsMsgRow({Key key, this.marginBottom = true}); 6 | 7 | /// 底部距离 8 | final bool marginBottom; 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Container( 13 | key: key, 14 | margin: EdgeInsets.only(bottom: 12), 15 | child: Row( 16 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 17 | children: [ 18 | Column( 19 | crossAxisAlignment: CrossAxisAlignment.start, 20 | mainAxisAlignment: MainAxisAlignment.center, 21 | children: [ 22 | Container( 23 | child: Text( 24 | '三问题', 25 | style: TextStyle( 26 | fontSize: 15, 27 | color: Color.fromRGBO(56, 56, 56, 1), 28 | fontWeight: FontWeight.bold), 29 | ), 30 | ), 31 | Container( 32 | child: Text( 33 | '${G.handleGoodsDesc("规格:中,温度:热,糖度:无糖").replaceAll(',', '/')}', 34 | style: TextStyle( 35 | fontSize: 10, 36 | color: Color.fromRGBO(56, 56, 56, 1), 37 | ), 38 | ), 39 | ), 40 | ], 41 | ), 42 | Row( 43 | children: [ 44 | Text( 45 | 'x23', 46 | style: TextStyle( 47 | color: Color.fromRGBO(80, 80, 80, 1), fontSize: 13), 48 | ), 49 | Container( 50 | margin: EdgeInsets.only(left: 80), 51 | child: Text( 52 | '¥ 36', 53 | style: TextStyle( 54 | fontSize: 14, 55 | color: Color.fromRGBO(56, 56, 56, 1), 56 | fontWeight: FontWeight.bold), 57 | ), 58 | ) 59 | ], 60 | ), 61 | ], 62 | ), 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/pages/selfstore/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:flutter_luckin_coffee/utils/global.dart'; 4 | 5 | class SelfStore extends StatefulWidget { 6 | SelfStore({Key key}) : super(key: key); 7 | 8 | _SelfStoreState createState() => _SelfStoreState(); 9 | } 10 | 11 | class _SelfStoreState extends State { 12 | @override 13 | Widget build(BuildContext context) { 14 | return Scaffold( 15 | appBar: customAppbar(title: '门店自提', context: context, actions: [ 16 | Container( 17 | padding: EdgeInsets.only(right: 10), 18 | child: Row( 19 | children: [ 20 | Text( 21 | '北京', 22 | style: TextStyle( 23 | color: Color.fromRGBO(56, 56, 56, 1), fontSize: 12), 24 | ), 25 | Icon( 26 | IconData(0xe622, fontFamily: 'iconfont'), 27 | size: 20, 28 | color: Color.fromRGBO(56, 56, 56, 1), 29 | ) 30 | ], 31 | ), 32 | ) 33 | ]), 34 | body: Container( 35 | color: Colors.white, 36 | child: ListView( 37 | children: [ 38 | AddressRow(), 39 | AddressRow(), 40 | AddressRow(), 41 | AddressRow(), 42 | AddressRow(), 43 | AddressRow(), 44 | AddressRow(), 45 | AddressRow(), 46 | AddressRow(), 47 | AddressRow(), 48 | AddressRow( 49 | borderBottom: false, 50 | ), 51 | ], 52 | ), 53 | ), 54 | ); 55 | } 56 | } 57 | 58 | class AddressRow extends StatelessWidget { 59 | final bool borderBottom; 60 | 61 | AddressRow({this.borderBottom = true}); 62 | 63 | @override 64 | Widget build(BuildContext context) { 65 | return Container( 66 | padding: EdgeInsets.symmetric(horizontal: 15), 67 | child: Container( 68 | padding: EdgeInsets.symmetric(vertical: 12), 69 | decoration: BoxDecoration(border: cuBorderBottom(show: borderBottom)), 70 | child: Column( 71 | children: [ 72 | Row( 73 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 74 | children: [ 75 | Text( 76 | '青年汇店(No.1795)', 77 | style: TextStyle( 78 | color: Color.fromRGBO(56, 56, 56, 1), fontSize: 16), 79 | ), 80 | Text( 81 | '55m', 82 | style: TextStyle( 83 | color: Color.fromRGBO(56, 56, 56, 1), fontSize: 14), 84 | ), 85 | ], 86 | ), 87 | Container( 88 | child: Row( 89 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 90 | crossAxisAlignment: CrossAxisAlignment.center, 91 | children: [ 92 | Row( 93 | crossAxisAlignment: CrossAxisAlignment.center, 94 | children: [ 95 | Container( 96 | alignment: Alignment.centerLeft, 97 | width: 15, 98 | height: 20, 99 | child: Icon( 100 | Icons.access_alarm, 101 | color: Color.fromRGBO(217, 217, 217, 1), 102 | size: 12, 103 | ), 104 | ), 105 | Container( 106 | margin: EdgeInsets.only(top: 1), 107 | child: Text( 108 | '07:00 - 20:00', 109 | style: TextStyle( 110 | color: Color.fromRGBO(217, 217, 217, 1), 111 | fontSize: 12), 112 | ), 113 | ), 114 | ], 115 | ), 116 | Row( 117 | children: [ 118 | InkWell( 119 | child: Text( 120 | '查看详情', 121 | style: TextStyle( 122 | color: Color.fromRGBO(85, 122, 157, 1), 123 | fontSize: 11), 124 | ), 125 | onTap: () => 126 | Navigator.pushNamed(context, '/store_detail'), 127 | ), 128 | Container( 129 | margin: EdgeInsets.only(top: 2), 130 | child: Icon( 131 | Icons.arrow_forward_ios, 132 | color: Color.fromRGBO(85, 122, 157, 1), 133 | size: 11, 134 | ), 135 | ) 136 | ], 137 | ), 138 | ], 139 | ), 140 | ), 141 | Row( 142 | children: [ 143 | Container( 144 | alignment: Alignment.centerLeft, 145 | width: 15, 146 | height: 20, 147 | child: Icon( 148 | Icons.location_on_outlined, 149 | color: Color.fromRGBO(166, 166, 166, 1), 150 | size: 12, 151 | ), 152 | ), 153 | Container( 154 | child: Text( 155 | '朝阳区朝阳北路青年汇102号楼一号3室', 156 | style: TextStyle( 157 | color: Color.fromRGBO(166, 166, 166, 1), fontSize: 12), 158 | ), 159 | ) 160 | ], 161 | ) 162 | ], 163 | ), 164 | ), 165 | ); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /lib/pages/storedetail/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:flutter_luckin_coffee/utils/global.dart'; 4 | 5 | class StoreDetail extends StatefulWidget { 6 | StoreDetail(); 7 | 8 | _StoreDetailState createState() => _StoreDetailState(); 9 | } 10 | 11 | class _StoreDetailState extends State { 12 | @override 13 | Widget build(BuildContext context) { 14 | return Scaffold( 15 | appBar: customAppbar(title: '门店详情', context: context), 16 | body: Container( 17 | child: Column( 18 | children: [ 19 | Container( 20 | padding: EdgeInsets.symmetric(horizontal: 15, vertical: 18), 21 | color: Colors.white, 22 | child: Column( 23 | children: [ 24 | Row( 25 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 26 | children: [ 27 | Row( 28 | children: [ 29 | Text( 30 | '青年汇店(No.1795)', 31 | style: TextStyle( 32 | color: Color.fromRGBO(56, 56, 56, 1), 33 | fontSize: 18), 34 | ), 35 | Container( 36 | decoration: BoxDecoration( 37 | border: Border.all( 38 | color: Color.fromRGBO(255, 141, 26, 1)), 39 | borderRadius: BorderRadius.circular(4), 40 | ), 41 | padding: EdgeInsets.symmetric( 42 | vertical: 2, horizontal: 4), 43 | child: Text( 44 | 'PICKUP', 45 | style: TextStyle( 46 | color: Color.fromRGBO(255, 141, 26, 1), 47 | fontSize: 14, 48 | fontStyle: FontStyle.italic), 49 | ), 50 | ), 51 | ], 52 | ), 53 | Container( 54 | margin: EdgeInsets.only(left: 3), 55 | child: Icon( 56 | Icons.arrow_forward_ios_outlined, 57 | color: Color.fromRGBO(148, 196, 236, 1), 58 | size: 16, 59 | ), 60 | ) 61 | ], 62 | ), 63 | Container( 64 | margin: EdgeInsets.only(top: 5), 65 | child: Row( 66 | children: [ 67 | Container( 68 | width: 65, 69 | child: Text( 70 | '营业时间:', 71 | style: TextStyle( 72 | color: Color.fromRGBO(128, 128, 128, 1), 73 | fontSize: 12), 74 | ), 75 | ), 76 | Text( 77 | '周一至周五 07:00-20:00', 78 | style: TextStyle( 79 | color: Color.fromRGBO(128, 128, 128, 1), 80 | fontSize: 12), 81 | ) 82 | ], 83 | ), 84 | ), 85 | Container( 86 | child: Row( 87 | children: [ 88 | Container( 89 | width: 65, 90 | ), 91 | Text( 92 | '周六 08:00-18:00', 93 | style: TextStyle( 94 | color: Color.fromRGBO(128, 128, 128, 1), 95 | fontSize: 12), 96 | ) 97 | ], 98 | ), 99 | ), 100 | Container( 101 | child: Row( 102 | children: [ 103 | Container( 104 | width: 65, 105 | ), 106 | Text( 107 | '周日 08:00-18:00', 108 | style: TextStyle( 109 | color: Color.fromRGBO(128, 128, 128, 1), 110 | fontSize: 12), 111 | ) 112 | ], 113 | ), 114 | ), 115 | Container( 116 | margin: EdgeInsets.only(top: 2), 117 | child: Row( 118 | mainAxisAlignment: MainAxisAlignment.start, 119 | children: [ 120 | Container( 121 | width: 65, 122 | child: Text( 123 | '门店地址:', 124 | style: TextStyle( 125 | color: Color.fromRGBO(128, 128, 128, 1), 126 | fontSize: 12), 127 | ), 128 | ), 129 | Expanded( 130 | child: Text( 131 | '奥术大师大撒大所大萨', 132 | style: TextStyle( 133 | color: Color.fromRGBO(128, 128, 128, 1), 134 | fontSize: 12), 135 | ), 136 | ) 137 | ], 138 | ), 139 | ), 140 | ], 141 | ), 142 | ), 143 | 144 | // 地图 145 | Container( 146 | margin: EdgeInsets.only(top: 25), 147 | padding: EdgeInsets.symmetric(horizontal: 15, vertical: 18), 148 | color: Colors.white, 149 | child: Column( 150 | children: [ 151 | Row( 152 | children: [ 153 | Text( 154 | '门店地图', 155 | style: TextStyle( 156 | color: Color.fromRGBO(56, 56, 56, 1), fontSize: 15), 157 | ) 158 | ], 159 | ), 160 | Container( 161 | margin: EdgeInsets.only(top: 10), 162 | child: Image.asset( 163 | 'lib/assets/images/map.png', 164 | height: 120, 165 | fit: BoxFit.cover, 166 | ), 167 | ) 168 | ], 169 | ), 170 | ) 171 | ], 172 | ), 173 | ), 174 | ); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /lib/pages/toolbar/home/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/components/a_row/a_row.dart'; 3 | import 'package:flutter_luckin_coffee/components/custom_swiper/index.dart'; 4 | import 'package:flutter_luckin_coffee/components/take_out_btn/index.dart'; 5 | import 'package:get/get.dart'; 6 | 7 | class Home extends StatefulWidget { 8 | _HomeState createState() => _HomeState(); 9 | } 10 | 11 | class _HomeState extends State { 12 | @override 13 | Widget build(BuildContext context) { 14 | return SingleChildScrollView( 15 | child: Column( 16 | children: [ 17 | // 头部banner 18 | Stack( 19 | children: [ 20 | // 轮播图 21 | Positioned( 22 | child: CustomSwiper( 23 | [ 24 | 'lib/assets/images/home/swiper1.jpg', 25 | 'lib/assets/images/home/swiper2.jpg', 26 | 'lib/assets/images/home/swiper3.jpg', 27 | ], 28 | height: 208 + Get.statusBarHeight, 29 | ), 30 | ), 31 | 32 | // 扫描二维码 33 | Positioned( 34 | top: 50, 35 | right: 10, 36 | child: rightChild( 37 | icon: Icon(Icons.camera_alt), 38 | bgColor: Color.fromRGBO(0, 0, 0, .3), 39 | showBorder: false, 40 | ), 41 | ) 42 | ], 43 | ), 44 | 45 | Container( 46 | padding: EdgeInsets.symmetric(horizontal: 20), 47 | child: Column( 48 | children: [ 49 | ARow( 50 | height: 70, 51 | padding: EdgeInsets.all(0), 52 | leftChild: 53 | leftChild(title: '火车南站', desc: '距您53m', titleBlod: false), 54 | rightChild: TakeOutBtn(), 55 | ), 56 | ARow( 57 | height: 70, 58 | padding: EdgeInsets.all(0), 59 | leftChild: leftChild(title: '现在下单', desc: 'ORDER NOW'), 60 | rightChild: rightChild( 61 | icon: Icon( 62 | Icons.shopping_bag, 63 | color: Color.fromRGBO(99, 71, 58, 1), 64 | size: 20, 65 | ), 66 | ), 67 | onPressed: () => {}, 68 | ), 69 | ARow( 70 | height: 70, 71 | padding: EdgeInsets.all(0), 72 | leftChild: leftChild(title: '咖啡钱包', desc: 'COFFRR WALLET'), 73 | rightChild: rightChild( 74 | icon: Icon( 75 | Icons.wallet_giftcard, 76 | size: 20, 77 | color: Color.fromRGBO(104, 68, 60, 1), 78 | ), 79 | ), 80 | ), 81 | ARow( 82 | height: 70, 83 | padding: EdgeInsets.all(0), 84 | leftChild: leftChild(title: '送Ta咖啡', desc: 'SEND COFFEE'), 85 | rightChild: rightChild( 86 | icon: Icon(Icons.coffee, 87 | size: 20, color: Color.fromRGBO(104, 68, 60, 1)), 88 | ), 89 | ), 90 | ARow( 91 | height: 70, 92 | padding: EdgeInsets.all(0), 93 | leftChild: 94 | leftChild(title: '企业账号', desc: 'ENTERPRISE ACCOUNT'), 95 | rightChild: rightChild( 96 | icon: Icon( 97 | Icons.article_rounded, 98 | size: 20, 99 | color: Color.fromRGBO(104, 68, 60, 1), 100 | ), 101 | ), 102 | onPressed: () => {}, 103 | ), 104 | Container( 105 | child: Image.asset('lib/assets/images/home/bottom_bar.png'), 106 | ) 107 | ], 108 | ), 109 | ), 110 | ], 111 | ), 112 | ); 113 | } 114 | 115 | Widget rightChild({Icon icon, Color bgColor, bool showBorder = true}) { 116 | return Container( 117 | width: 40, 118 | height: 40, 119 | decoration: BoxDecoration( 120 | color: bgColor, 121 | border: Border.all( 122 | color: 123 | showBorder ? Color.fromRGBO(99, 71, 58, 1) : Colors.transparent, 124 | ), 125 | borderRadius: BorderRadius.circular(20), 126 | ), 127 | child: Container( 128 | padding: EdgeInsets.only(left: 2), 129 | child: icon, 130 | ), 131 | ); 132 | } 133 | 134 | Column leftChild({String title, String desc, bool titleBlod = true}) { 135 | return Column( 136 | mainAxisAlignment: MainAxisAlignment.center, 137 | crossAxisAlignment: CrossAxisAlignment.start, 138 | children: [ 139 | Row( 140 | children: [ 141 | Text( 142 | title, 143 | style: TextStyle( 144 | color: Color.fromRGBO(56, 56, 56, 1), 145 | fontSize: 16, 146 | fontWeight: titleBlod ? FontWeight.bold : FontWeight.normal), 147 | ) 148 | ], 149 | ), 150 | Row( 151 | children: [ 152 | Text( 153 | desc, 154 | style: TextStyle( 155 | color: Color.fromRGBO(128, 128, 128, 1), fontSize: 12), 156 | ) 157 | ], 158 | ) 159 | ], 160 | ); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /lib/pages/toolbar/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/pages/toolbar/home/index.dart'; 3 | import 'package:flutter_luckin_coffee/pages/toolbar/menu/index.dart'; 4 | import 'package:flutter_luckin_coffee/pages/toolbar/mine/index.dart'; 5 | import 'package:flutter_luckin_coffee/pages/toolbar/order/index.dart'; 6 | import 'package:flutter_luckin_coffee/pages/toolbar/shopping_cart/index.dart'; 7 | 8 | class Toolbar extends StatefulWidget { 9 | Toolbar({Key key}) : super(key: key); 10 | 11 | @override 12 | State createState() => _ToolbarState(); 13 | } 14 | 15 | class _ToolbarState extends State { 16 | int cuttentIndex = 0; 17 | 18 | PageController _pageController = PageController(); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Scaffold( 23 | body: PageView( 24 | controller: _pageController, 25 | physics: NeverScrollableScrollPhysics(), 26 | children: [ 27 | Home(), 28 | Menu(), 29 | Order(), 30 | ShoppingCart(), 31 | Mine(), 32 | ], 33 | ), 34 | bottomNavigationBar: BottomNavigationBar( 35 | selectedItemColor: Color.fromRGBO(43, 76, 126, 1), 36 | unselectedItemColor: Color.fromRGBO(43, 76, 126, .5), 37 | currentIndex: cuttentIndex, 38 | type: BottomNavigationBarType.fixed, 39 | onTap: (index) { 40 | setState(() { 41 | _pageController.jumpToPage(index); 42 | cuttentIndex = index; 43 | }); 44 | }, 45 | items: [ 46 | BottomNavigationBarItem( 47 | icon: Icon(Icons.home), 48 | label: '首页', 49 | ), 50 | BottomNavigationBarItem( 51 | icon: Icon(Icons.menu_book), 52 | label: '菜单', 53 | ), 54 | BottomNavigationBarItem( 55 | icon: Icon(Icons.article_rounded), 56 | label: '订单', 57 | ), 58 | BottomNavigationBarItem( 59 | icon: Icon( 60 | Icons.shopping_cart, 61 | ), 62 | label: '购物车', 63 | ), 64 | BottomNavigationBarItem( 65 | icon: Icon(Icons.person), 66 | label: '我的', 67 | ), 68 | ], 69 | ), 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/pages/toolbar/menu/category.dart: -------------------------------------------------------------------------------- 1 | // import 'package:flutter/material.dart'; 2 | // import 'package:flutter_luckin_coffee/jsonserialize/goods_category/data.dart'; 3 | // import 'package:flutter_luckin_coffee/mock/goods_category.dart'; 4 | // import 'package:flutter_luckin_coffee/utils/global.dart'; 5 | 6 | // class Category extends StatelessWidget { 7 | // final Function getCayegoryId; 8 | // final int id; 9 | 10 | // const Category({Key key, @required this.getCayegoryId, this.id}) 11 | // : super(key: key); 12 | 13 | // List _render() { 14 | // List widgets = []; 15 | // Mocksssss.forEach((val) { 16 | // var index = Mocksssss.indexOf(val); 17 | // widgets.add(InkWell( 18 | // key: Key('$index'), 19 | // onTap: () => getCayegoryId("$index"), 20 | // child: Container( 21 | // alignment: Alignment.center, 22 | // height: 44, 23 | // decoration: BoxDecoration( 24 | // border: cuBorderBottom(show: id == index), 25 | // color: Color(0xffffff).withOpacity(id == index ? 1.0 : 0.0)), 26 | // child: Text(val), 27 | // ), 28 | // )); 29 | // }); 30 | 31 | // return widgets; 32 | // } 33 | 34 | // @override 35 | // Widget build(BuildContext context) { 36 | // return Column( 37 | // children: _render(), 38 | // ); 39 | // } 40 | // } 41 | -------------------------------------------------------------------------------- /lib/pages/toolbar/menu/goods_list_row.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/mock/goods_list.dart'; 3 | import 'package:flutter_luckin_coffee/utils/global.dart'; 4 | 5 | class GoodsListRow extends StatelessWidget { 6 | final bool border; 7 | final String activeDesc; 8 | final Function onPress; 9 | final MockGoods data; 10 | 11 | /// 创建商品 12 | /// 13 | /// ``` 14 | /// @param {bool} border - 是否显示底部的border 15 | /// @param {String} activeDesc - 活动描述 16 | /// @param {Function} onPress - 点击添加按钮 17 | /// @param {GoodsListDatum} data 18 | /// ``` 19 | GoodsListRow({this.border = true, this.activeDesc, this.onPress, this.data}); 20 | 21 | /// 商品信息中的文本 22 | /// 23 | /// ``` 24 | /// @param {String} title - 标题 25 | /// @param {double} fontSize 26 | /// @param {FontWeight} fontWeight 27 | /// @param {Color} color 28 | /// ``` 29 | Widget row(String title, 30 | {double fontSize = 11, FontWeight fontWeight, Color color}) { 31 | return Row( 32 | children: [ 33 | Expanded( 34 | child: Text( 35 | title, 36 | overflow: TextOverflow.ellipsis, 37 | style: TextStyle( 38 | color: color == null ? Color.fromRGBO(166, 166, 166, 1) : color, 39 | fontSize: fontSize, 40 | fontWeight: fontWeight == null ? FontWeight.normal : fontWeight, 41 | ), 42 | ), 43 | ) 44 | ], 45 | ); 46 | } 47 | 48 | /// 商品图片 49 | Widget goodsImg(String imgSrc) { 50 | return ClipRRect( 51 | borderRadius: new BorderRadius.circular(4.0), 52 | child: Image.network( 53 | imgSrc, 54 | width: 70, 55 | height: 70, 56 | fit: BoxFit.cover, 57 | ), 58 | ); 59 | } 60 | 61 | ///活动信息 62 | Widget activeMsg({String text}) { 63 | return text == null 64 | ? Container() 65 | : Positioned( 66 | right: 0, 67 | bottom: 5, 68 | child: Container( 69 | height: 16, 70 | padding: EdgeInsets.symmetric(horizontal: 4), 71 | alignment: Alignment.center, 72 | decoration: BoxDecoration( 73 | borderRadius: new BorderRadius.circular(2.0), 74 | color: Color.fromRGBO(255, 129, 2, 1)), 75 | child: Text( 76 | text, 77 | style: TextStyle(color: Colors.white, fontSize: 8), 78 | ), 79 | ), 80 | ); 81 | } 82 | 83 | @override 84 | Widget build(BuildContext context) { 85 | return Row( 86 | children: [ 87 | Expanded( 88 | child: Container( 89 | padding: EdgeInsets.symmetric(vertical: 10), 90 | decoration: BoxDecoration( 91 | border: cuBorderBottom(show: border), 92 | ), 93 | child: Row( 94 | children: [ 95 | Container( 96 | width: 80, 97 | height: 70, 98 | child: Stack( 99 | children: [ 100 | Positioned( 101 | child: goodsImg(data.pic), 102 | ), 103 | activeMsg(text: activeDesc) 104 | ], 105 | ), 106 | ), 107 | Expanded( 108 | child: Container( 109 | margin: EdgeInsets.only(left: 6), 110 | child: Column( 111 | children: [ 112 | row(data.name, 113 | color: Color.fromRGBO(56, 56, 56, 1), 114 | fontSize: 15, 115 | fontWeight: FontWeight.bold), 116 | row(data.characteristic.isEmpty 117 | ? '' 118 | : data.characteristic), 119 | row(""), 120 | Container( 121 | margin: EdgeInsets.only(top: 4), 122 | child: Row( 123 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 124 | crossAxisAlignment: CrossAxisAlignment.center, 125 | children: [ 126 | Text( 127 | "¥ ${data.originalPrice}", 128 | style: TextStyle( 129 | color: Color.fromRGBO(56, 56, 56, 1), 130 | fontSize: 15, 131 | fontWeight: FontWeight.bold), 132 | ), 133 | InkWell( 134 | child: Container( 135 | width: 25, 136 | height: 25, 137 | child: Icon( 138 | Icons.add_circle, 139 | color: Color.fromRGBO(136, 175, 213, 1), 140 | size: 25, 141 | ), 142 | ), 143 | onTap: () { 144 | if (onPress != null) { 145 | onPress(context); 146 | } 147 | }) 148 | ], 149 | ), 150 | ) 151 | ], 152 | ), 153 | ), 154 | ), 155 | ], 156 | ), 157 | ), 158 | ) 159 | ], 160 | ); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /lib/pages/toolbar/menu/index.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_luckin_coffee/components/custom_swiper/index.dart'; 5 | import 'package:flutter_luckin_coffee/components/goods_detail/index.dart'; 6 | import 'package:flutter_luckin_coffee/mock/goods_category.dart'; 7 | import 'package:flutter_luckin_coffee/mock/goods_list.dart'; 8 | import 'package:get/get.dart'; 9 | 10 | import 'goods_list_row.dart'; 11 | import 'widgets/classify_desc.dart'; 12 | 13 | /// TODO: 待解决:点击左侧菜单,右侧商品列表跳转 14 | class Menu extends StatefulWidget { 15 | static _MenuState _menuState; 16 | 17 | getAppBar() => _menuState.createAppBar(); 18 | 19 | Menu() { 20 | _menuState = _MenuState(); 21 | } 22 | 23 | _MenuState createState() => _MenuState(); 24 | } 25 | 26 | class _MenuState extends State { 27 | static int nowCategoryId; // 当前选中的菜单 28 | static double _nestedScrollOffet = 0; 29 | 30 | final ScrollController _nestedScrollController = new ScrollController(); 31 | 32 | Map categoryCombineGoods = {}; 33 | 34 | final List actives = ['满50减20', '充2赠1', '买2送1']; 35 | 36 | /// 把商品列表,和分类的部分设置为变量。 37 | /// 解决:滚动页面,重复渲染 38 | List goodsListWidgets = []; 39 | List category = MockGoodsCategory.data(); 40 | 41 | AppBar createAppBar() { 42 | return null; 43 | } 44 | 45 | @override 46 | void initState() { 47 | _nestedScrollController.addListener(() { 48 | setState(() { 49 | _nestedScrollOffet = _nestedScrollController.offset; 50 | }); 51 | }); 52 | 53 | Future.delayed(Duration.zero, () async { 54 | _init(context); 55 | }); 56 | 57 | super.initState(); 58 | } 59 | 60 | _init(BuildContext context) async { 61 | // G.loading.show(context); 62 | 63 | List goods = MockGoods.data(); 64 | 65 | List goodsListWidgetsTemp = []; 66 | Random rand = Random(); // 随机数 67 | 68 | category.forEach((item) { 69 | var categoryId = category.indexOf(item); 70 | // 商品列表 每类商品 标题 eg: 人气top 71 | goodsListWidgetsTemp.add(ClassifyDesc(item.name, desc: item.desc)); 72 | 73 | goods.forEach((goodsItem) { 74 | var goodsId = goods.indexOf(goodsItem); 75 | if (categoryId == goodsItem.categoryId) { 76 | // 商品列表 商品 77 | goodsListWidgetsTemp.add( 78 | GoodsListRow( 79 | // 点击添加按钮弹出dialog 80 | onPress: (BuildContext context) { 81 | /// 弹出商品详情 /widgets/goods_detail 82 | showDialog( 83 | context: context, 84 | barrierDismissible: false, 85 | builder: (BuildContext context) { 86 | return GoodsDetailDialog( 87 | id: goodsId, 88 | ); 89 | }, 90 | ); 91 | }, 92 | data: goodsItem, 93 | border: !(goodsId >= goods.length - 1), 94 | activeDesc: actives[rand.nextInt(3)], 95 | ), 96 | ); 97 | } 98 | }); 99 | }); 100 | 101 | setState(() { 102 | nowCategoryId = 0; 103 | goodsListWidgets = goodsListWidgetsTemp; 104 | category = MockGoodsCategory.data(); 105 | }); 106 | 107 | // G.loading.hide(context); 108 | } 109 | 110 | @override 111 | Widget build(BuildContext context) { 112 | return NestedScrollView( 113 | controller: _nestedScrollController, 114 | headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { 115 | return [ 116 | SliverAppBar( 117 | expandedHeight: 186, 118 | pinned: true, 119 | floating: false, 120 | elevation: 0, 121 | title: Text( 122 | '选择咖啡和小食', 123 | style: TextStyle( 124 | color: Color.fromRGBO(56, 56, 56, 1), 125 | fontSize: 16, 126 | fontWeight: FontWeight.bold), 127 | ), 128 | backgroundColor: Colors.white, 129 | flexibleSpace: FlexibleSpaceBar( 130 | background: SafeArea( 131 | child: Container( 132 | margin: EdgeInsets.only(top: 56), 133 | child: InkWell( 134 | child: Opacity( 135 | opacity: 1, 136 | child: CustomSwiper([ 137 | 'lib/assets/images/menu/swiper1.jpg', 138 | 'lib/assets/images/menu/swiper2.png', 139 | ], height: 130), 140 | ), 141 | onTap: () {}, 142 | ), 143 | ), 144 | ), 145 | ), 146 | ) 147 | ]; 148 | }, 149 | body: Container( 150 | padding: EdgeInsets.only( 151 | top: _nestedScrollOffet >= 130 ? (_nestedScrollOffet - 130) : 0), 152 | child: Row( 153 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 154 | children: [ 155 | // 左侧菜单列表 156 | Container( 157 | width: 90, 158 | color: Color.fromRGBO(248, 248, 248, 1), 159 | child: Column( 160 | children: category.map((item) { 161 | var index = category.indexOf(item); 162 | return InkWell( 163 | onTap: () { 164 | setState(() { 165 | nowCategoryId = index; 166 | }); 167 | }, 168 | child: Container( 169 | alignment: Alignment.center, 170 | height: 44, 171 | decoration: BoxDecoration( 172 | border: Border( 173 | bottom: BorderSide( 174 | color: nowCategoryId == index 175 | ? Color.fromRGBO(242, 242, 242, 1) 176 | : Colors.transparent, 177 | width: 1, 178 | ), 179 | ), 180 | color: Color(0xffffff) 181 | .withOpacity(nowCategoryId == index ? 1.0 : 0.0)), 182 | child: Text(item.name), 183 | ), 184 | ); 185 | }).toList(), 186 | ), 187 | ), 188 | 189 | // 右侧商品列表 190 | Container( 191 | width: Get.width - 90, 192 | padding: EdgeInsets.symmetric(horizontal: 14), 193 | // 使用listview中的scrollcontronal导致外层的NestedScrollView效果失效, 194 | // 使用NotificationListener完美解决 195 | child: NotificationListener( 196 | child: ListView( 197 | // 取消listView回弹 198 | physics: const ClampingScrollPhysics(), 199 | padding: EdgeInsets.zero, 200 | children: goodsListWidgets, 201 | ), 202 | onNotification: (ScrollNotification scrollInfo) { 203 | // print(scrollInfo); 204 | return null; 205 | }, 206 | ), 207 | ) 208 | ], 209 | ), 210 | ), 211 | ); 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /lib/pages/toolbar/menu/widgets/classify_desc.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: meetqy 3 | * @since: 2019-08-10 17:19:04 4 | * @lastTime: 2019-08-12 10:42:54 5 | * @LastEditors: meetqy 6 | */ 7 | import 'package:flutter/material.dart'; 8 | 9 | class ClassifyDesc extends StatelessWidget { 10 | final String name; 11 | final String desc; 12 | 13 | /// 创建商品列表显示的分类介绍 每一类商品顶部有一个对该类商品的介绍 14 | /// ``` 15 | /// @param {String} name - 名字 16 | /// @param {String} desc - 描述 17 | /// ``` 18 | ClassifyDesc(this.name, {this.desc}); 19 | 20 | _createNotDesc(String name) { 21 | return Container( 22 | child: Row( 23 | children: [ 24 | Text( 25 | name, 26 | style: TextStyle( 27 | fontSize: 12, 28 | color: Color.fromRGBO(56, 56, 56, 1), 29 | fontWeight: FontWeight.bold), 30 | ), 31 | Expanded( 32 | child: Container( 33 | margin: EdgeInsets.only(left: 10), 34 | height: 1, 35 | color: Color.fromRGBO(242, 242, 242, 1), 36 | ), 37 | ) 38 | ], 39 | )); 40 | } 41 | 42 | _create(String name, String desc) { 43 | return Column( 44 | children: [ 45 | Row( 46 | children: [ 47 | Expanded( 48 | child: Text( 49 | name, 50 | style: TextStyle( 51 | fontSize: 12, 52 | color: Color.fromRGBO(56, 56, 56, 1), 53 | fontWeight: FontWeight.bold), 54 | ), 55 | ) 56 | ], 57 | ), 58 | Row( 59 | children: [ 60 | Text( 61 | desc, 62 | style: TextStyle( 63 | fontSize: 10, 64 | color: Color.fromRGBO(166, 166, 166, 1), 65 | ), 66 | ), 67 | Expanded( 68 | child: Container( 69 | margin: EdgeInsets.only(left: 10), 70 | height: 1, 71 | color: Color.fromRGBO(242, 242, 242, 1), 72 | ), 73 | ) 74 | ], 75 | ) 76 | ], 77 | ); 78 | } 79 | 80 | @override 81 | Widget build(BuildContext context) { 82 | return Container( 83 | padding: EdgeInsets.only(top: 10, bottom: 10), 84 | child: desc == null ? _createNotDesc(name) : _create(name, desc), 85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lib/pages/toolbar/menu/widgets/dacai_say.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/utils/global.dart'; 3 | 4 | class DacaiSay extends StatelessWidget { 5 | final String avatar; 6 | final String name; 7 | final String label; 8 | final String desc; 9 | final String time; 10 | 11 | /// 大咖说 12 | /// 13 | /// ``` 14 | /// @param {String} avatar - 头像 15 | /// @param {String} name - 名字 16 | /// @param {String} label - 标签 17 | /// @param {String} desc - 描述 18 | /// @param {String} time - 时间 19 | /// ``` 20 | DacaiSay( 21 | {this.avatar, this.name, this.label = '', this.desc = '', this.time = ''}) 22 | : assert( 23 | avatar != null, 24 | name != null, 25 | ); 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return Container( 30 | padding: EdgeInsets.only(left: 15, right: 15), 31 | margin: EdgeInsets.only(top: 6), 32 | child: Container( 33 | padding: EdgeInsets.only(bottom: 8), 34 | decoration: BoxDecoration(border: cuBorderBottom()), 35 | child: Row( 36 | crossAxisAlignment: CrossAxisAlignment.start, 37 | children: [ 38 | Container( 39 | width: 30, 40 | height: 30, 41 | child: ClipRRect( 42 | borderRadius: new BorderRadius.circular(30), 43 | child: Image.asset( 44 | avatar, 45 | width: 30, 46 | height: 30, 47 | fit: BoxFit.cover, 48 | ), 49 | ), 50 | ), 51 | Expanded( 52 | child: Container( 53 | margin: EdgeInsets.only(top: 10, left: 10), 54 | child: Column( 55 | children: [ 56 | Container( 57 | child: Row( 58 | crossAxisAlignment: CrossAxisAlignment.center, 59 | children: [ 60 | Text( 61 | name, 62 | style: TextStyle(fontSize: 12), 63 | ), 64 | Container( 65 | padding: EdgeInsets.symmetric(horizontal: 5), 66 | decoration: BoxDecoration( 67 | borderRadius: BorderRadius.circular(2), 68 | color: Color.fromRGBO(242, 242, 242, 1)), 69 | margin: EdgeInsets.only(left: 5), 70 | child: Text( 71 | label, 72 | style: TextStyle( 73 | color: Color.fromRGBO(53, 60, 177, 1), 74 | fontSize: 10), 75 | ), 76 | ) 77 | ], 78 | ), 79 | ), 80 | Container( 81 | margin: EdgeInsets.only(top: 5), 82 | child: Column( 83 | children: [ 84 | Wrap( 85 | children: [ 86 | Text( 87 | desc, 88 | style: TextStyle( 89 | fontSize: 12, 90 | color: Color.fromRGBO(128, 128, 128, 1)), 91 | ), 92 | ], 93 | ) 94 | ], 95 | )), 96 | Container( 97 | margin: EdgeInsets.only(top: 5), 98 | alignment: Alignment.centerLeft, 99 | child: Text( 100 | time, 101 | style: TextStyle( 102 | color: Color.fromRGBO(136, 175, 213, 1), 103 | fontSize: 12), 104 | ), 105 | ) 106 | ], 107 | ), 108 | ), 109 | ) 110 | ], 111 | ), 112 | ), 113 | ); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /lib/pages/toolbar/menu/widgets/radius_btn.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class RadiusBtn extends StatelessWidget { 4 | final bool isActive; 5 | final String text; 6 | final Function onPress; 7 | 8 | RadiusBtn(this.text, {this.isActive = false, this.onPress}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return InkWell( 13 | onTap: () => onPress == null ? () {} : onPress(), 14 | child: Container( 15 | width: 80, 16 | height: 30, 17 | margin: EdgeInsets.only(right: 10, bottom: 10), 18 | alignment: Alignment.center, 19 | decoration: BoxDecoration( 20 | border: Border.all(color: Color.fromRGBO(204, 192, 180, 1)), 21 | borderRadius: BorderRadius.all(Radius.circular(15)), 22 | color: isActive ? Color.fromRGBO(204, 192, 180, 1) : Colors.white), 23 | child: Text( 24 | text, 25 | style: TextStyle( 26 | color: isActive ? Colors.white : Color.fromRGBO(204, 192, 180, 1), 27 | fontSize: 14), 28 | ), 29 | ), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/pages/toolbar/mine/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/components/a_row/a_row.dart'; 3 | import 'package:flutter_luckin_coffee/utils/global.dart'; 4 | import 'package:get/get.dart'; 5 | 6 | class Mine extends StatefulWidget { 7 | static _MineState _mineState; 8 | 9 | getAppBar() => _mineState.createAppBar(); 10 | 11 | Mine() { 12 | _mineState = _MineState(); 13 | } 14 | 15 | _MineState createState() => _MineState(); 16 | } 17 | 18 | class _MineState extends State { 19 | AppBar createAppBar() { 20 | return null; 21 | } 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return SingleChildScrollView( 26 | child: Container( 27 | color: Color.fromRGBO(248, 248, 248, 1), 28 | child: Column( 29 | children: [ 30 | // 头部 31 | Container( 32 | alignment: Alignment.centerLeft, 33 | height: 180, 34 | color: Color.fromRGBO(100, 68, 60, 1), 35 | padding: EdgeInsets.only(left: 20, right: 20, top: 44), 36 | child: Column( 37 | crossAxisAlignment: CrossAxisAlignment.center, 38 | children: [ 39 | Container( 40 | alignment: Alignment.centerRight, 41 | margin: EdgeInsets.only(bottom: 20), 42 | child: Icon( 43 | Icons.person, 44 | size: 24, 45 | color: Color.fromRGBO(255, 255, 255, .9), 46 | ), 47 | ), 48 | buildUser(), 49 | ], 50 | ), 51 | ), 52 | 53 | Container( 54 | color: Colors.white, 55 | padding: EdgeInsets.symmetric(horizontal: 15), 56 | child: Column( 57 | children: [ 58 | ARow( 59 | height: 50, 60 | padding: EdgeInsets.all(0), 61 | leftChild: Container( 62 | width: 30, 63 | alignment: Alignment.centerLeft, 64 | child: Icon( 65 | Icons.badge_rounded, 66 | color: Color.fromRGBO(220, 220, 220, 1), 67 | size: 20, 68 | ), 69 | ), 70 | centerChild: Text('个人资料'), 71 | rightChild: Icon( 72 | Icons.arrow_forward_ios, 73 | color: Color.fromRGBO(228, 228, 228, 1), 74 | size: 16, 75 | ), 76 | ), 77 | ARow( 78 | height: 50, 79 | padding: EdgeInsets.all(0), 80 | leftChild: Container( 81 | width: 30, 82 | alignment: Alignment.centerLeft, 83 | child: Container( 84 | padding: EdgeInsets.only(bottom: 5), 85 | child: Icon( 86 | Icons.card_giftcard, 87 | color: Color.fromRGBO(220, 220, 220, 1), 88 | size: 20, 89 | ), 90 | ), 91 | ), 92 | centerChild: Text('咖啡钱包'), 93 | rightChild: Icon( 94 | Icons.arrow_forward_ios, 95 | color: Color.fromRGBO(228, 228, 228, 1), 96 | size: 16, 97 | ), 98 | ), 99 | ARow( 100 | height: 50, 101 | padding: EdgeInsets.all(0), 102 | leftChild: Container( 103 | width: 30, 104 | alignment: Alignment.centerLeft, 105 | child: Container( 106 | child: Icon( 107 | Icons.discount_sharp, 108 | color: Color.fromRGBO(220, 220, 220, 1), 109 | size: 20, 110 | ), 111 | ), 112 | ), 113 | centerChild: Text('优惠券'), 114 | rightChild: Icon( 115 | Icons.arrow_forward_ios, 116 | color: Color.fromRGBO(228, 228, 228, 1), 117 | size: 16, 118 | ), 119 | onPressed: () => Get.toNamed('/coupon'), 120 | ), 121 | ARow( 122 | height: 50, 123 | padding: EdgeInsets.all(0), 124 | leftChild: Container( 125 | width: 30, 126 | alignment: Alignment.centerLeft, 127 | child: Container( 128 | child: Icon( 129 | Icons.explicit_sharp, 130 | color: Color.fromRGBO(220, 220, 220, 1), 131 | size: 20, 132 | ), 133 | ), 134 | ), 135 | centerChild: Text('兑换优惠'), 136 | rightChild: Icon( 137 | Icons.arrow_forward_ios, 138 | color: Color.fromRGBO(228, 228, 228, 1), 139 | size: 16, 140 | ), 141 | ), 142 | ARow( 143 | height: 50, 144 | padding: EdgeInsets.all(0), 145 | leftChild: Container( 146 | width: 30, 147 | alignment: Alignment.centerLeft, 148 | child: Container( 149 | child: Icon( 150 | Icons.inventory, 151 | color: Color.fromRGBO(220, 220, 220, 1), 152 | size: 20, 153 | ), 154 | ), 155 | ), 156 | centerChild: Text('发票管理'), 157 | rightChild: Icon( 158 | Icons.arrow_forward_ios, 159 | color: Color.fromRGBO(228, 228, 228, 1), 160 | size: 16, 161 | ), 162 | border: cuBorderBottom(show: false), 163 | ), 164 | ], 165 | ), 166 | ), 167 | 168 | ARow( 169 | height: 50, 170 | margin: EdgeInsets.only(top: 10), 171 | padding: EdgeInsets.symmetric(horizontal: 15), 172 | leftChild: Container( 173 | width: 30, 174 | alignment: Alignment.centerLeft, 175 | child: Container( 176 | child: Icon( 177 | Icons.favorite, 178 | color: Color.fromRGBO(220, 220, 220, 1), 179 | size: 20, 180 | ), 181 | ), 182 | ), 183 | centerChild: Text('帮助反馈'), 184 | rightChild: Icon( 185 | Icons.arrow_forward_ios, 186 | color: Color.fromRGBO(228, 228, 228, 1), 187 | size: 16, 188 | ), 189 | border: cuBorderBottom(show: false), 190 | ), 191 | 192 | Container( 193 | margin: EdgeInsets.only(top: 10), 194 | padding: EdgeInsets.symmetric(horizontal: 15), 195 | child: Image.asset( 196 | 'lib/assets/images/mine/mine2.jpg', 197 | fit: BoxFit.cover, 198 | )) 199 | ], 200 | ), 201 | ), 202 | ); 203 | } 204 | 205 | ARow buildUser() { 206 | return ARow( 207 | height: 55, 208 | color: Colors.transparent, 209 | border: cuBorderBottom(show: false), 210 | padding: EdgeInsets.all(0), 211 | leftChild: ClipRRect( 212 | borderRadius: new BorderRadius.circular(27), 213 | child: Image.asset( 214 | 'lib/assets/images/mine/mine1.png', 215 | width: 55, 216 | height: 55, 217 | fit: BoxFit.cover, 218 | )), 219 | centerChild: Container( 220 | margin: EdgeInsets.only(left: 10), 221 | child: Text( 222 | "UserName", 223 | style: 224 | TextStyle(color: Color.fromRGBO(255, 255, 255, 1), fontSize: 18), 225 | ), 226 | ), 227 | rightChild: Icon( 228 | Icons.arrow_forward_ios, 229 | size: 14, 230 | color: Color.fromRGBO(255, 255, 255, .6), 231 | ), 232 | onPressed: () { 233 | Get.toNamed('/login_mail'); 234 | }, 235 | ); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /lib/pages/toolbar/order/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'widgets/order_list_row.dart'; 4 | 5 | class Order extends StatefulWidget { 6 | Order({Key key}) : super(key: key); 7 | 8 | @override 9 | State createState() => _OrderState(); 10 | } 11 | 12 | class _OrderState extends State with TickerProviderStateMixin { 13 | List tabs = [ 14 | Tab( 15 | text: '全部', 16 | ), 17 | Tab( 18 | text: "未完成", 19 | ), 20 | Tab( 21 | text: "已完成", 22 | ), 23 | ]; 24 | 25 | TabController _tabController; 26 | 27 | @override 28 | void initState() { 29 | super.initState(); 30 | 31 | _tabController = TabController(vsync: this, length: tabs.length); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | return Scaffold( 37 | appBar: AppBar( 38 | centerTitle: true, 39 | title: Text( 40 | "订单列表", 41 | style: TextStyle( 42 | color: Color.fromRGBO(56, 56, 56, 1), 43 | fontSize: 18, 44 | fontWeight: FontWeight.bold), 45 | ), 46 | bottom: PreferredSize( 47 | preferredSize: Size.fromHeight(44), 48 | child: Theme( 49 | data: ThemeData( 50 | splashColor: Colors.transparent, 51 | highlightColor: Colors.transparent), 52 | child: TabBar( 53 | tabs: tabs, 54 | labelColor: Color.fromRGBO(136, 175, 213, 1), 55 | labelStyle: TextStyle(fontWeight: FontWeight.bold), 56 | unselectedLabelStyle: TextStyle(fontSize: 15), 57 | unselectedLabelColor: Color.fromRGBO(80, 80, 80, 1), 58 | controller: _tabController, 59 | ), 60 | ), 61 | ), 62 | backgroundColor: Color.fromRGBO(255, 255, 255, 1), 63 | ), 64 | body: Container( 65 | color: Color.fromRGBO(248, 248, 248, 1), 66 | child: TabBarView( 67 | children: [ 68 | ListView( 69 | children: [ 70 | OrderListRow(1, 71 | orderNum: '23847563928174', 72 | address: '北京市朝阳区青年汇佳园10号...', 73 | goodsName: '榛果拿铁', 74 | price: 20, 75 | time: '2019-01-08 08:05', 76 | onPress: () => Navigator.pushNamed(context, '/order_detail', 77 | arguments: {"status": 2})), 78 | OrderListRow( 79 | 2, 80 | orderNum: '23847563928174', 81 | address: '北京市朝阳区青年汇佳园10号...', 82 | goodsName: '榛果拿铁', 83 | price: 20, 84 | time: '2019-01-08 08:05', 85 | ), 86 | OrderListRow( 87 | 2, 88 | orderNum: '23847563928174', 89 | address: '北京市朝阳区青年汇佳园10号...', 90 | goodsName: '榛果拿铁', 91 | price: 20, 92 | time: '2019-01-08 08:05', 93 | ), 94 | OrderListRow( 95 | 2, 96 | orderNum: '23847563928174', 97 | address: '北京市朝阳区青年汇佳园10号...', 98 | goodsName: '榛果拿铁', 99 | price: 20, 100 | time: '2019-01-08 08:05', 101 | ), 102 | OrderListRow( 103 | 3, 104 | orderNum: '23847563928174', 105 | address: '北京市朝阳区青年汇佳园10号...', 106 | goodsName: '榛果拿铁', 107 | price: 20, 108 | time: '2019-01-08 08:05', 109 | ), 110 | ], 111 | ), 112 | ListView( 113 | children: [ 114 | OrderListRow( 115 | 1, 116 | orderNum: '23847563928174', 117 | address: '北京市朝阳区青年汇佳园10号...', 118 | goodsName: '榛果拿铁', 119 | price: 20, 120 | time: '2019-01-08 08:05', 121 | ), 122 | OrderListRow( 123 | 1, 124 | orderNum: '23847563928174', 125 | address: '北京市朝阳区青年汇佳园10号...', 126 | goodsName: '榛果拿铁', 127 | price: 20, 128 | time: '2019-01-08 08:05', 129 | ), 130 | OrderListRow( 131 | 1, 132 | orderNum: '23847563928174', 133 | address: '北京市朝阳区青年汇佳园10号...', 134 | goodsName: '榛果拿铁', 135 | price: 20, 136 | time: '2019-01-08 08:05', 137 | ), 138 | ], 139 | ), 140 | ListView( 141 | children: [ 142 | OrderListRow( 143 | 2, 144 | orderNum: '23847563928174', 145 | address: '北京市朝阳区青年汇佳园10号...', 146 | goodsName: '榛果拿铁', 147 | price: 20, 148 | time: '2019-01-08 08:05', 149 | ), 150 | OrderListRow( 151 | 2, 152 | orderNum: '23847563928174', 153 | address: '北京市朝阳区青年汇佳园10号...', 154 | goodsName: '榛果拿铁', 155 | price: 20, 156 | time: '2019-01-08 08:05', 157 | ), 158 | OrderListRow( 159 | 2, 160 | orderNum: '23847563928174', 161 | address: '北京市朝阳区青年汇佳园10号...', 162 | goodsName: '榛果拿铁', 163 | price: 20, 164 | time: '2019-01-08 08:05', 165 | ), 166 | ], 167 | ), 168 | ], 169 | controller: _tabController, 170 | ), 171 | ), 172 | ); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /lib/pages/toolbar/order/widgets/order_list_row.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/components/a_button/index.dart'; 3 | import 'package:flutter_luckin_coffee/utils/global.dart'; 4 | import 'package:get/get.dart'; 5 | 6 | class OrderListRow extends StatelessWidget { 7 | final int orderStatus; 8 | final String address; 9 | final String goodsName; 10 | final String orderNum; 11 | final String time; 12 | final double price; 13 | final Function onPress; 14 | 15 | /// 创建订单列表信息 16 | /// ``` 17 | /// @param {int} orderStatus - 订单状态 1: 待付款 2: 已完成 3: 已取消 18 | /// @param {String} address - 地址 19 | /// @param {String} goodsName - 商品名字 20 | /// @param {String} orderNum - 订单号 21 | /// @param {String} time - 订单时间 22 | /// @param {double} price - 价格 23 | /// ``` 24 | OrderListRow(this.orderStatus, 25 | {this.address, 26 | this.goodsName, 27 | this.orderNum, 28 | this.time, 29 | this.price, 30 | this.onPress}); 31 | 32 | /// 文字状态 33 | Widget textStatus() { 34 | var text = ''; 35 | var color = Color.fromRGBO(166, 166, 166, 1); 36 | if (orderStatus == 1) { 37 | color = Color.fromRGBO(136, 175, 213, 1); 38 | text = "待付款"; 39 | } else if (orderStatus == 2) 40 | text = "已完成"; 41 | else if (orderStatus == 3) text = "已取消"; 42 | 43 | return Text(text, style: TextStyle(fontSize: 12, color: color)); 44 | } 45 | 46 | /// 按钮状态 47 | List buttonStatus(BuildContext context) { 48 | List button = []; 49 | 50 | var btn1 = Container( 51 | margin: EdgeInsets.only(left: 10), 52 | child: AButton.normal( 53 | child: Text( 54 | '再来一单', 55 | style: TextStyle(fontSize: 12), 56 | ), 57 | color: Color.fromRGBO(56, 56, 56, 1), 58 | plain: true, 59 | height: 28, 60 | width: 74, 61 | borderColor: Color.fromRGBO(242, 242, 242, 1), 62 | onPressed: () => {}), 63 | ); 64 | 65 | var btn2 = Container( 66 | margin: EdgeInsets.only(left: 10), 67 | child: AButton.normal( 68 | child: Text( 69 | '去支付', 70 | style: TextStyle(fontSize: 12), 71 | ), 72 | color: Color.fromRGBO(255, 129, 2, 1), 73 | borderColor: Color.fromRGBO(255, 129, 2, 1), 74 | plain: true, 75 | height: 28, 76 | width: 74, 77 | onPressed: () => Get.toNamed('/order_confirm')), 78 | ); 79 | 80 | var btn3 = Container( 81 | margin: EdgeInsets.only(left: 10), 82 | child: AButton.normal( 83 | child: Text( 84 | '去评价', 85 | style: TextStyle(fontSize: 12), 86 | ), 87 | color: Color.fromRGBO(144, 192, 239, 1), 88 | plain: true, 89 | height: 28, 90 | width: 74, 91 | borderColor: Color.fromRGBO(144, 192, 239, 1), 92 | onPressed: () => Get.toNamed('/order_evaluation')), 93 | ); 94 | 95 | if (orderStatus == 1) { 96 | button.add(btn2); 97 | } else if (orderStatus == 2) { 98 | button.add(btn1); 99 | button.add(btn3); 100 | } else if (orderStatus == 3) { 101 | button.add(btn1); 102 | } 103 | 104 | return button; 105 | } 106 | 107 | @override 108 | Widget build(BuildContext context) { 109 | return InkWell( 110 | onTap: () => onPress == null ? () {} : onPress(), 111 | child: Container( 112 | margin: EdgeInsets.symmetric(vertical: 10), 113 | padding: EdgeInsets.symmetric(horizontal: 15, vertical: 12), 114 | height: 160, 115 | color: Colors.white, 116 | child: Column( 117 | children: [ 118 | Container( 119 | padding: EdgeInsets.only(bottom: 10), 120 | decoration: BoxDecoration(border: cuBorderBottom()), 121 | child: Row( 122 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 123 | children: [ 124 | Text( 125 | '外卖订单:$orderNum', 126 | style: TextStyle( 127 | color: Color.fromRGBO(166, 166, 166, 1), fontSize: 12), 128 | ), 129 | textStatus() 130 | ], 131 | ), 132 | ), 133 | Container( 134 | margin: EdgeInsets.only(top: 10), 135 | child: Row( 136 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 137 | children: [ 138 | Text( 139 | '$address...', 140 | style: TextStyle( 141 | color: Color.fromRGBO(56, 56, 56, 1), 142 | fontSize: 15, 143 | fontWeight: FontWeight.bold), 144 | ), 145 | Text( 146 | '$time', 147 | style: TextStyle( 148 | fontSize: 12, color: Color.fromRGBO(166, 166, 166, 1)), 149 | ) 150 | ], 151 | ), 152 | ), 153 | Row( 154 | children: [ 155 | Text( 156 | '$goodsName等 共1件商品', 157 | style: TextStyle( 158 | color: Color.fromRGBO(80, 80, 80, 1), 159 | fontSize: 12, 160 | ), 161 | ), 162 | ], 163 | ), 164 | Container( 165 | margin: EdgeInsets.only(top: 25), 166 | child: Row( 167 | crossAxisAlignment: CrossAxisAlignment.center, 168 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 169 | children: [ 170 | Text( 171 | '¥$price', 172 | style: TextStyle( 173 | color: Color.fromRGBO(56, 56, 56, 1), 174 | fontSize: 14, 175 | fontWeight: FontWeight.bold), 176 | ), 177 | Row(children: buttonStatus(context)) 178 | ], 179 | ), 180 | ), 181 | ], 182 | ), 183 | ), 184 | ); 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /lib/pages/toolbar/shopping_cart/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/components/a_button/index.dart'; 3 | import 'package:flutter_luckin_coffee/components/goods_detail/index.dart'; 4 | import 'package:flutter_luckin_coffee/mock/goods_list.dart'; 5 | import 'package:flutter_luckin_coffee/pages/toolbar/shopping_cart/widgets/recommend_goods.dart'; 6 | import 'package:get/get.dart'; 7 | 8 | import 'widgets/shopping_cart_row.dart'; 9 | 10 | class ShoppingCart extends StatefulWidget { 11 | ShoppingCart({Key key}) : super(key: key); 12 | 13 | @override 14 | State createState() => _ShoppingCartState(); 15 | } 16 | 17 | class _ShoppingCartState extends State { 18 | List goodsList = MockGoods.data(); 19 | 20 | /// 购物车为空 21 | Container shoppingCartNotData() { 22 | return Container( 23 | margin: EdgeInsets.only(bottom: 50), 24 | child: Column( 25 | children: [ 26 | Image.asset( 27 | './lib/assets/images/shopping_cart_null.png', 28 | width: 125, 29 | fit: BoxFit.contain, 30 | ), 31 | Container( 32 | margin: EdgeInsets.only(top: 10, bottom: 32), 33 | child: Text( 34 | '您的购物车有点寂寞', 35 | style: TextStyle( 36 | color: Color.fromRGBO(166, 166, 166, 1), fontSize: 12), 37 | ), 38 | ), 39 | AButton.normal( 40 | width: 100, 41 | height: 30, 42 | type: 'info', 43 | color: Color.fromRGBO(144, 192, 239, 1), 44 | borderColor: Color.fromRGBO(144, 192, 239, 1), 45 | plain: true, 46 | child: Text('去喝一杯'), 47 | onPressed: () => Get.toNamed('/menu')) 48 | ], 49 | ), 50 | ); 51 | } 52 | 53 | @override 54 | Widget build(BuildContext context) { 55 | return Scaffold( 56 | appBar: AppBar( 57 | title: Text( 58 | "购物车", 59 | style: TextStyle(color: Colors.black), 60 | ), 61 | backgroundColor: Colors.white, 62 | ), 63 | body: Container( 64 | color: Color(0xfff7f7f7), 65 | child: Column( 66 | children: [ 67 | Expanded( 68 | child: SingleChildScrollView( 69 | child: Container( 70 | padding: EdgeInsets.only(bottom: 20), 71 | child: Column( 72 | children: [ 73 | Container( 74 | child: Image.asset( 75 | 'lib/assets/images/order/order1.png', 76 | fit: BoxFit.cover, 77 | ), 78 | ), 79 | 80 | // 购物车列表展示 81 | Container( 82 | color: Colors.white, 83 | child: Column( 84 | children: [ShoppingCartRow()], 85 | ), 86 | ), 87 | 88 | // 购物车没有商品 89 | // Center(child: shoppingCartNotData()), 90 | 91 | guessLike(), 92 | ], 93 | )), 94 | ), 95 | ), 96 | buttomBtnRow( 97 | false, 98 | 23, 99 | ) 100 | ], 101 | ), 102 | ), 103 | ); 104 | } 105 | 106 | /// 猜你喜欢 107 | Container guessLike() { 108 | return goodsList == null 109 | ? Container() 110 | : Container( 111 | padding: EdgeInsets.symmetric(horizontal: 15), 112 | margin: EdgeInsets.only(top: 15), 113 | child: Column( 114 | children: [ 115 | // title 116 | Row( 117 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 118 | children: [ 119 | Text( 120 | '猜你喜欢', 121 | style: TextStyle( 122 | color: Color.fromRGBO(56, 56, 56, 1), 123 | fontSize: 15, 124 | fontWeight: FontWeight.bold), 125 | ), 126 | GestureDetector( 127 | child: Row( 128 | children: [ 129 | Icon( 130 | Icons.refresh, 131 | color: Color.fromRGBO(148, 196, 236, 1), 132 | size: 16, 133 | ), 134 | Container( 135 | margin: EdgeInsets.only(left: 5), 136 | child: Text( 137 | '换一批', 138 | style: TextStyle( 139 | color: Color.fromRGBO(144, 192, 239, 1), 140 | fontSize: 11), 141 | ), 142 | ) 143 | ], 144 | ), 145 | onTap: () {}, 146 | ) 147 | ], 148 | ), 149 | 150 | // 推荐商品 151 | Container( 152 | margin: EdgeInsets.only(top: 10), 153 | child: Row( 154 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 155 | children: goodsList.sublist(0, 3).map((item) { 156 | return RecommendGoods( 157 | data: item, 158 | onPress: () { 159 | /// 弹出商品详情 /widgets/goods_detail 160 | showDialog( 161 | context: context, 162 | barrierDismissible: false, 163 | builder: (BuildContext context) { 164 | return GoodsDetailDialog( 165 | id: goodsList.indexOf(item), 166 | ); 167 | }, 168 | ); 169 | }, 170 | ); 171 | }).toList(), 172 | ), 173 | ) 174 | ], 175 | ), 176 | ); 177 | } 178 | 179 | /// 底部合计 180 | Container buttomBtnRow(bool shoppingCartIsEmpty, num totalPrice) { 181 | return Container( 182 | child: shoppingCartIsEmpty 183 | ? null 184 | : Container( 185 | decoration: BoxDecoration( 186 | border: Border( 187 | top: BorderSide( 188 | width: 1, color: Color.fromRGBO(242, 242, 242, 1))), 189 | color: Colors.white, 190 | ), 191 | child: Row( 192 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 193 | children: [ 194 | // left 195 | Container( 196 | padding: EdgeInsets.only(left: 15), 197 | child: Row( 198 | crossAxisAlignment: CrossAxisAlignment.center, 199 | children: [ 200 | Container( 201 | child: Text( 202 | '应付合计', 203 | style: TextStyle( 204 | color: Color.fromRGBO(56, 56, 56, 1), 205 | fontWeight: FontWeight.bold, 206 | fontSize: 14), 207 | ), 208 | ), 209 | Container( 210 | margin: EdgeInsets.only(left: 10), 211 | child: Text( 212 | '¥ 22', 213 | style: TextStyle( 214 | color: Color.fromRGBO(56, 56, 56, 1), 215 | fontSize: 24, 216 | fontWeight: FontWeight.bold), 217 | ), 218 | ) 219 | ], 220 | ), 221 | ), 222 | 223 | // right button 224 | AButton.normal( 225 | child: Text('去结算'), 226 | color: Colors.white, 227 | bgColor: Color.fromRGBO(144, 192, 239, 1), 228 | width: 120, 229 | height: 60, 230 | borderRadius: BorderRadius.zero, 231 | onPressed: () { 232 | Get.toNamed('/order_confirm'); 233 | }, 234 | ) 235 | ], 236 | ), 237 | ), 238 | ); 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /lib/pages/toolbar/shopping_cart/widgets/recommend_goods.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/mock/goods_list.dart'; 3 | 4 | class RecommendGoods extends StatelessWidget { 5 | final Function onPress; 6 | final MockGoods data; 7 | 8 | /// 推荐商品 9 | RecommendGoods({this.data, this.onPress}); 10 | 11 | /// 商品图片 12 | Widget goodsImg(String imgSrc) { 13 | return ClipRRect( 14 | borderRadius: new BorderRadius.circular(0), 15 | child: Image.network( 16 | imgSrc, 17 | width: 108, 18 | height: 108, 19 | fit: BoxFit.cover, 20 | ), 21 | ); 22 | } 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | String desc = data.characteristic; 27 | desc = desc.length > 20 ? desc.substring(0, 18) + '...' : desc; 28 | return GestureDetector( 29 | onTap: onPress, 30 | child: Container( 31 | width: 108, 32 | color: Colors.white, 33 | child: Column( 34 | crossAxisAlignment: CrossAxisAlignment.start, 35 | children: [ 36 | goodsImg(data.pic), 37 | Container( 38 | padding: EdgeInsets.only(bottom: 5, left: 5, right: 5), 39 | child: Column( 40 | crossAxisAlignment: CrossAxisAlignment.start, 41 | children: [ 42 | Container( 43 | margin: EdgeInsets.only(top: 12), 44 | child: Text( 45 | '${data.name}', 46 | style: TextStyle( 47 | fontSize: 11, 48 | color: Color.fromRGBO(56, 56, 56, 1), 49 | fontWeight: FontWeight.bold), 50 | ), 51 | ), 52 | Text( 53 | '$desc', 54 | overflow: TextOverflow.ellipsis, 55 | style: TextStyle( 56 | color: Color.fromRGBO(166, 166, 166, 1), fontSize: 10), 57 | ), 58 | Container( 59 | margin: EdgeInsets.only(top: 15), 60 | child: Row( 61 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 62 | crossAxisAlignment: CrossAxisAlignment.center, 63 | children: [ 64 | Row( 65 | children: [ 66 | Container( 67 | child: Text( 68 | '¥${data.originalPrice}', 69 | style: TextStyle( 70 | fontSize: 12, 71 | color: Color.fromRGBO(255, 141, 26, 1), 72 | fontWeight: FontWeight.bold), 73 | ), 74 | ), 75 | ], 76 | ), 77 | Icon( 78 | Icons.add_circle, 79 | color: Color.fromRGBO(148, 196, 236, 1), 80 | ), 81 | ], 82 | ), 83 | ), 84 | ], 85 | ), 86 | ) 87 | ], 88 | ), 89 | ), 90 | ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/pages/toolbar/shopping_cart/widgets/shopping_cart_row.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_luckin_coffee/components/a_button/index.dart'; 3 | import 'package:flutter_luckin_coffee/components/a_row/a_row.dart'; 4 | import 'package:flutter_luckin_coffee/components/a_stepper/a_stepper.dart'; 5 | import 'package:flutter_luckin_coffee/utils/global.dart'; 6 | 7 | class ShoppingCartRow extends StatelessWidget { 8 | /// 购物车商品列表行 9 | const ShoppingCartRow({ 10 | Key key, 11 | }) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return ARow( 16 | height: null, 17 | padding: EdgeInsets.symmetric(horizontal: 15), 18 | border: Border.all(color: Colors.transparent), 19 | leftChild: Container( 20 | margin: EdgeInsets.only(right: 15), 21 | child: Checkbox( 22 | value: true, 23 | onChanged: (value) => {}, 24 | ), 25 | ), 26 | centerChild: Container( 27 | padding: EdgeInsets.symmetric(vertical: 15), 28 | decoration: BoxDecoration(border: cuBorderBottom(show: false)), 29 | child: Row( 30 | children: [ 31 | Expanded( 32 | child: Container( 33 | child: Column( 34 | crossAxisAlignment: CrossAxisAlignment.start, 35 | children: [ 36 | Row( 37 | children: [ 38 | Text( 39 | "拿铁", 40 | style: TextStyle( 41 | color: Color.fromRGBO(56, 56, 56, 1), 42 | fontWeight: FontWeight.bold, 43 | fontSize: 15), 44 | ), 45 | Container( 46 | margin: EdgeInsets.only(left: 5), 47 | child: AButton.normal( 48 | child: Text( 49 | '充2赠1', 50 | style: TextStyle(fontSize: 8), 51 | ), 52 | bgColor: Color.fromRGBO(255, 129, 2, 1), 53 | color: Colors.white, 54 | height: 16, 55 | width: 34, 56 | borderRadius: BorderRadius.circular(2), 57 | onPressed: () => {}), 58 | ) 59 | ], 60 | ), 61 | Row( 62 | children: [ 63 | Text( 64 | G.handleGoodsDesc("规格:中,温度:热,糖度:无糖"), 65 | style: TextStyle( 66 | color: Color.fromRGBO(80, 80, 80, 1), 67 | fontSize: 10), 68 | ), 69 | ], 70 | ), 71 | Row( 72 | children: [ 73 | Text( 74 | '仅限打包带走', 75 | style: TextStyle( 76 | fontSize: 10, 77 | color: Color.fromRGBO(85, 122, 157, 1)), 78 | ) 79 | ], 80 | ) 81 | ], 82 | ), 83 | ), 84 | ), 85 | Text( 86 | '¥ 24', 87 | style: TextStyle( 88 | color: Color.fromRGBO(56, 56, 56, 1), 89 | fontSize: 18, 90 | fontWeight: FontWeight.bold), 91 | ), 92 | Container( 93 | margin: EdgeInsets.only(left: 10), 94 | child: AStepper( 95 | value: 24, 96 | onChange: (val) => {}, 97 | )) 98 | ], 99 | ), 100 | ), 101 | ); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /lib/utils/custom_appbar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'global.dart'; 4 | 5 | /// 通用appbar 6 | /// 7 | /// ``` 8 | /// @param {BuildContext} - context 如果context存在:左边有返回按钮,反之没有 9 | /// @param {String} title - 标题 10 | /// @param {bool} borderBottom - 是否显示底部border 11 | /// ``` 12 | AppBar customAppbar( 13 | {BuildContext context, 14 | String title = '', 15 | bool borderBottom = true, 16 | List actions}) { 17 | return AppBar( 18 | centerTitle: true, 19 | title: Text( 20 | title, 21 | style: TextStyle( 22 | color: Color.fromRGBO(56, 56, 56, 1), 23 | fontSize: 18, 24 | fontWeight: FontWeight.bold), 25 | ), 26 | backgroundColor: Colors.white, 27 | elevation: 0, 28 | leading: context == null 29 | ? null 30 | : InkWell( 31 | child: Icon( 32 | Icons.arrow_back_ios, 33 | color: Color.fromRGBO(44, 44, 44, 1), 34 | size: 16, 35 | ), 36 | onTap: () => Navigator.pop(context), 37 | ), 38 | bottom: PreferredSize( 39 | child: Container( 40 | decoration: BoxDecoration(border: cuBorderBottom(show: borderBottom)), 41 | ), 42 | preferredSize: Size.fromHeight(0), 43 | ), 44 | actions: actions, 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /lib/utils/global.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: meetqy 3 | * @since: 2019-09-24 14:23:27 4 | * @lastTime: 2019-11-23 11:50:01 5 | * @LastEditors: meetqy 6 | */ 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_luckin_coffee/utils/loading.dart'; 10 | 11 | import 'pull_to_refresh_style.dart'; 12 | 13 | export './custom_appbar.dart'; 14 | 15 | /// 这个类是用来处理全局的Global的tools类 16 | class G { 17 | static final GlobalKey navigatorKey = GlobalKey(); 18 | 19 | /// toolbar routeName 20 | static final List toobarRouteNameList = [ 21 | '/', 22 | '/menu', 23 | '/order', 24 | '/shopping_cart', 25 | '/mine' 26 | ]; 27 | 28 | /// 处理商品描述 29 | static String handleGoodsDesc(String str) { 30 | return str 31 | .replaceAll(RegExp(',\$'), '') 32 | .replaceAll(RegExp('规格:|温度:|糖度:|奶油:|无'), ''); 33 | } 34 | } 35 | 36 | /// 底部border 37 | /// ``` 38 | /// @param {Color} color 39 | /// @param {bool} show 是否显示底部border 40 | /// ``` 41 | Border cuBorderBottom({Color color, bool show = true}) { 42 | return Border( 43 | bottom: BorderSide( 44 | color: (color == null || !show) 45 | ? (show ? Color.fromRGBO(242, 242, 242, 1) : Colors.transparent) 46 | : color, 47 | width: 1, 48 | ), 49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /lib/utils/loading.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | class Loading { 6 | show(BuildContext context) { 7 | showGeneralDialog( 8 | context: context, 9 | pageBuilder: (BuildContext buildContext, Animation animation, 10 | Animation secondaryAnimation) { 11 | return SafeArea( 12 | child: Builder(builder: (BuildContext context) { 13 | return Center( 14 | child: Container( 15 | decoration: BoxDecoration( 16 | borderRadius: BorderRadius.circular(4), 17 | color: Colors.black54, 18 | ), 19 | width: 64, 20 | height: 64, 21 | child: Container( 22 | alignment: Alignment.center, 23 | width: 40, 24 | height: 40, 25 | child: CupertinoActivityIndicator( 26 | radius: 14, 27 | ), 28 | ), 29 | ), 30 | ); 31 | }), 32 | ); 33 | }, 34 | barrierDismissible: false, 35 | barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, 36 | barrierColor: Color.fromRGBO(255, 255, 255, 0), 37 | transitionDuration: const Duration(milliseconds: 150), 38 | transitionBuilder: (BuildContext context, Animation animation, 39 | Animation secondaryAnimation, Widget child) { 40 | return FadeTransition( 41 | opacity: CurvedAnimation( 42 | parent: animation, 43 | curve: Curves.easeOut, 44 | ), 45 | child: child, 46 | ); 47 | }, 48 | ); 49 | } 50 | 51 | hide(BuildContext context) { 52 | Get.back(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/utils/pull_to_refresh_style.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 3 | 4 | /// 下拉刷新样式 5 | class PullToRefreshStyle { 6 | /// 顶部样式 7 | header() { 8 | return CustomHeader( 9 | builder: (BuildContext context, RefreshStatus mode) { 10 | print({'header': mode}); 11 | return Container( 12 | height: 44.0, 13 | child: Center(child: CupertinoActivityIndicator()), 14 | ); 15 | }, 16 | ); 17 | } 18 | 19 | /// 底部样式 20 | footer() { 21 | return CustomFooter( 22 | builder: (BuildContext context, LoadStatus mode) { 23 | print({'footer': mode}); 24 | Widget body; 25 | if (mode == LoadStatus.noMore) { 26 | body = Text( 27 | '--- 到底了 ---', 28 | style: TextStyle(color: Color.fromRGBO(73, 194, 101, 1)), 29 | ); 30 | } else { 31 | body = CupertinoActivityIndicator(); 32 | } 33 | return Container( 34 | height: 44.0, 35 | child: Center(child: body), 36 | ); 37 | }, 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_luckin_coffee 2 | description: flutter luckin coffee 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.10.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: ^1.0.4 26 | flutter_swiper: 1.1.6 27 | pull_to_refresh: ^2.0.0 28 | json_annotation: ^4.5.0 29 | dart_mock: ^2.0.0 30 | get: ^4.6.3 31 | 32 | dev_dependencies: 33 | flutter_test: 34 | sdk: flutter 35 | test: any 36 | build_runner: ^2.1.11 37 | json_serializable: ^6.2.0 38 | 39 | 40 | # For information on the generic Dart part of this file, see the 41 | # following page: https://dart.dev/tools/pub/pubspec 42 | 43 | # The following section is specific to Flutter. 44 | flutter: 45 | 46 | # The following line ensures that the Material Icons font is 47 | # included with your application, so that you can use the icons in 48 | # the material Icons class. 49 | uses-material-design: true 50 | 51 | # To add assets to your application, add an assets section, like this: 52 | assets: 53 | - lib/assets/images/ 54 | - lib/assets/images/home/ 55 | - lib/assets/images/menu/ 56 | - lib/assets/images/mine/ 57 | - lib/assets/images/order/ 58 | 59 | # An image asset can refer to one or more resolution-specific "variants", see 60 | # https://flutter.dev/assets-and-images/#resolution-aware. 61 | 62 | # For details regarding adding assets from package dependencies, see 63 | # https://flutter.dev/assets-and-images/#from-packages 64 | 65 | # To add custom fonts to your application, add a fonts section here, 66 | # in this "flutter" section. Each entry in this list should have a 67 | # "family" key with the font family name, and a "fonts" key with a 68 | # list giving the asset and other descriptors for the font. For 69 | # example: 70 | # fonts: 71 | # - family: Schyler 72 | # fonts: 73 | # - asset: fonts/Schyler-Regular.ttf 74 | # - asset: fonts/Schyler-Italic.ttf 75 | # style: italic 76 | # For details regarding fonts from package dependencies, 77 | # see https://flutter.dev/custom-fonts/#from-packages 78 | -------------------------------------------------------------------------------- /qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/qrcode.png -------------------------------------------------------------------------------- /readme/backlog/v1.x.x.md: -------------------------------------------------------------------------------- 1 | 7 | # v1.0.0 8 | 9 | 只注重ui还原,不包括动画特效实现 10 | 11 | ## 按模块拆分 12 | 13 | - [x] 首页 14 | - [x] 登录 15 | - [x] 登录方式 16 | - [x] 手机验证码登录 17 | - [x] 菜单界面 18 | - [x] 菜单页面弹窗 19 | - [x] 订单列表 20 | - [x] 订单评价 21 | - [x] 购物车 22 | - [x] 确认订单 23 | - [x] 我的 24 | - [x] 使用优惠券 25 | - [x] 备注特殊要求 26 | - [x] 门店自取 27 | - [x] 门店详情 28 | - [x] 订单详情(下单成功) 29 | - [x] 订单详情(待付款) 30 | - [x] 取餐码 31 | - [ ] 选择区号 32 | - [x] 路由管理 33 | - [x] 复选框交互动画实现及封装 34 | - [x] 步进器的实现及封装 35 | - [x] 封装好的步进器接入页面 36 | - [x] 常用字体大小 & 颜色封装 37 | - [x] 通用按钮的封装 38 | - [x] 封装好的按钮,接入页面 39 | - [x] dialog封装 40 | - [x] 页面关联 41 | - [x] 调研下拉刷新插件&实现demo 42 | - [x] 常用行的封装 & 优化 -------------------------------------------------------------------------------- /readme/backlog/v2.x.x.md: -------------------------------------------------------------------------------- 1 | # v2.0.0 2 | 3 | - [x] provider调研 4 | - [x] 接入provider 5 | - [x] 网络请求调研 6 | - [x] 在api工厂,准备app需要的数据(持续更新) 7 | - [x] 菜单联调 8 | - [x] 请求等待返回数据增加loading 9 | - [x] 商品详情联调 10 | - [x] 菜单和商品详情交互 11 | - [x] 使用provider实现加入购物车功能 12 | - [x] 购物车联调 13 | - [x] 购物车ui细节调整 14 | 15 | # v2.1.0 16 | 17 | - [ ] ~~订单交互~~,api工厂没有提供接口暂时移除 18 | - [ ] ~~订单功能实现~~,api工厂没有提供接口暂时移除 19 | - [x] 猜你喜欢交互 20 | - [x] 猜你喜欢联调 -------------------------------------------------------------------------------- /readme/images/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/readme/images/1.gif -------------------------------------------------------------------------------- /readme/images/2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/readme/images/2.gif -------------------------------------------------------------------------------- /readme/images/githead1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetqy/flutter_luckin_coffee/925a2b58e2fc3df9921dd44e89fd8cc53c1c4911/readme/images/githead1.png -------------------------------------------------------------------------------- /upload_android.sh: -------------------------------------------------------------------------------- 1 | _api_key="" 2 | _uKey="" 3 | # 相对于项目的路径 4 | _android_dir="/build/app/outputs/apk/release/app-armeabi-v7a-release.apk" 5 | 6 | # ######### 脚本样式 ############# 7 | __LINE_BREAK_LEFT="\033[32;1m" 8 | __LINE_BREAK_RIGHT="\033[0m" 9 | 10 | # 打印信息 11 | function printMessage() { 12 | pMessage=$1 13 | echo "${__LINE_BREAK_LEFT}${pMessage}${__LINE_BREAK_RIGHT}" 14 | } 15 | 16 | 17 | # 获取flutter路径 18 | _flutter=`which flutter` 19 | 20 | $_flutter build apk; 21 | 22 | printMessage "打包成功 🚀 🚀 🚀" 23 | 24 | printMessage "上传中..." 25 | 26 | curl -F "file=@`pwd`$_android_dir" \ 27 | -F "uKey=$_uKey" \ 28 | -F "_api_key=$_api_key" \ 29 | "http://www.pgyer.com/apiv1/app/upload" 30 | 31 | printMessage "\n上传成功 🚀 🚀 🚀" -------------------------------------------------------------------------------- /upload_ios.sh: -------------------------------------------------------------------------------- 1 | _api_key="" 2 | _uKey="" 3 | _dir="/app/" 4 | 5 | # ######### 脚本样式 ############# 6 | __LINE_BREAK_LEFT="\033[32;1m" 7 | __LINE_BREAK_RIGHT="\033[0m" 8 | 9 | # 打印信息 10 | function printMessage() { 11 | pMessage=$1 12 | echo "${__LINE_BREAK_LEFT}${pMessage}${__LINE_BREAK_RIGHT}" 13 | } 14 | 15 | flutter clean; 16 | 17 | flutter build ios; 18 | 19 | if [ -d build/ios/iphoneos/Runner.app ] 20 | then 21 | 22 | mkdir app/Payload 23 | 24 | cp -r build/ios/iphoneos/Runner.app app/Payload 25 | 26 | cd app 27 | filename=ios-$(date "+%Y%m%d%H%M").ipa 28 | _dir=$_dir$filename 29 | zip -r -m $filename Payload 30 | cd .. 31 | 32 | printMessage "打包成功😄" 33 | 34 | open app 35 | 36 | else 37 | echo "遇到报错了😭, 打开Xcode查找错误原因" 38 | say "打包失败" 39 | fi 40 | 41 | curl -F "file=@`pwd`$_dir" \ 42 | -F "uKey=$_uKey" \ 43 | -F "_api_key=$_api_key" \ 44 | "http://www.pgyer.com/apiv1/app/upload" 45 | 46 | printMessage "\n上传成功 🚀 🚀 🚀" --------------------------------------------------------------------------------