commit 43b55b378d2eb01f76595de082f8bcf9f3b31227 Author: 18631081161 <2088094923@qq.com> Date: Tue Jul 16 22:23:54 2024 +0800 First push diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2c15f2d --- /dev/null +++ b/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release +/pubspec.lock diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..e127d72 --- /dev/null +++ b/.metadata @@ -0,0 +1,36 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "a14f74ff3a1cbd521163c5f03d68113d50af93d3" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + - platform: android + create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + - platform: ios + create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + - platform: web + create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/README.md b/README.md new file mode 100644 index 0000000..fa24db3 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# talk + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..8030356 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,74 @@ +plugins { + id "com.android.application" + id "kotlin-android" + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id "dev.flutter.flutter-gradle-plugin" +} + +def localProperties = new Properties() +def localPropertiesFile = rootProject.file("local.properties") +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader("UTF-8") { reader -> + localProperties.load(reader) + } +} + +def flutterVersionCode = localProperties.getProperty("flutter.versionCode") +if (flutterVersionCode == null) { + flutterVersionCode = "1" +} + +def flutterVersionName = localProperties.getProperty("flutter.versionName") +if (flutterVersionName == null) { + flutterVersionName = "1.0" +} + +android { + namespace = "com.huanmeng.talk" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + signingConfigs { + //签名的配置 + signConfig { + storeFile file("talk.jks") + storePassword '123456' + keyAlias 'talk' + keyPassword '123456' + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.huanmeng.talk" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutterVersionCode.toInteger() + versionName = flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + signingConfig signingConfigs.signConfig //打包命令行:gradlew assembleRelease + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + //关闭混淆 + minifyEnabled false //删除无用代码 + shrinkResources false //删除无用资源 + } + debug { + signingConfig signingConfigs.signConfig + } + } +} + +flutter { + source = "../.." +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..f1d4733 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/java/com/huanmeng/talk/MainActivity.java b/android/app/src/main/java/com/huanmeng/talk/MainActivity.java new file mode 100644 index 0000000..10732ed --- /dev/null +++ b/android/app/src/main/java/com/huanmeng/talk/MainActivity.java @@ -0,0 +1,6 @@ +package com.huanmeng.talk; + +import io.flutter.embedding.android.FlutterActivity; + +public class MainActivity extends FlutterActivity { +} diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..f38d051 --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/img_start.png b/android/app/src/main/res/mipmap-xxhdpi/img_start.png new file mode 100644 index 0000000..47bc3e2 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/img_start.png differ diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..3f054f5 --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #FF121213 + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/talk.jks b/android/app/talk.jks new file mode 100644 index 0000000..f804989 Binary files /dev/null and b/android/app/talk.jks differ diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..d2ffbff --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,18 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = "../build" +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..3b5b324 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e1ca574 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..536165d --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,25 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} + +include ":app" diff --git a/assets/images/a1.png b/assets/images/a1.png new file mode 100644 index 0000000..b372ca3 Binary files /dev/null and b/assets/images/a1.png differ diff --git a/assets/images/a2.png b/assets/images/a2.png new file mode 100644 index 0000000..9f32f05 Binary files /dev/null and b/assets/images/a2.png differ diff --git a/assets/images/a3.png b/assets/images/a3.png new file mode 100644 index 0000000..3e0ff32 Binary files /dev/null and b/assets/images/a3.png differ diff --git a/assets/images/banner1.png b/assets/images/banner1.png new file mode 100644 index 0000000..27cb027 Binary files /dev/null and b/assets/images/banner1.png differ diff --git a/assets/images/bj.png b/assets/images/bj.png new file mode 100644 index 0000000..94a8906 Binary files /dev/null and b/assets/images/bj.png differ diff --git a/assets/images/ic_add.png b/assets/images/ic_add.png new file mode 100644 index 0000000..764858a Binary files /dev/null and b/assets/images/ic_add.png differ diff --git a/assets/images/ic_album.png b/assets/images/ic_album.png new file mode 100644 index 0000000..488b4a6 Binary files /dev/null and b/assets/images/ic_album.png differ diff --git a/assets/images/ic_back1.png b/assets/images/ic_back1.png new file mode 100644 index 0000000..c0767d0 Binary files /dev/null and b/assets/images/ic_back1.png differ diff --git a/assets/images/ic_beckoning.png b/assets/images/ic_beckoning.png new file mode 100644 index 0000000..e11660b Binary files /dev/null and b/assets/images/ic_beckoning.png differ diff --git a/assets/images/ic_ck.png b/assets/images/ic_ck.png new file mode 100644 index 0000000..c8adde2 Binary files /dev/null and b/assets/images/ic_ck.png differ diff --git a/assets/images/ic_ck_s.png b/assets/images/ic_ck_s.png new file mode 100644 index 0000000..71c6063 Binary files /dev/null and b/assets/images/ic_ck_s.png differ diff --git a/assets/images/ic_create.png b/assets/images/ic_create.png new file mode 100644 index 0000000..88386d3 Binary files /dev/null and b/assets/images/ic_create.png differ diff --git a/assets/images/ic_currency.png b/assets/images/ic_currency.png new file mode 100644 index 0000000..374536b Binary files /dev/null and b/assets/images/ic_currency.png differ diff --git a/assets/images/ic_empty.png b/assets/images/ic_empty.png new file mode 100644 index 0000000..0f6465e Binary files /dev/null and b/assets/images/ic_empty.png differ diff --git a/assets/images/ic_left_arrow.png b/assets/images/ic_left_arrow.png new file mode 100644 index 0000000..a7538c6 Binary files /dev/null and b/assets/images/ic_left_arrow.png differ diff --git a/assets/images/ic_mall.png b/assets/images/ic_mall.png new file mode 100644 index 0000000..86b80f8 Binary files /dev/null and b/assets/images/ic_mall.png differ diff --git a/assets/images/ic_man.png b/assets/images/ic_man.png new file mode 100644 index 0000000..aa95bec Binary files /dev/null and b/assets/images/ic_man.png differ diff --git a/assets/images/ic_memory.png b/assets/images/ic_memory.png new file mode 100644 index 0000000..1aea22c Binary files /dev/null and b/assets/images/ic_memory.png differ diff --git a/assets/images/ic_memory_p.png b/assets/images/ic_memory_p.png new file mode 100644 index 0000000..bab3f84 Binary files /dev/null and b/assets/images/ic_memory_p.png differ diff --git a/assets/images/ic_more.png b/assets/images/ic_more.png new file mode 100644 index 0000000..2f1d120 Binary files /dev/null and b/assets/images/ic_more.png differ diff --git a/assets/images/ic_note.png b/assets/images/ic_note.png new file mode 100644 index 0000000..a653dac Binary files /dev/null and b/assets/images/ic_note.png differ diff --git a/assets/images/ic_restart.png b/assets/images/ic_restart.png new file mode 100644 index 0000000..ec7e13e Binary files /dev/null and b/assets/images/ic_restart.png differ diff --git a/assets/images/ic_right_arrow.png b/assets/images/ic_right_arrow.png new file mode 100644 index 0000000..1a79636 Binary files /dev/null and b/assets/images/ic_right_arrow.png differ diff --git a/assets/images/ic_search.png b/assets/images/ic_search.png new file mode 100644 index 0000000..2af020a Binary files /dev/null and b/assets/images/ic_search.png differ diff --git a/assets/images/ic_search2.png b/assets/images/ic_search2.png new file mode 100644 index 0000000..0a9fb64 Binary files /dev/null and b/assets/images/ic_search2.png differ diff --git a/assets/images/ic_send.png b/assets/images/ic_send.png new file mode 100644 index 0000000..0d360e3 Binary files /dev/null and b/assets/images/ic_send.png differ diff --git a/assets/images/ic_send_n.png b/assets/images/ic_send_n.png new file mode 100644 index 0000000..ee0c26b Binary files /dev/null and b/assets/images/ic_send_n.png differ diff --git a/assets/images/ic_setting.png b/assets/images/ic_setting.png new file mode 100644 index 0000000..9818831 Binary files /dev/null and b/assets/images/ic_setting.png differ diff --git a/assets/images/ic_smart_chat.png b/assets/images/ic_smart_chat.png new file mode 100644 index 0000000..22025b3 Binary files /dev/null and b/assets/images/ic_smart_chat.png differ diff --git a/assets/images/ic_type.png b/assets/images/ic_type.png new file mode 100644 index 0000000..0d080db Binary files /dev/null and b/assets/images/ic_type.png differ diff --git a/assets/images/ic_voice.png b/assets/images/ic_voice.png new file mode 100644 index 0000000..4ab5b95 Binary files /dev/null and b/assets/images/ic_voice.png differ diff --git a/assets/images/ic_web.png b/assets/images/ic_web.png new file mode 100644 index 0000000..8f9eab2 Binary files /dev/null and b/assets/images/ic_web.png differ diff --git a/assets/images/ic_woman.png b/assets/images/ic_woman.png new file mode 100644 index 0000000..b30c1fd Binary files /dev/null and b/assets/images/ic_woman.png differ diff --git a/assets/images/img_head.png b/assets/images/img_head.png new file mode 100644 index 0000000..e0ea4c6 Binary files /dev/null and b/assets/images/img_head.png differ diff --git a/assets/images/img_head2.png b/assets/images/img_head2.png new file mode 100644 index 0000000..22a79a3 Binary files /dev/null and b/assets/images/img_head2.png differ diff --git a/assets/images/img_head3.png b/assets/images/img_head3.png new file mode 100644 index 0000000..7ce8fff Binary files /dev/null and b/assets/images/img_head3.png differ diff --git a/assets/images/img_start.png b/assets/images/img_start.png new file mode 100644 index 0000000..47bc3e2 Binary files /dev/null and b/assets/images/img_start.png differ diff --git a/assets/images/login_bj.png b/assets/images/login_bj.png new file mode 100644 index 0000000..37c84ec Binary files /dev/null and b/assets/images/login_bj.png differ diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..7c56964 --- /dev/null +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 12.0 + + diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..70b22a4 --- /dev/null +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,615 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C80F4294D02FB00263BE5 /* RunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 331C80F3294D02FB00263BE5 /* RunnerTests.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80F5294D02FB00263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 331C80F1294D02FB00263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80F3294D02FB00263BE5 /* RunnerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerTests.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80EE294D02FB00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80F2294D02FB00263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80F3294D02FB00263BE5 /* RunnerTests.m */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 331C80F2294D02FB00263BE5 /* RunnerTests */, + 97C146EF1CF9000F007C117D /* Products */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C80F1294D02FB00263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80F0294D02FB00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80F7294D02FB00263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C80ED294D02FB00263BE5 /* Sources */, + 331C80EE294D02FB00263BE5 /* Frameworks */, + 331C80EF294D02FB00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80F6294D02FB00263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80F1294D02FB00263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80F0294D02FB00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C80F0294D02FB00263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80EF294D02FB00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80ED294D02FB00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80F4294D02FB00263BE5 /* RunnerTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80F6294D02FB00263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C80F5294D02FB00263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.huanmeng.talk; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C80F8294D02FB00263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.huanmeng.talk.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C80F9294D02FB00263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.huanmeng.talk.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C80FA294D02FB00263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.huanmeng.talk.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.huanmeng.talk; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.huanmeng.talk; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80F7294D02FB00263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80F8294D02FB00263BE5 /* Debug */, + 331C80F9294D02FB00263BE5 /* Release */, + 331C80FA294D02FB00263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..3d0fb00 --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner/AppDelegate.h b/ios/Runner/AppDelegate.h new file mode 100644 index 0000000..36e21bb --- /dev/null +++ b/ios/Runner/AppDelegate.h @@ -0,0 +1,6 @@ +#import +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/ios/Runner/AppDelegate.m b/ios/Runner/AppDelegate.m new file mode 100644 index 0000000..70e8393 --- /dev/null +++ b/ios/Runner/AppDelegate.m @@ -0,0 +1,13 @@ +#import "AppDelegate.h" +#import "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + // Override point for customization after application launch. + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000..dc9ada4 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..7353c41 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..797d452 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..6ed2d93 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..4cd7b00 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..fe73094 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..321773c Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..797d452 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..502f463 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..0ec3034 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..0ec3034 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..e9f5fea Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..84ac32a Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..8953cba Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..0467bf1 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +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. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist new file mode 100644 index 0000000..6f7b074 --- /dev/null +++ b/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Talk + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + talk + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/ios/Runner/main.m b/ios/Runner/main.m new file mode 100644 index 0000000..dff6597 --- /dev/null +++ b/ios/Runner/main.m @@ -0,0 +1,9 @@ +#import +#import +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/ios/RunnerTests/RunnerTests.m b/ios/RunnerTests/RunnerTests.m new file mode 100644 index 0000000..6d8b0bd --- /dev/null +++ b/ios/RunnerTests/RunnerTests.m @@ -0,0 +1,16 @@ +#import +#import +#import + +@interface RunnerTests : XCTestCase + +@end + +@implementation RunnerTests + +- (void)testExample { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. +} + +@end diff --git a/lib/beans/EpgCategoryListBean.dart b/lib/beans/EpgCategoryListBean.dart new file mode 100644 index 0000000..6d161af --- /dev/null +++ b/lib/beans/EpgCategoryListBean.dart @@ -0,0 +1,24 @@ + +import 'package:json_annotation/json_annotation.dart'; + + +part 'EpgCategoryListBean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class EpgCategoryListBean { + + String? CategoryName; + String? CategoryType; + bool? HasMore; + int? RowNum; + bool? IsQuickStartPopUp; + + + EpgCategoryListBean(this.CategoryName, this.CategoryType, this.HasMore, + this.RowNum, this.IsQuickStartPopUp, ); + + factory EpgCategoryListBean.fromJson(Map json) => _$EpgCategoryListBeanFromJson(json); + + Map toJson() => _$EpgCategoryListBeanToJson(this); + +} diff --git a/lib/beans/EpgCategoryListBean.g.dart b/lib/beans/EpgCategoryListBean.g.dart new file mode 100644 index 0000000..e5ab3cb --- /dev/null +++ b/lib/beans/EpgCategoryListBean.g.dart @@ -0,0 +1,26 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'EpgCategoryListBean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +EpgCategoryListBean _$EpgCategoryListBeanFromJson(Map json) => + EpgCategoryListBean( + json['CategoryName'] as String?, + json['CategoryType'] as String?, + json['HasMore'] as bool?, + (json['RowNum'] as num?)?.toInt(), + json['IsQuickStartPopUp'] as bool?, + ); + +Map _$EpgCategoryListBeanToJson( + EpgCategoryListBean instance) => + { + 'CategoryName': instance.CategoryName, + 'CategoryType': instance.CategoryType, + 'HasMore': instance.HasMore, + 'RowNum': instance.RowNum, + 'IsQuickStartPopUp': instance.IsQuickStartPopUp, + }; diff --git a/lib/beans/HallEpgListBean.dart b/lib/beans/HallEpgListBean.dart new file mode 100644 index 0000000..f73f048 --- /dev/null +++ b/lib/beans/HallEpgListBean.dart @@ -0,0 +1,20 @@ + +import 'package:json_annotation/json_annotation.dart'; + +import 'EpgCategoryListBean.dart'; + +part 'HallEpgListBean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class HallEpgListBean { + + + List? EpgCategoryList; + + HallEpgListBean(this.EpgCategoryList); + + + factory HallEpgListBean.fromJson(Map json) => _$HallEpgListBeanFromJson(json); + Map toJson() => _$HallEpgListBeanToJson(this); + +} diff --git a/lib/beans/HallEpgListBean.g.dart b/lib/beans/HallEpgListBean.g.dart new file mode 100644 index 0000000..0690b2f --- /dev/null +++ b/lib/beans/HallEpgListBean.g.dart @@ -0,0 +1,20 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'HallEpgListBean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +HallEpgListBean _$HallEpgListBeanFromJson(Map json) => + HallEpgListBean( + (json['EpgCategoryList'] as List?) + ?.map((e) => EpgCategoryListBean.fromJson(e as Map)) + .toList(), + ); + +Map _$HallEpgListBeanToJson(HallEpgListBean instance) => + { + 'EpgCategoryList': + instance.EpgCategoryList?.map((e) => e.toJson()).toList(), + }; diff --git a/lib/beans/MessageBean.dart b/lib/beans/MessageBean.dart new file mode 100644 index 0000000..28b53ef --- /dev/null +++ b/lib/beans/MessageBean.dart @@ -0,0 +1,19 @@ + +import 'package:json_annotation/json_annotation.dart'; + +part 'MessageBean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class MessageBean{ + + String? role; + String? content; + + + MessageBean(this.role,this.content,); + + factory MessageBean.fromJson(Map json) => _$MessageBeanFromJson(json); + + Map toJson() => _$MessageBeanToJson(this); + +} \ No newline at end of file diff --git a/lib/beans/MessageBean.g.dart b/lib/beans/MessageBean.g.dart new file mode 100644 index 0000000..c2631e7 --- /dev/null +++ b/lib/beans/MessageBean.g.dart @@ -0,0 +1,18 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'MessageBean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +MessageBean _$MessageBeanFromJson(Map json) => MessageBean( + json['role'] as String?, + json['content'] as String?, + ); + +Map _$MessageBeanToJson(MessageBean instance) => + { + 'role': instance.role, + 'content': instance.content, + }; diff --git a/lib/beans/character_info_bean.dart b/lib/beans/character_info_bean.dart new file mode 100644 index 0000000..b6fe4cf --- /dev/null +++ b/lib/beans/character_info_bean.dart @@ -0,0 +1,27 @@ +import 'package:json_annotation/json_annotation.dart'; + +import 'label_bean.dart'; + +part 'character_info_bean.g.dart'; + +///任务信息 +@JsonSerializable(explicitToJson: true) +class CharacterInfoBean { + String? icon; + int? intimacy; //亲密值 + int? characterId; //ID + String? characterName; + int? gender; + int? lookCount; + String? bgUrl; + String? biography; + String? prologue; + List? label; + + CharacterInfoBean(this.icon, this.intimacy, this.characterId, this.characterName, this.gender, this.lookCount, this.bgUrl, this.biography, + this.prologue, this.label); + + factory CharacterInfoBean.fromJson(Map json) => _$CharacterInfoBeanFromJson(json); + + Map toJson() => _$CharacterInfoBeanToJson(this); +} diff --git a/lib/beans/character_info_bean.g.dart b/lib/beans/character_info_bean.g.dart new file mode 100644 index 0000000..33ea21f --- /dev/null +++ b/lib/beans/character_info_bean.g.dart @@ -0,0 +1,37 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'character_info_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +CharacterInfoBean _$CharacterInfoBeanFromJson(Map json) => + CharacterInfoBean( + json['icon'] as String?, + (json['intimacy'] as num?)?.toInt(), + (json['characterId'] as num?)?.toInt(), + json['characterName'] as String?, + (json['gender'] as num?)?.toInt(), + (json['lookCount'] as num?)?.toInt(), + json['bgUrl'] as String?, + json['biography'] as String?, + json['prologue'] as String?, + (json['label'] as List?) + ?.map((e) => LabelBean.fromJson(e as Map)) + .toList(), + ); + +Map _$CharacterInfoBeanToJson(CharacterInfoBean instance) => + { + 'icon': instance.icon, + 'intimacy': instance.intimacy, + 'characterId': instance.characterId, + 'characterName': instance.characterName, + 'gender': instance.gender, + 'lookCount': instance.lookCount, + 'bgUrl': instance.bgUrl, + 'biography': instance.biography, + 'prologue': instance.prologue, + 'label': instance.label?.map((e) => e.toJson()).toList(), + }; diff --git a/lib/beans/home_character_bean.dart b/lib/beans/home_character_bean.dart new file mode 100644 index 0000000..a69831b --- /dev/null +++ b/lib/beans/home_character_bean.dart @@ -0,0 +1,15 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'home_character_bean.g.dart'; + +///标签 +@JsonSerializable(explicitToJson: true) +class HomeCharacterBean { + int? characterId; + + HomeCharacterBean(this.characterId); + + factory HomeCharacterBean.fromJson(Map json) => _$HomeCharacterBeanFromJson(json); + + Map toJson() => _$HomeCharacterBeanToJson(this); +} diff --git a/lib/beans/home_character_bean.g.dart b/lib/beans/home_character_bean.g.dart new file mode 100644 index 0000000..369f48b --- /dev/null +++ b/lib/beans/home_character_bean.g.dart @@ -0,0 +1,17 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'home_character_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +HomeCharacterBean _$HomeCharacterBeanFromJson(Map json) => + HomeCharacterBean( + (json['characterId'] as num?)?.toInt(), + ); + +Map _$HomeCharacterBeanToJson(HomeCharacterBean instance) => + { + 'characterId': instance.characterId, + }; diff --git a/lib/beans/label_bean.dart b/lib/beans/label_bean.dart new file mode 100644 index 0000000..3be7536 --- /dev/null +++ b/lib/beans/label_bean.dart @@ -0,0 +1,16 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'label_bean.g.dart'; + +///标签 +@JsonSerializable(explicitToJson: true) +class LabelBean { + int? id; + String? name; + + LabelBean(this.id, this.name); + + factory LabelBean.fromJson(Map json) => _$LabelBeanFromJson(json); + + Map toJson() => _$LabelBeanToJson(this); +} diff --git a/lib/beans/label_bean.g.dart b/lib/beans/label_bean.g.dart new file mode 100644 index 0000000..0bf26b8 --- /dev/null +++ b/lib/beans/label_bean.g.dart @@ -0,0 +1,17 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'label_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +LabelBean _$LabelBeanFromJson(Map json) => LabelBean( + (json['id'] as num?)?.toInt(), + json['name'] as String?, + ); + +Map _$LabelBeanToJson(LabelBean instance) => { + 'id': instance.id, + 'name': instance.name, + }; diff --git a/lib/beans/login_bean.dart b/lib/beans/login_bean.dart new file mode 100644 index 0000000..e24666b --- /dev/null +++ b/lib/beans/login_bean.dart @@ -0,0 +1,16 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'login_bean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class LoginBean { + String? token; + String? nickName; + int? userId; + + LoginBean(this.token, this.nickName, this.userId); + + factory LoginBean.fromJson(Map json) => _$LoginBeanFromJson(json); + + Map toJson() => _$LoginBeanToJson(this); +} diff --git a/lib/beans/login_bean.g.dart b/lib/beans/login_bean.g.dart new file mode 100644 index 0000000..d101cfb --- /dev/null +++ b/lib/beans/login_bean.g.dart @@ -0,0 +1,19 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'login_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +LoginBean _$LoginBeanFromJson(Map json) => LoginBean( + json['token'] as String?, + json['nickName'] as String?, + (json['userId'] as num?)?.toInt(), + ); + +Map _$LoginBeanToJson(LoginBean instance) => { + 'token': instance.token, + 'nickName': instance.nickName, + 'userId': instance.userId, + }; diff --git a/lib/common/Global.dart b/lib/common/Global.dart new file mode 100644 index 0000000..219ca1d --- /dev/null +++ b/lib/common/Global.dart @@ -0,0 +1,56 @@ + +import 'dart:io'; + +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; + +import '../network/NetworkConfig.dart'; + +const int Environment_Dev = 1; +const int Environment_Pre = 2; +const int Environment_Online = 3; + +class Global { + factory Global() => _getInstance(); + + static Global get instance => _getInstance(); + static Global? _instance; + + Global._internal() { + // 初始化 + } + + static Global _getInstance() { + _instance ??= Global._internal(); + return _instance!; + } + + static const method = MethodChannel('samples.flutter.dev/battery'); + + static bool get isRelease => bool.fromEnvironment("dart.vm.product"); + static String? flatform_name; + + static Future initialize() async { + if (!NetworkConfig.isTest) { + NetworkConfig.BASE_URLS = NetworkConfig.BASE_URLS_AI; + } + + // if (Platform.isIOS) { + // //ios相关代码 + // flatform_name = 'iOS'; + // } else if (Platform.isAndroid) { + // //android相关代码 + // flatform_name = 'Android'; + // } else if (Platform.isWindows) { + // //android相关代码 + // flatform_name = 'Windows'; + // } + // WidgetsFlutterBinding.ensureInitialized(); //不加这个强制横/竖屏会报错 + // SystemChrome.setPreferredOrientations([ + // // 强制竖屏 + // DeviceOrientation.portraitUp, + // DeviceOrientation.portraitDown + // ]); + // Global a = Global.instance; + } +} diff --git a/lib/common/app_util.dart b/lib/common/app_util.dart new file mode 100644 index 0000000..5892ba7 --- /dev/null +++ b/lib/common/app_util.dart @@ -0,0 +1,237 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:http/http.dart' as http; +import 'package:image_cropper/image_cropper.dart'; +import 'package:image_gallery_saver/image_gallery_saver.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:mime/mime.dart'; +import 'package:permission_handler/permission_handler.dart'; + +import '../network/NetworkConfig.dart'; + +class AppUtil { + /// 默认为下载网络图片,如需下载资源图片,需要指定 [isAsset] 为 `true`。 + static Future saveImage(String imageUrl, {bool isAsset = false}) async { + try { + if (imageUrl == null) throw '保存失败,图片不存在!'; + + print(NetworkConfig.systemVersion); + + /// 权限检测 + PermissionStatus storageStatus; + + ///android13及以上版本 "storage" 已废除 图片 视频 音频权限需要分别申请 + if (int.parse(NetworkConfig.systemVersion) >= 13) { + storageStatus = await Permission.photos.status; + } else { + storageStatus = await Permission.storage.status; + } + + if (storageStatus != PermissionStatus.granted) { + if (int.parse(NetworkConfig.systemVersion) >= 13) { + storageStatus = await Permission.photos.request(); + } else { + storageStatus = await Permission.storage.request(); + } + if (storageStatus != PermissionStatus.granted) { + throw '无法存储图片,请先授权!'; + } + } + + /// 保存的图片数据 + Uint8List imageBytes; + + if (isAsset == true) { + /// 保存资源图片 + ByteData bytes = await rootBundle.load(imageUrl); + imageBytes = bytes.buffer.asUint8List(); + } else { + Map map = {}; + + /// 保存网络图片 + CachedNetworkImage image = CachedNetworkImage(imageUrl: imageUrl); + BaseCacheManager manager = image.cacheManager ?? DefaultCacheManager(); + Map headers = + image.httpHeaders != null ? image.httpHeaders! : map; + File file = await manager.getSingleFile( + image.imageUrl, + headers: headers, + ); + imageBytes = await file.readAsBytes(); + } + + /// 保存图片 + final result = + await ImageGallerySaver.saveImage(imageBytes, quality: 100); + + if (result == null || result == '') throw '图片保存失败'; + + EasyLoading.showToast("Successfully saved"); + } catch (e) { + print(e.toString()); + } + } + + ///保存Widget图片 + static Future saveWidgetImage(ByteData bytes) async { + try { + if (bytes == null) throw '保存失败,图片不存在!'; + + /// 权限检测 + PermissionStatus storageStatus; + + ///android13及以上版本 "storage" 已废除 图片 视频 音频权限需要分别申请 + if (int.parse(NetworkConfig.systemVersion) >= 13) { + storageStatus = await Permission.photos.status; + } else { + storageStatus = await Permission.storage.status; + } + + if (storageStatus != PermissionStatus.granted) { + if (int.parse(NetworkConfig.systemVersion) >= 13) { + storageStatus = await Permission.photos.request(); + } else { + storageStatus = await Permission.storage.request(); + } + + if (storageStatus != PermissionStatus.granted) { + throw '无法存储图片,请先授权!'; + } + } + + Uint8List imageBytes = bytes.buffer.asUint8List(); + + /// 保存图片 + final result = + await ImageGallerySaver.saveImage(imageBytes, quality: 100); + + if (result == null || result == '') throw '图片保存失败'; + + EasyLoading.showToast("Successfully saved"); + } catch (e) { + print(e.toString()); + } + } + + //获取图片 + static Future getImages() async { + final pkImage = await ImagePicker().pickImage( + source: ImageSource.gallery, + ); + return pkImage!; + } + + //裁剪 + static cropImage(BuildContext context, String path, int index) async { + double x = 1.0; + double y = 1.0; + switch (index) { + case 0: + x = 1.0; + y = 1.0; + break; + case 1: + x = 3.0; + y = 4.0; + break; + case 2: + x = 4.0; + y = 3.0; + break; + case 3: + x = 9.0; + y = 16.0; + break; + case 4: + x = 16.0; + y = 9.0; + break; + } + CroppedFile? croppedFile = await ImageCropper().cropImage( + maxHeight: 100, + sourcePath: path, + // aspectRatioPresets: [ + // CropAspectRatioPreset.square, //1:1 + // ], + //aspectRatio: CropAspectRatio(ratioX: x, ratioY: y), + uiSettings: [ + IOSUiSettings( + title: 'Cropper', + aspectRatioLockEnabled: true, + resetAspectRatioEnabled: false, + ), + AndroidUiSettings( + toolbarTitle: 'Cropper', + toolbarColor: Color(0xFFBE6FDF), + toolbarWidgetColor: Colors.white, + activeControlsWidgetColor: Color(0xFFBE6FDF), + initAspectRatio: CropAspectRatioPreset.square, + lockAspectRatio: true, + ), + WebUiSettings( + context: context, + viewwMode: WebViewMode.mode_1, + cropBoxMovable: false + /*customDialogBuilder: (cropper, initCropper, crop, rotate, scale) { + return CropperDialog( + cropper: cropper, + initCropper: initCropper, + crop: crop, + rotate: rotate, + scale: scale, + ); + },*/ + ), + ], + ); + return croppedFile; + } + + //解析图片转base64 + static uploadImage(XFile pickedFile) async { + var file = File(pickedFile.path); + String? type = lookupMimeType(pickedFile.path); //读取格式 + Uint8List imageBytes = await file.readAsBytes(); + String base64 = base64Encode(imageBytes); + return "data:$type;base64,$base64"; + } + + //解析图片转base64 + static uploadImage2(File pickedFile) async { + String? type = lookupMimeType(pickedFile.path); //读取格式 + Uint8List imageBytes = await pickedFile.readAsBytes(); + String base64 = base64Encode(imageBytes); + return "data:$type;base64,$base64"; + } + + // 获取文件大小 + static String getFileSizeString({int? bytes, int decimals = 0}) { + if (bytes! <= 0) return "0 Bytes"; + const suffixes = [" Bytes", "KB", "MB", "GB", "TB"]; + var i = (log(bytes) / log(1024)).floor(); + return ((bytes / pow(1024, i)).toStringAsFixed(decimals)) + suffixes[i]; + } + + // 获取文件大小(mb) + static double getFileSizeDouble({int? bytes, int decimals = 0}) { + final kb = bytes! / 1024; + final mb = kb / 1024; + print("object===$mb"); + return mb; + } + + ///网络图片转base64 + static Future networkImageToBase64(String imageUrl) async { + http.Response response = await http.get(Uri.parse(imageUrl)); + final bytes = response.bodyBytes; + print("bytes===$bytes"); + return bytes != null ? base64Encode(bytes) : null; + } +} diff --git a/lib/custom/custom_swiper_pagination.dart b/lib/custom/custom_swiper_pagination.dart new file mode 100644 index 0000000..8fea54a --- /dev/null +++ b/lib/custom/custom_swiper_pagination.dart @@ -0,0 +1,103 @@ +import 'dart:developer'; + +import 'package:card_swiper/card_swiper.dart'; +import 'package:flutter/material.dart'; + +/// 自定义页面指示器 +class CustomSwiperPaginationBuilder extends SwiperPlugin { + // 当滚动到此时的颜色 + late Color? activeColor; + + // 默认颜色 + late Color? color; + + // 每个圆点的间距 + final double space; + + // 每个圆点的大小 + final double size; + + // 特殊点的宽度 + final double activeSize; + + final double bottom; + + final AlignmentGeometry? alignment; + + final Key? key; + + CustomSwiperPaginationBuilder( + {this.color = Colors.grey, + this.activeColor = Colors.blue, + this.space = 3.0, + this.size = 6.0, + this.activeSize = 20.0, + this.bottom = 0.0, + this.alignment = Alignment.center, + this.key}); + + @override + Widget build(BuildContext context, SwiperPluginConfig config) { + // 处理边界情况 + if (config.itemCount > 20) { + log( + 'The itemCount is too big, we suggest use FractionPaginationBuilder ' + 'instead of DotSwiperPaginationBuilder in this situation', + ); + } + + int activeIndex = config.activeIndex; + // 用于存放小圆点 + List list = []; + for (var i = 0; i < config.itemCount; ++i) { + if (activeIndex == i) { + list.add(Container( + key: Key('pagination_$i'), + margin: EdgeInsets.all(space), + child: PhysicalModel( + color: Colors.transparent, + borderRadius: BorderRadius.circular(10), + clipBehavior: Clip.antiAlias, + child: Container( + color: activeColor, + width: activeSize, + height: size, + ), + ))); + } else { + list.add(Container( + key: Key('pagination_$i'), + margin: EdgeInsets.all(space), + child: ClipOval( + // 圆角组件 + child: Container( + color: color, + width: size, + height: size, + ), + ), + )); + } + } + + return Stack( + clipBehavior: Clip.none, + children: [ + Positioned( + left: 15, + right: 15, + bottom: bottom, + child: Container( + alignment: alignment, + color: Colors.transparent, + child: Row( + key: key, + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: list, + ), + )) + ], + ); + } +} diff --git a/lib/dialog/delete_dialog.dart b/lib/dialog/delete_dialog.dart new file mode 100644 index 0000000..8335ca5 --- /dev/null +++ b/lib/dialog/delete_dialog.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; + +class DeleteDialog extends StatefulWidget { + Function onTap; + + DeleteDialog({required this.onTap}); + + @override + State createState() => _DeleteDialogState(); +} + +class _DeleteDialogState extends State { + @override + void initState() { + // TODO: implement initState + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Material( + type: MaterialType.transparency, //透明类型 + color: Color(0x1A000000), + child: Center( + child: ClipRRect( + borderRadius: BorderRadius.circular(10.0), + child: Container( + width: 280, + color: Colors.white, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + margin: EdgeInsets.only(top: 34), + child: Text('确认删除该聊天记录吗'), + ), + Container( + margin: EdgeInsets.only(top: 36, left: 22, right: 22, bottom: 19), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Container( + width: 108, + height: 43, + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(12)), + border: Border.all(color: Color(0xFFD9D9D9), width: 1), + ), + child: Text( + '取消', + style: TextStyle(color: Color(0xFFD9D9D9), fontSize: 12), + ), + ), + ), + GestureDetector( + onTap: () { + widget.onTap(1); + }, + child: Container( + width: 108, + height: 43, + alignment: Alignment.center, + decoration: BoxDecoration( + color: Color(0xFFD9D9D9), + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + child: Text( + '确定', + style: TextStyle(color: Colors.black, fontSize: 12), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..74a4c1a --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,73 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:talk/tools/home_page.dart'; +import 'package:talk/tools/login/login_page.dart'; +import 'package:talk/tools/me/setting_page.dart'; +import 'package:talk/tools/shop/account_page.dart'; +import 'package:talk/tools/shop/problem_page.dart'; +import 'package:talk/tools/shop/shop_page.dart'; +import 'package:talk/tools/shop/transaction_page.dart'; +import 'package:talk/tools/search/search_page.dart'; +import 'package:talk/tools/start_page.dart'; + +import 'common/Global.dart'; + +Future main() async { + await runZonedGuarded(() async { + WidgetsFlutterBinding.ensureInitialized(); + Global.initialize().then((e) { + Global(); + runApp(const ChatApp()); + if (Platform.isAndroid) { + // 以下两行 设置android状态栏为透明的沉浸。写在组件渲染之后,是为了在渲染后进行set赋值,覆盖状态栏,写在渲染之前MaterialApp组件会覆盖掉这个值。 + SystemUiOverlayStyle systemUiOverlayStyle = const SystemUiOverlayStyle(statusBarColor: Colors.transparent); + SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle); + } + }); + }, (error, stackTrace) { + print("error==$error"); + }); +} + +class ChatApp extends StatefulWidget { + const ChatApp({super.key}); + + @override + State createState() => _ChatAppState(); +} + +class _ChatAppState extends State { + @override + void initState() { + // TODO: implement initState + super.initState(); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'FondleTalk', + home: const StartPage(), + //注册路由 + routes: { + '/HomePage': (BuildContext context) => const HomePage(), + '/SearchPage': (BuildContext context) => const SearchPage(), + '/LoginPage': (BuildContext context) => const LoginPage(), + '/AccountPage': (BuildContext context) => const AccountPage(), + '/TransactionPage': (BuildContext context) => const TransactionPage(), + '/ProblemPage': (BuildContext context) => const ProblemPage(), + '/ShopPage': (BuildContext context) => const ShopPage(), + '/SettingPage': (BuildContext context) => const SettingPage(), + }, + debugShowMaterialGrid: false, + //显示网格 + debugShowCheckedModeBanner: false, + //去掉右上角的debug + builder: EasyLoading.init(), + ); + } +} diff --git a/lib/network/BaseEntity.dart b/lib/network/BaseEntity.dart new file mode 100644 index 0000000..2954f28 --- /dev/null +++ b/lib/network/BaseEntity.dart @@ -0,0 +1,43 @@ +import 'dart:convert'; + +class BaseEntity { + int? code; + int? result; + String? message; + dynamic data; + + // 构造函数 + BaseEntity({this.code, this.result, this.message, this.data}); + + // 数据解析 + factory BaseEntity.fromJson(json) { + dynamic data = json["content"][0]["text"]; + return BaseEntity(code: 0,data: data); + } + + + // 数据解析 + factory BaseEntity.PlayfromComfyUi(json) { + Map responseData = json; + String message = responseData["type"]; //错误描述 + dynamic data = responseData["name"]; + return BaseEntity(code: 0, result: 0, message: message, data: data); + } + + // 数据解析 + factory BaseEntity.PlayfromJson(json) { + Map responseData = json; + int code = responseData["code"]; + // int result = responseData["Result"]; + String message = responseData["message"]; //错误描述 + dynamic data = responseData["data"]; + return BaseEntity(code: code, result: 0, message: message, data: data); + } +} + +class ErrorEntity { + int? code; + String? message; + + ErrorEntity({this.code, this.message}); +} diff --git a/lib/network/DioLogInterceptor.dart b/lib/network/DioLogInterceptor.dart new file mode 100644 index 0000000..308488d --- /dev/null +++ b/lib/network/DioLogInterceptor.dart @@ -0,0 +1,134 @@ +import 'package:dio/dio.dart'; + +///日志拦截器 +class DioLogInterceptor extends Interceptor { + ///请求前 + @override + void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + String requestStr = "\n 请求 - URL:${/*options.baseUrl + */ options.uri}" + " 时间 - :${DateTime.now().millisecondsSinceEpoch}\n"; + //requestStr += "- HEADER:\n${options.headers.mapToStructureString()}\n"; + final data = options.data; + if (data != null) { + if (data is Map) + requestStr += "- BODY:\n${data.mapToStructureString()}\n"; + else if (data is FormData) { + final formDataMap = Map() + ..addEntries(data.fields) + ..addEntries(data.files); + requestStr += "- BODY:\n${formDataMap.mapToStructureString()}\n"; + } else + requestStr += "- BODY:\n${data.toString()}\n"; + } + print(requestStr); + + handler.next(options); + } + + ///出错前 + @override + void onError(DioError err, ErrorInterceptorHandler handler) { + String errorStr = "\n==================== 响应 ====================\n" + "- URL:\n${err.requestOptions.path}\n" + "- METHOD: ${err.requestOptions.method}\n"; + + /* errorStr += + "- HEADER:\n${err.response.headers.map.mapToStructureString()}\n";*/ + if (err.response != null && err.response!.data != null) { + print('╔ ${err.type.toString()}'); + errorStr += "- ERROR:\n${_parseResponse(err.response!)}\n"; + } else { + errorStr += "- ERRORTYPE: ${err.type}\n"; + errorStr += "- MSG: ${err.message}\n"; + } + print(errorStr); + + handler.next(err); + } + + ///响应前 + @override + void onResponse(Response response, ResponseInterceptorHandler handler) { + String responseStr = "\n 响应 - URL:${response.requestOptions.uri}" + " 时间 - :${DateTime.now().millisecondsSinceEpoch}\n"; + + /* responseStr += "- HEADER:\n{"; + response.headers.forEach( + (key, list) => responseStr += "\n " + "\"$key\" : \"$list\","); + responseStr += "\n}\n";*/ + //responseStr += "- STATUS: ${response.statusCode}\n"; + + if (response.data != null) { + responseStr += "- BODY:\n ${_parseResponse(response)}"; + } + printWrapped(responseStr); + + handler.next(response); + } + + void printWrapped(String text) { + final pattern = new RegExp('.{1,800}'); // 800 is the size of each chunk + pattern.allMatches(text).forEach((match) => print(match.group(0))); + } + + String _parseResponse(Response response) { + String responseStr = ""; + var data = response.data; + if (data is Map) + responseStr += data.mapToStructureString(); + else if (data is List) + responseStr += data.listToStructureString(); + else + responseStr += response.data.toString(); + + return responseStr; + } +} + +///Map拓展,MAp转字符串输出 +extension Map2StringEx on Map { + String mapToStructureString({int indentation = 2}) { + String result = ""; + String indentationStr = " " * indentation; + if (true) { + result += " {"; + this.forEach((key, value) { + if (value is Map) { + var temp = value.mapToStructureString(indentation: indentation + 2); + result += "$indentationStr" + "\"$key\":$temp,"; + } else if (value is List) { + result += "$indentationStr" + "\"$key\":${value.listToStructureString(indentation: indentation + 2)},"; + } else { + result += "$indentationStr" + "\"$key\":\"$value\","; + } + }); + result = result.substring(0, result.length - 1); + result += indentation == 2 ? "}" : "\n${" " * (indentation - 1)}}"; + } + + return result; + } +} + +///List拓展,List转字符串输出 +extension List2StringEx on List { + String listToStructureString({int indentation = 2}) { + String result = ""; + String indentationStr = " " * indentation; + if (true) { + result += "$indentationStr["; + this.forEach((value) { + if (value is Map) { + var temp = value.mapToStructureString(indentation: indentation + 2); + result += "\n$indentationStr" + "\"$temp\","; + } else if (value is List) { + result += value.listToStructureString(indentation: indentation + 2); + } else { + result += "\n$indentationStr" + "\"$value\","; + } + }); + result = result.substring(0, result.length - 1); + result += "\n$indentationStr]"; + } + + return result; + } +} diff --git a/lib/network/NetworkConfig.dart b/lib/network/NetworkConfig.dart new file mode 100644 index 0000000..eaa03df --- /dev/null +++ b/lib/network/NetworkConfig.dart @@ -0,0 +1,43 @@ +class NetworkConfig { + static String ServerDomain_Online = BASE_URLS[SELECT_INDEX]; + static String deviceID = ""; //设备ID + static String systemVersion = ""; //设备ID + + /// 选择哪个域名做请求 + static int SELECT_INDEX = 0; + + static List BASE_URLS = [ + "http://101.43.19.200:90/", + "http://101.43.19.200:90/", + ]; + + static List BASE_URLS_AI = [ + "http://101.43.19.200:90/", + ]; + + static bool isTest = true; + + static String token = ""; + static String userToken = ""; //用户登录验签 + static String AppId = "1"; + static String BossId = ""; + static String userId = ""; + static String userName = ""; + static String Version = "1.0.0"; + static String Language = "en"; + + static const String accountLogin = "api/Account/AccountLogIn"; //登录 + static const String sendPhoneNumber = "api/Account/SendPhoneNumber"; //获取验证码 + + static const String getCharacterIdList = "api/Chat/GetCharacterIdList"; //获取首页展示的人物ID列表 + + static const String getCharacterInfo = "api/chat/getCharacterInfo"; //获取人物信息 + + static const String getChatInfo = "api/Chat/GetChatInfo"; //获取聊天内容 + + static const String chat = "/v1/chat/completions"; //聊天 + + static const String uploadImg = "/upload/image"; //审核模式上传图片 + + static const String prompt = "/prompt"; //审核模式上传图片 +} diff --git a/lib/network/RequestCenter.dart b/lib/network/RequestCenter.dart new file mode 100644 index 0000000..2d48b73 --- /dev/null +++ b/lib/network/RequestCenter.dart @@ -0,0 +1,457 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:crypto/crypto.dart'; +import 'package:dio/dio.dart'; + +import 'BaseEntity.dart'; +import 'DioLogInterceptor.dart'; +import 'NetworkConfig.dart'; + +enum RequestMethod { + Post, + Get, +} + +const RequestMethodValues = { + RequestMethod.Get: "GET", + RequestMethod.Post: "POST", +}; + +class RequestCenter { + factory RequestCenter() => _getInstance(); + + static RequestCenter get instance => _getInstance(); + static RequestCenter? _instance; + + static String signKey = "5159d59637fe1b79ba789e87443e8282"; + + Dio? _dio, _dioLog; + + final dio = Dio(); + + void setupDio() { + dio.options.baseUrl = 'https://openapi.shhuanmeng.com/'; + dio.interceptors.add(InterceptorsWrapper( + onRequest: (options, handler) { + // 可以在这里添加其他请求配置 + return handler.next(options); + }, + onResponse: (response, handler) { + // 处理响应 + return handler.next(response); + }, + onError: (DioError e, handler) { + // 处理错误 + return handler.next(e); + }, + )); + } + + RequestCenter._internal() { + setup(); + setupDio(); + } + + static RequestCenter _getInstance() { + _instance ??= RequestCenter._internal(); + //域名不一致,重新初始化 + if (_instance!._dio != null && + NetworkConfig.ServerDomain_Online != _instance!._dio!.options.baseUrl) { + _instance!._dio!.options.baseUrl = NetworkConfig.ServerDomain_Online; + } + return _instance!; + } + + void setup() { + // 初始化 + if (null == _dio) { + _dio = Dio(BaseOptions( + baseUrl: NetworkConfig.ServerDomain_Online, + sendTimeout: const Duration(seconds: 10), + receiveTimeout: const Duration(seconds: 20), + connectTimeout: const Duration(seconds: 20), + contentType: Headers.jsonContentType, + responseType: ResponseType.json)); + _dio!.interceptors.add(DioLogInterceptor()); + /* _dio.interceptors.add(new ResponseInterceptors());*/ + _dioLog = Dio(BaseOptions( + sendTimeout: const Duration(seconds: 10), + receiveTimeout: const Duration(seconds: 20), + connectTimeout: const Duration(seconds: 20), + contentType: Headers.jsonContentType, + responseType: ResponseType.json)); + //_dioLog!.interceptors.add(DioLogInterceptor()); + } + } + + // 网络请求默认为post + Future request( + path, + Map parmeters, + Function(BaseEntity dataEntity) success, + Function(ErrorEntity errorEntity) error, + {RequestMethod? method}) async { + try { + //FormData formData = FormData.fromMap(parmeters); + Response response = await _dio!.post(path, data: parmeters); + BaseEntity entity = BaseEntity.PlayfromJson(response.data); + success(entity); + return entity; + } catch (e) { + error(ErrorEntity(code: -1, message: "$e")); + return null; + } + } + + Future requestGet( + path, + Map parmeters, + Function(BaseEntity dataEntity) success, + Function(ErrorEntity errorEntity) error, + {RequestMethod? method}) async { + try { + //FormData formData = FormData.fromMap(parmeters); + Response response = await _dio!.get(path, queryParameters: parmeters); + BaseEntity entity = BaseEntity.PlayfromJson(response.data); + success(entity); + return entity; + } catch (e) { + error(ErrorEntity(code: -1, message: "$e")); + return null; + } + } + + Future sendRequest( + Map parmeters, + Function(BaseEntity dataEntity) success, + Function(ErrorEntity errorEntity) error, + ) async { + try { + print('Request: $parmeters'); + final response = await dio.post('/messages', + data: parmeters, + options: Options( + contentType: Headers.jsonContentType, + responseType: ResponseType.json, + headers: { + 'x-api-key': + 'sk-V6d51cc6aa28906caecb7f22803d92ae3f18cfeb799nh4mc', + 'anthropic-version': "2023-06-01" + }, + )); + print('Response: ${response.data}'); + BaseEntity entity = BaseEntity.fromJson(response.data); + success(entity); + return entity; + } on DioError catch (e) { + print('Error: ${e.response?.data}'); + } + } + + + + //特殊处理网络请求默认为post + Future request1( + path, + Map parmeters, + Function(BaseEntity dataEntity) success, + Function(ErrorEntity errorEntity) error, + {RequestMethod? method}) async { + Map headers = { + "AppId": NetworkConfig.AppId, + "userId": NetworkConfig.userId, + "Version": NetworkConfig.Version, + "Language": NetworkConfig.Language + }; + parmeters.addAll(headers); + Map parmetersSign = sign(parmeters); + try { + /*由于服务器是表单结构*/ + FormData formData = FormData.fromMap(parmetersSign); + Response response = await _dio!.post(path, data: formData); + if (response != null && response.statusCode == 200) { + BaseEntity entity = BaseEntity.fromJson(response.data); + success(entity); + return entity; + } else { + error(ErrorEntity(code: -1, message: "Network Anomaly")); + return null; + } + } catch (e) { + error(ErrorEntity(code: -1, message: "Network Anomaly")); + return null; + } + } + + Future requestLog( + path, + Map parmeters, + Function(BaseEntity dataEntity) success, + Function(ErrorEntity errorEntity) error, + {RequestMethod? method}) async { + Map headers = { + "AppId": NetworkConfig.AppId, + /*"BossId": NetworkConfig.BossId,*/ + "userId": NetworkConfig.userId, + "Version": NetworkConfig.Version, + "Language": NetworkConfig.Language + }; + parmeters.addAll(headers); + Map parmetersSign = sign(parmeters); + try { + /*由于服务器是表单结构*/ + FormData formData = FormData.fromMap(parmetersSign); + Response response = await _dioLog!.post(path, data: formData); + if (response != null && response.statusCode == 200) { + BaseEntity entity = BaseEntity.fromJson(response.data); + success(entity); + return entity; + } else { + error(ErrorEntity(code: -1, message: "Network Anomaly")); + return null; + } + } catch (e) { + error(ErrorEntity(code: -1, message: "Network Anomaly")); + return null; + } + } + + // 网络请求默认为post + Future requestPay( + path, + Map parmeters, + Function(BaseEntity dataEntity) success, + Function(ErrorEntity errorEntity) error, + {RequestMethod? method}) async { + Map headers = { + "AppId": NetworkConfig.AppId, + /*"BossId": NetworkConfig.BossId,*/ + "userId": NetworkConfig.userId, + "Version": NetworkConfig.Version, + "Language": NetworkConfig.Language + }; + parmeters.addAll(headers); + Map parmetersSign = sign(parmeters); + try { + FormData formData = FormData.fromMap(parmetersSign); + Response response = await _dio!.post(path, data: formData); + if (response != null && response.statusCode == 200) { + BaseEntity entity = BaseEntity.fromJson(response.data); + success(entity); + return entity; + } else { + error(ErrorEntity(code: -1, message: "未知错误")); + return null; + } + } catch (e) { + ErrorEntity(code: -1, message: "未知错误"); + return null; + } + } + + // 网络请求默认为post + Future requestComfyUI1( + formData, + Function(BaseEntity dataEntity) success, + Function(ErrorEntity errorEntity) error, + {RequestMethod? method}) async { + try { + var response = await _dio!.post( + "http://openapi.shhuanmeng.com${NetworkConfig.uploadImg}", + data: formData, + options: Options( + contentType: Headers.jsonContentType, + responseType: ResponseType.json, + headers: { + 'Content-Type': 'multipart/form-data', + }, + ), + ); + if (response.statusCode == 200) { + BaseEntity entity = BaseEntity.PlayfromComfyUi(response.data); + success(entity); + return entity; + } + } catch (e) { + print("e" + e.toString()); + //error(ErrorEntity(code: -1, message: "Network Anomaly")); + return null; + } + } + + Future requestComfyUI2( + formData, + Function(BaseEntity dataEntity) success, + Function(ErrorEntity errorEntity) error, + {RequestMethod? method}) async { + try { + var response = await _dio!.post( + "http://openapi.shhuanmeng.com${NetworkConfig.prompt}", + data: formData, + options: Options( + contentType: Headers.jsonContentType, + responseType: ResponseType.json, + ), + ); + if (response.statusCode == 200) { + BaseEntity entity = BaseEntity.PlayfromComfyUi(response.data); + success(entity); + return entity; + } + } catch (e) { + print("e" + e.toString()); + //error(ErrorEntity(code: -1, message: "Network Anomaly")); + return null; + } + } + + // 网络请求默认为post (网页) + Future requestWeb( + path, + Map parmeters, + Function(BaseEntity dataEntity) success, + Function(ErrorEntity errorEntity) error, + {RequestMethod? method}) async { + Map headers = { + "AppId": NetworkConfig.AppId, + /*"BossId": NetworkConfig.BossId,*/ + "UserId": NetworkConfig.userId, + "Version": NetworkConfig.Version, + "Language": NetworkConfig.Language + }; + parmeters.addAll(headers); + //签名加密 + Map parmetersSign = sign(parmeters); + try { + /*由于服务器是表单结构*/ + FormData formData = FormData.fromMap(parmetersSign); + Response response = await _dio!.post(path, data: formData); + if (response != null && response.statusCode == 200) { + BaseEntity entity = BaseEntity.fromJson(response.data); + Map parmeters = { + "AppId": NetworkConfig.AppId, + /*"BossId": NetworkConfig.BossId,*/ + "UserId": NetworkConfig.userId, + "Token": entity.message + }; + Map p1 = sign(parmeters); + parmeters.addAll(p1); + //data 信息添加用户信息 + entity.data = gettoken(parmeters); + success(entity); + + return entity; + } else { + error(ErrorEntity(code: -1, message: "Network Anomaly")); + return null; + } + } catch (e) { + ErrorEntity(code: -1, message: "Network Anomaly"); + return null; + } + } + + // 捕获异常的错误信息 + ErrorEntity _getErrorMsg(DioError error) { + switch (error.type) { + case DioErrorType.cancel: + { + return ErrorEntity(code: -1, message: "请求取消"); + } + break; + case DioErrorType.connectionTimeout: + { + return ErrorEntity(code: -1, message: "连接超时"); + } + break; + case DioErrorType.sendTimeout: + { + return ErrorEntity(code: -1, message: "请求超时"); + } + break; + case DioErrorType.receiveTimeout: + { + return ErrorEntity(code: -1, message: "响应超时"); + } + break; + case DioErrorType.badResponse: + { + try { + int errCode = error.response!.statusCode!; + String errorMsg = error.response!.statusMessage!; + return ErrorEntity(code: errCode, message: errorMsg); + } on Exception catch (_) { + return ErrorEntity(code: -1, message: "Network Anomaly"); + } + } + break; + default: + { + return ErrorEntity(code: -1, message: "Network Anomaly"); + } + } + } + + Map sign(Map parmeters) { + List keys = parmeters.keys.toList(); + // key排序 + keys.sort((a, b) { + List al = a.codeUnits; + List bl = b.codeUnits; + for (int i = 0; i < al.length; i++) { + if (bl.length <= i) return 1; + if (al[i] > bl[i]) { + return 1; + } else if (al[i] < bl[i]) return -1; + } + return 0; + }); + Map treeMap = Map(); + keys.forEach((element) { + treeMap[element] = parmeters[element]; + }); + String data = ""; + for (dynamic str in treeMap.values) { + if (str != null) { + data = data + str.toString(); + } + } + treeMap["sign"] = generateMd5(data + NetworkConfig.userToken); + return treeMap; + } + + String gettoken(Map parmeters) { + List keys = parmeters.keys.toList(); + // key排序 + keys.sort((a, b) { + List al = a.codeUnits; + List bl = b.codeUnits; + for (int i = 0; i < al.length; i++) { + if (bl.length <= i) return 1; + if (al[i] > bl[i]) { + return 1; + } else if (al[i] < bl[i]) return -1; + } + return 0; + }); + Map treeMap = Map(); + keys.forEach((element) { + treeMap[element] = parmeters[element]; + }); + String data = ""; + treeMap.forEach((key, value) { + if (data == "") { + data = key + "=" + value.toString(); + } else { + data = data + "&" + key + "=" + value.toString(); + } + }); + return data; + } + + String generateMd5(String data) { + return md5.convert(utf8.encode(data)).toString(); + } +} diff --git a/lib/tools/chat/chat_info_page.dart b/lib/tools/chat/chat_info_page.dart new file mode 100644 index 0000000..adf0155 --- /dev/null +++ b/lib/tools/chat/chat_info_page.dart @@ -0,0 +1,234 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; + +import '../../beans/character_info_bean.dart'; + +///人物详情页 +class ChatInfoPage extends StatefulWidget { + CharacterInfoBean data; + + ChatInfoPage({required this.data}); + + @override + State createState() => _ChatInfoPageState(); +} + +class _ChatInfoPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + children: [ + CachedNetworkImage( + fit: BoxFit.fitHeight, + imageUrl: widget.data.bgUrl!, + errorWidget: (context, url, error) => const Icon(Icons.error), + ), + NestedScrollView( + headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { + return [ + SliverOverlapAbsorber( + handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), + sliver: SliverAppBar( + backgroundColor: const Color(0x00FFFFFF), + automaticallyImplyLeading: false, + elevation: 0.0, + forceElevated: false, + centerTitle: true, + floating: false, + expandedHeight: 260, + pinned: true, + title: Row( + children: [ + Container( + padding: const EdgeInsets.all(10), + child: GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: const Image( + width: 24, + height: 24, + image: AssetImage('assets/images/ic_back1.png'), + ), + ), + ) + ], + ), + // flexibleSpace: FlexibleSpaceBar( + // background: Container( + // child: Column( + // children: [ + // Container( + // height: 260, + // ), + // Container( + // width: MediaQuery.of(context).size.width, + // height: 114, + // color: Color(0x331F1F1F), + // child: Image( + // width: 48, + // height: 48, + // image: AssetImage('assets/images/img_head2.png'), + // ), + // ), + // ], + // ), + // ), + // ), + ), + ), + ]; + + // SliverPersistentHeader(delegate: null,); + }, + body: Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + colors: [Color(0x001f1f1f), Color(0xFF000000), Color(0xFF000000)], // 三色渐变数组 + begin: Alignment.topCenter, // 渐变开始位置 + end: Alignment.bottomCenter, // 渐变结束位置 + ), + ), + child: SingleChildScrollView( + child: Column( + children: [ + Container( + margin: EdgeInsets.only(top: 114), + ), + SizedBox( + width: MediaQuery.of(context).size.width, + height: 80, + child: Stack( + alignment: Alignment.center, + children: [ + Positioned( + left: 16, + child: Row( + children: [ + ///名字 + Text( + widget.data.characterName!, + style: TextStyle(fontSize: 18, color: Colors.white), + ), + + ///性别 + Container( + margin: EdgeInsets.only(left: 10), + child: Image( + width: 13, + image: widget.data.gender == 1 + ? const AssetImage('assets/images/ic_woman.png') + : const AssetImage('assets/images/ic_man.png'), + ), + ) + ], + ), + ), + Positioned( + left: 16, + bottom: 10, + child: Text( + 'ID ${widget.data.characterId}', + style: const TextStyle(fontSize: 10, color: Color(0xFFC2C2C2)), + ), + ), + ], + ), + ), + Container( + alignment: Alignment.centerLeft, + margin: const EdgeInsets.only(left: 16, top: 20), + child: const Text( + '关于 TA', + style: TextStyle(color: Colors.white, fontSize: 15), + ), + ), + Container( + width: MediaQuery.of(context).size.width, + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 24), + padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 19), + decoration: const BoxDecoration( + color: Color(0x992A2A2A), + borderRadius: BorderRadius.all(Radius.circular(7)), + ), + child: Column( + children: [ + Container( + alignment: Alignment.centerLeft, + child: const Text( + '简介', + style: TextStyle(color: Colors.white, fontSize: 14), + ), + ), + Container( + alignment: Alignment.centerLeft, + margin: const EdgeInsets.only(top: 9), + child: Text( + widget.data.biography!, + style: const TextStyle(color: Color(0xFF979797), fontSize: 13), + ), + ), + Container( + alignment: Alignment.centerLeft, + margin: const EdgeInsets.only(top: 17), + child: const Text( + '开场白', + style: TextStyle(color: Colors.white, fontSize: 14), + ), + ), + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(top: 9), + child: Text( + widget.data.prologue!, + style: TextStyle(color: Color(0xFF979797), fontSize: 13), + ), + ), + Container( + alignment: Alignment.centerLeft, + margin: const EdgeInsets.only(top: 17), + child: const Text( + '标签', + style: TextStyle(color: Colors.white, fontSize: 14), + ), + ), + Container( + alignment: Alignment.centerLeft, + margin: const EdgeInsets.only(top: 12), + child: Wrap( + spacing: 5.0, + // gap between adjacent boxes + runSpacing: 8.0, + // vertical gap + alignment: WrapAlignment.start, + crossAxisAlignment: WrapCrossAlignment.center, + direction: Axis.horizontal, + children: [ + for (int i = 0; i < widget.data.label!.length; i++) + Container( + padding: const EdgeInsets.symmetric(horizontal: 11, vertical: 2), + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(10)), + border: Border.all(color: const Color(0xFFFF9000), width: 0.33)), + child: Text( + widget.data.label![i].name.toString(), + style: TextStyle(fontSize: 10, color: Color(0xFFFF9000)), + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/tools/chat/chat_model.dart b/lib/tools/chat/chat_model.dart new file mode 100644 index 0000000..3d5f912 --- /dev/null +++ b/lib/tools/chat/chat_model.dart @@ -0,0 +1,64 @@ +import 'dart:async'; + +import 'package:talk/network/NetworkConfig.dart'; +import 'package:talk/network/RequestCenter.dart'; + +import '../../beans/character_info_bean.dart'; +import '../../beans/home_character_bean.dart'; +import '../../network/BaseEntity.dart'; + +class ChatModel { + StreamController streamController = StreamController.broadcast(); + + ChatModel() { + setUp(); + } + + void setUp() {} + + ///获取人物信息 + Future getCharacterInfo(characterId) async { + RequestCenter.instance.requestGet(NetworkConfig.getCharacterInfo, {"characterId": characterId}, (BaseEntity dataEntity) { + print("dataEntity==$dataEntity"); + + if (dataEntity.code == 0) { + CharacterInfoBean characterInfoBean = CharacterInfoBean.fromJson(dataEntity.data); + + streamController.sink.add({ + 'code': "getCharacterInfo", //有数据 + 'data': characterInfoBean + }); + } else { + streamController.sink.add({ + 'code': "-1", //有数据 + 'data': dataEntity.message + }); + } + }, (ErrorEntity errorEntity) { + print("errorEntity==${errorEntity.message}"); + }); + } + + ///获取聊天内容 + Future getChatInfo(characterId) async { + RequestCenter.instance.requestGet(NetworkConfig.getChatInfo, {"characterId": characterId}, (BaseEntity dataEntity) { + print("dataEntity==$dataEntity"); + + if (dataEntity.code == 0) { + CharacterInfoBean characterInfoBean = CharacterInfoBean.fromJson(dataEntity.data); + + streamController.sink.add({ + 'code': "getChatInfo", //有数据 + 'data': characterInfoBean + }); + } else { + streamController.sink.add({ + 'code': "-1", //有数据 + 'data': dataEntity.message + }); + } + }, (ErrorEntity errorEntity) { + print("errorEntity==${errorEntity.message}"); + }); + } +} diff --git a/lib/tools/chat/chat_page.dart b/lib/tools/chat/chat_page.dart new file mode 100644 index 0000000..cdd7f8e --- /dev/null +++ b/lib/tools/chat/chat_page.dart @@ -0,0 +1,521 @@ +import 'package:expandable_text/expandable_text.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; + +import '../../beans/MessageBean.dart'; + +class ChatPage extends StatefulWidget { + String id; + + ChatPage({super.key, required this.id}); + + @override + State createState() => _ChatPageState(); +} + +class _ChatPageState extends State { + final TextEditingController _chatController = TextEditingController(); + final ScrollController _scrollController = ScrollController(); + List chatList = []; + + String text = ""; + bool isHalf = false; + bool isMore = false; + + void _textFieldChanged(String str) { + text = str; + setState(() {}); + } + + @override + void initState() { + // TODO: implement initState + super.initState(); + chatList = [MessageBean("user", "你好"), MessageBean("assistant", "你好"), MessageBean("time", "8:00AM"), MessageBean("user", "你好")]; + } + + @override + void dispose() { + // TODO: implement dispose + _chatController.dispose(); + _scrollController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Color(0xFFE8EEFC), + body: Stack( + children: [ + Image(image: AssetImage('assets/images/bj.png')), + Positioned( + bottom: 0, + child: Container( + width: MediaQuery.of(context).size.width, + height: 311.67, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [Color(0x00000000), Color(0xFF0C0909)], // 三色渐变数组 + begin: Alignment.topCenter, // 渐变开始位置 + end: Alignment.bottomCenter, // 渐变结束位置 + ), + ), + )), + Container( + child: Column( + children: [ + Container( + width: MediaQuery.of(context).size.width, + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top + 9, left: 16, right: 16), + child: GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Image( + height: 23, + image: AssetImage('assets/images/ic_left_arrow.png'), + ), + ), + ), + + ///title + Container( + width: double.infinity, + height: 30, + margin: EdgeInsets.only(top: 10, left: 16, right: 16), + child: Stack( + alignment: Alignment.center, + children: [ + Positioned( + left: 105, + child: Text( + "+1", + style: TextStyle(color: Color(0xFFF14476), fontSize: 10), + )), + Positioned( + left: 0, + child: Container( + width: 101, + height: 30, + decoration: BoxDecoration(color: Color(0x33000000), borderRadius: BorderRadius.all(Radius.circular(14))), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ///AI头像 + GestureDetector( + onTap: () { + // Navigator.push( + // context, + // MaterialPageRoute(builder: (context) => ChatInfoPage()), + // ); + }, + child: Container( + margin: EdgeInsets.only(left: 2), + child: Image( + width: 23, + image: AssetImage('assets/images/img_head2.png'), + ), + ), + ), + + ///AI名 + Container( + margin: EdgeInsets.only(left: 5), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: 35, + child: Text( + '柳如烟', + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 10, color: Colors.white), + ), + ), + SizedBox( + width: 35, + child: Text( + '1.2K 聊过', + style: TextStyle(fontSize: 7, color: Colors.white), + ), + ), + ], + ), + ), + + ///心动值 + Container( + margin: EdgeInsets.only(left: 6, right: 6), + child: GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ChatPage( + id: '123', + )), + ); + }, + child: Stack( + alignment: Alignment.center, + children: [ + Image( + width: 24, + height: 21, + image: AssetImage('assets/images/ic_beckoning.png'), + ), + Text( + '10', + style: TextStyle(fontSize: 8, color: Colors.white), + ) + ], + ), + ), + ), + ], + ), + ), + ), + + ///关注 + Positioned( + right: 0, + child: GestureDetector( + onTap: () { + isHalf = !isHalf; + setState(() {}); + }, + child: Container( + width: 50, + height: 24, + alignment: Alignment.center, + decoration: BoxDecoration(color: Color(0x33000000), borderRadius: BorderRadius.all(Radius.circular(12))), + child: Text( + '+ 关注', + style: TextStyle(fontSize: 12, color: Colors.white), + ), + ), + ), + ), + ], + ), + ), + isHalf + ? Container( + height: MediaQuery.of(context).size.height / 3.5, + ) + : Container(), + + ///聊天列表 + Expanded( + child: ListView.builder( + controller: _scrollController, + itemCount: chatList.length, + itemBuilder: (BuildContext context, int index) { + return _item(index); + })), + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: 16, bottom: 3), + child: Image(width: 63, height: 18, image: AssetImage('assets/images/ic_memory.png')), + ), + + ///输入 功能 + Container( + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.only(left: 16, right: 16, bottom: 20), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ///输入框 + Expanded( + child: Container( + padding: const EdgeInsets.symmetric(vertical: 5), + decoration: BoxDecoration( + color: Color(0x33FFFFFF), + borderRadius: BorderRadius.all(Radius.circular(20)), + ), + child: Row( + children: [ + Expanded( + child: Container( + padding: const EdgeInsets.only(left: 15), + child: TextField( + controller: _chatController, + onChanged: _textFieldChanged, + maxLines: null, + cursorColor: const Color(0xFFFF9000), + decoration: const InputDecoration( + border: InputBorder.none, + // 移除非聚焦状态下的边框 + enabledBorder: InputBorder.none, + // 移除获得焦点但未输入内容时的边框 + focusedBorder: InputBorder.none, + isCollapsed: true, + // 移除输入时的边框 + hintText: "打个招呼吧...", + hintStyle: TextStyle(color: Color(0xFFB6B6B6), fontSize: 13), + ), + style: const TextStyle(color: Colors.white), + ), + ), + ), + Container( + margin: const EdgeInsets.only(right: 7), + child: text == "" + ? const Image( + width: 27, + image: AssetImage('assets/images/ic_send_n.png'), + ) + : GestureDetector( + onTap: () { + chatList.add(MessageBean("user", text)); + _chatController.clear(); + text = ""; + Future.delayed(Duration(milliseconds: 200), () { + _scrollController.jumpTo(_scrollController.position.maxScrollExtent); + }); + setState(() {}); + }, + child: Image( + width: 27, + image: AssetImage('assets/images/ic_send.png'), + ), + ), + ) + ], + ), + )), + + ///智能输入 + Container( + margin: const EdgeInsets.only(left: 14, right: 14), + child: GestureDetector( + onTap: () { + Navigator.of(context).pushNamed('/LoginPage'); + }, + child: Image( + width: 27, + image: AssetImage('assets/images/ic_smart_chat.png'), + ), + ), + ), + + ///功能 更多 + GestureDetector( + onTap: () { + isMore = !isMore; + setState(() {}); + }, + child: Image( + width: 27, + image: AssetImage('assets/images/ic_more.png'), + ), + ), + ], + ), + ), + + isMore + ? Container( + height: 50, + alignment: Alignment.centerLeft, + margin: const EdgeInsets.only(left: 16, right: 16, bottom: 20), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + margin: EdgeInsets.only(left: 23), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Image(width: 26, image: AssetImage('assets/images/ic_album.png')), + Container( + margin: EdgeInsets.only(top: 9), + child: Text( + '角色相册', + style: TextStyle(fontSize: 10, color: Color(0xFFA2A2A2)), + ), + ), + ], + ), + ), + Container( + margin: EdgeInsets.only(left: 23), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Image(width: 26, image: AssetImage('assets/images/ic_more.png')), + Container( + margin: EdgeInsets.only(top: 9), + child: Text( + '重启对话', + style: TextStyle(fontSize: 10, color: Color(0xFFA2A2A2)), + ), + ), + ], + ), + ), + ], + ), + ) + : Container(), + ], + ), + ), + + ///记忆提升中 + Positioned( + top: 100, + left: 16, + child: Image( + width: 47, + height: 12, + image: AssetImage('assets/images/ic_memory_p.png'), + )), + ], + ), + ); + } + + ///聊天条目 + _item(index) { + if (index == 0) { + return Container(); + } + + ///简介 + if (chatList[index].role == 'introduce') { + return Center( + child: Container( + margin: EdgeInsets.only(left: 16, right: 16), + padding: EdgeInsets.only(left: 20, right: 20, top: 12, bottom: 12), + decoration: BoxDecoration(color: Color(0x99000000), borderRadius: BorderRadius.all(Radius.circular(13))), + child: ExpandableText( + chatList[index].content!, + expandText: '更多', + collapseText: '关闭', + maxLines: 3, + linkColor: Color(0xFFFF9000), + style: TextStyle(fontSize: 13, color: Colors.white), + ), + ), + ); + } + + ///时间 + // if (chatList[index].role == 'time') { + // return Center( + // child: Container( + // padding: EdgeInsets.only(left: 9, right: 9, top: 6, bottom: 6), + // decoration: BoxDecoration(color: Color(0x99FFFFFF), borderRadius: BorderRadius.all(Radius.circular(8))), + // child: Text(chatList[index].content!, style: TextStyle(color: Color(0xFFA7AFD9), fontSize: 10)), + // ), + // ); + // } + + ///聊天内容 + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: chatList[index].role != 'user' + ? Container( + ///AI + alignment: Alignment.centerLeft, + child: Stack( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + ConstrainedBox( + constraints: BoxConstraints( + maxWidth: MediaQuery.of(context).size.width - 50, // 确保不超过屏幕宽度 + ), + child: Container( + margin: const EdgeInsets.symmetric(vertical: 4, horizontal: 16), + padding: const EdgeInsets.all(11.0), + decoration: const BoxDecoration( + color: Color(0xFFFF9000), + borderRadius: BorderRadius.only( + topRight: Radius.circular(16.0), bottomLeft: Radius.circular(16.0), bottomRight: Radius.circular(16.0)), + ), + child: GestureDetector( + onTap: () { + EasyLoading.showToast("status"); + }, + child: SelectableText( + chatList[index].content!, + style: const TextStyle( + fontSize: 16, + color: Colors.black, + ), + ), + ), + ), + ), + ], + ), + // Container( + // width: 60, + // height: 27, + // margin: EdgeInsets.only(left: 16), + // alignment: Alignment.center, + // decoration: BoxDecoration( + // color: Color(0xFFF14476), + // borderRadius: BorderRadius.only( + // topLeft: Radius.circular(4), + // topRight: Radius.circular(13), + // bottomRight: Radius.circular(13), + // bottomLeft: Radius.circular(13), + // ), + // ), + // child: Row( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Image(width: 8, height: 13, image: AssetImage('assets/images/ic_voice.png')), + // Container( + // margin: EdgeInsets.only(left: 8), + // child: Text( + // '60s', + // style: TextStyle(fontSize: 13, color: Colors.white), + // ), + // ), + // ], + // ), + // ), + ], + ), + ) + : Row( + ///用户 + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ConstrainedBox( + constraints: BoxConstraints( + maxWidth: MediaQuery.of(context).size.width - 50, // 确保不超过屏幕宽度 + ), + child: Container( + margin: const EdgeInsets.symmetric(vertical: 4, horizontal: 16), + padding: const EdgeInsets.all(11.0), + decoration: const BoxDecoration( + color: Color(0x99252525), + borderRadius: + BorderRadius.only(topLeft: Radius.circular(16.0), bottomLeft: Radius.circular(16.0), bottomRight: Radius.circular(16.0)), + ), + child: Text( + chatList[index].content!, + style: const TextStyle( + fontSize: 16, + color: Colors.white, + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/tools/find/find_page.dart b/lib/tools/find/find_page.dart new file mode 100644 index 0000000..710711b --- /dev/null +++ b/lib/tools/find/find_page.dart @@ -0,0 +1,147 @@ +import 'package:flutter/material.dart'; +import 'package:talk/tools/find/recommend_page.dart'; + +import 'multiplex_page.dart'; + +///发现 +class FindPage extends StatefulWidget { + const FindPage({super.key}); + + @override + State createState() => _FindPageState(); +} + +class _FindPageState extends State { + final PageController _pageController = PageController(); + int currentIndex = 0; + + @override + void initState() { + // TODO: implement initState + super.initState(); + } + + @override + void dispose() { + // TODO: implement dispose + _pageController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Color(0xFF121213), + body: Stack( + children: [ + PageView.builder( + controller: _pageController, + itemCount: 10, // 假设有5个页面,其中第一个是固定的 + itemBuilder: (context, index) { + if (index == 0) { + // 第一个页面是固定的 + return RecommendPage(); + } else { + // 其他页面是动态生成的 + return MultiplexPage(); + } + }, + physics: NeverScrollableScrollPhysics(), + ), + + ///title + Container( + padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top + 9, left: 16, right: 16), + // width: MediaQuery.of(context).size.width, + color: Color(0xFF18181A), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ///展开分类 + // Image( + // width: 16, + // height: 15, + // image: AssetImage('assets/images/ic_type.png'), + // ), + + ///tab + Expanded( + child: Container( + height: 30, + child: ListView.builder( + itemCount: 10, + itemBuilder: (BuildContext context, int index) { + return _item(index); + }, + scrollDirection: Axis.horizontal, + ), + ), + ), + + ///搜索 + // Container( + // width: 72, + // height: 27, + // decoration: BoxDecoration( + // color: Color(0x1AFFFFFF), + // borderRadius: BorderRadius.all(Radius.circular(14)), + // border: Border.all(color: Color(0xFF777777), width: 0.33)), + // child: Row( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Text( + // '搜索', + // style: TextStyle(fontSize: 12, color: Color(0xFFAEAEAE)), + // ), + // Container( + // margin: EdgeInsets.only(left: 5), + // child: Image( + // width: 16, + // image: AssetImage('assets/images/ic_search2.png'), + // ), + // ) + // ], + // ), + // ), + ], + ), + ), + ], + ), + ); + } + + _item(index) { + return Container( + margin: EdgeInsets.only(left: 5, right: 12), + child: Column( + children: [ + GestureDetector( + onTap: () { + currentIndex = index; + _pageController.animateToPage( + index, + duration: const Duration(milliseconds: 500), // 动画时长 + curve: Curves.ease, // 动画曲线 + ); + setState(() {}); + }, + child: Text( + "推荐", + style: TextStyle(color: Color(currentIndex == index ? 0xFFFF9000 : 0xFF8D8D8D)), + ), + ), + currentIndex == index + ? Container( + child: Image( + width: 20, + height: 5, + image: AssetImage('assets/images/ic_note.png'), + ), + ) + : Container(), + ], + ), + ); + } +} diff --git a/lib/tools/find/multiplex_page.dart b/lib/tools/find/multiplex_page.dart new file mode 100644 index 0000000..cb32b3b --- /dev/null +++ b/lib/tools/find/multiplex_page.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; + +class MultiplexPage extends StatefulWidget { + const MultiplexPage({super.key}); + + @override + State createState() => _MultiplexPageState(); +} + +class _MultiplexPageState extends State { + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top + 25, bottom: 60, left: 16, right: 16), + child: ListView.builder( + itemCount: 10, + itemBuilder: (BuildContext context, int index) { + return _item2(index); + }), + ); + } + + _item2(index) { + return Container( + height: 130, + width: 50, + margin: EdgeInsets.only(bottom: 23), + decoration: BoxDecoration( + color: Color(0xFF202021), + borderRadius: BorderRadius.all(Radius.circular(7)), + border: Border.all(color: Color(0xFF7E7E7E), width: 0.3), + ), + child: Stack( + alignment: Alignment.center, + children: [ + Positioned( + left: 7, + child: Image( + width: 83, + height: 115, + image: AssetImage('assets/images/a3.png'), + )), + Positioned( + left: 101, + top: 19, + child: Text( + '重生之千金大小姐倒追我', + style: TextStyle(color: Colors.white, fontSize: 14), + )), + Positioned( + left: 101, + top: 45, + child: SizedBox( + width: 200, + height: 16, + child: ListView.builder( + itemCount: 3, + scrollDirection: Axis.horizontal, + itemBuilder: (BuildContext context, int index) { + return Container( + alignment: Alignment.center, + padding: EdgeInsets.symmetric(horizontal: 11), + margin: EdgeInsets.only(right: 5), + decoration: BoxDecoration(border: Border.all(color: Color(0xFFFF9000)), borderRadius: BorderRadius.all(Radius.circular(7))), + child: Text( + '123', + style: TextStyle(fontSize: 10, color: Color(0xFFFF9000)), + ), + ); + }), + )), + Positioned( + left: 102, + top: 70, + child: Container( + width: 200, + child: Text( + maxLines: 3, + '这里是属于斗气的世界,没有花俏艳丽的魔法有的,仅仅是,斗王,斗皇,斗宗,斗尊,斗圣,斗帝...', + overflow: TextOverflow.ellipsis, + style: TextStyle(color: Colors.white, fontSize: 10), + ), + )) + ], + ), + ); + } +} diff --git a/lib/tools/find/recommend_page.dart b/lib/tools/find/recommend_page.dart new file mode 100644 index 0000000..6dcc2c5 --- /dev/null +++ b/lib/tools/find/recommend_page.dart @@ -0,0 +1,220 @@ +import 'package:card_swiper/card_swiper.dart'; +import 'package:flutter/material.dart'; + +import '../../custom/custom_swiper_pagination.dart'; + +///推荐 +class RecommendPage extends StatefulWidget { + const RecommendPage({super.key}); + + @override + State createState() => _RecommendPageState(); +} + +class _RecommendPageState extends State { + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: SingleChildScrollView( + physics: BouncingScrollPhysics(), + child: Column( + children: [ + SizedBox( + height: 270, + child: Swiper( + itemBuilder: (BuildContext context, int index) { + return Container( + padding: EdgeInsets.only(bottom: 30), + child: Image( + image: AssetImage('assets/images/banner1.png'), + fit: BoxFit.fill, + ), + ); + }, + itemCount: 3, + pagination: SwiperPagination( + // 此处使用自己编写的样式 + builder: + CustomSwiperPaginationBuilder(alignment: Alignment.bottomRight, color: Color(0xFF646464), activeColor: Color(0xFFCFCFCF)), + ), + ), + ), + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: 16), + child: Text( + '热门推荐', + style: TextStyle(color: Colors.white, fontSize: 16), + ), + ), + Container( + margin: EdgeInsets.only(top: 21), + height: 360, + child: ListView.builder( + itemCount: 10, + scrollDirection: Axis.horizontal, + itemBuilder: (BuildContext context, int index) { + return _item(index); + }), + ), + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: 16), + child: Text( + '热门小说角色', + style: TextStyle(color: Colors.white, fontSize: 16), + ), + ), + Container( + height: 153 * 10, + margin: EdgeInsets.only(left: 16, right: 16, bottom: 60), + child: ListView.builder( + itemCount: 10, + physics: NeverScrollableScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + return _item2(index); + }), + ) + ], + ), + ), + ), + ], + ); + } +} + +_item(int index) { + return Container( + margin: EdgeInsets.only(right: 9, left: index == 0 ? 16 : 0), + child: Column( + children: [ + Stack( + children: [ + Image(width: 113, height: 159, image: AssetImage('assets/images/a1.png')), + Positioned( + left: 7, + bottom: 21, + child: Text( + '细弱', + style: TextStyle(color: Colors.white, fontSize: 12), + )), + Positioned( + left: 7, + bottom: 9, + child: Container( + width: 105, + child: Text( + '这里是属于斗气有圣斗帝...', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle(color: Color(0xFFC2C2C2), fontSize: 9), + ), + )), + ], + ), + Container( + margin: EdgeInsets.only(top: 12), + child: Stack( + children: [ + Image( + width: 113, + height: 159, + image: AssetImage('assets/images/a2.png'), + ), + Positioned( + left: 7, + bottom: 21, + child: Text( + '细弱', + style: TextStyle(color: Colors.white, fontSize: 12), + )), + Positioned( + left: 7, + bottom: 9, + child: Container( + width: 105, + child: Text( + '这里是属于斗气有圣斗帝...', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle(color: Color(0xFFC2C2C2), fontSize: 9), + ), + )), + ], + ), + ) + ], + ), + ); +} + +_item2(index) { + return Container( + height: 130, + width: 50, + margin: EdgeInsets.only(bottom: 20), + decoration: BoxDecoration( + color: Color(0xFF202021), + borderRadius: BorderRadius.all(Radius.circular(7)), + border: Border.all(color: Color(0xFF7E7E7E), width: 0.3), + ), + child: Stack( + alignment: Alignment.center, + children: [ + Positioned( + left: 7, + child: Image( + width: 83, + height: 115, + image: AssetImage('assets/images/a3.png'), + )), + Positioned( + left: 101, + top: 19, + child: Text( + '重生之千金大小姐倒追我', + style: TextStyle(color: Colors.white, fontSize: 14), + )), + Positioned( + left: 101, + top: 45, + child: SizedBox( + width: 200, + height: 16, + child: ListView.builder( + itemCount: 3, + scrollDirection: Axis.horizontal, + itemBuilder: (BuildContext context, int index) { + return Container( + alignment: Alignment.center, + padding: EdgeInsets.symmetric(horizontal: 11), + margin: EdgeInsets.only(right: 5), + decoration: BoxDecoration(border: Border.all(color: Color(0xFFFF9000)), borderRadius: BorderRadius.all(Radius.circular(7))), + child: Text( + '123', + style: TextStyle(fontSize: 10, color: Color(0xFFFF9000)), + ), + ); + }), + )), + Positioned( + left: 102, + top: 70, + child: Container( + width: 200, + child: Text( + maxLines: 3, + '这里是属于斗气的世界,没有花俏艳丽的魔法有的,仅仅是,斗王,斗皇,斗宗,斗尊,斗圣,斗帝...', + overflow: TextOverflow.ellipsis, + style: TextStyle(color: Colors.white, fontSize: 10), + ), + )) + ], + ), + ); +} diff --git a/lib/tools/home/home_chat_page.dart b/lib/tools/home/home_chat_page.dart new file mode 100644 index 0000000..99f1426 --- /dev/null +++ b/lib/tools/home/home_chat_page.dart @@ -0,0 +1,551 @@ +import 'dart:async'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:expandable_text/expandable_text.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; + +import '../../beans/MessageBean.dart'; +import '../../beans/character_info_bean.dart'; +import '../chat/chat_info_page.dart'; +import '../chat/chat_model.dart'; +import '../chat/chat_page.dart'; + +class HomeChatPage extends StatefulWidget { + String characterId; + + HomeChatPage({super.key, required this.characterId}); + + @override + State createState() => _HomeChatPageState(); +} + +class _HomeChatPageState extends State with AutomaticKeepAliveClientMixin { + final ScrollController _scrollController = ScrollController(); + final TextEditingController _chatController = TextEditingController(); + + late StreamSubscription subscription; + final ChatModel _viewmodel = ChatModel(); + + ///人物信息 + late CharacterInfoBean characterInfoBean; + + ///聊天列表 + List chatList = []; + + bool isMore = false; + + bool isHalf = true; + + ///输入框内容 + String text = ""; + + void _textFieldChanged(String str) { + text = str; + setState(() {}); + } + + @override + void initState() { + // TODO: implement initState + super.initState(); + + subscription = _viewmodel.streamController.stream.listen((newData) { + String code = newData['code']; + if (code.isNotEmpty) { + switch (code) { + case "getCharacterInfo": + characterInfoBean = newData['data']; + + break; + } + setState(() {}); + } + }); + + _viewmodel.getCharacterInfo(widget.characterId); + + chatList = [ + MessageBean("user", "你好"), + MessageBean("introduce", "王语嫣 年龄22 三围 28 36 48 分别多喜欢打台球旅行,唱歌,当绿茶喜欢打台球旅行,唱歌,当绿茶当绿茶喜欢打台球旅行,唱歌,当绿茶当绿茶喜欢打台球旅行,唱歌,当绿茶"), + MessageBean("assistant", "(仔细观察你的样子,露出满意的笑容我想...)"), + ]; + } + + @override + void dispose() { + // TODO: implement dispose + _scrollController.dispose(); + _chatController.dispose(); + subscription.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + CachedNetworkImage( + fit: BoxFit.fitHeight, + imageUrl: characterInfoBean.bgUrl!, + errorWidget: (context, url, error) => const Icon(Icons.error), + ), + Positioned( + bottom: 0, + child: Container( + width: MediaQuery.of(context).size.width, + height: 311.67, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [Color(0x00000000), Color(0xFF0C0909)], // 三色渐变数组 + begin: Alignment.topCenter, // 渐变开始位置 + end: Alignment.bottomCenter, // 渐变结束位置 + ), + ), + )), + Container( + child: Column( + children: [ + ///title + Container( + width: double.infinity, + height: 30, + margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top + 9, left: 16, right: 16), + child: Stack( + alignment: Alignment.center, + children: [ + Positioned( + left: 105, + child: Text( + "+1", + style: TextStyle(color: Color(0xFFF14476), fontSize: 10), + )), + Positioned( + left: 0, + child: Container( + width: 101, + height: 30, + decoration: BoxDecoration(color: Color(0x33000000), borderRadius: BorderRadius.all(Radius.circular(14))), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ///AI头像 + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ChatInfoPage( + data: characterInfoBean, + )), + ); + }, + child: Container( + margin: EdgeInsets.only(left: 2), + child: CachedNetworkImage( + width: 23, + imageUrl: characterInfoBean.icon!, + errorWidget: (context, url, error) => const Icon(Icons.error), + ), + ), + ), + + ///AI名 + Container( + margin: EdgeInsets.only(left: 5), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: 35, + child: Text( + characterInfoBean.characterName!, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 10, color: Colors.white), + ), + ), + Text( + '${characterInfoBean.lookCount} 聊过', + style: TextStyle(fontSize: 7, color: Colors.white), + ), + ], + ), + ), + + ///心动值 + Container( + margin: EdgeInsets.only(left: 6, right: 6), + child: GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ChatPage( + id: characterInfoBean.characterId.toString(), + )), + ); + }, + child: Stack( + alignment: Alignment.center, + children: [ + Image( + width: 24, + height: 21, + image: AssetImage('assets/images/ic_beckoning.png'), + ), + Text( + characterInfoBean.intimacy.toString(), + style: TextStyle(fontSize: 8, color: Colors.white), + ) + ], + ), + ), + ), + ], + ), + ), + ), + + ///关注 + Positioned( + right: 0, + child: GestureDetector( + onTap: () { + isHalf = !isHalf; + setState(() {}); + }, + child: Container( + width: 50, + height: 24, + alignment: Alignment.center, + decoration: BoxDecoration(color: Color(0x33000000), borderRadius: BorderRadius.all(Radius.circular(12))), + child: Text( + '+ 关注', + style: TextStyle(fontSize: 12, color: Colors.white), + ), + ), + ), + ), + ], + ), + ), + isHalf + ? Container( + height: MediaQuery.of(context).size.height / 3.5, + ) + : Container(), + + ///聊天列表 + Expanded( + child: ListView.builder( + controller: _scrollController, + itemCount: chatList.length, + itemBuilder: (BuildContext context, int index) { + return _item(index); + })), + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: 16, bottom: 3), + child: Image(width: 63, height: 18, image: AssetImage('assets/images/ic_memory.png')), + ), + + ///输入 功能 + Container( + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.only(left: 16, right: 16, bottom: !isMore ? 60 : 0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ///输入框 + Expanded( + child: Container( + padding: const EdgeInsets.symmetric(vertical: 5), + decoration: const BoxDecoration( + color: Color(0x33FFFFFF), + borderRadius: BorderRadius.all(Radius.circular(20)), + ), + child: Row( + children: [ + Expanded( + child: Container( + padding: const EdgeInsets.only(left: 15), + child: TextField( + controller: _chatController, + onChanged: _textFieldChanged, + maxLines: null, + cursorColor: const Color(0xFFFF9000), + decoration: const InputDecoration( + border: InputBorder.none, + // 移除非聚焦状态下的边框 + enabledBorder: InputBorder.none, + // 移除获得焦点但未输入内容时的边框 + focusedBorder: InputBorder.none, + isCollapsed: true, + // 移除输入时的边框 + hintText: "打个招呼吧...", + hintStyle: TextStyle(color: Color(0xFFB6B6B6), fontSize: 13), + ), + style: const TextStyle(color: Colors.white), + ), + ), + ), + Container( + margin: const EdgeInsets.only(right: 7), + child: text == "" + ? const Image( + width: 27, + image: AssetImage('assets/images/ic_send_n.png'), + ) + : GestureDetector( + onTap: () { + chatList.add(MessageBean("user", text)); + _chatController.clear(); + text = ""; + Future.delayed(Duration(milliseconds: 200), () { + _scrollController.jumpTo(_scrollController.position.maxScrollExtent); + }); + setState(() {}); + }, + child: Image( + width: 27, + image: AssetImage('assets/images/ic_send.png'), + ), + ), + ) + ], + ), + )), + + ///智能输入 + Container( + margin: const EdgeInsets.only(left: 14, right: 14), + child: GestureDetector( + onTap: () { + Navigator.of(context).pushNamed('/LoginPage'); + }, + child: Image( + width: 27, + image: AssetImage('assets/images/ic_smart_chat.png'), + ), + ), + ), + + ///功能 更多 + GestureDetector( + onTap: () { + isMore = !isMore; + setState(() {}); + }, + child: Image( + width: 27, + image: AssetImage('assets/images/ic_more.png'), + ), + ), + ], + ), + ), + + isMore + ? Container( + height: 70, + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: 16, right: 16, bottom: 60), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + margin: EdgeInsets.only(left: 23), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Image(width: 26, image: AssetImage('assets/images/ic_album.png')), + Container( + margin: EdgeInsets.only(top: 9), + child: Text( + '角色相册', + style: TextStyle(fontSize: 10, color: Color(0xFFA2A2A2)), + ), + ), + ], + ), + ), + Container( + margin: EdgeInsets.only(left: 23), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Image(width: 26, image: AssetImage('assets/images/ic_more.png')), + Container( + margin: EdgeInsets.only(top: 9), + child: Text( + '重启对话', + style: TextStyle(fontSize: 10, color: Color(0xFFA2A2A2)), + ), + ), + ], + ), + ), + ], + ), + ) + : Container(), + ], + ), + ), + + ///记忆提升中 + Positioned( + top: 65, + left: 16, + child: Image( + width: 47, + height: 12, + image: AssetImage('assets/images/ic_memory_p.png'), + )), + ], + ); + } + + ///聊天条目 + _item(index) { + if (index == 0) { + return Container(); + } + + ///简介 + if (chatList[index].role == 'introduce') { + return Center( + child: Container( + margin: EdgeInsets.only(left: 16, right: 16), + padding: EdgeInsets.only(left: 20, right: 20, top: 12, bottom: 12), + decoration: BoxDecoration(color: Color(0x99000000), borderRadius: BorderRadius.all(Radius.circular(13))), + child: ExpandableText( + chatList[index].content!, + expandText: '更多', + collapseText: '关闭', + maxLines: 3, + linkColor: Color(0xFFFF9000), + style: TextStyle(fontSize: 13, color: Colors.white), + ), + ), + ); + } + + ///时间 + // if (chatList[index].role == 'time') { + // return Center( + // child: Container( + // padding: EdgeInsets.only(left: 9, right: 9, top: 6, bottom: 6), + // decoration: BoxDecoration(color: Color(0x99FFFFFF), borderRadius: BorderRadius.all(Radius.circular(8))), + // child: Text(chatList[index].content!, style: TextStyle(color: Color(0xFFA7AFD9), fontSize: 10)), + // ), + // ); + // } + + ///聊天内容 + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: chatList[index].role != 'user' + ? Container( + ///AI + alignment: Alignment.centerLeft, + child: Stack( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + ConstrainedBox( + constraints: BoxConstraints( + maxWidth: MediaQuery.of(context).size.width - 50, // 确保不超过屏幕宽度 + ), + child: Container( + margin: const EdgeInsets.symmetric(vertical: 4, horizontal: 12), + padding: const EdgeInsets.all(11.0), + decoration: const BoxDecoration( + color: Color(0xFFFF9000), + borderRadius: BorderRadius.only( + topRight: Radius.circular(16.0), bottomLeft: Radius.circular(16.0), bottomRight: Radius.circular(16.0)), + ), + child: GestureDetector( + onTap: () { + EasyLoading.showToast("status"); + }, + child: SelectableText( + chatList[index].content!, + style: const TextStyle( + fontSize: 16, + color: Colors.black, + ), + ), + ), + ), + ), + ], + ), + // Container( + // width: 60, + // height: 27, + // margin: EdgeInsets.only(left: 16), + // alignment: Alignment.center, + // decoration: BoxDecoration( + // color: Color(0xFFF14476), + // borderRadius: BorderRadius.only( + // topLeft: Radius.circular(4), + // topRight: Radius.circular(13), + // bottomRight: Radius.circular(13), + // bottomLeft: Radius.circular(13), + // ), + // ), + // child: Row( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Image(width: 8, height: 13, image: AssetImage('assets/images/ic_voice.png')), + // Container( + // margin: EdgeInsets.only(left: 8), + // child: Text( + // '60s', + // style: TextStyle(fontSize: 13, color: Colors.white), + // ), + // ), + // ], + // ), + // ), + ], + ), + ) + : Row( + ///用户 + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ConstrainedBox( + constraints: BoxConstraints( + maxWidth: MediaQuery.of(context).size.width - 50, // 确保不超过屏幕宽度 + ), + child: Container( + margin: const EdgeInsets.symmetric(vertical: 4, horizontal: 12), + padding: const EdgeInsets.all(11.0), + decoration: const BoxDecoration( + color: Color(0x99252525), + borderRadius: + BorderRadius.only(topLeft: Radius.circular(16.0), bottomLeft: Radius.circular(16.0), bottomRight: Radius.circular(16.0)), + ), + child: Text( + chatList[index].content!, + style: const TextStyle( + fontSize: 16, + color: Colors.white, + ), + ), + ), + ), + ], + ), + ); + } + + @override + // TODO: implement wantKeepAlive + bool get wantKeepAlive => true; +} diff --git a/lib/tools/home/home_model.dart b/lib/tools/home/home_model.dart new file mode 100644 index 0000000..ab03d81 --- /dev/null +++ b/lib/tools/home/home_model.dart @@ -0,0 +1,41 @@ +import 'dart:async'; + +import 'package:talk/network/NetworkConfig.dart'; +import 'package:talk/network/RequestCenter.dart'; + +import '../../beans/character_info_bean.dart'; +import '../../beans/home_character_bean.dart'; +import '../../network/BaseEntity.dart'; + +class HomeModel { + StreamController streamController = StreamController.broadcast(); + + HomeModel() { + setUp(); + } + + void setUp() {} + + ///获取首页展示的人物 + Future getCharacterIdList() async { + RequestCenter.instance.requestGet(NetworkConfig.getCharacterIdList, {}, (BaseEntity dataEntity) { + print("dataEntity==$dataEntity"); + + if (dataEntity.code == 0) { + List data = (dataEntity.data as List).map((e) => HomeCharacterBean.fromJson(e as Map)).toList(); + streamController.sink.add({ + 'code': "getCharacterIdList", //有数据 + 'data': data + }); + }else{ + streamController.sink.add({ + 'code': "-1", //有数据 + 'data': dataEntity.message + }); + } + }, (ErrorEntity errorEntity) { + print("errorEntity==${errorEntity.message}"); + }); + } + +} diff --git a/lib/tools/home/my_home_page.dart b/lib/tools/home/my_home_page.dart new file mode 100644 index 0000000..473cb24 --- /dev/null +++ b/lib/tools/home/my_home_page.dart @@ -0,0 +1,91 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:talk/tools/home/home_chat_page.dart'; +import 'package:talk/tools/home/home_model.dart'; + +import '../../beans/home_character_bean.dart'; + +///首页 +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key}); + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State with SingleTickerProviderStateMixin { + final PageController _pageController = PageController(); + + late StreamSubscription subscription; + final HomeModel _viewmodel = HomeModel(); + + ///当前页下标 + int _currentIndex = 0; + + List idList = []; + + @override + void initState() { + // TODO: implement initState + super.initState(); + + subscription = _viewmodel.streamController.stream.listen((newData) { + String code = newData['code']; + if (code.isNotEmpty) { + switch (code) { + case "getCharacterIdList": + idList = newData['data']; + print("idList==${idList[0].characterId}"); + break; + case "getCharacterInfo": + break; + } + setState(() {}); + } + }); + + _viewmodel.getCharacterIdList(); + // _viewmodel.getCharacterInfo(1); + } + + @override + void dispose() { + // TODO: implement dispose + _pageController.dispose(); + subscription.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + Size size = MediaQuery.of(context).size; + + return Scaffold( + backgroundColor: Color(0xFFE8EEFC), + body: PageView.custom( + controller: _pageController, + childrenDelegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + // 使用PageStorageKey来保存页面状态 + final Key key = ValueKey(index); + return Container( + key: key, + child: HomeChatPage( + key: key, + characterId: idList[index].characterId.toString(), + ), + ); + }, + // 设置child数量 + childCount: idList.length, + ), + onPageChanged: (int index) { + setState(() { + _currentIndex = index; + }); + }, + ), + ); + } +} diff --git a/lib/tools/home_page.dart b/lib/tools/home_page.dart new file mode 100644 index 0000000..6e827a9 --- /dev/null +++ b/lib/tools/home_page.dart @@ -0,0 +1,134 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:talk/tools/home/my_home_page.dart'; +import 'package:talk/tools/me/me_page.dart'; +import 'package:talk/tools/message/message_page.dart'; + +import 'find/find_page.dart'; + +class HomePage extends StatefulWidget { + const HomePage({super.key}); + + @override + State createState() => _HomePageState(); +} + +class _HomePageState extends State with SingleTickerProviderStateMixin { + int currentIndex = 0; + late final TabController _tabController; + + @override + void initState() { + // TODO: implement initState + super.initState(); + _tabController = TabController(length: 4, vsync: this); + } + + @override + void dispose() { + // TODO: implement dispose + _tabController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: doubleClickBack, + child: Scaffold( + backgroundColor: Color(0x32FFFFFF), + body: Stack( + children: [ + TabBarView( + controller: _tabController, + physics: const NeverScrollableScrollPhysics(), + children: const [ + MyHomePage(), + FindPage(), + MessagePage(), + MePage(), + ], + ), + Positioned( + bottom: 0, + child: Container( + width: MediaQuery.of(context).size.width, + height: 60, + color: Color(currentIndex != 0 ? 0xFF121213 : 0x121213), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + GestureDetector( + onTap: () { + currentIndex = 0; + _tabController.animateTo(0); + setState(() {}); + }, + child: Text( + '首页', + style: TextStyle(color: Color(currentIndex == 0 ? 0xFFFFFFFF : 0xFF7E7E7E)), + ), + ), + GestureDetector( + onTap: () { + currentIndex = 1; + _tabController.animateTo(1); + setState(() {}); + }, + child: Text( + '发现', + style: TextStyle(color: Color(currentIndex == 1 ? 0xFFFFFFFF : 0xFF7E7E7E)), + ), + ), + Image( + width: 32, + image: AssetImage('assets/images/ic_create.png'), + ), + GestureDetector( + onTap: () { + currentIndex = 2; + _tabController.animateTo(2); + setState(() {}); + }, + child: Text( + '消息', + style: TextStyle(color: Color(currentIndex == 2 ? 0xFFFFFFFF : 0xFF7E7E7E)), + ), + ), + GestureDetector( + onTap: () { + currentIndex = 3; + _tabController.animateTo(3); + setState(() {}); + }, + child: Text( + '我的', + style: TextStyle(color: Color(currentIndex == 3 ? 0xFFFFFFFF : 0xFF7E7E7E)), + ), + ), + ], + ), + )) + ], + ), + ), + ); + } + + int last = 0; + + Future doubleClickBack() async { + int now = DateTime.now().millisecondsSinceEpoch; + + if (now - last > 2500) { + last = DateTime.now().millisecondsSinceEpoch; + EasyLoading.showToast("再按一次退出"); + return Future.value(false); + } else { + exit(0); + } + } +} diff --git a/lib/tools/login/login_model.dart b/lib/tools/login/login_model.dart new file mode 100644 index 0000000..eb5dcbc --- /dev/null +++ b/lib/tools/login/login_model.dart @@ -0,0 +1,75 @@ +import 'dart:async'; + +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:talk/network/NetworkConfig.dart'; +import 'package:talk/network/RequestCenter.dart'; + +import '../../beans/login_bean.dart'; +import '../../network/BaseEntity.dart'; + +class LoginModel { + StreamController streamController = StreamController.broadcast(); + + LoginModel() { + setup(); + } + + void setup() { + //初始化 + } + + ///获取验证码 + Future sendPhoneNumber(phoneNumber) async { + RequestCenter.instance.request(NetworkConfig.sendPhoneNumber, {"phoneNumber": phoneNumber}, (BaseEntity dataEntity) { + print("dataEntity==${dataEntity.message}"); + + if (dataEntity.code == 0) { + streamController.sink.add({ + 'code': "sendPhoneNumber", //有数据 + 'data': dataEntity.message, + }); + } else { + streamController.sink.add({ + 'code': "error", // + 'data': dataEntity.message, + }); + } + }, (ErrorEntity errorEntity) { + print("errorEntity==${errorEntity.message}"); + }); + } + + ///登录 + Future login(phoneNumber, verificationCode, loginType, token) async { + RequestCenter.instance.request(NetworkConfig.accountLogin, { + "phoneNumber": phoneNumber, + "verificationCode": verificationCode, + "loginType": loginType, + "token": token, + }, (BaseEntity dataEntity) async { + + if (dataEntity.code == 0) { + // Map json = jsonDecode(dataEntity.data); + LoginBean loginBean = LoginBean.fromJson(dataEntity.data); + NetworkConfig.userId = loginBean.userId!.toString(); + NetworkConfig.userName = loginBean.nickName!; + + // + final SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setString('token', loginBean.token!); + + streamController.sink.add({ + 'code': "login", //有数据 + 'data': dataEntity.message, + }); + } else { + streamController.sink.add({ + 'code': "error", // + 'data': dataEntity.message, + }); + } + }, (ErrorEntity errorEntity) { + print("errorEntity==${errorEntity.message}"); + }); + } +} diff --git a/lib/tools/login/login_page.dart b/lib/tools/login/login_page.dart new file mode 100644 index 0000000..00341d5 --- /dev/null +++ b/lib/tools/login/login_page.dart @@ -0,0 +1,273 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:talk/tools/login/login_model.dart'; + +class LoginPage extends StatefulWidget { + const LoginPage({super.key}); + + @override + State createState() => _LoginPageState(); +} + +class _LoginPageState extends State { + final TextEditingController _phoneController = TextEditingController(); + final TextEditingController _codeController = TextEditingController(); + + late StreamSubscription subscription; + final LoginModel _viewmodel = LoginModel(); + + //协议 + bool isCheck = false; + + ///输入框内容 + String phoneText = ""; + String codeText = ""; + + void _phoneChanged(String str) { + phoneText = str; + setState(() {}); + } + + void _codeChanged(String str) { + codeText = str; + setState(() {}); + } + + int _timeLeft = 5; // 倒计时时间,单位为秒 + bool _isCountingDown = false; + + ///获取验证码 + void getCode() { + if (phoneText == "") { + EasyLoading.showToast("请输入手机号"); + return; + } + _viewmodel.sendPhoneNumber(phoneText); + } + + void _startCountdown() { + setState(() { + _isCountingDown = true; + }); + + Timer.periodic(Duration(seconds: 1), (timer) { + if (_timeLeft > 0) { + setState(() { + _timeLeft--; + }); + } else { + timer.cancel(); + setState(() { + _isCountingDown = false; + _timeLeft = 5; + }); + } + }); + } + + @override + void initState() { + // TODO: implement initState + super.initState(); + + subscription = _viewmodel.streamController.stream.listen((newData) { + String code = newData['code']; + if (code.isNotEmpty) { + switch (code) { + case "sendPhoneNumber": + EasyLoading.showToast(newData['data']); + _startCountdown(); + break; + case "login": + EasyLoading.showToast(newData['data']); + Navigator.pushReplacementNamed(context, "/HomePage"); + break; + + default: + EasyLoading.showToast(newData['data']); + break; + } + } + }); + } + + @override + void dispose() { + // TODO: implement dispose + _phoneController.dispose(); + _codeController.dispose(); + subscription.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + alignment: Alignment.center, + children: [ + Image(fit: BoxFit.fill, image: AssetImage('assets/images/login_bj.png')), + Positioned( + left: 22, + top: 28, + child: GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Image( + height: 23, + image: AssetImage('assets/images/ic_left_arrow.png'), + ), + )), + Positioned( + left: 36, + top: 76, + child: Text( + '妙语', + style: TextStyle(color: Colors.white, fontSize: 33), + )), + Positioned( + left: 36, + top: 115, + child: Text( + '登陆 / 注册', + style: TextStyle(color: Color(0xFFD5D5D5), fontSize: 18), + )), + Positioned( + bottom: 35, + child: Container( + width: MediaQuery.of(context).size.width, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GestureDetector( + onTap: () { + isCheck = !isCheck; + setState(() {}); + }, + child: Image(width: 16, image: isCheck ? AssetImage('assets/images/ic_ck_s.png') : AssetImage('assets/images/ic_ck.png')), + ), + Container( + margin: EdgeInsets.only(left: 10), + child: RichText( + text: TextSpan(children: [ + TextSpan(text: '我已阅读并同意', style: TextStyle(fontSize: 12, color: Color(0xFF5F5F5F))), + TextSpan(text: '用户协议', style: TextStyle(fontSize: 12, color: Color(0xFFFF9000))), + TextSpan(text: '和', style: TextStyle(fontSize: 12, color: Color(0xFF5F5F5F))), + TextSpan(text: '隐私协议', style: TextStyle(fontSize: 12, color: Color(0xFFFF9000))), + ]), + ), + ) + ], + ), + )), + Positioned( + bottom: 70, + child: GestureDetector( + onTap: () { + if (phoneText != "" && codeText != "") { + if (isCheck) { + EasyLoading.showToast("登录"); + _viewmodel.login(phoneText, codeText, 0, + "eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJOaWNrTmFtZSI6IuaWsOeUqOaItyIsIlVzZXJJZCI6IjMiLCJleHAiOjE3MjEyMjQzNjksImlzcyI6Ikh1YW5NZW5nIiwiYXVkIjoiSHVhbk1lbmdBcHAifQ.gxf2y47qWuNtEf2xvGSG6ATEgaXOrTWbJpi_Bp6eU2k"); + } else { + EasyLoading.showToast("请选中协议"); + } + } + }, + child: Container( + height: 43, + width: 288, + alignment: Alignment.center, + decoration: BoxDecoration( + color: Color(phoneText != "" && codeText != "" ? 0xFFFF9000 : 0xFFAC6D1C), + borderRadius: BorderRadius.all(Radius.circular(7)), + ), + child: Text( + '登录', + style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600), + ), + ), + )), + Positioned( + bottom: 125, + child: Container( + height: 43, + width: 288, + alignment: Alignment.center, + decoration: BoxDecoration( + color: Color(0x33FFFFFF), + borderRadius: BorderRadius.all(Radius.circular(7)), + ), + child: Row( + children: [ + Expanded( + child: Container( + padding: EdgeInsets.only(left: 17, bottom: 6), + child: TextField( + controller: _codeController, + onChanged: _codeChanged, + cursorColor: Color(0xFFFF9000), + keyboardType: TextInputType.number, + decoration: InputDecoration( + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + hintText: "请输入验证码", + hintStyle: TextStyle(color: Color(0xFFB5B5B5), fontSize: 15), + ), + style: TextStyle(color: Colors.white), + ), + ), + ), + GestureDetector( + onTap: () { + if (!_isCountingDown) { + getCode(); + } + }, + child: Container( + margin: EdgeInsets.only(right: 14), + child: Text( + !_isCountingDown ? "获取验证码" : "$_timeLeft后重新获取", + style: TextStyle(fontSize: 15, color: Color(0xFFB5B5B5)), + ), + ), + ), + ], + ), + )), + Positioned( + bottom: 178, + child: Container( + height: 43, + width: 288, + alignment: Alignment.center, + padding: EdgeInsets.only(left: 17, bottom: 6), + decoration: BoxDecoration( + color: Color(0x33FFFFFF), + borderRadius: BorderRadius.all(Radius.circular(7)), + ), + child: TextField( + controller: _phoneController, + onChanged: _phoneChanged, + cursorColor: Color(0xFFFF9000), + keyboardType: TextInputType.number, + decoration: InputDecoration( + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + hintText: "请输入手机号", + hintStyle: TextStyle(color: Color(0xFFB5B5B5), fontSize: 15), + ), + style: TextStyle(color: Colors.white), + ), + )), + ], + ), + ); + } +} diff --git a/lib/tools/me/me_page.dart b/lib/tools/me/me_page.dart new file mode 100644 index 0000000..ef933e9 --- /dev/null +++ b/lib/tools/me/me_page.dart @@ -0,0 +1,266 @@ +import 'package:flutter/material.dart'; + +class MePage extends StatefulWidget { + const MePage({super.key}); + + @override + State createState() => _MePageState(); +} + +class _MePageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Color(0xFF121213), + body: Stack( + children: [ + Container( + margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top + 9), + child: Column( + children: [ + ///title + SizedBox( + width: double.infinity, + child: Stack( + alignment: Alignment.center, + children: [ + Text( + '', + style: TextStyle(color: Colors.white, fontSize: 18), + ), + + ///设置按钮 + Positioned( + right: 15, + child: GestureDetector( + onTap: () { + Navigator.pushNamed(context, '/SettingPage'); + }, + child: Image( + width: 21, + height: 21, + image: AssetImage('assets/images/ic_setting.png'), + ), + )) + ], + ), + ), + + Container( + width: double.infinity, + margin: EdgeInsets.only(left: 18, right: 18, top: 38), + child: Stack( + children: [ + Image(width: 58, image: AssetImage('assets/images/img_head2.png')), + Positioned( + top: 5, + left: 80, + child: Text( + '王语嫣', + style: TextStyle(fontSize: 15, color: Color(0xFFE1E1E1)), + )), + Positioned( + bottom: 6, + left: 80, + child: Text( + 'id 13121331', + style: TextStyle(fontSize: 13, color: Color(0xFF4D4D4D)), + )), + ], + ), + ), + Container( + margin: EdgeInsets.only(left: 16, top: 17), + child: Row( + children: [ + Text( + '9', + style: TextStyle(color: Colors.white, fontSize: 16), + ), + Container( + margin: EdgeInsets.only(left: 6), + child: Text( + '相册', + style: TextStyle(color: Color(0xFF4D4D4D), fontSize: 12), + ), + ), + Container( + margin: EdgeInsets.only(left: 22), + child: Text( + '9', + style: TextStyle(color: Colors.white, fontSize: 16), + ), + ), + Container( + margin: EdgeInsets.only(left: 6), + child: Text( + '聊过', + style: TextStyle(color: Color(0xFF4D4D4D), fontSize: 12), + ), + ), + ], + ), + ), + + ///货币 商城 + Container( + margin: EdgeInsets.symmetric(horizontal: 16, vertical: 22), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + Navigator.pushNamed(context, '/AccountPage'); + }, + child: Container( + width: 151, + height: 38, + decoration: BoxDecoration(color: Color(0xFF2A2A2A), borderRadius: BorderRadius.all(Radius.circular(13))), + child: Row( + children: [ + Container( + margin: EdgeInsets.only(left: 15), + child: Image( + width: 23, + height: 20, + image: AssetImage('assets/images/ic_currency.png'), + ), + ), + Container( + margin: EdgeInsets.only(left: 17), + child: Text( + '货币:23233', + style: TextStyle(color: Color(0xFFE1E1E1), fontSize: 13), + ), + ), + ], + ), + ), + ), + GestureDetector( + onTap: () { + Navigator.pushNamed(context, '/ShopPage'); + }, + child: Container( + width: 151, + height: 38, + margin: EdgeInsets.only(left: 26), + decoration: BoxDecoration(color: Color(0xFF2A2A2A), borderRadius: BorderRadius.all(Radius.circular(13))), + child: Row( + children: [ + Container( + margin: EdgeInsets.only(left: 15), + child: Image( + width: 23, + height: 20, + image: AssetImage('assets/images/ic_mall.png'), + ), + ), + Container( + margin: EdgeInsets.only(left: 24), + child: Text( + '货币商城', + style: TextStyle(color: Color(0xFFE1E1E1), fontSize: 13), + ), + ), + ], + ), + ), + ), + ], + ), + ), + + Container( + height: 50, + margin: EdgeInsets.symmetric(horizontal: 16), + child: Image( + image: AssetImage('assets/images/ic_web.png'), + ), + ), + + Container( + margin: EdgeInsets.only(left: 16, top: 33), + child: Row( + children: [ + Text( + '创作中心', + style: TextStyle(color: Color(0xFFE1E1E1)), + ), + Container( + margin: EdgeInsets.only(left: 9), + child: Text( + '0', + style: TextStyle(color: Color(0xFF4D4D4D)), + ), + ), + ], + ), + ), + + Container( + margin: EdgeInsets.symmetric(horizontal: 16, vertical: 17), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: 150, + height: 58, + padding: EdgeInsets.only(left: 20), + decoration: BoxDecoration(color: Color(0xFF2A2A2A), borderRadius: BorderRadius.all(Radius.circular(13))), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + child: Text( + '创建个人模型', + style: TextStyle(fontSize: 12, color: Color(0xFFE1E1E1)), + ), + ), + Container( + margin: EdgeInsets.only(top: 7), + child: Text( + '本周剩余(5/5)', + style: TextStyle(fontSize: 10, color: Color(0xFF4D4D4D)), + ), + ), + ], + ), + ), + Container( + width: 150, + height: 58, + padding: EdgeInsets.only(left: 20), + decoration: BoxDecoration(color: Color(0xFF2A2A2A), borderRadius: BorderRadius.all(Radius.circular(13))), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + child: Text( + '创建个人模型', + style: TextStyle(fontSize: 12, color: Color(0xFFE1E1E1)), + ), + ), + Container( + margin: EdgeInsets.only(top: 7), + child: Text( + '本周剩余(5/5)', + style: TextStyle(fontSize: 10, color: Color(0xFF4D4D4D)), + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ) + ], + ), + ); + } +} diff --git a/lib/tools/me/setting_page.dart b/lib/tools/me/setting_page.dart new file mode 100644 index 0000000..e41c14e --- /dev/null +++ b/lib/tools/me/setting_page.dart @@ -0,0 +1,115 @@ +import 'package:flutter/material.dart'; + +class SettingPage extends StatefulWidget { + const SettingPage({super.key}); + + @override + State createState() => _SettingApgeState(); +} + +class _SettingApgeState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Color(0xFF121213), + appBar: AppBar( + backgroundColor: Color(0xFF121213), + scrolledUnderElevation: 0.0, + title: Text( + '设置', + style: TextStyle(fontSize: 16, color: Colors.white), + ), + centerTitle: true, + leading: IconButton( + iconSize: 18, + icon: Icon(Icons.arrow_back_ios_sharp), + color: Colors.white, + onPressed: () { + // 处理返回操作 + Navigator.pop(context); + }, + ), + ), + body: Container( + margin: EdgeInsets.symmetric(horizontal: 16), + padding: EdgeInsets.all(21), + decoration: BoxDecoration(color: Color(0xFF202020), borderRadius: BorderRadius.all(Radius.circular(7))), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + height: 50, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '关于', + style: TextStyle(fontSize: 13, color: Colors.white), + ), + Icon( + Icons.keyboard_arrow_right, + color: Color(0xFF6F6F6F), + ), + ], + ), + ), + Container( + height: 0.33, + color: Color(0xFF3A3A3A), + ), + Container( + height: 50, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '用户协议', + style: TextStyle(fontSize: 13, color: Colors.white), + ), + Icon( + Icons.keyboard_arrow_right, + color: Color(0xFF6F6F6F), + ), + ], + ), + ), + Container( + height: 0.33, + color: Color(0xFF3A3A3A), + ), + Container( + height: 50, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '隐私协议', + style: TextStyle(fontSize: 13, color: Colors.white), + ), + Icon( + Icons.keyboard_arrow_right, + color: Color(0xFF6F6F6F), + ), + ], + ), + ), + Container( + width: 221, + height: 37, + alignment: Alignment.center, + margin: EdgeInsets.only(top: 40), + decoration: BoxDecoration( + color: Color(0xFFFF9000), + borderRadius: BorderRadius.all(Radius.circular(7.0)), + ), + child: Text( + '退出登录', + style: TextStyle(fontSize: 13, color: Colors.black), + ), + ) + ], + ), + ), + ); + } +} diff --git a/lib/tools/message/message_page.dart b/lib/tools/message/message_page.dart new file mode 100644 index 0000000..3a7cf3e --- /dev/null +++ b/lib/tools/message/message_page.dart @@ -0,0 +1,125 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; + +class MessagePage extends StatefulWidget { + const MessagePage({super.key}); + + @override + State createState() => _MessagePageState(); +} + +class _MessagePageState extends State { + List messageList = [1, 2, 3, 4, 5, 6]; + + @override + void initState() { + // TODO: implement initState + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Color(0xFF121213), + body: Stack( + children: [ + Column( + children: [ + Container( + margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top + 9, left: 16, right: 16), + alignment: Alignment.centerLeft, + child: Text( + '消息列表', + style: TextStyle(color: Color(0xFFFF9000), fontSize: 16), + ), + ), + Expanded( + child: Container( + padding: EdgeInsets.only(bottom: 60), + child: ListView.builder( + itemCount: 10, + itemBuilder: (BuildContext context, int index) { + return _item(index); + }), + )) + ], + ) + ], + ), + ); + } + + _item(index) { + return Slidable( + //左滑划出的菜单 + endActionPane: ActionPane( + key: Key(UniqueKey().toString()), + // 菜单宽度 + extentRatio: 0.3, + dragDismissible: false, + // 滑动动效 + // DrawerMotion() StretchMotion() + // motion: ScrollMotion(), + motion: BehindMotion(), + children: const [ + SlidableAction( + onPressed: doNothing, + backgroundColor: Color(0xFFFF9000), + foregroundColor: Colors.black, + label: '删除', + ), + ], + ), + + child: Container( + height: 50, + margin: EdgeInsets.only(bottom: 10), + child: Stack( + alignment: Alignment.center, + children: [ + Positioned( + left: 16, + child: Image( + width: 40, + height: 40, + image: AssetImage('assets/images/img_head2.png'), + )), + Positioned( + left: 68, + top: 6, + child: Text( + '王语嫣', + style: TextStyle(fontSize: 13, color: Color(0xFFE1E1E1)), + )), + Positioned( + left: 68, + top: 25, + child: Container( + width: 160, + child: Text( + '你好', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 12, color: Color(0xFF777777)), + ), + )), + Positioned( + right: 16, + top: 6, + child: Container( + child: Text( + '12:39', + style: TextStyle(fontSize: 10, color: Color(0xFF8D8D8D)), + ), + )), + ], + ), + ), + ); + } +} + +void doNothing(BuildContext context) { + EasyLoading.showToast("删除"); +} diff --git a/lib/tools/search/search_page.dart b/lib/tools/search/search_page.dart new file mode 100644 index 0000000..276fac5 --- /dev/null +++ b/lib/tools/search/search_page.dart @@ -0,0 +1,249 @@ +import 'package:flutter/material.dart'; + +///搜索 +class SearchPage extends StatefulWidget { + const SearchPage({super.key}); + + @override + State createState() => _SearchPageState(); +} + +class _SearchPageState extends State { + final TextEditingController _chatController = TextEditingController(); + FocusNode myFocusNode = FocusNode(); + + String text = ""; + bool isFocusNode = false; + + void _textFieldChanged(String str) { + text = str; + setState(() {}); + } + + @override + void initState() { + // TODO: implement initState + super.initState(); + myFocusNode.addListener(() { + if (myFocusNode.hasFocus) { + print('TextField 获得焦点'); + isFocusNode = true; + } else { + print('TextField 失去焦点'); + isFocusNode = false; + } + setState(() {}); + }); + } + + @override + void dispose() { + // TODO: implement dispose + _chatController.dispose(); + myFocusNode.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Color(0xFFE8EEFC), + body: Stack( + children: [ + ///顶部背景 + Container( + width: double.infinity, + height: 302, + decoration: const BoxDecoration( + gradient: LinearGradient( + colors: [Color(0xFF4D49FF), Color(0xFFDAA5FF), Color(0x00DEB0FF)], // 三色渐变数组 + begin: Alignment.topCenter, // 渐变开始位置 + end: Alignment.bottomCenter, // 渐变结束位置 + ), + ), + ), + Container( + margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top + 9), + child: Column( + children: [ + SizedBox( + width: double.infinity, + child: Row( + children: [ + Expanded( + child: Container( + height: 42, + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: 16, right: 10), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(16)), + ), + child: Row( + children: [ + Container( + margin: EdgeInsets.only(left: 8), + child: Image(width: 32, image: AssetImage('assets/images/ic_search2.png')), + ), + Expanded( + child: Container( + margin: EdgeInsets.only(left: 5), + padding: EdgeInsets.only(bottom: 10), + child: TextField( + focusNode: myFocusNode, + controller: _chatController, + onChanged: _textFieldChanged, + decoration: InputDecoration( + border: InputBorder.none, + // 移除非聚焦状态下的边框 + enabledBorder: InputBorder.none, + // 移除获得焦点但未输入内容时的边框 + focusedBorder: InputBorder.none, + // 移除输入时的边框 + hintText: "打个招呼吧...", + hintStyle: TextStyle(color: Color(0xFFB6B6B6), fontSize: 13), + suffixStyle: TextStyle(fontSize: 13), + ), + ), + ), + ), + text != "" + ? Container( + width: 21, + margin: EdgeInsets.only(right: 9), + child: GestureDetector( + onTap: () { + _chatController.clear(); + text = ""; + setState(() {}); + }, + child: Image( + image: AssetImage('assets/images/ic_empty.png'), + ), + ), + ) + : Container(), + ], + ), + ), + ), + + ///添加按钮 + GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Container( + margin: EdgeInsets.only(right: 16), + child: Text( + '取消', + style: TextStyle(fontSize: 16, color: Colors.white), + )), + ) + ], + ), + ), + !isFocusNode + ? Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: 16, top: 22), + child: Text( + '热门搜索', + style: TextStyle(color: Colors.white, fontSize: 18), + ), + ) + : Container(), + !isFocusNode + ? Container( + width: double.infinity, + height: 110, + margin: EdgeInsets.only(top: 10), + child: ListView.builder( + itemCount: 6, + itemBuilder: (BuildContext context, int index) { + return _item1(index); + }, + scrollDirection: Axis.horizontal, + ), + ) + : Container(), + isFocusNode + ? Expanded( + child: Container( + width: double.infinity, + height: 110, + margin: EdgeInsets.only(top: 10, left: 16, right: 16), + child: ListView.builder( + itemCount: 6, + itemBuilder: (BuildContext context, int index) { + return _item2(index); + }, + scrollDirection: Axis.vertical, + ), + ), + ) + : Container(), + ], + ), + ), + ], + ), + ); + } + + _item1(index) { + return Container( + margin: EdgeInsets.only(left: index == 0 ? 16 : 0), + width: 86, + height: 106, + child: Card( + color: Colors.white, + child: Column( + children: [ + Container( + alignment: Alignment.center, + margin: EdgeInsets.only(top: 12), + child: Image(width: 57, image: AssetImage('assets/images/img_head2.png')), + ), + Container( + margin: EdgeInsets.only(top: 8), + child: Text( + '撒打算大时代', + style: TextStyle(color: Color(0xFF616161), fontSize: 11), + ), + ), + ], + ), + ), + ); + } + + _item2(index) { + return const SizedBox( + width: double.infinity, + height: 80, + child: Card( + color: Colors.white, + child: Stack( + alignment: Alignment.center, + children: [ + Positioned( + left: 13, + child: Image(width: 54, image: AssetImage('assets/images/img_head2.png')), + ), + Positioned( + left: 80, + top: 19, + child: Text('菠萝菠萝', style: TextStyle(fontSize: 13)), + ), + Positioned( + left: 80, + top: 43, + child: Text('好吃懒做,热爱睡觉,细心', style: TextStyle(fontSize: 12, color: Color(0xFF8B8B8B))), + ), + ], + ), + ), + ); + } +} diff --git a/lib/tools/shop/account_page.dart b/lib/tools/shop/account_page.dart new file mode 100644 index 0000000..afba2f5 --- /dev/null +++ b/lib/tools/shop/account_page.dart @@ -0,0 +1,201 @@ +import 'package:flutter/material.dart'; + + +///货币 +class AccountPage extends StatefulWidget { + const AccountPage({super.key}); + + @override + State createState() => _AccountPageState(); +} + +class _AccountPageState extends State { + List dataList = [1, 2, 3, 4, 5, 6]; + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Color(0xFF121213), + appBar: AppBar( + backgroundColor: Color(0xFF121213), + scrolledUnderElevation: 0.0, + title: Text( + '我的账户', + style: TextStyle(fontSize: 16, color: Colors.white), + ), + centerTitle: true, + leading: IconButton( + iconSize: 18, + icon: Icon(Icons.arrow_back_ios_sharp), + color: Colors.white, + onPressed: () { + // 处理返回操作 + Navigator.pop(context); + }, + ), + ), + body: SingleChildScrollView( + child: Container( + margin: EdgeInsets.symmetric(horizontal: 16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + alignment: Alignment.center, + margin: EdgeInsets.only(top: 40), + child: Text( + '0', + style: TextStyle(fontSize: 24, color: Color(0xFFE1E1E1)), + ), + ), + Container( + alignment: Alignment.center, + margin: EdgeInsets.only(top: 11), + child: Text( + '货币余额', + style: TextStyle(fontSize: 10, color: Color(0xFF686868)), + ), + ), + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(top: 39), + child: Text( + '货币余额', + style: TextStyle(fontSize: 13, color: Color(0xFFACACAC)), + ), + ), + Container( + margin: EdgeInsets.only(top: 16), + child: GridView.count( + shrinkWrap: true, + //水平子Widget之间间距 + crossAxisSpacing: 17.0, + //垂直子Widget之间间距 + mainAxisSpacing: 16.0, + //GridView内边距 + padding: EdgeInsets.zero, + //一行的Widget数量 + crossAxisCount: 2, + //子Widget宽高比例 + childAspectRatio: 2.67, + //子Widget列表 + children: _item(), + physics: NeverScrollableScrollPhysics(), + //类似 cellForRow 函数 + scrollDirection: Axis.vertical), + ), + Container( + margin: EdgeInsets.only(top: 14), + decoration: BoxDecoration(color: Color(0xFF2A2A2A), borderRadius: BorderRadius.all(Radius.circular(17))), + child: Column( + children: [ + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + Navigator.pushNamed(context, '/TransactionPage'); + }, + child: const SizedBox( + height: 35, + child: Stack( + alignment: Alignment.center, + children: [ + Positioned( + left: 21, + child: Text( + '交易记录', + style: TextStyle(color: Color(0xFF8E8E8E), fontSize: 12), + )), + Positioned( + right: 21, + child: Icon( + size: 10, + Icons.arrow_forward_ios_sharp, + color: Color(0xFF6E6E6E), + )) + ], + ), + ), + ), + Container( + width: double.infinity, + height: 0.33, + margin: EdgeInsets.symmetric(horizontal: 21), + color: Color(0xFF3A3A3A), + ), + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + Navigator.pushNamed(context, '/ProblemPage'); + }, + child: SizedBox( + height: 35, + child: Stack( + alignment: Alignment.center, + children: [ + Positioned( + left: 21, + child: Text( + '常见问题', + style: TextStyle(color: Color(0xFF8E8E8E), fontSize: 12), + )), + Positioned( + right: 35, + child: Text( + '充值须知、充值异常与帮助', + style: TextStyle(color: Color(0xFF8E8E8E), fontSize: 12), + )), + Positioned( + right: 21, + child: Icon( + size: 10, + Icons.arrow_forward_ios_sharp, + color: Color(0xFF6E6E6E), + )), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ); + } + + _item() { + return dataList.map((res) { + return Container( + height: 58, + decoration: BoxDecoration(color: Color(0xFF2A2A2A), borderRadius: BorderRadius.all(Radius.circular(17))), + child: Stack( + children: [ + Positioned( + left: 21, + top: 14, + child: Text( + '100', + style: TextStyle(color: Color(0xFFFF9000), fontSize: 13), + )), + Positioned( + left: 48, + top: 16, + child: Text( + '货币', + style: TextStyle(color: Color(0xFF6F6F6F), fontSize: 10), + )), + Positioned( + left: 21, + top: 34, + child: Text( + '1元', + style: TextStyle(color: Colors.white, fontSize: 10), + )), + ], + ), + ); + }).toList(); + } +} diff --git a/lib/tools/shop/problem_page.dart b/lib/tools/shop/problem_page.dart new file mode 100644 index 0000000..0789993 --- /dev/null +++ b/lib/tools/shop/problem_page.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; + +class ProblemPage extends StatefulWidget { + const ProblemPage({super.key}); + + @override + State createState() => _ProblemPageState(); +} + +class _ProblemPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Color(0xFF121213), + appBar: AppBar( + backgroundColor: Color(0xFF121213), + title: Text( + '我的账户', + style: TextStyle(fontSize: 16, color: Colors.white), + ), + centerTitle: true, + leading: IconButton( + iconSize: 18, + icon: Icon(Icons.arrow_back_ios_sharp), + color: Colors.white, + onPressed: () { + // 处理返回操作 + Navigator.pop(context); + }, + ), + ), + body: Container( + margin: EdgeInsets.symmetric(horizontal: 16), + child: Column( + children: [ + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(top: 20), + child: Text( + '充值须知', + style: TextStyle(fontSize: 13, color: Color(0xFFACACAC)), + ), + ), + Container( + margin: EdgeInsets.only(top: 12), + child: Text( + '充值须知充值须知充值须知充值须知充值须知充值须知充值须知充值须知充值须知', + style: TextStyle(fontSize: 10, color: Color(0xFFACACAC)), + ), + ), + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(top: 20), + child: Text( + '充值异常与帮助', + style: TextStyle(fontSize: 13, color: Color(0xFFACACAC)), + ), + ), + Container( + margin: EdgeInsets.only(top: 12), + child: Text( + '充值须知充值须知充值须知充值须知充值须知充值须知充值须知充值须知充值须知', + style: TextStyle(fontSize: 10, color: Color(0xFFACACAC)), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/tools/shop/shop_page.dart b/lib/tools/shop/shop_page.dart new file mode 100644 index 0000000..70af81c --- /dev/null +++ b/lib/tools/shop/shop_page.dart @@ -0,0 +1,163 @@ +import 'package:flutter/material.dart'; + +///货币商城 +class ShopPage extends StatefulWidget { + const ShopPage({super.key}); + + @override + State createState() => _ShopPageState(); +} + +class _ShopPageState extends State { + int currentIndex = 0; + List dataList = [1, 2, 3, 4, 5]; + + @override + void initState() { + // TODO: implement initState + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Color(0xFF121213), + appBar: AppBar( + backgroundColor: Color(0xFF121213), + scrolledUnderElevation: 0.0, + title: Text( + '商店', + style: TextStyle(fontSize: 16, color: Colors.white), + ), + centerTitle: true, + leading: IconButton( + iconSize: 18, + icon: Icon(Icons.arrow_back_ios_sharp), + color: Colors.white, + onPressed: () { + // 处理返回操作 + Navigator.pop(context); + }, + ), + ), + body: SingleChildScrollView( + child: Container( + margin: EdgeInsets.symmetric(horizontal: 16), + child: Column( + children: [ + Container( + margin: EdgeInsets.only(top: 20), + child: Row( + children: [ + GestureDetector( + onTap: () { + currentIndex = 0; + setState(() {}); + }, + child: Column( + children: [ + Text( + '在售', + style: TextStyle(fontSize: currentIndex == 0 ? 13 : 12, color: Color(currentIndex == 0 ? 0xFFFFFFFF : 0xFF686868)), + ), + currentIndex == 0 + ? Image( + width: 20, + height: 5, + image: AssetImage('assets/images/ic_note.png'), + ) + : Container( + height: 5, + ) + ], + ), + ), + Container( + margin: EdgeInsets.only(left: 27), + child: GestureDetector( + onTap: () { + currentIndex = 1; + setState(() {}); + }, + child: Column( + children: [ + Text( + '已拥有', + style: TextStyle(fontSize: currentIndex != 0 ? 13 : 12, color: Color(currentIndex != 0 ? 0xFFFFFFFF : 0xFF686868)), + ), + currentIndex != 0 + ? Image( + width: 20, + height: 5, + image: AssetImage('assets/images/ic_note.png'), + ) + : Container( + height: 5, + ), + ], + ), + ), + ), + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 16), + child: GridView.count( + shrinkWrap: true, + //水平子Widget之间间距 + crossAxisSpacing: 17.0, + //垂直子Widget之间间距 + mainAxisSpacing: 16.0, + //GridView内边距 + padding: EdgeInsets.zero, + //一行的Widget数量 + crossAxisCount: 2, + //子Widget宽高比例 + childAspectRatio: 1.0, + //子Widget列表 + children: _item(), + physics: NeverScrollableScrollPhysics(), + //类似 cellForRow 函数 + scrollDirection: Axis.vertical), + ), + ], + ), + ), + ), + ); + } + + _item() { + return dataList.map((res) { + return Column( + children: [ + Container( + width: double.infinity, + height: 100, + decoration: BoxDecoration( + color: Color(0xFF2A2A2A), + borderRadius: BorderRadius.all(Radius.circular(7)), + ), + ), + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(top: 7), + child: Text( + '气泡气泡', + style: TextStyle(color: Colors.white, fontSize: 11), + ), + ), + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(top: 7), + child: Text( + '100 货币', + style: TextStyle(color: Color(0xFFFF9000), fontSize: 11), + ), + ), + ], + ); + }).toList(); + } +} diff --git a/lib/tools/shop/transaction_page.dart b/lib/tools/shop/transaction_page.dart new file mode 100644 index 0000000..9eeee1d --- /dev/null +++ b/lib/tools/shop/transaction_page.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; + +class TransactionPage extends StatefulWidget { + const TransactionPage({super.key}); + + @override + State createState() => _TransactionPageState(); +} + +class _TransactionPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Color(0xFF121213), + appBar: AppBar( + backgroundColor: Color(0xFF121213), + title: Text( + '我的账户', + style: TextStyle(fontSize: 16, color: Colors.white), + ), + centerTitle: true, + leading: IconButton( + iconSize: 18, + icon: Icon(Icons.arrow_back_ios_sharp), + color: Colors.white, + onPressed: () { + // 处理返回操作 + Navigator.pop(context); + }, + ), + ), + body: Column( + children: [ + Expanded( + child: Container( + margin: EdgeInsets.symmetric(horizontal: 16, vertical: 20), + child: ListView.builder( + itemCount: 5, + itemBuilder: (BuildContext context, int index) { + return _item(index); + }), + ), + ), + ], + ), + ); + } + + _item(index) { + return Container( + width: double.infinity, + height: 70, + child: Stack( + alignment: Alignment.center, + children: [ + index != 0 + ? Positioned( + top: 0, + child: Container( + width: MediaQuery.of(context).size.width, + height: 0.33, + color: Color(0xFF3A3A3A), + )) + : Container(), + Positioned( + top: 15, + left: 0, + child: Text( + '100货币', + style: TextStyle(fontSize: 11, color: Colors.white), + )), + Positioned( + bottom: 14, + left: 0, + child: Text( + '2024-7-3 10:00', + style: TextStyle(fontSize: 10, color: Color(0xFF6F6F6F)), + )), + Positioned( + right: 0, + child: Text( + '-100元', + style: TextStyle(fontSize: 11, color: Colors.white), + )), + ], + ), + ); + } +} diff --git a/lib/tools/start_page.dart b/lib/tools/start_page.dart new file mode 100644 index 0000000..4255d77 --- /dev/null +++ b/lib/tools/start_page.dart @@ -0,0 +1,71 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'login/login_model.dart'; + +class StartPage extends StatefulWidget { + const StartPage({super.key}); + + @override + State createState() => _StartPageState(); +} + +class _StartPageState extends State { + late StreamSubscription subscription; + LoginModel _viewmodel = new LoginModel(); + + @override + void initState() { + // TODO: implement initState + super.initState(); + subscription = _viewmodel.streamController.stream.listen((newData) { + String code = newData['code']; + if (code.isNotEmpty) { + switch (code) { + case "login": + Navigator.pushReplacementNamed(context, "/HomePage"); + break; + + default: + Navigator.pushReplacementNamed(context, "/HomePage"); + break; + } + } + }); + + _loadData(); + } + + @override + void dispose() { + // TODO: implement dispose + subscription.cancel(); + super.dispose(); + } + + Future _loadData() async { + // 获取token + final SharedPreferences prefs = await SharedPreferences.getInstance(); + + final String? token = prefs.getString('token'); + + if (token != "") { + _viewmodel.login("", "", 0, token); + } else { + Navigator.pushReplacementNamed(context, "/HomePage"); + } + } + + @override + Widget build(BuildContext context) { + return Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: Image( + image: AssetImage('assets/images/img_start.png'), + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..91e6b3e --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,62 @@ +name: talk +description: "A new Flutter project." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: '>=3.4.1 <4.0.0' + + +dependencies: + flutter: + sdk: flutter + + + dio: ^5.4.3+1 + cupertino_icons: ^1.0.6 + flutter_easyloading: ^3.0.5 + crypto: ^3.0.3 + shared_preferences: ^2.2.3 + json_annotation: ^4.9.0 + cached_network_image: ^3.2.3 + image_cropper: ^7.0.5 + flutter_native_image: ^0.0.6 + photo_view: ^0.14.0 + image_picker: ^0.8.4 + permission_handler: ^10.4.5 + image_gallery_saver: ^2.0.3 + web_socket_channel: ^3.0.0 + flutter_client_sse: ^2.0.1 + expandable_text: ^2.3.0 + card_swiper: ^3.0.1 + flutter_slidable: ^3.1.0 + +dev_dependencies: + flutter_test: + sdk: flutter + + flutter_lints: ^3.0.0 + build_runner: ^2.4.11 + json_serializable: ^6.8.0 + + +flutter: + uses-material-design: true + assets: + - assets/ + - assets/images/ diff --git a/web/favicon.png b/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/web/favicon.png differ diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/web/icons/Icon-192.png differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/web/icons/Icon-512.png differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000..eb9b4d7 Binary files /dev/null and b/web/icons/Icon-maskable-192.png differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000..d69c566 Binary files /dev/null and b/web/icons/Icon-maskable-512.png differ diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..472d47d --- /dev/null +++ b/web/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + talk + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 0000000..2a594c3 --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "talk", + "short_name": "talk", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +}