├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── zh │ │ │ │ └── flutter_plus │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── 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-night │ │ │ └── styles.xml │ │ │ ├── values │ │ │ └── styles.xml │ │ │ └── xml │ │ │ └── network_security_config.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── fonts │ └── iconfont.ttf ├── images │ ├── animationImage.gif │ ├── bottom_add.png │ ├── ic_arrow_right.png │ ├── ic_back_black.png │ ├── icon_search.png │ ├── index_banner.png │ ├── logo.png │ ├── love.png │ ├── loveme.png │ ├── me.png │ ├── message.png │ ├── message1.png │ ├── none.png │ ├── order_delete.png │ ├── splash.png │ ├── system_message.png │ ├── szz.png │ ├── zhu.jpg │ ├── zhuanfa.png │ └── zzc.jpeg └── json │ ├── disconnect.json │ ├── empty.json │ ├── empty2.json │ ├── empty3.json │ ├── error.json │ ├── error2.json │ ├── loading.json │ ├── loading2.json │ └── loadingYellow.json ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── 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 ├── common │ ├── application.dart │ └── constant.dart ├── main.dart ├── model │ ├── base │ │ ├── home.dart │ │ ├── home.freezed.dart │ │ ├── home.g.dart │ │ ├── tab.dart │ │ └── tab.freezed.dart │ ├── breed │ │ ├── breed.dart │ │ ├── breed.freezed.dart │ │ └── breed.g.dart │ ├── moment │ │ ├── moment.dart │ │ ├── moment.freezed.dart │ │ ├── moment.g.dart │ │ ├── moment_response.dart │ │ ├── moment_response.freezed.dart │ │ └── moment_response.g.dart │ └── user │ │ ├── user.dart │ │ ├── user.freezed.dart │ │ └── user.g.dart ├── net │ ├── api_client.dart │ ├── api_client.g.dart │ ├── base_dio.dart │ ├── base_error.dart │ ├── http_provider.dart │ └── interceptor │ │ ├── header_interceptor.dart │ │ ├── log_interceptor.dart │ │ └── response_interceptor.dart ├── provider │ ├── breed_provider.dart │ └── moment_provider.dart ├── route │ ├── handler.dart │ └── router.dart ├── styles │ ├── app_colors.dart │ ├── app_spacers.dart │ └── app_styles.dart ├── ui │ ├── common_base_page.dart │ ├── components │ │ ├── header_swiper_pagination.dart │ │ ├── login_button.dart │ │ └── persistent_header_builder.dart │ ├── discovery │ │ └── discovery_page.dart │ ├── home │ │ ├── breed_detail_page.dart │ │ ├── breed_page.dart │ │ ├── components │ │ │ ├── head_swiper.dart │ │ │ ├── home_header.dart │ │ │ ├── home_horizontal_view.dart │ │ │ ├── left_title.dart │ │ │ └── sliver_header.dart │ │ └── home_page.dart │ ├── login_page.dart │ ├── main_page.dart │ ├── message │ │ ├── components │ │ │ ├── avatar_widget.dart │ │ │ └── user_item_widget.dart │ │ └── message_page.dart │ ├── moment │ │ ├── components │ │ │ └── navigatorActionBar.dart │ │ ├── moment_page.dart │ │ └── test_page.dart │ ├── personal │ │ ├── components │ │ │ └── sliver_delegate.dart │ │ └── personal_page.dart │ └── splash_page.dart └── widgets │ ├── app_topbar.dart │ ├── cache_image.dart │ ├── custom_indicator.dart │ ├── custom_tabs.dart │ ├── error_page.dart │ ├── iconfont.dart │ ├── my_appbar.dart │ ├── over_scroll_behavior.dart │ ├── page_state.dart │ ├── per_flexible_space_bar.dart │ └── refresh.dart ├── pubspec.lock ├── pubspec.yaml └── web ├── favicon.png ├── icons ├── Icon-192.png └── Icon-512.png ├── index.html └── manifest.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /.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: b1395592de68cc8ac4522094ae59956dd21a91db 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flutter_plus 2 | 3 | 核心: 4 | 5 | 1.hooks_riverpod:状态管理,provider的升级版本,详情自行查看官方文档,上面有介绍对比当前几种主流状态管理框架 6 | 7 | 2.retrofit:网络请求,熟悉Android的应该知道 8 | 9 | 3.freezed:实体类对象 10 | 11 | 还有一些用到的组件自行参考yaml文件 12 | 13 | 14 | 功能: 15 | 16 | 1.登录 17 | 18 | 2.复杂的布局动画(京东首页顶部,个人界面的动画,商品详情页的动画) 19 | 20 | 3.网络请求的下拉刷新和basepage管理页面状态 21 | 22 | 4.封装了很多复杂的部件,可以直接拿来使用 23 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | analyzer: 2 | exclude: 3 | - '**/*.g.dart' 4 | - '**/*.freezed.dart' 5 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | -------------------------------------------------------------------------------- /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 30 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | defaultConfig { 36 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 37 | applicationId "com.zh.flutter_plus" 38 | multiDexEnabled true 39 | minSdkVersion 21 40 | targetSdkVersion 30 41 | versionCode flutterVersionCode.toInteger() 42 | versionName flutterVersionName 43 | } 44 | 45 | buildTypes { 46 | release { 47 | // TODO: Add your own signing config for the release build. 48 | // Signing with the debug keys for now, so `flutter run --release` works. 49 | signingConfig signingConfigs.debug 50 | } 51 | } 52 | } 53 | 54 | flutter { 55 | source '../..' 56 | } 57 | 58 | dependencies { 59 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 60 | implementation 'com.android.support:multidex:1.0.3' 61 | } 62 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 20 | 24 | 28 | 33 | 37 | 38 | 39 | 40 | 41 | 42 | 44 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/zh/flutter_plus/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.zh.flutter_plus 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /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/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.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.useAndroidX=true 3 | android.enableJetifier=true 4 | #org.gradle.java.home=D\:\\JavaTool\\jdk -------------------------------------------------------------------------------- /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.7-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /assets/fonts/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/fonts/iconfont.ttf -------------------------------------------------------------------------------- /assets/images/animationImage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/animationImage.gif -------------------------------------------------------------------------------- /assets/images/bottom_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/bottom_add.png -------------------------------------------------------------------------------- /assets/images/ic_arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/ic_arrow_right.png -------------------------------------------------------------------------------- /assets/images/ic_back_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/ic_back_black.png -------------------------------------------------------------------------------- /assets/images/icon_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/icon_search.png -------------------------------------------------------------------------------- /assets/images/index_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/index_banner.png -------------------------------------------------------------------------------- /assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/logo.png -------------------------------------------------------------------------------- /assets/images/love.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/love.png -------------------------------------------------------------------------------- /assets/images/loveme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/loveme.png -------------------------------------------------------------------------------- /assets/images/me.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/me.png -------------------------------------------------------------------------------- /assets/images/message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/message.png -------------------------------------------------------------------------------- /assets/images/message1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/message1.png -------------------------------------------------------------------------------- /assets/images/none.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/none.png -------------------------------------------------------------------------------- /assets/images/order_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/order_delete.png -------------------------------------------------------------------------------- /assets/images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/splash.png -------------------------------------------------------------------------------- /assets/images/system_message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/system_message.png -------------------------------------------------------------------------------- /assets/images/szz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/szz.png -------------------------------------------------------------------------------- /assets/images/zhu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/zhu.jpg -------------------------------------------------------------------------------- /assets/images/zhuanfa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/zhuanfa.png -------------------------------------------------------------------------------- /assets/images/zzc.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/assets/images/zzc.jpeg -------------------------------------------------------------------------------- /assets/json/loading.json: -------------------------------------------------------------------------------- 1 | {"v":"5.5.5","fr":29.9700012207031,"ip":0,"op":60.0000024438501,"w":1920,"h":1080,"nm":"lottifier","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"cogwheel-outline Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":61.0000024845809,"s":[360]}],"ix":10},"p":{"a":0,"k":[960,540,0],"ix":2},"a":{"a":0,"k":[306,306,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[4.875,-0.506],[0,0],[1.061,-3.771],[8.636,-15.353],[-2.285,-3.187],[0,0],[3.075,-3.064],[0,0],[4.359,3.508],[0,0],[3.408,-1.921],[16.937,-4.793],[0.638,-3.852],[0,0],[4.257,0],[0,0],[0.505,4.874],[0,0],[3.763,1.061],[15.412,8.636],[1.698,0],[1.76,-1.246],[0,0],[2.973,2.963],[0,0],[-3.154,3.882],[0,0],[1.912,3.418],[4.783,16.918],[3.862,0.639],[0,0],[0,4.329],[0,0],[-4.956,0.526],[0,0],[-1.052,3.762],[-8.615,15.331],[2.286,3.196],[0,0],[-3.054,3.053],[0,0],[-4.349,-3.479],[0,0],[-3.438,1.912],[-17.029,4.823],[-0.638,3.864],[0,0],[-4.338,0],[0,0],[-0.535,-4.976],[0,0],[-3.761,-1.053],[-15.36,-8.626],[-3.185,2.285],[0,0],[-2.942,-2.942],[0,0],[3.125,-3.862],[0,0],[-1.91,-3.428],[-4.794,-17.01],[-3.873,-0.646],[0,0],[0,-4.258]],"o":[[0,4.259],[0,0],[-3.873,0.646],[-4.794,17.011],[-1.911,3.427],[0,0],[2.73,3.345],[0,0],[-2.951,2.943],[0,0],[-3.186,-2.284],[-15.422,8.647],[-3.764,1.063],[0,0],[-0.415,4.217],[0,0],[-4.257,0],[0,0],[-0.648,-3.862],[-16.98,-4.804],[-1.537,-0.871],[-2.064,0],[0,0],[-3.185,2.651],[0,0],[-3.064,-3.065],[0,0],[2.286,-3.185],[-8.657,-15.452],[-1.062,-3.772],[0,0],[-4.277,-0.414],[0,0],[0,-4.34],[0,0],[3.863,-0.647],[4.824,-17.02],[1.911,-3.419],[0,0],[-2.731,-3.327],[0,0],[2.943,-2.964],[0,0],[3.176,2.286],[15.32,-8.626],[3.763,-1.062],[0,0],[0.425,-4.288],[0,0],[4.327,0],[0,0],[0.647,3.864],[16.999,4.814],[3.407,1.911],[0,0],[3.197,-2.618],[0,0],[3.055,3.055],[0,0],[-2.286,3.185],[8.626,15.33],[1.071,3.771],[0,0],[4.217,0.415],[0,0]],"v":[[285.764,37.001],[277.582,45.415],[225.229,54.145],[217.15,61.374],[196.923,110.139],[197.52,120.969],[228.788,164.686],[228.202,175.921],[175.889,228.233],[164.138,228.395],[120.949,197.54],[110.127,196.944],[61.365,217.189],[54.143,225.249],[45.316,278.262],[36.983,285.775],[-36.993,285.775],[-45.426,277.595],[-54.133,225.249],[-61.354,217.181],[-110.148,196.936],[-115.092,195.642],[-120.978,197.522],[-164.726,228.78],[-175.931,228.204],[-228.244,175.901],[-228.395,164.141],[-197.541,120.949],[-196.944,110.139],[-217.19,61.355],[-225.259,54.124],[-278.281,45.307],[-285.774,36.984],[-285.774,-36.979],[-277.592,-45.435],[-225.25,-54.163],[-217.18,-61.383],[-196.934,-110.137],[-197.531,-120.967],[-228.789,-164.725],[-228.233,-175.919],[-175.921,-228.233],[-164.17,-228.414],[-120.938,-197.54],[-110.107,-196.944],[-61.354,-217.189],[-54.133,-225.26],[-45.336,-278.26],[-36.993,-285.773],[36.983,-285.773],[45.436,-277.562],[54.154,-225.22],[61.374,-217.15],[110.138,-196.905],[120.958,-197.5],[164.705,-228.78],[175.91,-228.203],[228.222,-175.87],[228.405,-164.138],[197.531,-120.927],[196.933,-110.096],[217.16,-61.352],[225.24,-54.124],[278.25,-45.305],[285.764,-36.961]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[14.017,1.375],[0,0],[6.957,13.187],[0,0],[10.436,10.407],[0,0],[7.646,0],[4.541,-3.763],[0,0],[14.259,4.439],[0,0],[14.754,0],[0,0],[1.366,-14.017],[0,0],[13.176,-6.947],[0,0],[10.083,-10.062],[0,0],[-8.96,-10.871],[0,0],[4.439,-14.269],[0,0],[0,-14.776],[0,0],[-13.996,-1.343],[0,0],[-6.978,-13.247],[0,0],[-10.446,-10.447],[0,0],[-7.646,0],[-4.552,3.771],[0,0],[-14.249,-4.431],[0,0],[-14.775,0],[0,0],[-1.375,14.007],[0,0],[-13.228,6.968],[0,0],[-10.042,10.052],[0,0],[8.949,10.912],[0,0],[-4.438,14.28],[0,0],[0,14.795],[0,0]],"o":[[0,0],[-4.439,-14.269],[0,0],[9.334,-11.438],[0,0],[-5.4,-5.4],[-6.591,0],[0,0],[-13.188,-6.947],[0,0],[-1.486,-14.664],[0,0],[-14.775,0],[0,0],[-14.279,4.44],[0,0],[-10.981,-8.98],[0,0],[-10.457,10.437],[0,0],[-6.939,13.187],[0,0],[-14.673,1.466],[0,0],[0,14.795],[0,0],[4.419,14.209],[0,0],[-9.354,11.428],[0,0],[5.411,5.411],[6.594,0],[0,0],[13.227,6.957],[0,0],[1.456,14.675],[0,0],[14.764,0],[0,0],[14.219,-4.421],[0,0],[11.011,9.029],[0,0],[10.456,-10.416],[0,0],[6.947,-13.187],[0,0],[14.694,-1.426],[0,0],[0,-14.776]],"v":[[280.921,-65.368],[234.807,-73.063],[217.676,-114.365],[244.464,-151.904],[242.524,-190.22],[190.22,-242.543],[169.994,-250.915],[152.429,-244.858],[114.356,-217.676],[73.066,-234.806],[65.47,-280.272],[36.993,-305.999],[-36.982,-305.999],[-65.369,-280.91],[-73.044,-234.806],[-114.343,-217.676],[-151.903,-244.465],[-190.23,-242.543],[-242.543,-190.22],[-244.849,-152.43],[-217.675,-114.366],[-234.807,-73.064],[-280.253,-65.46],[-306,-36.982],[-306,36.982],[-280.921,65.358],[-234.807,73.053],[-217.666,114.363],[-244.444,151.871],[-242.543,190.229],[-190.24,242.533],[-170.004,250.926],[-152.427,244.85],[-114.364,217.665],[-73.044,234.808],[-65.46,280.252],[-36.993,305.999],[36.983,305.999],[65.359,280.921],[73.065,234.796],[114.354,217.653],[151.863,244.423],[190.219,242.512],[242.512,190.22],[244.859,152.397],[217.675,114.344],[234.806,73.022],[280.233,65.428],[306,36.982],[306,-36.982]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[306,306],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":6,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[55.601,0],[0,55.601],[-55.609,0],[-0.01,-55.61]],"o":[[-55.61,0],[0,-55.609],[55.6,0],[0,55.601]],"v":[[0.005,100.842],[-100.849,0.01],[0.005,-100.844],[100.849,0.01]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[66.766,0],[0,-66.764],[-66.764,0],[-0.009,66.743]],"o":[[-66.764,0],[0,66.754],[66.754,0],[0,-66.764]],"v":[[0.005,-121.069],[-121.074,0.01],[0.005,121.07],[121.074,0.01]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[306.005,305.98],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[66.754,0],[0,66.754],[-66.764,0],[0,-66.763]],"o":[[-66.764,0],[0,-66.763],[66.766,0],[-0.009,66.744]],"v":[[0.015,121.048],[-121.064,-0.012],[0.015,-121.09],[121.084,-0.012]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[4.217,0.416],[0,0],[1.072,3.772],[8.626,15.331],[-2.286,3.185],[0,0],[3.055,3.054],[0,0],[3.197,-2.618],[0,0],[3.407,1.912],[16.999,4.814],[0.647,3.864],[0,0],[4.327,0],[0,0],[0.425,-4.288],[0,0],[3.762,-1.062],[15.32,-8.626],[3.176,2.286],[0,0],[2.943,-2.964],[0,0],[-2.731,-3.328],[0,0],[1.911,-3.419],[4.824,-17.02],[3.863,-0.647],[0,0],[0,-4.34],[0,0],[-4.277,-0.414],[0,0],[-1.062,-3.772],[-8.657,-15.452],[2.286,-3.185],[0,0],[-3.064,-3.066],[0,0],[-3.185,2.651],[0,0],[-2.063,0],[-1.537,-0.87],[-16.98,-4.804],[-0.647,-3.862],[0,0],[-4.257,0],[0,0],[-0.415,4.217],[0,0],[-3.763,1.062],[-15.422,8.646],[-3.185,-2.284],[0,0],[-2.951,2.942],[0,0],[2.73,3.345],[0,0],[-1.911,3.428],[-4.794,17.011],[-3.873,0.646],[0,0],[0,4.259],[0,0]],"o":[[0,0],[-3.873,-0.647],[-4.794,-17.01],[-1.91,-3.428],[0,0],[3.125,-3.863],[0,0],[-2.942,-2.943],[0,0],[-3.185,2.285],[-15.36,-8.626],[-3.761,-1.052],[0,0],[-0.535,-4.976],[0,0],[-4.338,0],[0,0],[-0.637,3.864],[-17.03,4.823],[-3.438,1.912],[0,0],[-4.349,-3.479],[0,0],[-3.054,3.053],[0,0],[2.286,3.196],[-8.616,15.331],[-1.052,3.763],[0,0],[-4.956,0.525],[0,0],[0,4.328],[0,0],[3.863,0.639],[4.783,16.919],[1.912,3.418],[0,0],[-3.155,3.883],[0,0],[2.973,2.963],[0,0],[1.759,-1.245],[1.699,0],[15.412,8.636],[3.762,1.061],[0,0],[0.506,4.874],[0,0],[4.256,0],[0,0],[0.638,-3.852],[16.938,-4.794],[3.408,-1.921],[0,0],[4.359,3.508],[0,0],[3.075,-3.063],[0,0],[-2.285,-3.187],[8.636,-15.353],[1.061,-3.771],[0,0],[4.875,-0.506],[0,0],[0,-4.258]],"v":[[278.255,-45.307],[225.244,-54.124],[217.164,-61.354],[196.938,-110.098],[197.536,-120.929],[228.41,-164.139],[228.227,-175.87],[175.915,-228.203],[164.71,-228.781],[120.963,-197.501],[110.143,-196.906],[61.379,-217.151],[54.159,-225.221],[45.441,-277.563],[36.988,-285.774],[-36.988,-285.774],[-45.331,-278.261],[-54.129,-225.261],[-61.349,-217.19],[-110.102,-196.945],[-120.933,-197.542],[-164.165,-228.415],[-175.916,-228.234],[-228.228,-175.921],[-228.784,-164.725],[-197.526,-120.969],[-196.929,-110.138],[-217.175,-61.385],[-225.245,-54.163],[-277.588,-45.436],[-285.769,-36.981],[-285.769,36.983],[-278.276,45.305],[-225.255,54.122],[-217.185,61.353],[-196.939,110.137],[-197.536,120.947],[-228.39,164.139],[-228.239,175.9],[-175.926,228.202],[-164.721,228.779],[-120.973,197.521],[-115.088,195.641],[-110.143,196.934],[-61.349,217.18],[-54.129,225.248],[-45.422,277.593],[-36.988,285.774],[36.988,285.774],[45.321,278.26],[54.148,225.248],[61.369,217.189],[110.132,196.943],[120.953,197.539],[164.143,228.394],[175.894,228.232],[228.207,175.919],[228.793,164.685],[197.525,120.967],[196.928,110.137],[217.155,61.372],[225.234,54.143],[277.587,45.414],[285.769,37],[285.769,-36.963]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.808000033509,0.263000009574,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[305.995,306.001],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":4,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900.000036657751,"st":0,"bm":0}],"markers":[]} -------------------------------------------------------------------------------- /assets/json/loading2.json: -------------------------------------------------------------------------------- 1 | {"v":"5.5.8","fr":50,"ip":0,"op":147,"w":800,"h":600,"nm":"Paperplane","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"planete Outlines - Group 4","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":38,"s":[50]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":88,"s":[50]},{"t":120,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[468.336,323.378,0],"to":[-29,0,0],"ti":[29,0,0]},{"t":102,"s":[294.336,323.378,0]}],"ix":2},"a":{"a":0,"k":[453.672,304.756,0],"ix":1},"s":{"a":0,"k":[50,50,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[6.742,0],[0.741,-0.14],[0,0.074],[13.484,0],[1.669,-0.361],[19.79,0],[3.317,-19.082],[2.691,0],[0,-13.484],[-0.048,-0.629],[2.405,0],[0,-6.742],[-6.742,0],[0,0],[0,6.743]],"o":[[-0.781,0],[0.001,-0.074],[0,-13.484],[-1.778,0],[-3.594,-18.742],[-20.03,0],[-2.421,-0.804],[-13.485,0],[0,0.642],[-1.89,-1.199],[-6.742,0],[0,6.743],[0,0],[6.742,0],[0,-6.742]],"v":[[75.134,16.175],[72.85,16.396],[72.856,16.175],[48.44,-8.241],[43.262,-7.685],[3.406,-40.591],[-36.571,-6.995],[-44.269,-8.241],[-68.685,16.175],[-68.604,18.079],[-75.133,16.175],[-87.341,28.383],[-75.133,40.592],[75.134,40.592],[87.342,28.383]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.815686334348,0.823529471603,0.827451040231,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[453.672,304.756],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":151,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Merged Shape Layer","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.547],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.845],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":77,"s":[35]},{"t":150,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[390.319,298.2,0],"to":[0,-2.583,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":44,"s":[390.319,282.7,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":110,"s":[390.319,319.25,0],"to":[0,0,0],"ti":[0,0,0]},{"t":150,"s":[390.319,298.2,0]}],"ix":2},"a":{"a":0,"k":[664.319,256.2,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[18.967,-3.189],[-18.967,19.935],[-0.949,-19.935]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.223528981209,0.192156970501,0.674510002136,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[236.879,292.737],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[633.939,275.369],"ix":2},"a":{"a":0,"k":[236.879,292.737],"ix":1},"s":{"a":0,"k":[50,50],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"planete Outlines - Group 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-98.335,64.79],[-105.619,4.984],[105.619,-64.79],[-80.316,24.919]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.278430998325,0.294117987156,0.847059011459,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[316.247,247.882],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[673.623,252.941],"ix":2},"a":{"a":0,"k":[316.247,247.882],"ix":1},"s":{"a":0,"k":[50,50],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"planete Outlines - Group 2","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-133.812,-42.171],[133.812,-75.141],[5.765,75.141],[-61.708,18.402],[124.227,-71.307],[-87.011,-1.534]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.365000009537,0.407999992371,0.976000010967,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[297.638,254.4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[664.319,256.2],"ix":2},"a":{"a":0,"k":[297.638,254.4],"ix":1},"s":{"a":0,"k":[50,50],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"planete Outlines - Group 3","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":151,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"planete Outlines - Group 5","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":45,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":102,"s":[100]},{"t":150,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[327.38,267.583,0],"to":[25.833,0,0],"ti":[-25.833,0,0]},{"t":150,"s":[482.38,267.583,0]}],"ix":2},"a":{"a":0,"k":[171.76,193.166,0],"ix":1},"s":{"a":0,"k":[50,50,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[13.485,0],[4.38,-4.171],[21.913,0],[3.575,-18.765],[1.851,0],[0,-13.484],[-0.011,-0.291],[1.599,0],[0,-6.743],[-6.742,0],[0,0],[0,13.485]],"o":[[-6.526,0],[-0.793,-21.719],[-19.806,0],[-1.734,-0.391],[-13.485,0],[0,0.293],[-1.4,-0.559],[-6.742,0],[0,6.742],[0,0],[13.485,0],[0,-13.484]],"v":[[59.669,-8.242],[42.84,-1.506],[2.287,-40.592],[-37.576,-7.638],[-42.962,-8.242],[-67.378,16.174],[-67.356,17.049],[-71.878,16.174],[-84.086,28.383],[-71.878,40.591],[59.669,40.591],[84.086,16.174]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.816000007181,0.823999980852,0.827000038297,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[171.76,193.166],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":151,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Pre-comp 1","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[406,306,0],"ix":2},"a":{"a":0,"k":[400,300,0],"ix":1},"s":{"a":0,"k":[179,179,100],"ix":6}},"ao":0,"w":800,"h":600,"ip":0,"op":147,"st":0,"bm":0}],"markers":[]} -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 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/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/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/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/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/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/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/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/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/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/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/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/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/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/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/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/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/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/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/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/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/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/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/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/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/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/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/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/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/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/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/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/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 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutter_plus 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/common/application.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Application { 4 | } 5 | -------------------------------------------------------------------------------- /lib/common/constant.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Constant { 4 | static const APP_BAR = 50.0; 5 | static const DEBUG = true; 6 | static const PAGE_SIZE = 20; 7 | // static const BASE_URL = "http://139.9.214.31:8008/"; 8 | static const BASE_URL = "http://10.2.15.74:8008/"; 9 | static const TOKEN = "TOKEN"; 10 | static const USER = "USER"; 11 | 12 | static const accesskey = ""; 13 | static const OSSAccessKeyId = ""; 14 | static const OSSurl = ""; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flustars/flustars.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/services.dart'; 6 | import 'package:flutter_hooks/flutter_hooks.dart'; 7 | import 'package:flutter_plus/route/router.dart'; 8 | import 'package:flutter_plus/styles/app_colors.dart'; 9 | import 'package:flutter_plus/ui/login_page.dart'; 10 | import 'package:flutter_plus/ui/main_page.dart'; 11 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 12 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 13 | 14 | import 'common/constant.dart'; 15 | 16 | void main() async { 17 | WidgetsFlutterBinding.ensureInitialized(); 18 | await SpUtil.getInstance(); 19 | Routers.configureRoutes(); 20 | runApp( 21 | ProviderScope( 22 | child: MyApp(), 23 | ), 24 | ); 25 | if (Platform.isAndroid) { 26 | //设置Android头部的导航栏透明 27 | SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle( 28 | statusBarColor: Colors.transparent, //全局设置透明 29 | statusBarIconBrightness: Brightness.dark); 30 | SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle); 31 | } 32 | } 33 | final GlobalKey navigatorKey = new GlobalKey(); 34 | class MyApp extends HookWidget { 35 | @override 36 | Widget build(BuildContext context) { 37 | setDesignWHD(360.0, 640.0, density: 3); 38 | String? token = SpUtil.getString(Constant.TOKEN); 39 | return RefreshConfiguration( 40 | footerTriggerDistance: 15, 41 | headerTriggerDistance: 90.0, 42 | maxOverScrollExtent: 100, 43 | dragSpeedRatio: 0.91, 44 | headerBuilder: () => MaterialClassicHeader(), 45 | footerBuilder: () => ClassicFooter(), 46 | enableLoadingWhenNoData: false, 47 | enableRefreshVibrate: false, 48 | enableLoadMoreVibrate: false, 49 | hideFooterWhenNotFull: true, 50 | shouldFooterFollowWhenNotFull: (state) { 51 | return false; 52 | }, 53 | child: MaterialApp( 54 | navigatorKey: navigatorKey, 55 | theme: ThemeData( 56 | primaryColor: AppColors.appBarTopBg, //bar的颜色 57 | scaffoldBackgroundColor: AppColors.appBg, //scaffold的颜色, 58 | ), 59 | title: 'Flutter App', 60 | // home:MainPage(), 61 | home: TextUtil.isEmpty(token)?LoginPage(): MainPage(), 62 | //注册路由 63 | onGenerateRoute: Routers.router.generator, 64 | ///初始化loading 65 | builder: (context, child) => Scaffold( 66 | ///全局点击空白关闭软键盘 67 | body: GestureDetector( 68 | onTap: () { 69 | FocusScopeNode currentFocus = FocusScope.of(context); 70 | if (!currentFocus.hasPrimaryFocus && 71 | currentFocus.focusedChild != null) { 72 | FocusManager.instance.primaryFocus!.unfocus(); 73 | } 74 | }, 75 | child: child, 76 | ), 77 | ), 78 | ), 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/model/base/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | 4 | part 'home.g.dart'; 5 | 6 | part 'home.freezed.dart'; 7 | 8 | @freezed 9 | class GridEntity with _$GridEntity { 10 | const factory GridEntity( 11 | String name, 12 | String imgUrl, 13 | String path, 14 | ) = _GridEntity; 15 | 16 | factory GridEntity.fromJson(Map json) => _$GridEntityFromJson(json); 17 | } 18 | 19 | @freezed 20 | class SwiperEntity with _$SwiperEntity { 21 | const factory SwiperEntity( 22 | String name, 23 | String imgUrl, 24 | String path, 25 | ) = _SwiperEntity; 26 | 27 | factory SwiperEntity.fromJson(Map json) => _$SwiperEntityFromJson(json); 28 | } 29 | -------------------------------------------------------------------------------- /lib/model/base/home.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'home.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$_GridEntity _$_$_GridEntityFromJson(Map json) { 10 | return _$_GridEntity( 11 | json['name'] as String, 12 | json['imgUrl'] as String, 13 | json['path'] as String, 14 | ); 15 | } 16 | 17 | Map _$_$_GridEntityToJson(_$_GridEntity instance) => 18 | { 19 | 'name': instance.name, 20 | 'imgUrl': instance.imgUrl, 21 | 'path': instance.path, 22 | }; 23 | 24 | _$_SwiperEntity _$_$_SwiperEntityFromJson(Map json) { 25 | return _$_SwiperEntity( 26 | json['name'] as String, 27 | json['imgUrl'] as String, 28 | json['path'] as String, 29 | ); 30 | } 31 | 32 | Map _$_$_SwiperEntityToJson(_$_SwiperEntity instance) => 33 | { 34 | 'name': instance.name, 35 | 'imgUrl': instance.imgUrl, 36 | 'path': instance.path, 37 | }; 38 | -------------------------------------------------------------------------------- /lib/model/base/tab.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | part 'tab.freezed.dart'; 4 | 5 | @freezed 6 | class TabEntity with _$TabEntity { 7 | const factory TabEntity( 8 | Widget widget, 9 | String title, 10 | IconData icon, 11 | ) = _TabEntity; 12 | } 13 | -------------------------------------------------------------------------------- /lib/model/base/tab.freezed.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides 3 | 4 | part of 'tab.dart'; 5 | 6 | // ************************************************************************** 7 | // FreezedGenerator 8 | // ************************************************************************** 9 | 10 | T _$identity(T value) => value; 11 | 12 | final _privateConstructorUsedError = UnsupportedError( 13 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); 14 | 15 | /// @nodoc 16 | class _$TabEntityTearOff { 17 | const _$TabEntityTearOff(); 18 | 19 | _TabEntity call(Widget widget, String title, IconData icon) { 20 | return _TabEntity( 21 | widget, 22 | title, 23 | icon, 24 | ); 25 | } 26 | } 27 | 28 | /// @nodoc 29 | const $TabEntity = _$TabEntityTearOff(); 30 | 31 | /// @nodoc 32 | mixin _$TabEntity { 33 | Widget get widget => throw _privateConstructorUsedError; 34 | String get title => throw _privateConstructorUsedError; 35 | IconData get icon => throw _privateConstructorUsedError; 36 | 37 | @JsonKey(ignore: true) 38 | $TabEntityCopyWith get copyWith => 39 | throw _privateConstructorUsedError; 40 | } 41 | 42 | /// @nodoc 43 | abstract class $TabEntityCopyWith<$Res> { 44 | factory $TabEntityCopyWith(TabEntity value, $Res Function(TabEntity) then) = 45 | _$TabEntityCopyWithImpl<$Res>; 46 | $Res call({Widget widget, String title, IconData icon}); 47 | } 48 | 49 | /// @nodoc 50 | class _$TabEntityCopyWithImpl<$Res> implements $TabEntityCopyWith<$Res> { 51 | _$TabEntityCopyWithImpl(this._value, this._then); 52 | 53 | final TabEntity _value; 54 | // ignore: unused_field 55 | final $Res Function(TabEntity) _then; 56 | 57 | @override 58 | $Res call({ 59 | Object? widget = freezed, 60 | Object? title = freezed, 61 | Object? icon = freezed, 62 | }) { 63 | return _then(_value.copyWith( 64 | widget: widget == freezed 65 | ? _value.widget 66 | : widget // ignore: cast_nullable_to_non_nullable 67 | as Widget, 68 | title: title == freezed 69 | ? _value.title 70 | : title // ignore: cast_nullable_to_non_nullable 71 | as String, 72 | icon: icon == freezed 73 | ? _value.icon 74 | : icon // ignore: cast_nullable_to_non_nullable 75 | as IconData, 76 | )); 77 | } 78 | } 79 | 80 | /// @nodoc 81 | abstract class _$TabEntityCopyWith<$Res> implements $TabEntityCopyWith<$Res> { 82 | factory _$TabEntityCopyWith( 83 | _TabEntity value, $Res Function(_TabEntity) then) = 84 | __$TabEntityCopyWithImpl<$Res>; 85 | @override 86 | $Res call({Widget widget, String title, IconData icon}); 87 | } 88 | 89 | /// @nodoc 90 | class __$TabEntityCopyWithImpl<$Res> extends _$TabEntityCopyWithImpl<$Res> 91 | implements _$TabEntityCopyWith<$Res> { 92 | __$TabEntityCopyWithImpl(_TabEntity _value, $Res Function(_TabEntity) _then) 93 | : super(_value, (v) => _then(v as _TabEntity)); 94 | 95 | @override 96 | _TabEntity get _value => super._value as _TabEntity; 97 | 98 | @override 99 | $Res call({ 100 | Object? widget = freezed, 101 | Object? title = freezed, 102 | Object? icon = freezed, 103 | }) { 104 | return _then(_TabEntity( 105 | widget == freezed 106 | ? _value.widget 107 | : widget // ignore: cast_nullable_to_non_nullable 108 | as Widget, 109 | title == freezed 110 | ? _value.title 111 | : title // ignore: cast_nullable_to_non_nullable 112 | as String, 113 | icon == freezed 114 | ? _value.icon 115 | : icon // ignore: cast_nullable_to_non_nullable 116 | as IconData, 117 | )); 118 | } 119 | } 120 | 121 | /// @nodoc 122 | class _$_TabEntity implements _TabEntity { 123 | const _$_TabEntity(this.widget, this.title, this.icon); 124 | 125 | @override 126 | final Widget widget; 127 | @override 128 | final String title; 129 | @override 130 | final IconData icon; 131 | 132 | @override 133 | String toString() { 134 | return 'TabEntity(widget: $widget, title: $title, icon: $icon)'; 135 | } 136 | 137 | @override 138 | bool operator ==(dynamic other) { 139 | return identical(this, other) || 140 | (other is _TabEntity && 141 | (identical(other.widget, widget) || 142 | const DeepCollectionEquality().equals(other.widget, widget)) && 143 | (identical(other.title, title) || 144 | const DeepCollectionEquality().equals(other.title, title)) && 145 | (identical(other.icon, icon) || 146 | const DeepCollectionEquality().equals(other.icon, icon))); 147 | } 148 | 149 | @override 150 | int get hashCode => 151 | runtimeType.hashCode ^ 152 | const DeepCollectionEquality().hash(widget) ^ 153 | const DeepCollectionEquality().hash(title) ^ 154 | const DeepCollectionEquality().hash(icon); 155 | 156 | @JsonKey(ignore: true) 157 | @override 158 | _$TabEntityCopyWith<_TabEntity> get copyWith => 159 | __$TabEntityCopyWithImpl<_TabEntity>(this, _$identity); 160 | } 161 | 162 | abstract class _TabEntity implements TabEntity { 163 | const factory _TabEntity(Widget widget, String title, IconData icon) = 164 | _$_TabEntity; 165 | 166 | @override 167 | Widget get widget => throw _privateConstructorUsedError; 168 | @override 169 | String get title => throw _privateConstructorUsedError; 170 | @override 171 | IconData get icon => throw _privateConstructorUsedError; 172 | @override 173 | @JsonKey(ignore: true) 174 | _$TabEntityCopyWith<_TabEntity> get copyWith => 175 | throw _privateConstructorUsedError; 176 | } 177 | -------------------------------------------------------------------------------- /lib/model/breed/breed.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | part 'breed.freezed.dart'; 3 | part 'breed.g.dart'; 4 | @freezed 5 | class BreedEntity with _$BreedEntity { 6 | const factory BreedEntity( 7 | int id, 8 | String name, 9 | String picture, 10 | String content, 11 | ) = _BreedEntity; 12 | factory BreedEntity.fromJson(Map json) => 13 | _$BreedEntityFromJson(json); 14 | } -------------------------------------------------------------------------------- /lib/model/breed/breed.freezed.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides 3 | 4 | part of 'breed.dart'; 5 | 6 | // ************************************************************************** 7 | // FreezedGenerator 8 | // ************************************************************************** 9 | 10 | T _$identity(T value) => value; 11 | 12 | final _privateConstructorUsedError = UnsupportedError( 13 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); 14 | 15 | BreedEntity _$BreedEntityFromJson(Map json) { 16 | return _BreedEntity.fromJson(json); 17 | } 18 | 19 | /// @nodoc 20 | class _$BreedEntityTearOff { 21 | const _$BreedEntityTearOff(); 22 | 23 | _BreedEntity call(int id, String name, String picture, String content) { 24 | return _BreedEntity( 25 | id, 26 | name, 27 | picture, 28 | content, 29 | ); 30 | } 31 | 32 | BreedEntity fromJson(Map json) { 33 | return BreedEntity.fromJson(json); 34 | } 35 | } 36 | 37 | /// @nodoc 38 | const $BreedEntity = _$BreedEntityTearOff(); 39 | 40 | /// @nodoc 41 | mixin _$BreedEntity { 42 | int get id => throw _privateConstructorUsedError; 43 | String get name => throw _privateConstructorUsedError; 44 | String get picture => throw _privateConstructorUsedError; 45 | String get content => throw _privateConstructorUsedError; 46 | 47 | Map toJson() => throw _privateConstructorUsedError; 48 | @JsonKey(ignore: true) 49 | $BreedEntityCopyWith get copyWith => 50 | throw _privateConstructorUsedError; 51 | } 52 | 53 | /// @nodoc 54 | abstract class $BreedEntityCopyWith<$Res> { 55 | factory $BreedEntityCopyWith( 56 | BreedEntity value, $Res Function(BreedEntity) then) = 57 | _$BreedEntityCopyWithImpl<$Res>; 58 | $Res call({int id, String name, String picture, String content}); 59 | } 60 | 61 | /// @nodoc 62 | class _$BreedEntityCopyWithImpl<$Res> implements $BreedEntityCopyWith<$Res> { 63 | _$BreedEntityCopyWithImpl(this._value, this._then); 64 | 65 | final BreedEntity _value; 66 | // ignore: unused_field 67 | final $Res Function(BreedEntity) _then; 68 | 69 | @override 70 | $Res call({ 71 | Object? id = freezed, 72 | Object? name = freezed, 73 | Object? picture = freezed, 74 | Object? content = freezed, 75 | }) { 76 | return _then(_value.copyWith( 77 | id: id == freezed 78 | ? _value.id 79 | : id // ignore: cast_nullable_to_non_nullable 80 | as int, 81 | name: name == freezed 82 | ? _value.name 83 | : name // ignore: cast_nullable_to_non_nullable 84 | as String, 85 | picture: picture == freezed 86 | ? _value.picture 87 | : picture // ignore: cast_nullable_to_non_nullable 88 | as String, 89 | content: content == freezed 90 | ? _value.content 91 | : content // ignore: cast_nullable_to_non_nullable 92 | as String, 93 | )); 94 | } 95 | } 96 | 97 | /// @nodoc 98 | abstract class _$BreedEntityCopyWith<$Res> 99 | implements $BreedEntityCopyWith<$Res> { 100 | factory _$BreedEntityCopyWith( 101 | _BreedEntity value, $Res Function(_BreedEntity) then) = 102 | __$BreedEntityCopyWithImpl<$Res>; 103 | @override 104 | $Res call({int id, String name, String picture, String content}); 105 | } 106 | 107 | /// @nodoc 108 | class __$BreedEntityCopyWithImpl<$Res> extends _$BreedEntityCopyWithImpl<$Res> 109 | implements _$BreedEntityCopyWith<$Res> { 110 | __$BreedEntityCopyWithImpl( 111 | _BreedEntity _value, $Res Function(_BreedEntity) _then) 112 | : super(_value, (v) => _then(v as _BreedEntity)); 113 | 114 | @override 115 | _BreedEntity get _value => super._value as _BreedEntity; 116 | 117 | @override 118 | $Res call({ 119 | Object? id = freezed, 120 | Object? name = freezed, 121 | Object? picture = freezed, 122 | Object? content = freezed, 123 | }) { 124 | return _then(_BreedEntity( 125 | id == freezed 126 | ? _value.id 127 | : id // ignore: cast_nullable_to_non_nullable 128 | as int, 129 | name == freezed 130 | ? _value.name 131 | : name // ignore: cast_nullable_to_non_nullable 132 | as String, 133 | picture == freezed 134 | ? _value.picture 135 | : picture // ignore: cast_nullable_to_non_nullable 136 | as String, 137 | content == freezed 138 | ? _value.content 139 | : content // ignore: cast_nullable_to_non_nullable 140 | as String, 141 | )); 142 | } 143 | } 144 | 145 | @JsonSerializable() 146 | 147 | /// @nodoc 148 | class _$_BreedEntity implements _BreedEntity { 149 | const _$_BreedEntity(this.id, this.name, this.picture, this.content); 150 | 151 | factory _$_BreedEntity.fromJson(Map json) => 152 | _$_$_BreedEntityFromJson(json); 153 | 154 | @override 155 | final int id; 156 | @override 157 | final String name; 158 | @override 159 | final String picture; 160 | @override 161 | final String content; 162 | 163 | @override 164 | String toString() { 165 | return 'BreedEntity(id: $id, name: $name, picture: $picture, content: $content)'; 166 | } 167 | 168 | @override 169 | bool operator ==(dynamic other) { 170 | return identical(this, other) || 171 | (other is _BreedEntity && 172 | (identical(other.id, id) || 173 | const DeepCollectionEquality().equals(other.id, id)) && 174 | (identical(other.name, name) || 175 | const DeepCollectionEquality().equals(other.name, name)) && 176 | (identical(other.picture, picture) || 177 | const DeepCollectionEquality() 178 | .equals(other.picture, picture)) && 179 | (identical(other.content, content) || 180 | const DeepCollectionEquality().equals(other.content, content))); 181 | } 182 | 183 | @override 184 | int get hashCode => 185 | runtimeType.hashCode ^ 186 | const DeepCollectionEquality().hash(id) ^ 187 | const DeepCollectionEquality().hash(name) ^ 188 | const DeepCollectionEquality().hash(picture) ^ 189 | const DeepCollectionEquality().hash(content); 190 | 191 | @JsonKey(ignore: true) 192 | @override 193 | _$BreedEntityCopyWith<_BreedEntity> get copyWith => 194 | __$BreedEntityCopyWithImpl<_BreedEntity>(this, _$identity); 195 | 196 | @override 197 | Map toJson() { 198 | return _$_$_BreedEntityToJson(this); 199 | } 200 | } 201 | 202 | abstract class _BreedEntity implements BreedEntity { 203 | const factory _BreedEntity( 204 | int id, String name, String picture, String content) = _$_BreedEntity; 205 | 206 | factory _BreedEntity.fromJson(Map json) = 207 | _$_BreedEntity.fromJson; 208 | 209 | @override 210 | int get id => throw _privateConstructorUsedError; 211 | @override 212 | String get name => throw _privateConstructorUsedError; 213 | @override 214 | String get picture => throw _privateConstructorUsedError; 215 | @override 216 | String get content => throw _privateConstructorUsedError; 217 | @override 218 | @JsonKey(ignore: true) 219 | _$BreedEntityCopyWith<_BreedEntity> get copyWith => 220 | throw _privateConstructorUsedError; 221 | } 222 | -------------------------------------------------------------------------------- /lib/model/breed/breed.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'breed.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$_BreedEntity _$_$_BreedEntityFromJson(Map json) { 10 | return _$_BreedEntity( 11 | json['id'] as int, 12 | json['name'] as String, 13 | json['picture'] as String, 14 | json['content'] as String, 15 | ); 16 | } 17 | 18 | Map _$_$_BreedEntityToJson(_$_BreedEntity instance) => 19 | { 20 | 'id': instance.id, 21 | 'name': instance.name, 22 | 'picture': instance.picture, 23 | 'content': instance.content, 24 | }; 25 | -------------------------------------------------------------------------------- /lib/model/moment/moment.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'moment.freezed.dart'; 4 | part 'moment.g.dart'; 5 | 6 | @freezed 7 | class MomentEntity with _$MomentEntity { 8 | const factory MomentEntity( 9 | String id, 10 | String type, 11 | String content, 12 | String? createUserId, 13 | String? createUserName, 14 | String? updateUserId, 15 | String? updateUserName, 16 | ) = _MomentEntity; 17 | 18 | factory MomentEntity.fromJson(Map json) => _$MomentEntityFromJson(json); 19 | } 20 | -------------------------------------------------------------------------------- /lib/model/moment/moment.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'moment.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$_MomentEntity _$_$_MomentEntityFromJson(Map json) { 10 | return _$_MomentEntity( 11 | json['id'] as String, 12 | json['type'] as String, 13 | json['content'] as String, 14 | json['createUserId'] as String?, 15 | json['createUserName'] as String?, 16 | json['updateUserId'] as String?, 17 | json['updateUserName'] as String?, 18 | ); 19 | } 20 | 21 | Map _$_$_MomentEntityToJson(_$_MomentEntity instance) => 22 | { 23 | 'id': instance.id, 24 | 'type': instance.type, 25 | 'content': instance.content, 26 | 'createUserId': instance.createUserId, 27 | 'createUserName': instance.createUserName, 28 | 'updateUserId': instance.updateUserId, 29 | 'updateUserName': instance.updateUserName, 30 | }; 31 | -------------------------------------------------------------------------------- /lib/model/moment/moment_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | import 'moment.dart'; 4 | 5 | part 'moment_response.freezed.dart'; 6 | 7 | part 'moment_response.g.dart'; 8 | 9 | @freezed 10 | class MomentResponse with _$MomentResponse { 11 | const factory MomentResponse(int state, String msg, 12 | {List? aaData}) = _MomentResponse; 13 | 14 | factory MomentResponse.fromJson(Map json) => 15 | _$MomentResponseFromJson(json); 16 | } 17 | -------------------------------------------------------------------------------- /lib/model/moment/moment_response.freezed.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides 3 | 4 | part of 'moment_response.dart'; 5 | 6 | // ************************************************************************** 7 | // FreezedGenerator 8 | // ************************************************************************** 9 | 10 | T _$identity(T value) => value; 11 | 12 | final _privateConstructorUsedError = UnsupportedError( 13 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); 14 | 15 | MomentResponse _$MomentResponseFromJson(Map json) { 16 | return _MomentResponse.fromJson(json); 17 | } 18 | 19 | /// @nodoc 20 | class _$MomentResponseTearOff { 21 | const _$MomentResponseTearOff(); 22 | 23 | _MomentResponse call(int state, String msg, {List? aaData}) { 24 | return _MomentResponse( 25 | state, 26 | msg, 27 | aaData: aaData, 28 | ); 29 | } 30 | 31 | MomentResponse fromJson(Map json) { 32 | return MomentResponse.fromJson(json); 33 | } 34 | } 35 | 36 | /// @nodoc 37 | const $MomentResponse = _$MomentResponseTearOff(); 38 | 39 | /// @nodoc 40 | mixin _$MomentResponse { 41 | int get state => throw _privateConstructorUsedError; 42 | String get msg => throw _privateConstructorUsedError; 43 | List? get aaData => throw _privateConstructorUsedError; 44 | 45 | Map toJson() => throw _privateConstructorUsedError; 46 | @JsonKey(ignore: true) 47 | $MomentResponseCopyWith get copyWith => 48 | throw _privateConstructorUsedError; 49 | } 50 | 51 | /// @nodoc 52 | abstract class $MomentResponseCopyWith<$Res> { 53 | factory $MomentResponseCopyWith( 54 | MomentResponse value, $Res Function(MomentResponse) then) = 55 | _$MomentResponseCopyWithImpl<$Res>; 56 | $Res call({int state, String msg, List? aaData}); 57 | } 58 | 59 | /// @nodoc 60 | class _$MomentResponseCopyWithImpl<$Res> 61 | implements $MomentResponseCopyWith<$Res> { 62 | _$MomentResponseCopyWithImpl(this._value, this._then); 63 | 64 | final MomentResponse _value; 65 | // ignore: unused_field 66 | final $Res Function(MomentResponse) _then; 67 | 68 | @override 69 | $Res call({ 70 | Object? state = freezed, 71 | Object? msg = freezed, 72 | Object? aaData = freezed, 73 | }) { 74 | return _then(_value.copyWith( 75 | state: state == freezed 76 | ? _value.state 77 | : state // ignore: cast_nullable_to_non_nullable 78 | as int, 79 | msg: msg == freezed 80 | ? _value.msg 81 | : msg // ignore: cast_nullable_to_non_nullable 82 | as String, 83 | aaData: aaData == freezed 84 | ? _value.aaData 85 | : aaData // ignore: cast_nullable_to_non_nullable 86 | as List?, 87 | )); 88 | } 89 | } 90 | 91 | /// @nodoc 92 | abstract class _$MomentResponseCopyWith<$Res> 93 | implements $MomentResponseCopyWith<$Res> { 94 | factory _$MomentResponseCopyWith( 95 | _MomentResponse value, $Res Function(_MomentResponse) then) = 96 | __$MomentResponseCopyWithImpl<$Res>; 97 | @override 98 | $Res call({int state, String msg, List? aaData}); 99 | } 100 | 101 | /// @nodoc 102 | class __$MomentResponseCopyWithImpl<$Res> 103 | extends _$MomentResponseCopyWithImpl<$Res> 104 | implements _$MomentResponseCopyWith<$Res> { 105 | __$MomentResponseCopyWithImpl( 106 | _MomentResponse _value, $Res Function(_MomentResponse) _then) 107 | : super(_value, (v) => _then(v as _MomentResponse)); 108 | 109 | @override 110 | _MomentResponse get _value => super._value as _MomentResponse; 111 | 112 | @override 113 | $Res call({ 114 | Object? state = freezed, 115 | Object? msg = freezed, 116 | Object? aaData = freezed, 117 | }) { 118 | return _then(_MomentResponse( 119 | state == freezed 120 | ? _value.state 121 | : state // ignore: cast_nullable_to_non_nullable 122 | as int, 123 | msg == freezed 124 | ? _value.msg 125 | : msg // ignore: cast_nullable_to_non_nullable 126 | as String, 127 | aaData: aaData == freezed 128 | ? _value.aaData 129 | : aaData // ignore: cast_nullable_to_non_nullable 130 | as List?, 131 | )); 132 | } 133 | } 134 | 135 | @JsonSerializable() 136 | 137 | /// @nodoc 138 | class _$_MomentResponse implements _MomentResponse { 139 | const _$_MomentResponse(this.state, this.msg, {this.aaData}); 140 | 141 | factory _$_MomentResponse.fromJson(Map json) => 142 | _$_$_MomentResponseFromJson(json); 143 | 144 | @override 145 | final int state; 146 | @override 147 | final String msg; 148 | @override 149 | final List? aaData; 150 | 151 | @override 152 | String toString() { 153 | return 'MomentResponse(state: $state, msg: $msg, aaData: $aaData)'; 154 | } 155 | 156 | @override 157 | bool operator ==(dynamic other) { 158 | return identical(this, other) || 159 | (other is _MomentResponse && 160 | (identical(other.state, state) || 161 | const DeepCollectionEquality().equals(other.state, state)) && 162 | (identical(other.msg, msg) || 163 | const DeepCollectionEquality().equals(other.msg, msg)) && 164 | (identical(other.aaData, aaData) || 165 | const DeepCollectionEquality().equals(other.aaData, aaData))); 166 | } 167 | 168 | @override 169 | int get hashCode => 170 | runtimeType.hashCode ^ 171 | const DeepCollectionEquality().hash(state) ^ 172 | const DeepCollectionEquality().hash(msg) ^ 173 | const DeepCollectionEquality().hash(aaData); 174 | 175 | @JsonKey(ignore: true) 176 | @override 177 | _$MomentResponseCopyWith<_MomentResponse> get copyWith => 178 | __$MomentResponseCopyWithImpl<_MomentResponse>(this, _$identity); 179 | 180 | @override 181 | Map toJson() { 182 | return _$_$_MomentResponseToJson(this); 183 | } 184 | } 185 | 186 | abstract class _MomentResponse implements MomentResponse { 187 | const factory _MomentResponse(int state, String msg, 188 | {List? aaData}) = _$_MomentResponse; 189 | 190 | factory _MomentResponse.fromJson(Map json) = 191 | _$_MomentResponse.fromJson; 192 | 193 | @override 194 | int get state => throw _privateConstructorUsedError; 195 | @override 196 | String get msg => throw _privateConstructorUsedError; 197 | @override 198 | List? get aaData => throw _privateConstructorUsedError; 199 | @override 200 | @JsonKey(ignore: true) 201 | _$MomentResponseCopyWith<_MomentResponse> get copyWith => 202 | throw _privateConstructorUsedError; 203 | } 204 | -------------------------------------------------------------------------------- /lib/model/moment/moment_response.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'moment_response.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$_MomentResponse _$_$_MomentResponseFromJson(Map json) { 10 | return _$_MomentResponse( 11 | json['state'] as int, 12 | json['msg'] as String, 13 | aaData: (json['aaData'] as List?) 14 | ?.map((e) => MomentEntity.fromJson(e as Map)) 15 | .toList(), 16 | ); 17 | } 18 | 19 | Map _$_$_MomentResponseToJson(_$_MomentResponse instance) => 20 | { 21 | 'state': instance.state, 22 | 'msg': instance.msg, 23 | 'aaData': instance.aaData, 24 | }; 25 | -------------------------------------------------------------------------------- /lib/model/user/user.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'user.g.dart'; 4 | 5 | part 'user.freezed.dart'; 6 | 7 | @freezed 8 | class UserEntity with _$UserEntity { 9 | const factory UserEntity( 10 | int id, 11 | String username, 12 | String password, 13 | String? salt, 14 | String? nickName, 15 | String? headPortrait, 16 | String? phone, 17 | String? email, 18 | int? sex, 19 | String? introduce, 20 | int status, 21 | int? createWhere, 22 | String? pairId, 23 | ) = _UserEntity; 24 | 25 | factory UserEntity.fromJson(Map json) => _$UserEntityFromJson(json); 26 | } 27 | -------------------------------------------------------------------------------- /lib/model/user/user.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'user.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$_UserEntity _$_$_UserEntityFromJson(Map json) { 10 | return _$_UserEntity( 11 | json['id'] as int, 12 | json['username'] as String, 13 | json['password'] as String, 14 | json['salt'] as String?, 15 | json['nickName'] as String?, 16 | json['headPortrait'] as String?, 17 | json['phone'] as String?, 18 | json['email'] as String?, 19 | json['sex'] as int?, 20 | json['introduce'] as String?, 21 | json['status'] as int, 22 | json['createWhere'] as int?, 23 | json['pairId'] as String?, 24 | ); 25 | } 26 | 27 | Map _$_$_UserEntityToJson(_$_UserEntity instance) => 28 | { 29 | 'id': instance.id, 30 | 'username': instance.username, 31 | 'password': instance.password, 32 | 'salt': instance.salt, 33 | 'nickName': instance.nickName, 34 | 'headPortrait': instance.headPortrait, 35 | 'phone': instance.phone, 36 | 'email': instance.email, 37 | 'sex': instance.sex, 38 | 'introduce': instance.introduce, 39 | 'status': instance.status, 40 | 'createWhere': instance.createWhere, 41 | 'pairId': instance.pairId, 42 | }; 43 | -------------------------------------------------------------------------------- /lib/net/api_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:flutter_plus/model/moment/moment_response.dart'; 3 | import 'package:retrofit/retrofit.dart'; 4 | import 'base_dio.dart'; 5 | part 'api_client.g.dart'; 6 | @RestApi() 7 | abstract class ApiClient { 8 | factory ApiClient({Dio? dio,String? baseUrl}) { 9 | dio = BaseDio.getInstance().getDio(); 10 | return _ApiClient(dio, baseUrl: baseUrl); 11 | } 12 | 13 | @POST('login') 14 | Future login(@Body() Map map); 15 | @POST('api/cat/moment/page') 16 | Future getmoment(); 17 | @POST('api/cat/breed/page') 18 | Future getbreed(); 19 | } 20 | -------------------------------------------------------------------------------- /lib/net/api_client.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'api_client.dart'; 4 | 5 | // ************************************************************************** 6 | // RetrofitGenerator 7 | // ************************************************************************** 8 | 9 | class _ApiClient implements ApiClient { 10 | _ApiClient(this._dio, {this.baseUrl}); 11 | 12 | final Dio _dio; 13 | 14 | String? baseUrl; 15 | 16 | @override 17 | Future> login(map) async { 18 | const _extra = {}; 19 | final queryParameters = {}; 20 | final _data = {}; 21 | _data.addAll(map); 22 | final _result = await _dio.fetch(_setStreamType>( 23 | Options(method: 'POST', headers: {}, extra: _extra) 24 | .compose(_dio.options, 'login', 25 | queryParameters: queryParameters, data: _data) 26 | .copyWith(baseUrl: baseUrl ?? _dio.options.baseUrl))); 27 | final value = _result.data!; 28 | final httpResponse = HttpResponse(value, _result); 29 | return httpResponse; 30 | } 31 | 32 | @override 33 | Future getmoment() async { 34 | const _extra = {}; 35 | final queryParameters = {}; 36 | final _data = {}; 37 | final _result = await _dio.fetch>( 38 | _setStreamType( 39 | Options(method: 'POST', headers: {}, extra: _extra) 40 | .compose(_dio.options, 'api/cat/moment/page', 41 | queryParameters: queryParameters, data: _data) 42 | .copyWith(baseUrl: baseUrl ?? _dio.options.baseUrl))); 43 | final value = MomentResponse.fromJson(_result.data!); 44 | return value; 45 | } 46 | 47 | @override 48 | Future> getbreed() async { 49 | const _extra = {}; 50 | final queryParameters = {}; 51 | final _data = {}; 52 | final _result = await _dio.fetch(_setStreamType>( 53 | Options(method: 'POST', headers: {}, extra: _extra) 54 | .compose(_dio.options, 'api/cat/breed/page', 55 | queryParameters: queryParameters, data: _data) 56 | .copyWith(baseUrl: baseUrl ?? _dio.options.baseUrl))); 57 | final value = _result.data!; 58 | final httpResponse = HttpResponse(value, _result); 59 | return httpResponse; 60 | } 61 | 62 | RequestOptions _setStreamType(RequestOptions requestOptions) { 63 | if (T != dynamic && 64 | !(requestOptions.responseType == ResponseType.bytes || 65 | requestOptions.responseType == ResponseType.stream)) { 66 | if (T == String) { 67 | requestOptions.responseType = ResponseType.plain; 68 | } else { 69 | requestOptions.responseType = ResponseType.json; 70 | } 71 | } 72 | return requestOptions; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/net/base_dio.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:cookie_jar/cookie_jar.dart'; 5 | import 'package:dio/dio.dart'; 6 | import 'package:dio_cookie_manager/dio_cookie_manager.dart'; 7 | import 'package:flutter/foundation.dart'; 8 | import 'package:flutter_plus/common/constant.dart'; 9 | import 'package:flutter_plus/net/interceptor/log_interceptor.dart'; 10 | import 'package:flutter_plus/net/interceptor/response_interceptor.dart'; 11 | import 'base_error.dart'; 12 | import 'interceptor/header_interceptor.dart'; 13 | 14 | export 'package:dio/dio.dart'; 15 | 16 | class BaseDio { 17 | BaseDio._(); 18 | static BaseDio? _instance; 19 | static BaseDio getInstance() { 20 | return _instance ??= BaseDio._(); 21 | } 22 | Dio getDio() { 23 | final Dio dio = Dio(); 24 | (dio.transformer as DefaultTransformer).jsonDecodeCallback = parseJson; 25 | dio.options = BaseOptions( 26 | baseUrl: Constant.BASE_URL, 27 | receiveTimeout: 66000, 28 | connectTimeout: 66000); // 设置超时时间等 ... 29 | dio.interceptors.add(HeaderInterceptor()); // 添加拦截器,如 token之类,需要全局使用的参数 30 | dio.interceptors.add(LogsInterceptor()); 31 | dio.interceptors.add(ResponseInterceptor()); 32 | // cookie持久化 异步 33 | dio.interceptors.add(CookieManager(CookieJar())); 34 | return dio; 35 | } 36 | /** 37 | * 这里封装了一个 BaseError 类,会根据后端返回的code返回不同的错误类 38 | */ 39 | BaseError getDioError(e) { 40 | if (e is DioError) { 41 | if (e.type == DioErrorType.connectTimeout || 42 | e.type == DioErrorType.sendTimeout || 43 | e.type == DioErrorType.receiveTimeout) { 44 | // timeout 45 | return OtherError(statusCode: 96, statusMessage: e.error); 46 | } else if (e.type == DioErrorType.response) { 47 | // incorrect status, such as 404, 503... 48 | return OtherError(statusCode: 97, statusMessage: e.error); 49 | } else if (e.type == DioErrorType.cancel) { 50 | // to be continue... 51 | return OtherError(statusCode: 98, statusMessage: e.error); 52 | } else { 53 | // dio将原error重新套了一层 54 | e = e.error; 55 | if (e is NeedAuth) { 56 | return NeedAuth(); 57 | } else if (e is OtherError) { 58 | return OtherError( 59 | statusCode: e.statusCode, statusMessage: e.statusMessage); 60 | } else if (e is SocketException) { 61 | return NetworkTimeOutError(); 62 | } 63 | } 64 | } 65 | return UnknownError(); 66 | } 67 | } 68 | 69 | // 必须是顶层函数 70 | _parseAndDecode(String response) { 71 | return jsonDecode(response); 72 | } 73 | 74 | parseJson(String text) { 75 | return compute(_parseAndDecode, text); 76 | } 77 | -------------------------------------------------------------------------------- /lib/net/base_error.dart: -------------------------------------------------------------------------------- 1 | abstract class BaseError implements Exception{ 2 | final int code; 3 | final String message; 4 | 5 | BaseError({required this.code, required this.message}); 6 | } 7 | 8 | class NeedAuth implements BaseError { 9 | @override 10 | int get code => 97; 11 | 12 | @override 13 | String get message => "非法访问,请使用正确的token"; 14 | } 15 | class NetworkTimeOutError implements BaseError { 16 | @override 17 | int get code => 98; 18 | 19 | @override 20 | String get message => "网络错误!"; 21 | } 22 | class UnknownError implements BaseError { 23 | @override 24 | int get code => 99; 25 | 26 | @override 27 | String get message => "未知错误!"; 28 | } 29 | 30 | class OtherError implements BaseError { 31 | final int statusCode; 32 | final String statusMessage; 33 | 34 | OtherError({required this.statusCode, required this.statusMessage}); 35 | 36 | @override 37 | int get code => statusCode; 38 | 39 | @override 40 | String get message => statusMessage; 41 | } 42 | -------------------------------------------------------------------------------- /lib/net/http_provider.dart: -------------------------------------------------------------------------------- 1 | // import 'dart:async'; 2 | // import 'dart:convert'; 3 | // 4 | // import 'package:crypto/crypto.dart'; 5 | // import 'package:dio/dio.dart'; 6 | // import 'package:flutter/services.dart'; 7 | // import 'package:flutter_plus/common/constant.dart'; 8 | // import 'package:flutter_plus/common/exceptions.dart'; 9 | // import 'package:flutter_plus/net/interceptor/header_interceptor.dart'; 10 | // import 'package:flutter_plus/net/interceptor/log_interceptor.dart'; 11 | // import 'package:hooks_riverpod/hooks_riverpod.dart'; 12 | // import 'package:multi_image_picker/multi_image_picker.dart'; 13 | // 14 | // final httpUtilProvider = Provider((ref) => HttpUtil()); 15 | // 16 | // class HttpUtil { 17 | // static final BaseOptions options = BaseOptions( 18 | // //请求基地址,可以包含子路径 19 | // baseUrl: Constant.BASE_URL, 20 | // //连接服务器超时时间,单位是毫秒. 21 | // connectTimeout: 10000, 22 | // //响应流上前后两次接受到数据的间隔,单位为毫秒。 23 | // receiveTimeout: 5000, 24 | // ); 25 | // static final Dio dio = new Dio(options); 26 | // 27 | // //拦截器部分 28 | // static tokenInter() { 29 | // dio.interceptors.add(new HeaderInterceptor()); 30 | // dio.interceptors.add(new LogsInterceptor()); 31 | // } 32 | // 33 | // /// 动态替换BaseUrl 34 | // _setBaseUrl(String? baseUrl) { 35 | // if (baseUrl != null) { 36 | // options.baseUrl = baseUrl; 37 | // } else { 38 | // options.baseUrl = Constant.BASE_URL; 39 | // } 40 | // } 41 | // 42 | // HttpUtil() { 43 | // tokenInter(); 44 | // } 45 | // 46 | // Future request(url, {baseUrl, queryData, formData, options}) async { 47 | // _setBaseUrl(baseUrl); 48 | // try { 49 | // Response response = await dio.post(url, 50 | // data: formData, queryParameters: queryData, options: options); 51 | // return response.data; 52 | // } on DioError catch (error) { 53 | // throw DataException.fromDioError(error); 54 | // } 55 | // } 56 | // 57 | // Future> uploadOSS(List list) async { 58 | // List urlList = []; 59 | // if (list.isEmpty) { 60 | // return urlList; 61 | // } 62 | // _setBaseUrl(Constant.OSSurl); 63 | // String policyText = '{"expiration": "2120-01-01T12:00:00.000Z",' 64 | // '"conditions": [["content-length-range", 0, 1048576000]]}'; 65 | // List uPolicyText = utf8.encode(policyText); 66 | // String bPolicyText = base64.encode(uPolicyText); 67 | // List policy = utf8.encode(bPolicyText); 68 | // List keyM = utf8.encode(Constant.accesskey); 69 | // List signaturePre = new Hmac(sha1, keyM).convert(policy).bytes; 70 | // String signature = base64.encode(signaturePre); 71 | // for (Asset asset in list) { 72 | // ByteData byteData = await asset.getByteData(); 73 | // List imageData = byteData.buffer.asUint8List(); 74 | // // 构建formData数据 75 | // FormData data = new FormData.fromMap({ 76 | // 'key': "images/" + asset.name!, 77 | // 'policy': bPolicyText, 78 | // 'OSSAccessKeyId': Constant.OSSAccessKeyId, 79 | // 'signature': signature, 80 | // 'file': MultipartFile.fromBytes(imageData), 81 | // }); 82 | // try { 83 | // await dio 84 | // .post("", 85 | // data: data, 86 | // options: Options( 87 | // contentType: "multipart/form-data", 88 | // responseType: ResponseType.json)) 89 | // .then( 90 | // (value) { 91 | // if (value.statusCode == 204) { 92 | // urlList.add(Constant.OSSurl + '/images/${asset.name!}'); 93 | // } 94 | // }, 95 | // ); 96 | // } on DioError catch (error) { 97 | // throw DataException.fromDioError(error); 98 | // } 99 | // } 100 | // return urlList; 101 | // } 102 | // } 103 | -------------------------------------------------------------------------------- /lib/net/interceptor/header_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:flustars/flustars.dart'; 3 | import 'package:flutter_plus/common/constant.dart'; 4 | 5 | ///header拦截器test 6 | class HeaderInterceptor extends InterceptorsWrapper { 7 | @override 8 | onRequest(options, handler){ 9 | ///超时 10 | options.connectTimeout = 20000; 11 | options.receiveTimeout = 20000; 12 | //登录接口 13 | if (options.path.contains("login")) { 14 | return super.onRequest(options, handler); 15 | } 16 | String? token = SpUtil.getString(Constant.TOKEN); 17 | if (!TextUtil.isEmpty(token)) { 18 | options.headers['Authorization'] = token; 19 | } 20 | return super.onRequest(options, handler); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/net/interceptor/log_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'package:dio/dio.dart'; 3 | import 'package:flutter_plus/common/constant.dart'; 4 | ///Log 拦截器 5 | class LogsInterceptor extends InterceptorsWrapper { 6 | static List sHttpResponses = []; 7 | static List sResponsesHttpUrl = []; 8 | 9 | static List> sHttpRequest = []; 10 | static List sRequestHttpUrl = []; 11 | 12 | static List> sHttpError = []; 13 | static List sHttpErrorUrl = []; 14 | 15 | @override 16 | onRequest(RequestOptions options,RequestInterceptorHandler handler) async { 17 | if (Constant.DEBUG) { 18 | print("请求url:${options.baseUrl}${options.path} ${options.method}"); 19 | print('请求头: ' + options.headers.toString()); 20 | if (options.data != null) { 21 | print('请求参数: ' + options.data.toString()); 22 | } 23 | } 24 | try { 25 | addLogic(sRequestHttpUrl, options.path); 26 | var data; 27 | if (options.data is Map) { 28 | data = options.data; 29 | } else { 30 | data = Map(); 31 | } 32 | var map = { 33 | "header:": {...options.headers}, 34 | }; 35 | if (options.method == "POST") { 36 | map["data"] = data; 37 | } 38 | addLogic(sHttpRequest, map); 39 | } catch (e) { 40 | print(e); 41 | } 42 | return super.onRequest(options, handler); 43 | } 44 | 45 | @override 46 | onResponse(Response response,ResponseInterceptorHandler handler) async { 47 | if (Constant.DEBUG) { 48 | if (response != null) { 49 | print('返回参数: ' + response.toString()); 50 | } 51 | } 52 | if (response.data is Map || response.data is List) { 53 | try { 54 | var data = Map(); 55 | data["data"] = response.data; 56 | addLogic(sResponsesHttpUrl, response.realUri.toString()); 57 | addLogic(sHttpResponses, data); 58 | } catch (e) { 59 | print(e); 60 | } 61 | } else if (response.data is String) { 62 | try { 63 | var data = Map(); 64 | data["data"] = response.data; 65 | addLogic(sResponsesHttpUrl, response.realUri.toString()); 66 | addLogic(sHttpResponses, data); 67 | } catch (e) { 68 | print(e); 69 | } 70 | } else if (response.data != null) { 71 | try { 72 | String data = response.data.toJson(); 73 | addLogic(sResponsesHttpUrl,response.realUri.toString()); 74 | addLogic(sHttpResponses, json.decode(data)); 75 | } catch (e) { 76 | print(e); 77 | } 78 | } 79 | return super.onResponse(response, handler); 80 | } 81 | 82 | @override 83 | onError(DioError err,ErrorInterceptorHandler handler) async { 84 | if (Constant.DEBUG) { 85 | print('请求异常信息: ' + (err.error.toString())); 86 | } 87 | try { 88 | addLogic(sHttpErrorUrl, err.requestOptions.path); 89 | var errors = Map(); 90 | errors["error"] = err.message; 91 | addLogic(sHttpError, errors); 92 | } catch (e) { 93 | print(e); 94 | } 95 | return super.onError(err, handler); 96 | } 97 | 98 | static addLogic(List list, data) { 99 | if (list.length > 20) { 100 | list.removeAt(0); 101 | } 102 | list.add(data); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /lib/net/interceptor/response_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:flutter_plus/net/base_error.dart'; 3 | 4 | import '../base_dio.dart'; 5 | class ResponseInterceptor extends InterceptorsWrapper { 6 | @override 7 | void onResponse(Response response, ResponseInterceptorHandler handler) { 8 | // TODO: implement onResponse 9 | if (response.data['state']==0) { 10 | return handler.resolve(response); 11 | } else { 12 | if (response.data['state'] == 97) { 13 | throw NeedAuth(); 14 | } else { 15 | throw OtherError(statusCode: response.data['state'], statusMessage: response.data['msg']); 16 | } 17 | } 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /lib/provider/breed_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:core'; 2 | import 'package:flutter_plus/model/breed/breed.dart'; 3 | import 'package:flutter_plus/net/api_client.dart'; 4 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 5 | 6 | final breedProvider = 7 | StateNotifierProvider.autoDispose>((ref) { 8 | return BreedNotifier(ref.read); 9 | }); 10 | List list = []; 11 | 12 | class BreedNotifier extends StateNotifier> { 13 | final Reader read; 14 | 15 | BreedNotifier(this.read) : super(list) { 16 | initData(); 17 | } 18 | 19 | void initData() async { 20 | final response = await ApiClient().getbreed(); 21 | if (response.data['state'] == 0) { 22 | response.data['aaData']?.forEach((c) async { 23 | list.add(BreedEntity.fromJson(c)); 24 | }); 25 | } 26 | state=list; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/provider/moment_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:core'; 2 | 3 | import 'package:flutter_plus/model/moment/moment.dart'; 4 | import 'package:flutter_plus/model/moment/moment_response.dart'; 5 | import 'package:flutter_plus/net/api_client.dart'; 6 | import 'package:flutter_plus/net/base_dio.dart'; 7 | import 'package:flutter_plus/net/base_error.dart'; 8 | import 'package:flutter_plus/widgets/page_state.dart'; 9 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 10 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 11 | 12 | final momentProvider = 13 | StateNotifierProvider((ref) { 14 | return MomentNotifier(); 15 | }); 16 | 17 | class MomentState { 18 | final List list; 19 | final int pageIndex; 20 | final PageState pageState; 21 | final BaseError? error; 22 | 23 | MomentState( 24 | {required this.list, 25 | required this.pageIndex, 26 | required this.pageState, 27 | this.error}); 28 | 29 | MomentState.initial() 30 | : list = [], 31 | pageIndex = 1, 32 | pageState = PageState.idle, 33 | error = null; 34 | 35 | MomentState copyWith( 36 | {List? list, 37 | int? pageIndex, 38 | PageState? pageState, 39 | BaseError? error}) { 40 | return MomentState( 41 | list: list ?? this.list, 42 | pageIndex: pageIndex ?? this.pageIndex, 43 | pageState: pageState ?? this.pageState, 44 | error: error ?? this.error, 45 | ); 46 | } 47 | } 48 | 49 | class MomentNotifier extends StateNotifier { 50 | final RefreshController refreshController = 51 | RefreshController(initialRefresh: false); 52 | 53 | MomentNotifier() : super(MomentState.initial()) { 54 | initData(); 55 | } 56 | 57 | Future initData({bool isRefresh = false}) async { 58 | if (state.pageState == PageState.idle) { 59 | state = state.copyWith(pageState: PageState.busy); 60 | } 61 | try { 62 | if (isRefresh) { 63 | MomentResponse result = await ApiClient().getmoment(); 64 | if (result.state == 0) { 65 | state = state.copyWith( 66 | list: [...result.aaData!], 67 | pageState: PageState.success, 68 | pageIndex: 2, 69 | ); 70 | refreshController.refreshCompleted(); 71 | refreshController.footerMode!.value = LoadStatus.canLoading; 72 | } 73 | } else { 74 | MomentResponse result = await ApiClient().getmoment(); 75 | if (result.aaData!.isEmpty && state.pageIndex == 1) { 76 | state = state.copyWith(pageState: PageState.empty); 77 | } else { 78 | state = state.copyWith( 79 | list: [...state.list, ...result.aaData!], 80 | pageIndex: state.pageIndex + 1, 81 | pageState: PageState.success); 82 | refreshController.loadComplete(); 83 | if (result.aaData!.isEmpty || result.aaData!.length < 10) { 84 | refreshController.loadNoData(); 85 | } 86 | } 87 | } 88 | } catch (e) { 89 | state = state.copyWith(pageState: PageState.error, error: BaseDio.getInstance().getDioError(e)); 90 | refreshController.refreshFailed(); 91 | refreshController.loadFailed(); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /lib/route/handler.dart: -------------------------------------------------------------------------------- 1 | import 'package:fluro/fluro.dart'; 2 | import 'package:flutter_plus/ui/main_page.dart'; 3 | import 'package:flutter_plus/ui/home/breed_detail_page.dart'; 4 | import 'package:flutter_plus/ui/home/breed_page.dart'; 5 | import 'package:flutter_plus/ui/login_page.dart'; 6 | import 'package:flutter_plus/ui/moment/test_page.dart'; 7 | import 'package:flutter_plus/ui/personal/personal_page.dart'; 8 | import 'package:flutter_plus/ui/splash_page.dart'; // 引入路由包依赖文件 9 | var rootHandler = Handler(handlerFunc: (_, params) => SplashPage()); 10 | var loginHandler = new Handler(handlerFunc: (_, params) => LoginPage()); 11 | var homeHandler = new Handler(handlerFunc: (_, params) { 12 | return MainPage(); 13 | }); 14 | var personalHandler = new Handler(handlerFunc: (_, params) => PersonalPage()); 15 | var breedHandler = new Handler(handlerFunc: (_, params) => BreedPage()); 16 | var breedDetailHandler = new Handler(handlerFunc: (_, params) => BreedDetailPage(title:params['title']!.first,content:params['content']!.first)); 17 | var testHandler= new Handler(handlerFunc: (_, params) => TestPage()); 18 | 19 | -------------------------------------------------------------------------------- /lib/route/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:fluro/fluro.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | 4 | import '../main.dart'; 5 | import 'handler.dart'; 6 | 7 | // 定义路由配置类 8 | class Routers { 9 | static final FluroRouter router = new FluroRouter(); 10 | static String root = "/"; 11 | static String login = "/login"; 12 | static String home = "/home"; 13 | static String personal = "/personal"; 14 | static String breed = "/breed"; 15 | static String breeddetail = "/breeddetail"; 16 | static String test = "/test"; 17 | static String fooPage = '/'; 18 | static String barPage = '/bar'; 19 | static void configureRoutes() { 20 | router.notFoundHandler = Handler(handlerFunc: 21 | (BuildContext? context, Map>? params) { 22 | print("ROUTE WAS NOT FOUND !!!"); 23 | return; 24 | }); 25 | router.define(root, handler: rootHandler); 26 | router.define(login, handler: loginHandler); 27 | router.define(home, handler: homeHandler); 28 | router.define(personal, handler: personalHandler); 29 | router.define(breed, handler: breedHandler); 30 | router.define(breeddetail, handler: breedDetailHandler); 31 | router.define(test, handler: testHandler); 32 | } 33 | 34 | // 自定义的参数跳转,对参数进行encode,解决参数中有特殊字符,影响fluro路由匹配 35 | static Future navigateTo(path, {params, replace = false}) { 36 | String query = ""; 37 | if (params != null) { 38 | int index = 0; 39 | for (var key in params.keys) { 40 | var value = Uri.encodeComponent(params[key]); 41 | if (index == 0) { 42 | query = "?"; 43 | } else { 44 | query = query + "\&"; 45 | } 46 | query += "$key=$value"; 47 | index++; 48 | } 49 | } 50 | path = path + query; 51 | BuildContext context = navigatorKey.currentState!.context; 52 | return router.navigateTo(context, path, 53 | transition: TransitionType.native, replace: replace); 54 | } 55 | 56 | //返回页面 57 | static void pop(BuildContext context, [T? result]) => 58 | Navigator.of(context).pop(result); 59 | } 60 | -------------------------------------------------------------------------------- /lib/styles/app_colors.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | abstract class AppColors { 6 | //颜色转换 7 | static Color hexToColor(String s) { 8 | // 如果传入的十六进制颜色值不符合要求,返回默认值 9 | if (s == null || 10 | s.length != 7 || 11 | int.tryParse(s.substring(1, 7), radix: 16) == null) { 12 | s = '#999999'; 13 | } 14 | return new Color(int.parse(s.substring(1, 7), radix: 16) + 0xFF000000); 15 | } 16 | 17 | static const Color appBg = const Color(0xfffefefe); 18 | static const Color appBarTopBg = const Color(0xfffefefe); 19 | static const Color appBarBottomBg = const Color(0xfffefefe); 20 | static const Color pageBg = const Color(0xfff7f7f7); 21 | static const Color dividerBg = const Color(0xffdbdce1); 22 | static const Color titleColor = const Color(0xff060606); 23 | static const Color hintTextColor = const Color(0xffd1d5dd); 24 | static const Color mainTextColor = const Color(0xff060606); 25 | static const Color subTextColor = const Color(0xffb2b9c4); 26 | static const Color loignColor = const Color(0xFFFE976A); 27 | static const Color greyColor = const Color(0xFFdadbe0); 28 | static const Color whileColor = const Color(0xFFffffff); 29 | 30 | /// 主背景 31 | static const Color primaryBackground = Color(0xFFF4F6FA); 32 | 33 | /// 主文本 34 | static const Color primaryText = Color(0xFF2D3142); 35 | 36 | /// 主文本灰色 37 | static const Color primaryGreyText = Color(0xFF9B9B9B); 38 | 39 | /// 主文本灰色 40 | static const Color primaryGreyText1 = Color(0xFFE0DDF5); 41 | 42 | /// tabBar 默认颜色 灰色 43 | static const Color tabBarElement = Color(0xFFE0DDF5); 44 | 45 | /// tabBar 激活颜色 46 | static const Color tabBarActive = Color(0xFF8B63E6); 47 | 48 | /// 分类tab渐变色 49 | static Color buttonLine1 = Colors.purple[300]!; 50 | 51 | /// 分类tab渐变色 52 | static const Color buttonLine2 = Color(0xFF7265E3); 53 | 54 | /// 主题色 55 | static const Color primaryColor = Color(0xFF8B63E6); 56 | 57 | static const Color primaryColorAccent = Color(0xFFE1DDF5); 58 | 59 | /// 价格颜色 60 | static const Color priceColor = Color(0xFFF77777); 61 | 62 | /// 提货方式-自提 63 | static const Color deliveryColor1 = Color(0xFFFE9C5E); 64 | 65 | /// 提货方式-物流 66 | static const Color deliveryColor2 = Color(0xFF6155CC); 67 | 68 | /// 提货方式-自提 69 | static const Color deliveryBackColor1 = Color.fromRGBO(254, 156, 94, 0.15); 70 | 71 | /// 提货方式-物流 72 | static const Color deliveryBackColor2 = Color.fromRGBO(97, 85, 204, 0.15); 73 | 74 | /// 加入购物车 75 | static const Color addToCart2 = Color(0xFFFE9C5E); 76 | static const Color addToCart1 = Color(0xFFfdcb6e); 77 | 78 | /// 立即采购 79 | static const Color buyNow1 = Color(0xFFF77777); 80 | static const Color buyNow2 = Color(0xFFd63031); 81 | 82 | /// splashColor 83 | static const Color splashColor = Color(0xFFE1DDF5); 84 | 85 | /// 店铺背景渐变 86 | static const Color supplierColor1 = Color(0xFF8B63E6); 87 | static const Color supplierColor2 = Color(0xFF7265E3); 88 | } 89 | -------------------------------------------------------------------------------- /lib/styles/app_spacers.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// Contains useful consts to reduce boilerplate and duplicate code 4 | abstract class AppSpacers { 5 | // Vertical spacing constants. Adjust to your liking. 6 | static final double _verticalSpaceSmall = 10.0; 7 | static final double _verticalSpaceMedium = 15.0; 8 | static final double _verticalSpaceLarge = 64.0; 9 | 10 | // Vertical spacing constants. Adjust to your liking. 11 | static final double _horizontalSpaceSmall = 10.0; 12 | static final double _horizontalSpaceMedium = 15.0; 13 | static final double _horizontalSpaceLarge = 64.0; 14 | static final double _horizontalSpaceEditPages = 40.0; 15 | 16 | static final Widget verticalSpaceSmall = 17 | SizedBox(height: _verticalSpaceSmall); 18 | static final Widget verticalSpaceMedium = 19 | SizedBox(height: _verticalSpaceMedium); 20 | static final Widget verticalSpaceLarge = 21 | SizedBox(height: _verticalSpaceLarge); 22 | 23 | static final Widget horizontalSpaceSmall = 24 | SizedBox(width: _horizontalSpaceSmall); 25 | static final Widget horizontalSpaceMedium = 26 | SizedBox(width: _horizontalSpaceMedium); 27 | static final Widget horizontalSpaceLarge = 28 | SizedBox(width: _horizontalSpaceLarge); 29 | static final Widget horizontalSpaceEditPages = 30 | SizedBox(width: _horizontalSpaceEditPages); 31 | } 32 | -------------------------------------------------------------------------------- /lib/styles/app_styles.dart: -------------------------------------------------------------------------------- 1 | import 'package:flustars/flustars.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'app_colors.dart'; 4 | abstract class AppStyles { 5 | static final titleTextStyle = 6 | TextStyle(fontSize: ScreenUtil.getInstance().getSp(18), color: AppColors.mainTextColor); 7 | static final seachTextStyle = 8 | TextStyle(fontSize: ScreenUtil.getInstance().getSp(12), color: AppColors.subTextColor); 9 | static final mainTextStyle = 10 | TextStyle(fontSize: ScreenUtil.getInstance().getSp(16), color: AppColors.mainTextColor); 11 | static final mainBlodTextStyle = TextStyle( 12 | fontSize: ScreenUtil.getInstance().getSp(16), 13 | fontWeight: FontWeight.bold, 14 | color: AppColors.mainTextColor); 15 | static final subTextStyle = TextStyle( 16 | fontSize: ScreenUtil.getInstance().getSp(14), 17 | fontWeight: FontWeight.bold, 18 | color: AppColors.subTextColor); 19 | 20 | static final largeTextStyle = 21 | TextStyle(fontSize: ScreenUtil.getInstance().getSp(30), color: AppColors.mainTextColor); 22 | } 23 | -------------------------------------------------------------------------------- /lib/ui/common_base_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flustars/flustars.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_plus/net/base_error.dart'; 4 | import 'package:flutter_plus/route/router.dart'; 5 | import 'package:flutter_plus/widgets/error_page.dart'; 6 | import 'package:flutter_plus/widgets/page_state.dart'; 7 | import 'package:lottie/lottie.dart'; 8 | 9 | class CommonBasePage extends StatelessWidget { 10 | final PageState pageState; 11 | final BaseError? baseError; 12 | final VoidCallback buttonActionCallback; 13 | final Widget child; 14 | 15 | const CommonBasePage( 16 | {Key? key, 17 | required this.pageState, 18 | this.baseError, 19 | required this.buttonActionCallback, 20 | required this.child}) 21 | : super(key: key); 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | if (pageState == PageState.busy || pageState == PageState.idle) { 26 | return Center( 27 | child: Lottie.asset( 28 | 'assets/json/loading2.json', 29 | width: 126, 30 | fit: BoxFit.cover, 31 | alignment: Alignment.center, 32 | ), 33 | ); 34 | } 35 | 36 | if (pageState == PageState.empty) { 37 | return ErrorPage( 38 | isEmptyPage: true, 39 | icon: Lottie.asset( 40 | 'assets/json/empty3.json', 41 | width: ScreenUtil.getInstance().screenWidth / 1.8, 42 | height: 220, 43 | fit: BoxFit.contain, 44 | alignment: Alignment.center, 45 | ), 46 | desc: '暂 无 数 据', 47 | buttonAction: buttonActionCallback, 48 | ); 49 | } 50 | 51 | if (pageState == PageState.error) { 52 | if (baseError is NeedAuth) { 53 | Future.delayed(Duration(seconds: 2), () { 54 | Routers.navigateTo(Routers.login, replace: true); 55 | }); 56 | } else { 57 | return ErrorPage( 58 | title: baseError?.code.toString(), 59 | desc: baseError?.message, 60 | buttonAction: () async { 61 | buttonActionCallback(); 62 | }, 63 | buttonText: baseError is NeedAuth ? '登录' : null, 64 | ); 65 | } 66 | } 67 | 68 | return child; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/ui/components/header_swiper_pagination.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_card_swipper/flutter_card_swiper.dart'; 3 | import 'package:flutter_plus/styles/app_colors.dart'; 4 | // 自定义分页效果 5 | class MyCustomPagination extends SwiperPlugin { 6 | @override 7 | Widget build(BuildContext context, SwiperPluginConfig? config) { 8 | int activeIndex=0; 9 | if(config!=null){ 10 | activeIndex=config.activeIndex!; 11 | } 12 | List list = []; 13 | for (var i = 0; i < config!.itemCount!; i++) { 14 | if (i == activeIndex) { 15 | list.add(Container( 16 | width: 20, 17 | height: 5, 18 | margin: EdgeInsets.only(left: 2.5, right: 2.5), 19 | decoration: BoxDecoration( 20 | borderRadius: BorderRadius.circular(5), color: AppColors.primaryColor), 21 | )); 22 | continue; 23 | } 24 | list.add(Container( 25 | width: 10, 26 | height: 5, 27 | margin: EdgeInsets.only(left: 2.5, right: 2.5), 28 | decoration: BoxDecoration( 29 | borderRadius: (BorderRadius.circular(5)), color: Color(0xffC6CBCE)), 30 | )); 31 | } 32 | return Positioned( 33 | bottom: 10, 34 | left: 0, 35 | right: 0, 36 | child: Row( 37 | mainAxisAlignment: MainAxisAlignment.center, 38 | children: list, 39 | )); 40 | } 41 | } 42 | // 自定义分页效果 43 | class MyCustomNumPagination extends SwiperPlugin { 44 | @override 45 | Widget build(BuildContext context, SwiperPluginConfig? config) { 46 | return Container( 47 | child: Positioned( 48 | bottom: 10, 49 | right: 10, 50 | child: Container( 51 | height: 25, 52 | width: 35, 53 | decoration: BoxDecoration( 54 | color: Color.fromRGBO(39, 39, 39, 0.7), 55 | borderRadius: BorderRadius.circular(25), 56 | ), 57 | child: Center( 58 | child: Row( 59 | mainAxisAlignment: MainAxisAlignment.center, 60 | children: [ 61 | Text( 62 | (config!.activeIndex! + 1).toString(), 63 | style: TextStyle(color: Colors.white), 64 | ), 65 | Text( 66 | '/', 67 | style: TextStyle(color: Colors.white), 68 | ), 69 | Text( 70 | config.itemCount.toString(), 71 | style: TextStyle(color: Colors.white), 72 | ) 73 | ], 74 | ), 75 | ), 76 | ), 77 | ), 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/ui/components/login_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_plus/styles/app_colors.dart'; 3 | 4 | class LoginButton extends StatelessWidget { 5 | final String text; 6 | final VoidCallback handleOk; 7 | 8 | const LoginButton({Key? key, required this.handleOk, required this.text}) 9 | : super(key: key); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return GestureDetector( 14 | onTap: () => handleOk(), 15 | child: Container( 16 | margin: EdgeInsets.only( 17 | bottom: MediaQuery.of(context).padding.bottom, 18 | left: 10, 19 | right: 10, 20 | ), 21 | height: 60, 22 | decoration: BoxDecoration( 23 | gradient: LinearGradient( 24 | //背景径向渐变 25 | colors: [AppColors.buttonLine1, AppColors.buttonLine2], 26 | ), 27 | borderRadius: BorderRadius.circular(20.0), 28 | ), 29 | child: Center( 30 | child: Text( 31 | text, 32 | style: TextStyle( 33 | color: Colors.white, 34 | fontSize: 16, 35 | fontWeight: FontWeight.w500, 36 | ), 37 | ), 38 | ), 39 | ), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/ui/components/persistent_header_builder.dart: -------------------------------------------------------------------------------- 1 | import 'package:flustars/flustars.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/widgets.dart'; 4 | import 'package:flutter_plus/common/constant.dart'; 5 | 6 | class PersistentHeaderBuilder extends SliverPersistentHeaderDelegate { 7 | final double expandedHeight; 8 | final Widget Function(BuildContext context, double offset) builder; 9 | PersistentHeaderBuilder({ 10 | required this.builder, 11 | required this.expandedHeight, 12 | }); 13 | 14 | @override 15 | Widget build( 16 | BuildContext context, double shrinkOffset, bool overlapsContent) { 17 | return builder(context, shrinkOffset); 18 | } 19 | 20 | @override 21 | double get maxExtent => expandedHeight; 22 | 23 | @override 24 | double get minExtent => Constant.APP_BAR + ScreenUtil.getInstance().statusBarHeight; 25 | 26 | @override 27 | bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => false; 28 | } 29 | class CommonSliverHeaderDelegate extends SliverPersistentHeaderDelegate { 30 | PreferredSize child; //传入preferredsize组件,因为此组件需要固定高度 31 | bool islucency; //入参 是否更加滑动变化透明度,true,false 32 | Color? backgroundColor; //需要设置的背景色 33 | CommonSliverHeaderDelegate( 34 | {required this.islucency, required this.child, this.backgroundColor}); 35 | 36 | @override 37 | Widget build( 38 | BuildContext context, double shrinkOffset, bool overlapsContent) { 39 | double mainHeight = maxExtent - shrinkOffset; //动态获取滑动剩余高度 40 | return Container( 41 | color: backgroundColor ?? Colors.white, 42 | child: Opacity( 43 | opacity: islucency == true && mainHeight != maxExtent 44 | ? ((mainHeight / maxExtent) * 0.5).clamp(0, 1) 45 | : 1, //根据滑动高度隐藏显示 46 | child: child), 47 | ); 48 | } 49 | 50 | @override 51 | // TODO: implement maxExtent 52 | double get maxExtent => this.child.preferredSize.height; 53 | 54 | @override 55 | // TODO: implement minExtent 56 | double get minExtent => this.child.preferredSize.height; 57 | 58 | @override 59 | bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) { 60 | // TODO: implement shouldRebuild 61 | return true; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/ui/discovery/discovery_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_hooks/flutter_hooks.dart'; 3 | import 'package:flutter_plus/model/moment/moment.dart'; 4 | import 'package:flutter_plus/provider/moment_provider.dart'; 5 | import 'package:flutter_plus/route/router.dart'; 6 | import 'package:flutter_plus/widgets/app_topbar.dart'; 7 | import 'package:flutter_plus/widgets/my_appbar.dart'; 8 | import 'package:flutter_plus/widgets/refresh.dart'; 9 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 10 | import '../common_base_page.dart'; 11 | class DiscoveryPage extends HookWidget { 12 | @override 13 | Widget build(BuildContext context) { 14 | final postState = useProvider(momentProvider); 15 | final scrollController = useScrollController(); 16 | return Scaffold( 17 | appBar: MyAppBar( 18 | child: CustomBar( 19 | title: "标题", 20 | ), 21 | ), 22 | body: CommonBasePage( 23 | pageState: postState.pageState, 24 | baseError: postState.error, 25 | buttonActionCallback: () { 26 | print("刷新"); 27 | context.refresh(momentProvider).initData(isRefresh: true); 28 | }, 29 | child: Refresh( 30 | controller: context.refresh(momentProvider).refreshController, 31 | onLoading: () async { 32 | context.refresh(momentProvider).initData(); 33 | }, 34 | onRefresh: () async { 35 | context.refresh(momentProvider).initData(isRefresh: true); 36 | }, 37 | content: ListView.separated( 38 | shrinkWrap: true, 39 | separatorBuilder: (context, index) { 40 | return Padding(padding: EdgeInsets.only(top: 12)); 41 | }, 42 | padding: EdgeInsets.fromLTRB(12, 18, 12, 18), 43 | reverse: false, 44 | controller: scrollController, 45 | itemCount: postState.list.length, 46 | itemBuilder: (BuildContext context, int index) { 47 | MomentEntity item = postState.list[index]; 48 | return Padding( 49 | padding: EdgeInsets.all(10), 50 | child: ListTile( 51 | onTap: () { 52 | Routers.navigateTo(Routers.test); 53 | }, 54 | title: Text(item.content.toString(), 55 | maxLines: 1, overflow: TextOverflow.ellipsis), 56 | )); 57 | }, 58 | )), 59 | ), 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/ui/home/breed_detail_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/src/widgets/framework.dart'; 3 | import 'package:flutter_hooks/flutter_hooks.dart'; 4 | import 'package:flutter_html/flutter_html.dart'; 5 | import 'package:flutter_plus/widgets/app_topbar.dart'; 6 | import 'package:flutter_plus/widgets/my_appbar.dart'; 7 | class BreedDetailPage extends HookWidget { 8 | BreedDetailPage({required this.title,required this.content}); 9 | final String title; 10 | final String content; 11 | @override 12 | Widget build(BuildContext context) => Scaffold( 13 | appBar: MyAppBar(child: CustomBar(title: title,isBack: true,),), 14 | body: SingleChildScrollView( 15 | child: Html( 16 | data:content, 17 | )), 18 | ); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /lib/ui/home/breed_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/src/widgets/framework.dart'; 4 | import 'package:flutter_hooks/flutter_hooks.dart'; 5 | import 'package:flutter_plus/model/breed/breed.dart'; 6 | import 'package:flutter_plus/provider/breed_provider.dart'; 7 | import 'package:flutter_plus/route/router.dart'; 8 | import 'package:flutter_plus/widgets/app_topbar.dart'; 9 | import 'package:flutter_plus/widgets/my_appbar.dart'; 10 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 11 | 12 | class BreedPage extends HookWidget { 13 | @override 14 | Widget build(BuildContext context) { 15 | return Scaffold( 16 | appBar: MyAppBar( 17 | child: CustomBar( 18 | title: "品种百科", 19 | isBack: true, 20 | )), 21 | body: CustomScrollView( 22 | slivers: [ 23 | buildSliverFixedExtentList(), 24 | ], 25 | ), 26 | ); 27 | } 28 | 29 | SliverFixedExtentList buildSliverFixedExtentList() { 30 | final dataList = useProvider(breedProvider); 31 | return SliverFixedExtentList( 32 | itemExtent: 200, 33 | delegate: new SliverChildBuilderDelegate( 34 | (_, index) { 35 | BreedEntity item = dataList[index]; 36 | Map p = {"title": item.name, "content": item.content}; 37 | 38 | ///子条目的布局样式 39 | return GestureDetector( 40 | onTap: () => Routers.navigateTo(Routers.breeddetail, params: p), 41 | child: Card( 42 | elevation: 0, 43 | shape: RoundedRectangleBorder( 44 | borderRadius: BorderRadius.circular(10)), 45 | clipBehavior: Clip.antiAlias, 46 | child: Stack( 47 | fit: StackFit.expand, 48 | children: [ 49 | Image.network( 50 | item.picture, 51 | fit: BoxFit.cover, 52 | ), 53 | Center( 54 | child: Text( 55 | item.name, 56 | style: TextStyle( 57 | fontSize: 30, color: Colors.white), 58 | )), 59 | ], 60 | ), 61 | margin: EdgeInsets.all(5), 62 | )); 63 | }, 64 | childCount: dataList.length, 65 | ), 66 | ); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/ui/home/components/head_swiper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/src/widgets/framework.dart'; 4 | import 'package:flutter_card_swipper/flutter_card_swiper.dart'; 5 | import 'package:flutter_hooks/flutter_hooks.dart'; 6 | import 'package:flutter_plus/model/base/home.dart'; 7 | import 'package:flutter_plus/ui/components/header_swiper_pagination.dart'; 8 | import 'package:flutter_plus/widgets/cache_image.dart'; 9 | 10 | class HeadSwiper extends HookWidget { 11 | final List swiperList; 12 | 13 | HeadSwiper(this.swiperList, {Key? key}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | final indexPage = useState(0); 18 | return Container( 19 | height: 300, 20 | margin: EdgeInsets.only(top: 10, left: 15, right: 15), 21 | color: Colors.white, 22 | child: Stack(children: [ 23 | Swiper( 24 | loop: false, 25 | autoplay: true, 26 | autoplayDelay: 4000, 27 | duration: 750, 28 | itemBuilder: (BuildContext context, int index) { 29 | return Container( 30 | height: 135, 31 | child: ClipRRect( 32 | borderRadius: BorderRadius.circular(6), 33 | child: CacheImage( 34 | url: swiperList[index].imgUrl, 35 | ), 36 | ), 37 | ); 38 | }, 39 | onIndexChanged: (index) { 40 | indexPage.value = index; 41 | }, 42 | index: indexPage.value, 43 | itemCount: swiperList.length, 44 | pagination: MyCustomPagination(), 45 | ), 46 | ]), 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/ui/home/components/home_header.dart: -------------------------------------------------------------------------------- 1 | import 'package:flustars/flustars.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_hooks/flutter_hooks.dart'; 5 | import 'package:flutter_plus/model/base/home.dart'; 6 | import 'package:flutter_plus/styles/app_colors.dart'; 7 | import 'package:flutter_plus/widgets/app_topbar.dart'; 8 | import 'package:flutter_plus/widgets/my_appbar.dart'; 9 | import 'package:flutter_plus/ui/components/persistent_header_builder.dart'; 10 | import 'package:flutter_plus/ui/home/components/head_swiper.dart'; 11 | 12 | class HomeHeader extends HookWidget { 13 | double _expandedHeigh = 300; 14 | List swiperList; 15 | 16 | HomeHeader(this.swiperList, {Key? key}) : super(key: key); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return SliverPersistentHeader( 21 | pinned: true, 22 | floating: false, 23 | delegate: PersistentHeaderBuilder( 24 | expandedHeight: _expandedHeigh, 25 | builder: (ctx, offset) => Stack( 26 | clipBehavior: Clip.none, 27 | fit: StackFit.expand, 28 | children: [ 29 | buildAppBar(offset), 30 | buildSwiper(offset), 31 | buildTopBar(offset), 32 | ], 33 | ))); 34 | } 35 | 36 | Widget buildTopBar(double shrinkOffset) => Opacity( 37 | opacity: 1 - shrinkOffset / _expandedHeigh, 38 | child: Container( 39 | margin: EdgeInsets.only(top: ScreenUtil.getInstance().statusBarHeight), 40 | child: Wrap( 41 | children: [ 42 | Container( 43 | height: 50, 44 | child: Center( 45 | child: Container( 46 | margin: EdgeInsets.symmetric(horizontal: 25), 47 | child: Row( 48 | children: [ 49 | Icon( 50 | Icons.search_rounded, 51 | color: AppColors.subTextColor, 52 | ), 53 | SizedBox( 54 | width: 5, 55 | ), 56 | Text( 57 | '搜索', 58 | style: TextStyle(color: AppColors.subTextColor), 59 | ), 60 | ], 61 | ), 62 | padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), 63 | decoration: BoxDecoration( 64 | color: Color(0x30060606), 65 | borderRadius: BorderRadius.circular(25), 66 | border: Border.all( 67 | color: Color(0xffd8d1d3), 68 | width: 0.2, 69 | ), 70 | ), 71 | ), 72 | ), 73 | // color: AppColors.primaryColor, 74 | ) 75 | ], 76 | ), 77 | )); 78 | 79 | Widget buildAppBar(double shrinkOffset) => Opacity( 80 | opacity: shrinkOffset / _expandedHeigh, 81 | child: MyAppBar( 82 | child: SearchBar(), 83 | )); 84 | 85 | Widget buildSwiper(double shrinkOffset) { 86 | return Opacity( 87 | opacity: 1 - shrinkOffset / _expandedHeigh, 88 | child: HeadSwiper(swiperList)); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /lib/ui/home/components/home_horizontal_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_hooks/flutter_hooks.dart'; 4 | import 'package:flutter_plus/model/base/home.dart'; 5 | import 'package:flutter_plus/route/router.dart'; 6 | import 'package:flutter_plus/styles/app_colors.dart'; 7 | import 'package:flutter_plus/widgets/cache_image.dart'; 8 | import 'package:flutter_plus/widgets/over_scroll_behavior.dart'; 9 | import 'left_title.dart'; 10 | 11 | class HorizontalView extends HookWidget { 12 | final List brandList; 13 | 14 | const HorizontalView({Key? key, required this.brandList}) : super(key: key); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Container( 19 | margin: EdgeInsets.only(left: 15, top: 15), 20 | child: Column( 21 | children: [ 22 | LeftTitle( 23 | tipColor: AppColors.primaryColor, 24 | title: '品牌专场', 25 | ), 26 | Container( 27 | height: 146, 28 | child: ScrollConfiguration( 29 | behavior: OverScrollBehavior(), 30 | child: ListView.separated( 31 | scrollDirection: Axis.horizontal, 32 | separatorBuilder: (context, index) { 33 | return Padding(padding: EdgeInsets.only(right: 10)); 34 | }, 35 | padding: EdgeInsets.all(10), 36 | itemCount: brandList.length, 37 | itemBuilder: (BuildContext context, int index) { 38 | return AspectRatio( 39 | aspectRatio: 3 / 2, 40 | child: GestureDetector( 41 | onTap: () { 42 | Routers.navigateTo(Routers.test); 43 | }, 44 | child: Container( 45 | decoration: BoxDecoration( 46 | boxShadow: [ 47 | BoxShadow( 48 | color: Colors.grey.withOpacity(0.1), 49 | blurRadius: 9.0, 50 | spreadRadius: 0.6, 51 | ), 52 | ], 53 | ), 54 | child: ClipRRect( 55 | borderRadius: BorderRadius.circular(14), 56 | child: CacheImage( 57 | url: brandList[index].imgUrl, 58 | ), 59 | ), 60 | ), 61 | ), 62 | ); 63 | }, 64 | ), 65 | ), 66 | ) 67 | ], 68 | ), 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/ui/home/components/left_title.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_plus/styles/app_colors.dart'; 3 | class LeftTitle extends StatelessWidget { 4 | final Color tipColor; 5 | final String title; 6 | const LeftTitle({Key? key, required this.tipColor, required this.title}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Container( 11 | height: 23, 12 | child: Row( 13 | children: [ 14 | Container( 15 | color: tipColor, 16 | margin: EdgeInsets.only(right: 15), 17 | width: 3, 18 | height: 14, 19 | ), 20 | Text( 21 | title, 22 | style: TextStyle( 23 | color: AppColors.primaryText, 24 | fontSize: 16, 25 | fontWeight: FontWeight.w500, 26 | ), 27 | ) 28 | ], 29 | ), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/ui/home/components/sliver_header.dart: -------------------------------------------------------------------------------- 1 | import 'package:customizable_space_bar/customizable_space_bar.dart'; 2 | import 'package:flustars/flustars.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | import 'package:flutter_plus/styles/app_colors.dart'; 6 | 7 | class SliverHeader extends StatefulWidget { 8 | @override 9 | State createState() => SliverHeaderState(); 10 | } 11 | 12 | class SliverHeaderState extends State { 13 | @override 14 | Widget build(BuildContext context) { 15 | return SliverAppBar( 16 | leading: Container( 17 | padding: EdgeInsets.all(10), 18 | child: Center( 19 | child: IconButton( 20 | icon: Icon( 21 | Icons.icecream, 22 | color: Colors.white, 23 | size: 25, 24 | ), 25 | onPressed: () {}, 26 | ), 27 | ), 28 | ), 29 | actions: [ 30 | Container( 31 | padding: EdgeInsets.all(10), 32 | child: Center( 33 | child: IconButton( 34 | icon: Icon( 35 | Icons.crop_free, 36 | color: Colors.white, 37 | size: 25, 38 | ), 39 | onPressed: () {}, 40 | ), 41 | ), 42 | ), 43 | ], 44 | //右侧的内容和点击事件啥的 45 | elevation: 0, 46 | //阴影的高度 47 | forceElevated: false, 48 | //黑底白字,lignt 白底黑字 49 | expandedHeight: 100.0, 50 | //默认高度是状态栏和导航栏的高度,如果有滚动视差的话,要大于前两者的高度 51 | floating: false, 52 | //滑动到最上面,再滑动是否隐藏导航栏的文字和标题等的具体内容,为true是隐藏,为false是不隐藏 53 | pinned: true, 54 | //是否固定导航栏,为true是固定,为false是不固定,往上滑,导航栏可以隐藏 55 | snap: false, 56 | //只跟floating相对应,如果为true,floating必须为true,也就是向下滑动一点儿,整个大背景就会动画显示全部,网上滑动整个导航栏的内容就会消失 57 | flexibleSpace: CustomizableSpaceBar(builder: (context, scrollingRate) { 58 | return Container( 59 | color: AppColors.primaryColor, 60 | // padding: EdgeInsets.only(bottom: 10), 61 | child: Align( 62 | alignment: Alignment.bottomCenter, 63 | child: Container( 64 | width: ScreenUtil.getInstance().screenWidth * 0.9 - 65 | 70 * scrollingRate, 66 | child: Wrap( 67 | children: [ 68 | Container( 69 | height: 50, 70 | child: Center( 71 | child: Container( 72 | // margin: EdgeInsets.symmetric(horizontal: 25), 73 | child: Row( 74 | children: [ 75 | Icon( 76 | Icons.search_rounded, 77 | color: AppColors.subTextColor, 78 | ), 79 | SizedBox( 80 | width: 5, 81 | ), 82 | Text( 83 | '搜索', 84 | style: 85 | TextStyle(color: AppColors.subTextColor), 86 | ), 87 | ], 88 | ), 89 | padding: EdgeInsets.symmetric( 90 | horizontal: 10, vertical: 5), 91 | decoration: BoxDecoration( 92 | color: AppColors.whileColor, 93 | borderRadius: BorderRadius.circular(25), 94 | border: Border.all( 95 | color: Color(0xffd8d1d3), 96 | width: 0.2, 97 | ), 98 | ), 99 | ), 100 | ), 101 | // color: AppColors.primaryColor, 102 | ) 103 | ], 104 | ), 105 | ))); 106 | }), 107 | ); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /lib/ui/home/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_hooks/flutter_hooks.dart'; 4 | import 'package:flutter_plus/model/base/home.dart'; 5 | import 'package:flutter_plus/route/router.dart'; 6 | import 'package:flutter_plus/styles/app_colors.dart'; 7 | import 'package:flutter_plus/ui/home/components/head_swiper.dart'; 8 | import 'package:flutter_plus/ui/home/components/home_horizontal_view.dart'; 9 | import 'package:flutter_plus/widgets/cache_image.dart'; 10 | 11 | import 'components/home_header.dart'; 12 | import 'components/left_title.dart'; 13 | import 'components/sliver_header.dart'; 14 | 15 | /* 16 | * 首页 17 | */ 18 | class HomePage extends HookWidget { 19 | List swiperList = [ 20 | SwiperEntity( 21 | "轮播图1", 22 | 'http://pic.51yuansu.com/pic3/cover/02/88/54/5ab0ce0084961_610.jpg', 23 | "/home"), 24 | SwiperEntity( 25 | "轮播图1", 26 | 'http://pic.51yuansu.com/pic3/cover/02/88/54/5ab0ce0084961_610.jpg', 27 | "/home"), 28 | ]; 29 | List brandList = [ 30 | GridEntity( 31 | "来了老弟1", 32 | 'http://pic.51yuansu.com/pic3/cover/02/88/54/5ab0ce0084961_610.jpg', 33 | "/home"), 34 | GridEntity( 35 | "来了老弟2", 36 | 'http://pic.51yuansu.com/pic3/cover/02/88/54/5ab0ce0084961_610.jpg', 37 | "/home"), 38 | GridEntity( 39 | "来了老弟1", 40 | 'http://pic.51yuansu.com/pic3/cover/02/88/54/5ab0ce0084961_610.jpg', 41 | "/home"), 42 | GridEntity( 43 | "来了老弟2", 44 | 'http://pic.51yuansu.com/pic3/cover/02/88/54/5ab0ce0084961_610.jpg', 45 | "/home"), 46 | GridEntity( 47 | "来了老弟1", 48 | 'http://pic.51yuansu.com/pic3/cover/02/88/54/5ab0ce0084961_610.jpg', 49 | "/home"), 50 | GridEntity( 51 | "来了老弟2", 52 | 'http://pic.51yuansu.com/pic3/cover/02/88/54/5ab0ce0084961_610.jpg', 53 | "/home"), 54 | GridEntity( 55 | "来了老弟1", 56 | 'http://pic.51yuansu.com/pic3/cover/02/88/54/5ab0ce0084961_610.jpg', 57 | "/home"), 58 | GridEntity( 59 | "来了老弟2", 60 | 'http://pic.51yuansu.com/pic3/cover/02/88/54/5ab0ce0084961_610.jpg', 61 | "/home"), 62 | ]; 63 | 64 | @override 65 | Widget build(BuildContext context) { 66 | return Scaffold( 67 | body: CustomScrollView( 68 | physics: BouncingScrollPhysics(), 69 | slivers: [ 70 | HomeHeader(swiperList), 71 | SliverHeader(), 72 | SliverToBoxAdapter( 73 | child: HeadSwiper(swiperList), 74 | ), 75 | SliverToBoxAdapter( 76 | child: HorizontalView(brandList: brandList), 77 | ), 78 | ] + 79 | hotCommodity(brandList))); 80 | } 81 | 82 | /// 热销商品区域 83 | List hotCommodity(List hotList) { 84 | return [ 85 | SliverToBoxAdapter( 86 | child: Container( 87 | margin: EdgeInsets.only(left: 15, top: 15), 88 | child: LeftTitle( 89 | tipColor: AppColors.primaryColor, 90 | title: '热销商品', 91 | )), 92 | ), 93 | SliverPadding( 94 | padding: EdgeInsets.all(15.0), 95 | sliver: SliverGrid( 96 | delegate: 97 | SliverChildBuilderDelegate((BuildContext context, int index) { 98 | return GestureDetector( 99 | onTap: () => Routers.navigateTo(Routers.breed), 100 | child: CacheImage( 101 | url: hotList[index].imgUrl, 102 | ), 103 | ); 104 | }, childCount: hotList.length), 105 | gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( 106 | crossAxisCount: 2, 107 | crossAxisSpacing: 8.0, 108 | mainAxisSpacing: 8.0, 109 | childAspectRatio: 1.0)), 110 | ), 111 | ]; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /lib/ui/main_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_hooks/flutter_hooks.dart'; 4 | import 'package:fluttertoast/fluttertoast.dart'; 5 | import 'package:flutter_plus/model/base/tab.dart'; 6 | import 'package:flutter_plus/styles/app_colors.dart'; 7 | import 'package:flutter_plus/ui/personal/personal_page.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | import 'discovery/discovery_page.dart'; 10 | import 'home/home_page.dart'; 11 | import 'message/message_page.dart'; 12 | 13 | final navigationProvider = 14 | StateNotifierProvider((ref) { 15 | return navigationNotifier(); 16 | }); 17 | 18 | class navigationNotifier extends StateNotifier { 19 | navigationNotifier() : super(0); 20 | 21 | void selectPage(int index) { 22 | state = index; 23 | } 24 | } 25 | 26 | class MainPage extends HookWidget { 27 | final pageList = [ 28 | TabEntity(HomePage(), "首页", Icons.home), 29 | TabEntity(DiscoveryPage(), "社区", Icons.supervised_user_circle), 30 | TabEntity(MessagePage(), "消息", Icons.message), 31 | TabEntity(PersonalPage(), "我的", Icons.person) 32 | ]; 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | DateTime? lastPopTime; 37 | final navigation = useProvider(navigationProvider); 38 | return WillPopScope( 39 | onWillPop: () async { 40 | if (lastPopTime == null || 41 | DateTime.now().difference(lastPopTime!) > Duration(seconds: 2)) { 42 | Fluttertoast.showToast(msg: "再按一次退出"); 43 | lastPopTime = DateTime.now(); 44 | return false; 45 | } else { 46 | lastPopTime = DateTime.now(); 47 | // 退出app 48 | return await SystemChannels.platform 49 | .invokeMethod('SystemNavigator.pop'); 50 | } 51 | }, 52 | child: Scaffold( 53 | body: IndexedStack( 54 | index: navigation, 55 | children: pageList.map((item) => item.widget).toList(), 56 | ), 57 | bottomNavigationBar: BottomNavigationBar( 58 | backgroundColor: AppColors.appBarBottomBg, 59 | fixedColor: AppColors.mainTextColor, 60 | unselectedItemColor: AppColors.subTextColor, 61 | items: pageList 62 | .map((info) => BottomNavigationBarItem( 63 | label: info.title, icon: Icon(info.icon))) 64 | .toList(), 65 | currentIndex: navigation, 66 | type: BottomNavigationBarType.fixed, 67 | onTap: (index) { 68 | context.refresh(navigationProvider).selectPage(index); 69 | }, 70 | ), 71 | ), 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/ui/message/components/avatar_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_plus/widgets/cache_image.dart'; 3 | 4 | class AvatarWidget extends StatelessWidget { 5 | final String url; 6 | final double width; 7 | final double height; 8 | 9 | const AvatarWidget(this.url, 10 | {Key? key, required this.width, required this.height}) 11 | : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return ClipOval( 16 | child: CacheImage( 17 | url: url, 18 | width: width, 19 | height: height, 20 | ), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/ui/message/components/user_item_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_plus/ui/message/components/avatar_widget.dart'; 3 | 4 | class ListUser { 5 | final String avatar; 6 | final String name; 7 | final String time; 8 | final String content; 9 | final int count; 10 | 11 | const ListUser( 12 | {required this.avatar, 13 | required this.name, 14 | required this.time, 15 | required this.count, 16 | required this.content}); 17 | } 18 | 19 | class UserItemWidget extends StatelessWidget { 20 | final GestureTapCallback onTap; 21 | final ListUser data; 22 | 23 | const UserItemWidget({Key? key, required this.onTap, required this.data}) 24 | : super(key: key); 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return InkWell( 29 | onTap: () {}, 30 | child: Container( 31 | height: 62, 32 | padding: EdgeInsets.only(left: 15, right: 15, top: 8, bottom: 8), 33 | child: Row( 34 | children: [ 35 | AvatarWidget(data.avatar, width: 40, height: 40), 36 | SizedBox( 37 | width: 16, 38 | ), 39 | Expanded( 40 | child: Column( 41 | crossAxisAlignment: CrossAxisAlignment.start, 42 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 43 | children: [ 44 | Text( 45 | data.name, 46 | style: TextStyle(fontSize: 16, color: Color(0xFFff7faa)), 47 | ), 48 | Container( 49 | child: Text( 50 | data.content, 51 | overflow: TextOverflow.ellipsis, 52 | style: TextStyle(fontSize: 14, color: Color(0xFF666666)), 53 | ), 54 | ) 55 | ], 56 | ), 57 | ), 58 | Container( 59 | child: Column( 60 | crossAxisAlignment: CrossAxisAlignment.end, 61 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 62 | children: [ 63 | Text( 64 | data.time, 65 | style: TextStyle(fontSize: 12, color: Color(0xFF999999)), 66 | ), 67 | Container( 68 | width: 16, 69 | height: 16, 70 | alignment: Alignment.center, 71 | decoration: BoxDecoration( 72 | shape: BoxShape.circle, color: Color(0xFFff7faa)), 73 | child: Text( 74 | data.count.toString(), 75 | style: TextStyle(color: Colors.white, fontSize: 12), 76 | ), 77 | ) 78 | ], 79 | ), 80 | ) 81 | ], 82 | ), 83 | ), 84 | ); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/ui/message/message_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/painting.dart'; 3 | import 'package:flutter_hooks/flutter_hooks.dart'; 4 | import 'package:flutter_plus/widgets/app_topbar.dart'; 5 | import 'package:flutter_plus/widgets/my_appbar.dart'; 6 | 7 | import 'components/user_item_widget.dart'; 8 | const List userList = [ 9 | ListUser( 10 | avatar: 'https://img2.woyaogexing.com/2020/02/07/bc5c623d1e0648c1b300f702fd944c86!400x400.jpeg', 11 | name: '木木哒', 12 | count: 9, 13 | time: '20s', 14 | content: '╬═☆丕傠ぬ丕迎匼,丕夠傠囍狚炔洎怞ルo~~~~', 15 | ), 16 | ListUser( 17 | avatar: 'https://img2.woyaogexing.com/2020/02/14/79c9df02200f49caabaad1f4e8caedcb!400x400.jpeg', 18 | name: '木头', 19 | count: 4, 20 | time: '21:18', 21 | content: '~~~~', 22 | ), 23 | ]; 24 | class MessagePage extends HookWidget { 25 | @override 26 | Widget build(BuildContext context) { 27 | return Scaffold( 28 | appBar: MyAppBar( 29 | child: CustomBar(title: '消息中心'), 30 | ), 31 | body: SingleChildScrollView( 32 | child: Column( 33 | crossAxisAlignment: CrossAxisAlignment.start, 34 | children: [ 35 | Container( 36 | height: 15, 37 | decoration: BoxDecoration( 38 | color: Colors.white, 39 | ), 40 | ), 41 | Container( 42 | height: 94, 43 | color: Colors.white, 44 | padding: EdgeInsets.symmetric(horizontal: 30), 45 | child: Row( 46 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 47 | children: [ 48 | Column( 49 | children: [ 50 | CircleAvatar( 51 | radius: 22, 52 | backgroundColor: Colors.white, 53 | child: Image.asset('assets/images/message.png'), 54 | ), 55 | SizedBox( 56 | height: 12, 57 | ), 58 | Text( 59 | '消息通知', 60 | style: 61 | TextStyle(color: Color(0xff666666), fontSize: 14), 62 | ) 63 | ], 64 | ), 65 | Column( 66 | children: [ 67 | CircleAvatar( 68 | radius: 22, 69 | backgroundColor: Colors.white, 70 | child: Image.asset('assets/images/me.png'), 71 | ), 72 | SizedBox( 73 | height: 12, 74 | ), 75 | Text( 76 | '@我', 77 | style: 78 | TextStyle(color: Color(0xff666666), fontSize: 14), 79 | ) 80 | ], 81 | ), 82 | Column( 83 | children: [ 84 | CircleAvatar( 85 | radius: 22, 86 | backgroundColor: Colors.white, 87 | child: Image.asset('assets/images/loveme.png'), 88 | ), 89 | SizedBox( 90 | height: 12, 91 | ), 92 | Text( 93 | '收到的赞', 94 | style: 95 | TextStyle(color: Color(0xff666666), fontSize: 14), 96 | ) 97 | ], 98 | ), 99 | Column( 100 | children: [ 101 | CircleAvatar( 102 | radius: 22, 103 | backgroundColor: Colors.white, 104 | child: Image.asset( 105 | 'assets/images/system_message.png'), 106 | ), 107 | SizedBox( 108 | height: 12, 109 | ), 110 | Text( 111 | '系统通知', 112 | style: 113 | TextStyle(color: Color(0xff666666), fontSize: 14), 114 | ) 115 | ], 116 | ), 117 | ], 118 | )), 119 | Container( 120 | height: 10, 121 | decoration: BoxDecoration( 122 | color: Color(0xFFF4F4F4), 123 | ), 124 | ), 125 | Container( 126 | child: ListView.builder( 127 | itemCount: userList.length, 128 | shrinkWrap: true, 129 | scrollDirection: Axis.vertical, 130 | physics: new NeverScrollableScrollPhysics(), 131 | itemBuilder: (BuildContext context, int index) { 132 | return UserItemWidget( 133 | data: userList[index], 134 | onTap: () { 135 | print('111'); 136 | }, 137 | ); 138 | }), 139 | ), 140 | ], 141 | )), 142 | ); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /lib/ui/moment/components/navigatorActionBar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_plus/styles/app_colors.dart'; 4 | 5 | class navigatorActionBar extends StatelessWidget { 6 | final IconData icon; 7 | final VoidCallback? onTap; 8 | final double opacity; 9 | final double? iconSize; 10 | navigatorActionBar({required this.icon, this.onTap,required this.opacity,this.iconSize=28}); 11 | @override 12 | Widget build(BuildContext context) { 13 | return GestureDetector( 14 | child: Container( 15 | width: 30, 16 | height: 30, 17 | decoration: BoxDecoration( 18 | color: Color.fromRGBO(99, 99, 99, 1 - opacity), 19 | borderRadius: BorderRadius.all( 20 | Radius.circular(15), 21 | ), 22 | ), 23 | child: Icon( 24 | icon, 25 | color: opacity == 1 ? AppColors.hexToColor("#2B2B2B") : Colors.white, 26 | size: iconSize, 27 | ), 28 | ), 29 | ); 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /lib/ui/moment/moment_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_hooks/flutter_hooks.dart'; 4 | import 'package:flutter_plus/model/moment/moment.dart'; 5 | import 'package:flutter_plus/provider/moment_provider.dart'; 6 | import 'package:flutter_plus/route/router.dart'; 7 | import 'package:flutter_plus/widgets/app_topbar.dart'; 8 | import 'package:flutter_plus/widgets/my_appbar.dart'; 9 | import 'package:flutter_plus/widgets/refresh.dart'; 10 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 11 | /* 12 | * 社区 13 | */ 14 | class MomentPage extends HookWidget { 15 | @override 16 | Widget build(BuildContext context) { 17 | final datalist = useProvider(momentProvider); 18 | return Scaffold( 19 | appBar: MyAppBar( 20 | child: CustomBar( 21 | title: "标题", 22 | ), 23 | ), 24 | body: Refresh( 25 | controller: context.refresh(momentProvider).refreshController, 26 | onRefresh: () => 27 | context.refresh(momentProvider).initData(isRefresh: true), 28 | content: CustomScrollView(slivers: [ 29 | SliverList( 30 | delegate: SliverChildBuilderDelegate( 31 | (_, index) { 32 | MomentEntity item = datalist.list[index]; 33 | return Padding( 34 | padding: EdgeInsets.all(10), 35 | child: ListTile( 36 | onTap: () { 37 | Routers.navigateTo(Routers.test); 38 | }, 39 | title: Text(item.content.toString(), 40 | maxLines: 1, overflow: TextOverflow.ellipsis), 41 | )); 42 | }, 43 | childCount: datalist.list.length, 44 | ), 45 | ) 46 | ]), 47 | onLoading: () {}, 48 | )); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/ui/moment/test_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/src/widgets/framework.dart'; 4 | import 'package:flutter_hooks/flutter_hooks.dart'; 5 | import 'package:flutter_plus/model/base/home.dart'; 6 | import 'package:flutter_plus/styles/app_colors.dart'; 7 | import 'package:flutter_plus/ui/moment/components/navigatorActionBar.dart'; 8 | import 'package:flutter_plus/ui/home/components/head_swiper.dart'; 9 | class TestPage extends HookWidget { 10 | final List swiperList = [ 11 | SwiperEntity( 12 | "轮播图1", 13 | 'http://pic.51yuansu.com/pic3/cover/02/88/54/5ab0ce0084961_610.jpg', 14 | "/home"), 15 | SwiperEntity( 16 | "轮播图1", 17 | 'http://pic.51yuansu.com/pic3/cover/02/61/33/59fc2546ebeb7_610.jpg', 18 | "/home"), 19 | ]; 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | final appBarOpacity = useState(0.0); 24 | Future _gerData() async { 25 | return appBarOpacity.value == 1; 26 | } 27 | 28 | return Scaffold( 29 | body: Stack( 30 | children: [ 31 | NotificationListener( 32 | onNotification: (ScrollNotification notification) { 33 | if (notification.metrics.axis == Axis.vertical) { 34 | var scrollY = notification.metrics.pixels; 35 | if (scrollY > 0) { 36 | var opacity = scrollY / 230; 37 | appBarOpacity.value = opacity <= 1 ? opacity : 1; 38 | } else if (scrollY < 0) { 39 | appBarOpacity.value = 0; 40 | } 41 | } 42 | return false; 43 | }, 44 | child: SingleChildScrollView( 45 | child: FutureBuilder( 46 | future: _gerData(), 47 | builder: 48 | (BuildContext context, AsyncSnapshot snapshot) { 49 | return Container( 50 | color: AppColors.hexToColor("#F2F2F2"), 51 | child: Column( 52 | children: [ 53 | Container( 54 | height: 300, 55 | child: HeadSwiper(swiperList), 56 | ), 57 | // 商品详情 58 | Container( 59 | width: MediaQuery.of(context).size.width, 60 | height: 5500, 61 | ), 62 | ], 63 | ), 64 | ); 65 | }, 66 | ), 67 | ), 68 | ), 69 | Positioned( 70 | top: 0, 71 | left: 0, 72 | width: MediaQuery.of(context).size.width, 73 | height: 75, 74 | child: Container( 75 | color: Color.fromRGBO(255, 255, 255, appBarOpacity.value), 76 | padding: EdgeInsets.fromLTRB(0, 30, 0, 0), 77 | child: Row( 78 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 79 | crossAxisAlignment: CrossAxisAlignment.center, 80 | children: [ 81 | navigatorActionBar( 82 | icon: Icons.navigate_before, 83 | opacity: appBarOpacity.value, 84 | onTap: () { 85 | Navigator.of(context).pop(); 86 | }), 87 | Container( 88 | width: 240, 89 | child: Offstage( 90 | offstage: appBarOpacity.value < 1, 91 | child: TabBar( 92 | labelPadding: EdgeInsets.all(0), 93 | indicatorColor: Colors.red, 94 | indicatorSize: TabBarIndicatorSize.label, 95 | labelColor: Colors.black, 96 | labelStyle: TextStyle( 97 | color: Colors.black, 98 | fontSize: 16, 99 | fontWeight: FontWeight.bold, 100 | fontFamily: "PingFang"), 101 | unselectedLabelStyle: TextStyle( 102 | color: Colors.black, 103 | fontSize: 16, 104 | fontWeight: FontWeight.normal, 105 | fontFamily: "PingFang"), 106 | controller: useTabController(initialLength: 4), 107 | tabs: [ 108 | ...["商品", "评价", "详情", "推荐"].map((item) { 109 | return Text(item); 110 | }).toList(), 111 | ], 112 | ), 113 | ), 114 | ), 115 | navigatorActionBar( 116 | icon: Icons.share, 117 | opacity: appBarOpacity.value, 118 | iconSize: 18, 119 | onTap: () { 120 | Navigator.of(context).pop(); 121 | }), 122 | navigatorActionBar( 123 | icon: Icons.more_horiz, 124 | opacity: appBarOpacity.value, 125 | onTap: () { 126 | Navigator.of(context).pop(); 127 | }), 128 | ], 129 | ), 130 | ), 131 | ), 132 | ], 133 | ), 134 | ); 135 | } 136 | } 137 | 138 | SliverFixedExtentList buildSliverFixedExtentList() { 139 | return SliverFixedExtentList( 140 | itemExtent: 100, 141 | delegate: new SliverChildBuilderDelegate( 142 | (BuildContext context, num index) { 143 | ///子条目的布局样式 144 | return Container( 145 | child: Text("list $index"), 146 | margin: EdgeInsets.all(10), 147 | ); 148 | }, 149 | childCount: 20, 150 | ), 151 | ); 152 | } 153 | -------------------------------------------------------------------------------- /lib/ui/personal/components/sliver_delegate.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flustars/flustars.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_plus/ui/personal/personal_page.dart'; 7 | import 'package:transparent_image/transparent_image.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | 10 | class SliverDelegate extends SliverPersistentHeaderDelegate { 11 | BuildContext context; 12 | GlobalKey textKey; 13 | Rect? textSize; 14 | bool isCreatePage; 15 | SliverDelegate(this.context, this.textKey, this.textSize,{this.isCreatePage=false}) { 16 | WidgetsBinding.instance!.addPostFrameCallback((_) { 17 | textSize = WidgetUtil.getWidgetBounds(textKey.currentContext!); 18 | context.refresh(textSizeProvider).getTextRect(textSize!); 19 | }); 20 | } 21 | 22 | @override 23 | Widget build( 24 | BuildContext context, double shrinkOffset, bool overlapsContent) { 25 | double textWidth = textSize != null ? textSize!.width : 0.0; 26 | double maxShrinkOffset = this.maxExtent - this.minExtent; 27 | double t = (shrinkOffset / maxShrinkOffset).clamp(0.0, 1.0); 28 | double mt = Tween(begin: 66.0, end: 16.0).transform(t); 29 | double ctt = Tween(begin: 0, end: 32).transform(t); 30 | double minTop = mt + ScreenUtil.getInstance().statusBarHeight; 31 | double textTop = ((maxShrinkOffset - shrinkOffset) / 3 + minTop) >= minTop 32 | ? ((maxShrinkOffset - shrinkOffset) / 3 + minTop) 33 | : minTop; 34 | double textLeft = (ScreenUtil.getInstance().screenWidth - textWidth) / 2; 35 | textLeft = textLeft - (textLeft - 60) * t; // left 36 | double cTextLeft = textLeft + ctt; // center 37 | double imt = Tween(begin: 0.0, end: 12).transform(t); 38 | double imageTop = minTop - imt; 39 | double imageLeft = (ScreenUtil.getInstance().screenWidth - 56) / 2; 40 | imageLeft = imageLeft - (imageLeft - 6) * t; // left 41 | double cImageLeft = imageLeft + ctt; // center 42 | double scaleImageValue = Tween(begin: 1, end: 0.6).transform(t); 43 | double opacity = 1.0 - Interval(0, 1).transform(t); 44 | 45 | return Stack( 46 | fit: StackFit.expand, 47 | children: [ 48 | Container( 49 | decoration: BoxDecoration( 50 | gradient: LinearGradient( 51 | colors: [ 52 | Theme.of(context).primaryColor, 53 | Theme.of(context).accentColor, 54 | Theme.of(context).highlightColor 55 | ], 56 | tileMode: TileMode.mirror, 57 | begin: Alignment.centerLeft, 58 | end: Alignment.bottomRight, 59 | ), 60 | ), 61 | child: Opacity( 62 | opacity: opacity, 63 | child: Stack( 64 | children: [ 65 | LayoutBuilder(builder: 66 | (BuildContext context, BoxConstraints constraints) { 67 | return FadeInImage.memoryNetwork( 68 | height: constraints.maxHeight, 69 | width: ScreenUtil.getInstance().screenWidth, 70 | placeholder: kTransparentImage, 71 | fit: BoxFit.cover, 72 | image: 73 | "http://pic.51yuansu.com/pic3/cover/02/88/54/5ab0ce0084961_610.jpg", 74 | ); 75 | }), 76 | Container( 77 | decoration: BoxDecoration( 78 | gradient: LinearGradient( 79 | colors: [ 80 | Color.fromRGBO(249, 249, 249, 1).withOpacity(0), 81 | Color.fromRGBO(249, 249, 249, 1).withOpacity(1), 82 | ], 83 | stops: [0, 0.96], 84 | begin: Alignment.topCenter, 85 | end: Alignment.bottomCenter, 86 | ), 87 | ), 88 | child: Stack( 89 | children: [ 90 | ClipRRect( 91 | child: BackdropFilter( 92 | filter: ImageFilter.blur( 93 | sigmaX: 16, 94 | sigmaY: 16, 95 | ), 96 | child: Container( 97 | decoration: BoxDecoration( 98 | color: Color.fromRGBO(249, 249, 249, 1) 99 | .withOpacity(0), 100 | ), 101 | ), 102 | ), 103 | ), 104 | ], 105 | ), 106 | ), 107 | Positioned( 108 | bottom: 26, 109 | child: Container( 110 | width: ScreenUtil.getInstance().screenWidth, 111 | child: Row( 112 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 113 | children: [ 114 | _createNumTag("22", '作品'), 115 | _createNumTag("22", '赞过'), 116 | _createNumTag('666', '粉丝'), 117 | _createNumTag('36', '关注'), 118 | ], 119 | ), 120 | ), 121 | ), 122 | ], 123 | ), 124 | ), 125 | ), 126 | Positioned( 127 | right: 6, 128 | top: 32, 129 | child: !isCreatePage 130 | ? IconButton( 131 | icon: Icon( 132 | Icons.settings, 133 | color: Colors.black87, 134 | size: 18, 135 | ), 136 | onPressed: () { 137 | // Navigator.of(context).push(MaterialPageRoute( 138 | // builder: (context) => MyPage())); 139 | }, 140 | ) 141 | : IconButton( 142 | icon: Icon( 143 | Icons.insert_comment_rounded, 144 | color: Colors.black87, 145 | size: 18, 146 | ), 147 | onPressed: () {}, 148 | ), 149 | ), 150 | Positioned( 151 | left: 6, 152 | top: 32, 153 | child: isCreatePage 154 | ? IconButton( 155 | icon: Icon( 156 | Icons.arrow_back, 157 | color: Colors.black87, 158 | size: 17, 159 | ), 160 | onPressed: () { 161 | Navigator.of(context).pop(); 162 | }, 163 | ) 164 | : Container(), 165 | ), 166 | Positioned( 167 | top: imageTop, 168 | left: isCreatePage ? cImageLeft : imageLeft, 169 | child: Transform.scale( 170 | scale: scaleImageValue, 171 | child: ClipOval( 172 | child: FadeInImage.memoryNetwork( 173 | placeholder: kTransparentImage, 174 | image: 175 | "http://pic.51yuansu.com/pic3/cover/02/88/54/5ab0ce0084961_610.jpg", 176 | fit: BoxFit.cover, 177 | width: 56.0, 178 | height: 56.0, 179 | ), 180 | ), 181 | ), 182 | ), 183 | Positioned( 184 | top: textTop, 185 | left: isCreatePage ? cTextLeft : textLeft, 186 | child: LayoutBuilder( 187 | builder: (BuildContext context, BoxConstraints constraints) { 188 | return Text( 189 | "走在冷风中", 190 | key: textKey, 191 | style: TextStyle( 192 | fontSize: 18, 193 | color: Colors.black87, 194 | fontWeight: FontWeight.bold, 195 | fontFamily: 'SourceHanSans', 196 | ), 197 | textAlign: TextAlign.center, 198 | overflow: TextOverflow.ellipsis, 199 | maxLines: 1, 200 | ); 201 | }), 202 | ), 203 | ], 204 | ); 205 | } 206 | 207 | Column _createNumTag(String value, String name) { 208 | return Column( 209 | children: [ 210 | Text( 211 | value, 212 | textAlign: TextAlign.center, 213 | ), 214 | Text( 215 | name, 216 | style: TextStyle( 217 | fontSize: 12, 218 | color: Colors.grey, 219 | fontFamily: 'SourceHanSans', 220 | ), 221 | textAlign: TextAlign.center, 222 | ), 223 | ], 224 | ); 225 | } 226 | 227 | @override 228 | double get maxExtent => 300; 229 | 230 | @override 231 | double get minExtent => 86.0; 232 | 233 | @override 234 | bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) { 235 | return true; 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /lib/ui/personal/personal_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flustars/flustars.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_hooks/flutter_hooks.dart'; 4 | import 'package:flutter_plus/common/constant.dart'; 5 | import 'package:flutter_plus/styles/app_colors.dart'; 6 | import 'package:flutter_plus/widgets/app_topbar.dart'; 7 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 8 | import 'package:multi_image_picker/multi_image_picker.dart'; 9 | 10 | import 'components/sliver_delegate.dart'; 11 | 12 | /* 13 | * 我的页面 14 | */ 15 | final textSizeProvider = StateNotifierProvider((ref) { 16 | return textSizwNotifier(); 17 | }); 18 | 19 | class textSizwNotifier extends StateNotifier { 20 | textSizwNotifier() : super(Rect.zero); 21 | 22 | void getTextRect(Rect text) { 23 | state = text; 24 | } 25 | } 26 | 27 | class PersonalPage extends HookWidget { 28 | final GlobalKey textKey = GlobalKey(); 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | final textSize = useProvider(textSizeProvider); 33 | return Scaffold( 34 | floatingActionButton: FloatingActionButton( 35 | onPressed: () { 36 | loadAssets(); 37 | }, 38 | child: Icon(Icons.add), 39 | ), 40 | body: CustomScrollView( 41 | slivers: [ 42 | SliverPersistentHeader( 43 | delegate: SliverDelegate(context, textKey, textSize,isCreatePage: true), 44 | pinned: true, 45 | ), 46 | buildSliverFixedExtentList(), 47 | ], 48 | ), 49 | ); 50 | } 51 | } 52 | 53 | Widget buildTopBar() => Container( 54 | margin: EdgeInsets.only(top: ScreenUtil.getInstance().statusBarHeight), 55 | child: Wrap( 56 | children: [ 57 | Container( 58 | height: 50, 59 | child: Center( 60 | child: Container( 61 | margin: EdgeInsets.symmetric(horizontal: 25), 62 | child: Row( 63 | children: [ 64 | Icon( 65 | Icons.search_rounded, 66 | color: AppColors.subTextColor, 67 | ), 68 | SizedBox( 69 | width: 5, 70 | ), 71 | Text( 72 | '搜索', 73 | style: TextStyle(color: AppColors.subTextColor), 74 | ), 75 | ], 76 | ), 77 | padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), 78 | decoration: BoxDecoration( 79 | color: Color(0x30060606), 80 | borderRadius: BorderRadius.circular(25), 81 | border: Border.all( 82 | color: Color(0xffd8d1d3), 83 | width: 0.2, 84 | ), 85 | ), 86 | ), 87 | ), 88 | // color: AppColors.primaryColor, 89 | ) 90 | ], 91 | ), 92 | ); 93 | 94 | Widget topBar() => Container( 95 | height: Constant.APP_BAR + ScreenUtil().statusBarHeight, 96 | color: AppColors.whileColor, 97 | child: SearchBar(), 98 | ); 99 | 100 | SliverFixedExtentList buildSliverFixedExtentList() { 101 | return SliverFixedExtentList( 102 | itemExtent: ScreenUtil.getInstance().getHeight(100), 103 | delegate: new SliverChildBuilderDelegate( 104 | (BuildContext context, num index) { 105 | ///子条目的布局样式 106 | return Container( 107 | child: Text("list $index"), 108 | margin: EdgeInsets.all(10), 109 | ); 110 | }, 111 | childCount: 10, 112 | ), 113 | ); 114 | } 115 | 116 | Future> loadAssets() async { 117 | try { 118 | return await MultiImagePicker.pickImages( 119 | // 选择图片的最大数量 120 | maxImages: 9, 121 | // 是否支持拍照 122 | enableCamera: true, 123 | materialOptions: MaterialOptions( 124 | // 显示所有照片,值为 false 时显示相册 125 | startInAllView: true, 126 | allViewTitle: '所有照片', 127 | actionBarColor: '#2196F3', 128 | textOnNothingSelected: '没有选择照片'), 129 | ); 130 | } on Exception catch (e) { 131 | e.toString(); 132 | } 133 | return []; 134 | } 135 | -------------------------------------------------------------------------------- /lib/ui/splash_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | class SplashPage extends StatefulWidget { 3 | @override 4 | _SplashPageState createState() => _SplashPageState(); 5 | } 6 | 7 | class _SplashPageState extends State { 8 | @override 9 | Widget build(BuildContext context) { 10 | return Scaffold( 11 | body: Container( 12 | alignment: Alignment.center, 13 | color: Colors.white, 14 | child: Column( 15 | mainAxisAlignment: MainAxisAlignment.center, 16 | children: [ 17 | Image.asset("assets/images/splash.png", width: 200.0, height: 200.0), 18 | SizedBox( 19 | width: MediaQuery.of(context).size.width * 0.7, 20 | child: Text( 21 | '来了老弟', 22 | textAlign: TextAlign.center, 23 | softWrap: true, 24 | style: TextStyle(color: Colors.red[700], fontSize: 16.0), 25 | )) 26 | ], 27 | ), 28 | ), 29 | ); 30 | } 31 | 32 | @override 33 | Future initState() async { 34 | // 启动的时候将屏幕设置成全屏模式 35 | // SystemChrome.setEnabledSystemUIOverlays([]); 36 | super.initState(); 37 | // Stream.fromIterable([1]).delay(Duration(seconds: 2)).listen((_) { 38 | // if (token != null && token != '') { 39 | // Routes.navigateTo(Routes.home, replace: true); 40 | // }else{ 41 | // Routes.navigateTo(Routes.login, replace: true); 42 | // } 43 | // }); // [after one second delay] prints 1, 2, 3, 4 immediately 44 | } 45 | 46 | @override 47 | void dispose() { 48 | // 关闭的时候将屏幕设置成原来的状态 49 | // SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values); 50 | super.dispose(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/widgets/app_topbar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/painting.dart'; 4 | import 'package:flutter_hooks/flutter_hooks.dart'; 5 | import 'package:flutter_plus/styles/app_colors.dart'; 6 | import 'package:flutter_plus/styles/app_styles.dart'; 7 | class SearchBar extends StatelessWidget { 8 | @override 9 | Widget build(BuildContext context) { 10 | return Center(child: Container( 11 | margin:EdgeInsets.symmetric(horizontal: 15), 12 | child: Row(children: [ 13 | Icon(Icons.search_rounded,color: AppColors.subTextColor,), 14 | SizedBox(width: 5,), 15 | Text('搜索',style: AppStyles.seachTextStyle,), 16 | ]), 17 | padding: EdgeInsets.symmetric(horizontal: 10,vertical: 4), 18 | decoration: BoxDecoration( 19 | borderRadius: BorderRadius.circular(25), 20 | border: Border.all( 21 | color: AppColors.dividerBg, 22 | width: 1, 23 | ), 24 | ), 25 | ),) ; 26 | 27 | } 28 | } 29 | 30 | //正常的title 31 | class CustomBar extends HookWidget { 32 | final bool? isBack; 33 | final String title; 34 | final String? menu; 35 | final VoidCallback? onMenu; 36 | 37 | CustomBar({required this.title, this.isBack, this.onMenu, this.menu}); 38 | 39 | @override 40 | Widget build(BuildContext context) { 41 | return Container( 42 | decoration: BoxDecoration( 43 | border: Border( 44 | bottom: BorderSide(width: 0.5, color: AppColors.dividerBg))), 45 | child: Stack( 46 | children: [ 47 | Align( 48 | alignment: Alignment.centerLeft, 49 | child: Visibility( 50 | visible: isBack ?? false, 51 | child: BackButton(), 52 | )), 53 | Align( 54 | child: Container( 55 | child: Text(title, style: AppStyles.titleTextStyle))), 56 | Align( 57 | alignment: Alignment.centerRight, 58 | child: InkWell( 59 | onTap: onMenu, 60 | child: Container( 61 | padding: EdgeInsets.symmetric(vertical: 20,horizontal: 50), 62 | child: Text(menu ?? "", style: AppStyles.titleTextStyle), 63 | ))), 64 | ], 65 | )); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/widgets/cache_image.dart: -------------------------------------------------------------------------------- 1 | import 'package:extended_image/extended_image.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:lottie/lottie.dart'; 4 | 5 | class CacheImage extends StatefulWidget { 6 | final url; 7 | final placeholder; 8 | final width; 9 | final height; 10 | 11 | const CacheImage({Key? key, @required this.url, this.placeholder = 'assets/images/animationImage.gif', this.width, this.height}) : super(key: key); 12 | 13 | @override 14 | _CacheImageState createState() => _CacheImageState(); 15 | } 16 | 17 | class _CacheImageState extends State 18 | with SingleTickerProviderStateMixin { 19 | late AnimationController _controller; 20 | 21 | @override 22 | void initState() { 23 | _controller = AnimationController( 24 | vsync: this, 25 | duration: Duration(milliseconds: 2200), 26 | lowerBound: 0.0, 27 | upperBound: 1.0); 28 | super.initState(); 29 | } 30 | 31 | @override 32 | void dispose() { 33 | _controller.dispose(); 34 | super.dispose(); 35 | } 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | return LayoutBuilder( 40 | builder: (context, constraints) { 41 | return ExtendedImage.network( 42 | widget.url, 43 | width: widget.width ?? null, 44 | height: widget.height ?? null, 45 | fit: BoxFit.cover, 46 | cache: true, 47 | enableLoadState: true, 48 | loadStateChanged: (ExtendedImageState state) { 49 | switch (state.extendedImageLoadState) { 50 | case LoadState.loading: 51 | _controller.reset(); 52 | return Image.asset( 53 | widget.placeholder, 54 | fit: BoxFit.cover, 55 | ); 56 | break; 57 | case LoadState.completed: 58 | _controller.forward(); 59 | return FadeTransition( 60 | opacity: _controller, 61 | child: ExtendedRawImage( 62 | image: state.extendedImageInfo?.image, 63 | fit: BoxFit.cover, 64 | ), 65 | ); 66 | break; 67 | case LoadState.failed: 68 | _controller.reset(); 69 | state.imageProvider.evict(); 70 | return GestureDetector( 71 | child: Stack( 72 | fit: StackFit.expand, 73 | children: [ 74 | Lottie.asset( 75 | 'assets/json/error2.json', 76 | width: 66, 77 | height: 66, 78 | fit: BoxFit.cover, 79 | alignment: Alignment.center, 80 | ), 81 | Positioned( 82 | bottom: 6.0, 83 | left: 0.0, 84 | right: 0.0, 85 | child: Text( 86 | '图片加载失败,点击重试', 87 | textAlign: TextAlign.center, 88 | ), 89 | ) 90 | ], 91 | ), 92 | onTap: () { 93 | state.reLoadImage(); 94 | }, 95 | ); 96 | break; 97 | } 98 | 99 | return Container(); 100 | }, 101 | ); 102 | } 103 | ); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /lib/widgets/custom_indicator.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Flutter Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // @dart = 2.8 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter/widgets.dart'; 9 | 10 | 11 | 12 | /// Used with [TabBar.indicator] to draw a horizontal line below the 13 | /// selected tab. 14 | /// 15 | /// The selected tab underline is inset from the tab's boundary by [insets]. 16 | /// The [borderSide] defines the line's color and weight. 17 | /// 18 | /// The [TabBar.indicatorSize] property can be used to define the indicator's 19 | /// bounds in terms of its (centered) widget with [TabBarIndicatorSize.label], 20 | /// or the entire tab with [TabBarIndicatorSize.tab]. 21 | class UnderlineTabIndicator extends Decoration { 22 | /// Create an underline style selected tab indicator. 23 | /// 24 | /// The [borderSide] and [insets] arguments must not be null. 25 | const UnderlineTabIndicator({ 26 | this.borderSide = const BorderSide(width: 2.0, color: Colors.white), 27 | this.insets = EdgeInsets.zero, 28 | this.hPadding = 2.0, 29 | }) : assert(hPadding != null), 30 | assert(borderSide != null), 31 | assert(insets != null); 32 | 33 | final double hPadding; 34 | 35 | /// The color and weight of the horizontal line drawn below the selected tab. 36 | final BorderSide borderSide; 37 | 38 | /// Locates the selected tab's underline relative to the tab's boundary. 39 | /// 40 | /// The [TabBar.indicatorSize] property can be used to define the tab 41 | /// indicator's bounds in terms of its (centered) tab widget with 42 | /// [TabBarIndicatorSize.label], or the entire tab with 43 | /// [TabBarIndicatorSize.tab]. 44 | final EdgeInsetsGeometry insets; 45 | 46 | @override 47 | Decoration lerpFrom(Decoration a, double t) { 48 | if (a is UnderlineTabIndicator) { 49 | return UnderlineTabIndicator( 50 | borderSide: BorderSide.lerp(a.borderSide, borderSide, t), 51 | insets: EdgeInsetsGeometry.lerp(a.insets, insets, t), 52 | ); 53 | } 54 | return super.lerpFrom(a, t); 55 | } 56 | 57 | @override 58 | Decoration lerpTo(Decoration b, double t) { 59 | if (b is UnderlineTabIndicator) { 60 | return UnderlineTabIndicator( 61 | borderSide: BorderSide.lerp(borderSide, b.borderSide, t), 62 | insets: EdgeInsetsGeometry.lerp(insets, b.insets, t), 63 | ); 64 | } 65 | return super.lerpTo(b, t); 66 | } 67 | 68 | @override 69 | _UnderlinePainter createBoxPainter([ VoidCallback onChanged ]) { 70 | return _UnderlinePainter(this, onChanged); 71 | } 72 | 73 | RRect _indicatorRectFor(Rect rect, TextDirection textDirection) { 74 | assert(rect != null); 75 | assert(textDirection != null); 76 | final Rect indicator = insets.resolve(textDirection).deflateRect(rect); 77 | Rect _rect = Rect.fromLTWH( 78 | indicator.left, 79 | indicator.bottom - borderSide.width - hPadding, 80 | indicator.width, 81 | borderSide.width, 82 | ); 83 | 84 | return RRect.fromRectAndRadius(_rect, Radius.circular(borderSide.width / 2)); 85 | } 86 | 87 | @override 88 | Path getClipPath(Rect rect, TextDirection textDirection) { 89 | return Path()..addRRect(_indicatorRectFor(rect, textDirection)); 90 | } 91 | 92 | } 93 | 94 | class _UnderlinePainter extends BoxPainter { 95 | _UnderlinePainter(this.decoration, VoidCallback onChanged) 96 | : assert(decoration != null), 97 | super(onChanged); 98 | 99 | final UnderlineTabIndicator decoration; 100 | 101 | @override 102 | void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) { 103 | assert(configuration != null); 104 | assert(configuration.size != null); 105 | final Rect rect = offset & configuration.size; 106 | final TextDirection textDirection = configuration.textDirection; 107 | final RRect indicator = decoration._indicatorRectFor(rect, textDirection); 108 | final Paint paint = decoration.borderSide.toPaint()..strokeCap = StrokeCap.square; 109 | canvas..drawRRect(indicator, paint); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /lib/widgets/error_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:lottie/lottie.dart'; 3 | 4 | class ErrorPage extends StatelessWidget { 5 | final Widget? icon; 6 | final String? title; 7 | final String? desc; 8 | final String? buttonText; 9 | final VoidCallback buttonAction; 10 | final bool isEmptyPage; 11 | 12 | ErrorPage( 13 | {this.icon, 14 | this.title, 15 | this.desc, 16 | this.buttonText, 17 | required this.buttonAction, 18 | this.isEmptyPage = false}); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | var size = MediaQuery.of(context).size; 23 | 24 | return Scaffold( 25 | body: Container( 26 | alignment: Alignment.center, 27 | child: Column( 28 | mainAxisAlignment: MainAxisAlignment.center, 29 | children: [ 30 | Container( 31 | alignment: Alignment.center, 32 | child: this.icon != null 33 | ? this.icon 34 | : Lottie.asset( 35 | 'assets/json/error2.json', 36 | width: size.width / 1.3, 37 | height: 160, 38 | fit: BoxFit.fill, 39 | alignment: Alignment.center, 40 | ), 41 | ), 42 | !isEmptyPage 43 | ? Padding( 44 | padding: EdgeInsets.only( 45 | top: 10, 46 | bottom: 12, 47 | ), 48 | child: Text( 49 | this.title ?? '哦豁😯,出现了蜜汁错误!', 50 | style: TextStyle(fontSize: 16, color: Colors.grey), 51 | ), 52 | ) 53 | : Container(), 54 | Row( 55 | mainAxisAlignment: MainAxisAlignment.center, 56 | children: [ 57 | Text( 58 | this.desc ?? '虽然什么也没有,要不刷新看看', 59 | style: TextStyle(fontSize: 15, color: Colors.grey.shade400), 60 | ), 61 | ], 62 | ), 63 | !isEmptyPage 64 | ? Padding( 65 | padding: const EdgeInsets.all(12.0), 66 | child: MaterialButton( 67 | onPressed: () => buttonAction(), 68 | child: Text( 69 | this.buttonText ?? '刷新', 70 | style: TextStyle(fontSize: 16, color: Colors.black), 71 | ), 72 | color: Theme.of(context).primaryColor, 73 | shape: RoundedRectangleBorder( 74 | side: BorderSide.none, 75 | borderRadius: BorderRadius.circular(8)), 76 | ), 77 | ) 78 | : Container(), 79 | ], 80 | ), 81 | ), 82 | ); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/widgets/iconfont.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class IconFont { 4 | static const String _family = 'iconfont'; 5 | 6 | IconFont._(); 7 | 8 | static const IconData icon_user2 = IconData(0xe625, fontFamily: _family); 9 | static const IconData icon_search1 = IconData(0xe7a2, fontFamily: _family); 10 | static const IconData icon_message_fill = IconData(0xe621, fontFamily: _family); 11 | static const IconData icon_message = IconData(0xe730, fontFamily: _family); 12 | static const IconData icon_search = IconData(0xe60c, fontFamily: _family); 13 | static const IconData icon_empty = IconData(0xe639, fontFamily: _family); 14 | static const IconData icon_empty_status = IconData(0xe60e, fontFamily: _family); 15 | static const IconData icon_info = IconData(0xe64b, fontFamily: _family); 16 | static const IconData icon_Info = IconData(0xe692, fontFamily: _family); 17 | static const IconData icon_message4 = IconData(0xe6a8, fontFamily: _family); 18 | static const IconData icon_message3 = IconData(0xe6a9, fontFamily: _family); 19 | static const IconData icon_user = IconData(0xe73d, fontFamily: _family); 20 | static const IconData icon_search2 = IconData(0xe7c9, fontFamily: _family); 21 | static const IconData icon_home = IconData(0xe607, fontFamily: _family); 22 | static const IconData icon_Home = IconData(0xe709, fontFamily: _family); 23 | static const IconData icon_home1 = IconData(0xe605, fontFamily: _family); 24 | static const IconData icon_user1 = IconData(0xe66f, fontFamily: _family); 25 | static const IconData icon_back = IconData(0xe614, fontFamily: _family); 26 | static const IconData icon_moreif = IconData(0xe686, fontFamily: _family); 27 | static const IconData icon_zan = IconData(0xe617, fontFamily: _family); 28 | static const IconData icon_fenxiang = IconData(0xe63a, fontFamily: _family); 29 | static const IconData icon_back1 = IconData(0xe615, fontFamily: _family); 30 | static const IconData icon_zan1 = IconData(0xe829, fontFamily: _family); 31 | static const IconData icon_follow = IconData(0xe62c, fontFamily: _family); 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /lib/widgets/my_appbar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flustars/flustars.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_plus/common/constant.dart'; 4 | 5 | class MyAppBar extends StatelessWidget implements PreferredSizeWidget { 6 | final Widget child; 7 | 8 | const MyAppBar({ 9 | Key? key, 10 | required this.child, 11 | }) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | double _statusHeight = ScreenUtil.getInstance().statusBarHeight; 16 | return Container( 17 | color: Theme.of(context).primaryColor, 18 | padding: EdgeInsets.only(top: _statusHeight), 19 | child: this.child, 20 | ); 21 | } 22 | 23 | @override 24 | // TODO: implement preferredSize 25 | Size get preferredSize => new Size.fromHeight(Constant.APP_BAR); 26 | } 27 | -------------------------------------------------------------------------------- /lib/widgets/over_scroll_behavior.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class OverScrollBehavior extends ScrollBehavior { 4 | @override 5 | Widget buildViewportChrome( 6 | BuildContext context, Widget child, AxisDirection axisDirection) { 7 | switch (getPlatform(context)) { 8 | case TargetPlatform.linux: 9 | case TargetPlatform.macOS: 10 | case TargetPlatform.windows: 11 | case TargetPlatform.iOS: 12 | return child; 13 | case TargetPlatform.android: 14 | case TargetPlatform.fuchsia: 15 | return GlowingOverscrollIndicator( 16 | child: child, 17 | showLeading: false, 18 | showTrailing: false, 19 | axisDirection: axisDirection, 20 | color: Theme.of(context).accentColor, 21 | ); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/widgets/page_state.dart: -------------------------------------------------------------------------------- 1 | enum PageState { 2 | idle, 3 | busy, //加载中 4 | empty, //无数据 5 | error, //加载失败 6 | success,//加载成功 7 | } 8 | /// 错误类型 9 | enum PageStateErrorType { 10 | defaultError, 11 | networkTimeOutError, //网络错误 12 | unauthorizedError //为授权(一般为未登录) 13 | } 14 | -------------------------------------------------------------------------------- /lib/widgets/refresh.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:lottie/lottie.dart'; 4 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 5 | import 'over_scroll_behavior.dart'; 6 | 7 | class Refresh extends StatelessWidget { 8 | final RefreshController controller; 9 | final VoidCallback onRefresh; 10 | final VoidCallback onLoading; 11 | final Widget content; 12 | 13 | Refresh( 14 | {required this.controller, 15 | required this.onRefresh, 16 | required this.onLoading, 17 | required this.content}); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return RefreshConfiguration.copyAncestor( 22 | context: context, 23 | child: RefreshConfiguration.copyAncestor( 24 | context: context, 25 | child: ScrollConfiguration( 26 | behavior: OverScrollBehavior(), 27 | child: SmartRefresher( 28 | controller: this.controller, 29 | enablePullDown: true, 30 | enablePullUp: true, 31 | header: CustomHeader( 32 | builder: (BuildContext context, RefreshStatus? mode) { 33 | Widget? body; 34 | if (mode == RefreshStatus.canRefresh) { 35 | body = textIndicator("松开刷新"); 36 | } else if (mode == RefreshStatus.refreshing) { 37 | body = textIndicator("加载中..."); 38 | } else if (mode == RefreshStatus.idle) { 39 | body = textIndicator("下拉刷新"); 40 | } else if (mode == RefreshStatus.completed) { 41 | body = textIndicator("加载成功"); 42 | } 43 | return Container( 44 | padding: EdgeInsets.only(top: 6), 45 | height: 76, 46 | child: Center( 47 | child: body, 48 | ), 49 | ); 50 | }, 51 | ), 52 | footer: CustomFooter( 53 | loadStyle: LoadStyle.ShowWhenLoading, 54 | builder: (BuildContext context, LoadStatus? mode) { 55 | Widget body; 56 | if (mode == LoadStatus.idle) { 57 | body = Text("上拉加载", style: TextStyle(fontSize: 12)); 58 | } else if (mode == LoadStatus.loading) { 59 | body = Container( 60 | child: Row( 61 | mainAxisAlignment: MainAxisAlignment.center, 62 | children: [ 63 | SizedBox( 64 | child: CircularProgressIndicator( 65 | valueColor: 66 | AlwaysStoppedAnimation(Colors.grey), 67 | strokeWidth: 1.6, 68 | ), 69 | width: 16, 70 | height: 16, 71 | ), 72 | Padding(padding: EdgeInsets.only(left: 10)), 73 | Text("加载中...", style: TextStyle(fontSize: 12)) 74 | ], 75 | ), 76 | ); 77 | } else if (mode == LoadStatus.failed) { 78 | body = Text("加载失败!点击重试!", style: TextStyle(fontSize: 12)); 79 | } else if (mode == LoadStatus.canLoading) { 80 | body = Text("松手,加载更多!", style: TextStyle(fontSize: 12)); 81 | } else { 82 | body = Text("没有更多数据了!", style: TextStyle(fontSize: 12)); 83 | } 84 | return Container( 85 | height: 55.0, 86 | child: Center(child: body), 87 | ); 88 | }, 89 | ), 90 | onRefresh: this.onRefresh, 91 | onLoading: this.onLoading, 92 | child: this.content, 93 | ), 94 | ), 95 | enableLoadingWhenFailed: true, 96 | maxUnderScrollExtent: 100.0, 97 | footerTriggerDistance: -45.0, 98 | ), 99 | enableLoadingWhenFailed: true, 100 | footerTriggerDistance: -60.0, 101 | ); 102 | } 103 | 104 | Widget textIndicator(String statusStr) { 105 | return Container( 106 | child: Stack( 107 | children: [ 108 | Lottie.asset( 109 | 'assets/json/loading2.json', 110 | width: 96, 111 | fit: BoxFit.cover, 112 | alignment: Alignment.center, 113 | ), 114 | Positioned( 115 | bottom: -2, 116 | left: 24, 117 | right: 0, 118 | child: Text( 119 | statusStr, 120 | style: TextStyle(fontSize: 12), 121 | ), 122 | ) 123 | ], 124 | ), 125 | ); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_plus 2 | description: A new Flutter project. 3 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 4 | version: 1.0.0+1 5 | environment: 6 | sdk: ">=2.12.0-0 <3.0.0" 7 | flutter: ">=1.17.0" 8 | dependencies: 9 | flutter: 10 | sdk: flutter 11 | intl: any 12 | # shared_preferences: 2.0.1 #本地存储 13 | dio: 4.0.0 #网络请求 14 | dio_cookie_manager: ^2.0.0 #网络请求 15 | retrofit: any #网络请求 16 | logger: any #网络请求 17 | permission_handler: ^8.1.2 #权限 18 | fluro: ^2.0.3 #路由 19 | connectivity: ^3.0.6 #网络连接 20 | connectivity_for_web: 0.4.0 #网络连接 21 | flutter_hooks: ^0.17.0 22 | hooks_riverpod: ^0.14.0+4 23 | pull_to_refresh: ^2.0.0 #加载刷新 24 | freezed_annotation: ^0.14.2 #实体类 25 | fluttertoast: ^8.0.7 #toast 26 | flutter_card_swipper: 0.4.0 #轮播图 27 | multi_image_picker: ^4.8.1 #照片多选 28 | flutter_form_builder: ^6.0.1 29 | customizable_space_bar: ^0.2.0 30 | flutter_html: ^2.1.0 31 | flustars: ^2.0.1 #工具类 32 | extended_image: ^4.1.0 #网络图片缓存 33 | lottie: ^1.1.0 #json格式的动画 34 | transparent_image: 2.0.0 #透明图像 35 | json_serializable: ^4.0.0 36 | dev_dependencies: 37 | flutter_test: 38 | sdk: flutter 39 | build_runner: any 40 | freezed: ^0.14.2 41 | retrofit_generator: any 42 | flutter: 43 | uses-material-design: true 44 | assets: 45 | - assets/images/ 46 | - assets/json/ 47 | fonts: 48 | - family: iconfont 49 | fonts: 50 | - asset: assets/fonts/iconfont.ttf 51 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh6/flutter_plus/941a162151a64900fc55eb749574ab59fd66706d/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | flutter_plus 30 | 31 | 32 | 33 | 36 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutter_plus", 3 | "short_name": "flutter_plus", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | } 22 | ] 23 | } 24 | --------------------------------------------------------------------------------