feat: app 端 ui 设计完成

This commit is contained in:
liqupan
2026-01-28 19:10:19 +08:00
commit a4e7898e94
149 changed files with 11302 additions and 0 deletions

45
wei_ai_app/.gitignore vendored Normal file
View File

@@ -0,0 +1,45 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
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-dependencies
.pub-cache/
.pub/
/build/
/coverage/
# 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

30
wei_ai_app/.metadata Normal file
View File

@@ -0,0 +1,30 @@
# 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: "19074d12f7eaf6a8180cd4036a430c1d76de904e"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 19074d12f7eaf6a8180cd4036a430c1d76de904e
base_revision: 19074d12f7eaf6a8180cd4036a430c1d76de904e
- platform: macos
create_revision: 19074d12f7eaf6a8180cd4036a430c1d76de904e
base_revision: 19074d12f7eaf6a8180cd4036a430c1d76de904e
# 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'

16
wei_ai_app/README.md Normal file
View File

@@ -0,0 +1,16 @@
# wei_ai_app
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.

View File

@@ -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

14
wei_ai_app/android/.gitignore vendored Normal file
View File

@@ -0,0 +1,14 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
.cxx/
# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks

View File

@@ -0,0 +1,44 @@
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")
}
android {
namespace = "com.weiai.wei_ai_app"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.weiai.wei_ai_app"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
}
}
}
flutter {
source = "../.."
}

View File

@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@@ -0,0 +1,45 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="wei_ai_app"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
</manifest>

View File

@@ -0,0 +1,5 @@
package com.weiai.wei_ai_app
import io.flutter.embedding.android.FlutterActivity
class MainActivity : FlutterActivity()

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@@ -0,0 +1,24 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
val newBuildDir: Directory =
rootProject.layout.buildDirectory
.dir("../../build")
.get()
rootProject.layout.buildDirectory.value(newBuildDir)
subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory)
}

View File

@@ -0,0 +1,2 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true

View File

@@ -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-8.14-all.zip

View File

@@ -0,0 +1,26 @@
pluginManagement {
val flutterSdkPath =
run {
val properties = java.util.Properties()
file("local.properties").inputStream().use { properties.load(it) }
val flutterSdkPath = properties.getProperty("flutter.sdk")
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
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 "8.11.1" apply false
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
}
include(":app")

34
wei_ai_app/ios/.gitignore vendored Normal file
View File

@@ -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

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>13.0</string>
</dict>
</plist>

View File

@@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"

View File

@@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"

43
wei_ai_app/ios/Podfile Normal file
View File

@@ -0,0 +1,43 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end

View File

@@ -0,0 +1,616 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
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 */
331C8085294A63A400263BE5 /* 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 = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
331C807B294A618700263BE5 /* RunnerTests.swift */,
);
path = RunnerTests;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
331C8080294A63A400263BE5 /* RunnerTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */,
);
buildRules = (
);
dependencies = (
331C8086294A63A400263BE5 /* PBXTargetDependency */,
);
name = RunnerTests;
productName = RunnerTests;
productReference = 331C8081294A63A400263BE5 /* 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 = {
331C8080294A63A400263BE5 = {
CreatedOnToolsVersion = 14.0;
TestTargetID = 97C146ED1CF9000F007C117D;
};
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
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 */,
331C8080294A63A400263BE5 /* RunnerTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
331C807F294A63A400263BE5 /* 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 */
331C807D294A63A400263BE5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 97C146ED1CF9000F007C117D /* Runner */;
targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* 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 = 13.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;
CLANG_ENABLE_MODULES = YES;
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.weiai.weiAiApp;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.weiai.weiAiApp.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Debug;
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.weiai.weiAiApp.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Release;
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.weiai.weiAiApp.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
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 = 13.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 = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
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.weiai.weiAiApp;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
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.weiai.weiAiApp;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C8088294A63A400263BE5 /* Debug */,
331C8089294A63A400263BE5 /* Release */,
331C808A294A63A400263BE5 /* 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 */;
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C8080294A63A400263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@@ -0,0 +1,13 @@
import Flutter
import UIKit
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}

View File

@@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

View File

@@ -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.

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Wei Ai App</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>wei_ai_app</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1 @@
#import "GeneratedPluginRegistrant.h"

View File

@@ -0,0 +1,12 @@
import Flutter
import UIKit
import XCTest
class RunnerTests: XCTestCase {
func 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.
}
}

View File

@@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class AppTheme {
// Cyberpunk Color Palette
static const Color neonBlue = Color(0xFF00F0FF);
static const Color neonPurple = Color(0xFFBC13FE);
static const Color neonGreen = Color(0xFF0AFF99);
static const Color darkBg = Color(0xFF050510);
static const Color darkSurface = Color(0xFF13132B);
static const Color textPrimary = Color(0xFFE0E0FF);
static const Color textSecondary = Color(0xFFA0A0C0);
static final ThemeData darkTheme = ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
scaffoldBackgroundColor: darkBg,
// Typography
textTheme: GoogleFonts.outfitTextTheme(ThemeData.dark().textTheme).copyWith(
displayLarge: const TextStyle(color: textPrimary, fontWeight: FontWeight.bold),
displayMedium: const TextStyle(color: textPrimary, fontWeight: FontWeight.bold),
bodyLarge: const TextStyle(color: textPrimary),
bodyMedium: const TextStyle(color: textSecondary),
),
// Color Scheme
colorScheme: const ColorScheme.dark(
primary: neonBlue,
secondary: neonPurple,
surface: darkBg, // Use darkBg as surface or stick to darkSurface? Let's use darkBg here as background replacement
error: Color(0xFFFF2A6D),
onPrimary: Colors.black,
onSecondary: Colors.white,
onSurface: textPrimary,
),
// Component Themes
appBarTheme: const AppBarTheme(
backgroundColor: Colors.transparent,
elevation: 0,
centerTitle: true,
titleTextStyle: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: textPrimary),
),
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
backgroundColor: darkSurface,
selectedItemColor: neonBlue,
unselectedItemColor: textSecondary,
type: BottomNavigationBarType.fixed,
showSelectedLabels: true,
showUnselectedLabels: true,
),
iconTheme: const IconThemeData(
color: neonBlue,
),
);
}

View File

@@ -0,0 +1,150 @@
import '../models/character.dart';
import '../models/message.dart';
import '../models/scenario.dart';
String _getImg(String keyword) =>
'https://tse1.mm.bing.net/th?q=${Uri.encodeComponent(keyword)}&w=600&h=900&c=7&rs=1&p=0&dpr=2&pid=1.7&mkt=en-US&adlt=moderate';
final List<Message> mockMessages = [
Message(
id: '1',
text: '连接已建立,正在校准生物反馈信号...',
sender: MessageSender.ai,
type: MessageType.text,
timestamp: DateTime.now().subtract(const Duration(minutes: 5)),
),
Message(
id: '2',
text: '检测到心率略有上升,你需要我的安抚吗?',
sender: MessageSender.ai,
type: MessageType.text,
timestamp: DateTime.now().subtract(const Duration(minutes: 4)),
),
];
final List<Character> mockCharacters = [
Character(
id: 'c1',
name: 'Eva-09',
tagline: '私人仿生护理专员',
avatar: _getImg('anime girl white bikini silver hair gentle portrait masterpiece'),
description: '专为高压人群设计的仿生人型号,擅长通过精准的触觉反馈缓解神经紧张。',
tags: ['温顺', '医疗', '治愈'],
compatibility: 98,
status: 'online',
),
Character(
id: 'c2',
name: 'Commander V',
tagline: '深空舰队指挥官',
avatar: _getImg('anime girl black bikini military cap domineering expression dark hair'),
description: '性格强势,喜欢掌控一切。在连接中,你需要完全服从她的指令。',
tags: ['强势', '指令', '调教'],
compatibility: 85,
status: 'online',
),
Character(
id: 'c3',
name: 'Yuki (故障版)',
tagline: '觉醒的虚拟偶像',
avatar: _getImg('anime girl pink bikini cyberpunk neon colorful hair yandere'),
description: '核心代码出现异常逻辑,表现出极强的占有欲和不可预测的信号波动。',
tags: ['病娇', '不稳定', '高频'],
compatibility: 92,
status: 'busy',
),
Character(
id: 'c4',
name: 'Secret X',
tagline: '未知信号源',
avatar: _getImg('anime girl purple micro bikini mysterious dark glowing eyes sexy'),
description: '权限不足,请提升会员等级以解码该信号源。',
tags: ['神秘', '极乐'],
compatibility: 0,
status: 'offline',
isLocked: true,
),
];
// Scenario cover helper
String _getCover(String keyword) =>
'https://tse1.mm.bing.net/th?q=${Uri.encodeComponent(keyword)}&w=400&h=600&c=7&rs=1&p=0&dpr=2&pid=1.7&mkt=en-US&adlt=moderate';
// Mock Scenarios for Library
final List<Scenario> mockScenarios = [
Scenario(
id: '1',
title: '午夜办公室的加班',
category: '职场',
cover: _getCover('anime girl office lady lingerie night city window'),
duration: '12:30',
intensity: 'Medium',
isLocked: false,
tags: ['沉浸', 'ASMR'],
),
Scenario(
id: '2',
title: '私人医生的检查',
category: '角色扮演',
cover: _getCover('anime nurse girl white bikini hospital room'),
duration: '18:00',
intensity: 'High',
isLocked: true,
tags: ['强互动', '语音'],
),
Scenario(
id: '3',
title: '海边度假的偶遇',
category: '邻家',
cover: _getCover('anime girl blue bikini running beach ocean sunny'),
duration: '25:00',
intensity: 'Low',
isLocked: false,
tags: ['纯爱', '剧情'],
),
Scenario(
id: '4',
title: '赛博仿生人测试',
category: '科幻',
cover: _getCover('anime cyborg girl metallic bikini sci-fi lab wires'),
duration: '10:00',
intensity: 'Extreme',
isLocked: true,
tags: ['硬核', '指令'],
),
Scenario(
id: '5',
title: '深夜电台主播',
category: 'ASMR',
cover: _getCover('anime girl headphones microphone studio night'),
duration: '15:00',
intensity: 'Low',
isLocked: false,
tags: ['ASMR', '治愈'],
),
];
// Dialogue script synced with progress (0-100)
class DialogueLine {
final double time; // 0-100 progress percentage
final String text;
const DialogueLine({required this.time, required this.text});
}
const List<DialogueLine> dialogueScript = [
DialogueLine(time: 0, text: '正在建立神经连接...'),
DialogueLine(time: 5, text: '(检测到心率轻微上升)'),
DialogueLine(time: 12, text: '"放松,把控制权交给我。"'),
DialogueLine(time: 20, text: '"很好,保持呼吸频率..."'),
DialogueLine(time: 28, text: '正在启动触觉反馈模块'),
DialogueLine(time: 35, text: '"感觉到那个节奏了吗?"'),
DialogueLine(time: 45, text: '强度正在逐渐增加...'),
DialogueLine(time: 55, text: '"不要抵抗,顺从它。"'),
DialogueLine(time: 65, text: '"我会稍微加快一点速度。"'),
DialogueLine(time: 75, text: '(设备输出功率提升至 80%)'),
DialogueLine(time: 85, text: '"就是现在..."'),
DialogueLine(time: 92, text: '"做得很好,指挥官。"'),
DialogueLine(time: 100, text: '连接结束。'),
];

22
wei_ai_app/lib/main.dart Normal file
View File

@@ -0,0 +1,22 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'config/theme.dart';
import 'router/app_router.dart';
void main() {
runApp(const ProviderScope(child: WeiAiApp()));
}
class WeiAiApp extends StatelessWidget {
const WeiAiApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'Wei AI - Cyber Space',
theme: AppTheme.darkTheme,
routerConfig: appRouter,
debugShowCheckedModeBanner: false,
);
}
}

View File

@@ -0,0 +1,23 @@
class Character {
final String id;
final String name;
final String tagline;
final String avatar;
final String description;
final List<String> tags;
final double compatibility; // 硬件契合度 %
final String status; // 'online' | 'busy' | 'offline'
final bool isLocked;
const Character({
required this.id,
required this.name,
required this.tagline,
required this.avatar,
required this.description,
required this.tags,
required this.compatibility,
required this.status,
this.isLocked = false,
});
}

View File

@@ -0,0 +1,33 @@
enum DeviceMode { idle, pattern, manual }
class DeviceStatus {
final bool connected;
final double battery;
final double temperature;
final int signalStrength;
final DeviceMode currentMode;
const DeviceStatus({
this.connected = false,
this.battery = 100.0,
this.temperature = 36.5,
this.signalStrength = 0,
this.currentMode = DeviceMode.idle,
});
DeviceStatus copyWith({
bool? connected,
double? battery,
double? temperature,
int? signalStrength,
DeviceMode? currentMode,
}) {
return DeviceStatus(
connected: connected ?? this.connected,
battery: battery ?? this.battery,
temperature: temperature ?? this.temperature,
signalStrength: signalStrength ?? this.signalStrength,
currentMode: currentMode ?? this.currentMode,
);
}
}

View File

@@ -0,0 +1,22 @@
enum MessageType { text, image, audio }
enum MessageSender { user, ai }
class Message {
final String id;
final String text;
final MessageSender sender;
final MessageType type;
final DateTime timestamp;
final String? imageUrl;
final bool isLocked;
const Message({
required this.id,
required this.text,
required this.sender,
required this.type,
required this.timestamp,
this.imageUrl,
this.isLocked = false,
});
}

View File

@@ -0,0 +1,21 @@
class Scenario {
final String id;
final String title;
final String category;
final String cover;
final String duration;
final String intensity; // 'Low' | 'Medium' | 'High' | 'Extreme'
final bool isLocked;
final List<String> tags;
const Scenario({
required this.id,
required this.title,
required this.category,
required this.cover,
required this.duration,
required this.intensity,
required this.isLocked,
required this.tags,
});
}

View File

@@ -0,0 +1,33 @@
import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../models/device_status.dart';
class DeviceNotifier extends Notifier<DeviceStatus> {
Timer? _timer;
@override
DeviceStatus build() {
_startSimulation();
ref.onDispose(() {
_timer?.cancel();
});
return const DeviceStatus(connected: true, battery: 82.0);
}
void _startSimulation() {
_timer = Timer.periodic(const Duration(seconds: 5), (timer) {
if (state.battery > 0) {
state = state.copyWith(
battery: state.connected ? state.battery - 0.05 : state.battery,
signalStrength: state.connected ? (85 + (timer.tick % 10)).toInt() : 0,
);
}
});
}
void toggleConnection() {
state = state.copyWith(connected: !state.connected);
}
}
final deviceProvider = NotifierProvider<DeviceNotifier, DeviceStatus>(DeviceNotifier.new);

View File

@@ -0,0 +1,135 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import '../screens/main/main_screen.dart';
import '../screens/discovery/discovery_screen.dart';
import '../screens/library/library_screen.dart';
import '../screens/control/control_screen.dart';
import '../screens/control/free_control_screen.dart';
import '../screens/control/pattern_control_screen.dart';
import '../screens/profile/profile_screen.dart';
import '../screens/profile/settings_screen.dart';
import '../screens/profile/topup_screen.dart';
import '../screens/profile/device_manager_screen.dart';
import '../screens/profile/subscription_screen.dart';
import '../screens/profile/privacy_screen.dart';
import '../screens/profile/help_screen.dart';
import '../screens/interaction/interaction_screen.dart';
import '../screens/player/script_player_screen.dart';
// Private navigators
final _rootNavigatorKey = GlobalKey<NavigatorState>();
final _shellNavigatorDiscoveryKey = GlobalKey<NavigatorState>(debugLabel: 'shellDiscovery');
final _shellNavigatorLibraryKey = GlobalKey<NavigatorState>(debugLabel: 'shellLibrary');
final _shellNavigatorControlKey = GlobalKey<NavigatorState>(debugLabel: 'shellControl');
final _shellNavigatorProfileKey = GlobalKey<NavigatorState>(debugLabel: 'shellProfile');
final appRouter = GoRouter(
navigatorKey: _rootNavigatorKey,
initialLocation: '/discovery',
routes: [
// Top-level route for Interaction to cover BottomNav
GoRoute(
path: '/interaction/:characterId',
parentNavigatorKey: _rootNavigatorKey,
builder: (context, state) {
final characterId = state.pathParameters['characterId']!;
return InteractionScreen(characterId: characterId);
},
),
// Top-level route for Script Player to cover BottomNav
GoRoute(
path: '/player/:scenarioId',
parentNavigatorKey: _rootNavigatorKey,
builder: (context, state) {
final scenarioId = state.pathParameters['scenarioId']!;
return ScriptPlayerScreen(scenarioId: scenarioId);
},
),
// Top-level route for Free Control to cover BottomNav
GoRoute(
path: '/control/free',
parentNavigatorKey: _rootNavigatorKey,
builder: (context, state) => const FreeControlScreen(),
),
// Top-level route for Pattern Control to cover BottomNav
GoRoute(
path: '/control/pattern',
parentNavigatorKey: _rootNavigatorKey,
builder: (context, state) => const PatternControlScreen(),
),
// Profile sub-pages
GoRoute(
path: '/profile/settings',
parentNavigatorKey: _rootNavigatorKey,
builder: (context, state) => const SettingsScreen(),
),
GoRoute(
path: '/profile/topup',
parentNavigatorKey: _rootNavigatorKey,
builder: (context, state) => const TopupScreen(),
),
GoRoute(
path: '/profile/device',
parentNavigatorKey: _rootNavigatorKey,
builder: (context, state) => const DeviceManagerScreen(),
),
GoRoute(
path: '/profile/subscription',
parentNavigatorKey: _rootNavigatorKey,
builder: (context, state) => const SubscriptionScreen(),
),
GoRoute(
path: '/profile/privacy',
parentNavigatorKey: _rootNavigatorKey,
builder: (context, state) => const PrivacyScreen(),
),
GoRoute(
path: '/profile/help',
parentNavigatorKey: _rootNavigatorKey,
builder: (context, state) => const HelpScreen(),
),
StatefulShellRoute.indexedStack(
builder: (context, state, navigationShell) {
return MainScreen(navigationShell: navigationShell);
},
branches: [
StatefulShellBranch(
navigatorKey: _shellNavigatorDiscoveryKey,
routes: [
GoRoute(
path: '/discovery',
builder: (context, state) => const DiscoveryScreen(),
),
],
),
StatefulShellBranch(
navigatorKey: _shellNavigatorLibraryKey,
routes: [
GoRoute(
path: '/library',
builder: (context, state) => const LibraryScreen(),
),
],
),
StatefulShellBranch(
navigatorKey: _shellNavigatorControlKey,
routes: [
GoRoute(
path: '/control',
builder: (context, state) => const ControlScreen(),
),
],
),
StatefulShellBranch(
navigatorKey: _shellNavigatorProfileKey,
routes: [
GoRoute(
path: '/profile',
builder: (context, state) => const ProfileScreen(),
),
],
),
],
),
],
);

View File

@@ -0,0 +1,315 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:lucide_icons/lucide_icons.dart';
import '../../widgets/tab_content_layout.dart';
enum DeviceState { disconnected, connecting, connected }
class ControlScreen extends StatefulWidget {
const ControlScreen({super.key});
@override
State<ControlScreen> createState() => _ControlScreenState();
}
class _ControlScreenState extends State<ControlScreen> {
DeviceState _deviceStatus = DeviceState.disconnected;
void _connectDevice() {
setState(() => _deviceStatus = DeviceState.connecting);
Future.delayed(const Duration(milliseconds: 1500), () {
if (mounted) setState(() => _deviceStatus = DeviceState.connected);
});
}
void _disconnectDevice() {
setState(() => _deviceStatus = DeviceState.disconnected);
}
@override
Widget build(BuildContext context) {
const double bottomNavHeight = 90;
return TabContentLayout(
child: SingleChildScrollView(
padding: EdgeInsets.fromLTRB(16, 8, 16, bottomNavHeight + 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Device Card
_DeviceCard(
status: _deviceStatus,
onConnect: _connectDevice,
onDisconnect: _disconnectDevice,
),
const SizedBox(height: 16),
// Mode Selection Title
Text(
'操控模式',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
letterSpacing: 2,
color: Colors.white.withOpacity(0.4),
),
),
const SizedBox(height: 12),
// Free Control Button
_ModeButton(
title: '自由操控',
subtitle: '指尖滑动控制 • 实时反馈',
icon: LucideIcons.sliders,
iconColor: const Color(0xFFC084FC),
enabled: _deviceStatus == DeviceState.connected,
onTap: () => context.push('/control/free'),
),
const SizedBox(height: 12),
// Pattern Control Button
_ModeButton(
title: '波形模式',
subtitle: '6种预设震动韵律',
icon: LucideIcons.waves,
iconColor: const Color(0xFF60A5FA),
enabled: _deviceStatus == DeviceState.connected,
onTap: () => context.push('/control/pattern'),
),
],
),
),
);
}
}
// Device Card Widget
class _DeviceCard extends StatelessWidget {
final DeviceState status;
final VoidCallback onConnect;
final VoidCallback onDisconnect;
const _DeviceCard({
required this.status,
required this.onConnect,
required this.onDisconnect,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.circular(24),
border: Border.all(color: Colors.white.withOpacity(0.2)),
),
child: Stack(
children: [
// Background Icon
Positioned(
right: -20,
top: -20,
child: Icon(
LucideIcons.bluetooth,
size: 100,
color: Colors.white.withOpacity(0.05),
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Text(
'Link-X Pro',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
if (status == DeviceState.connected) ...[
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: const Color(0xFF34D399).withOpacity(0.2),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFF34D399).withOpacity(0.3)),
),
child: const Text(
'ONLINE',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Color(0xFF34D399),
),
),
),
],
],
),
const SizedBox(height: 4),
Text(
status == DeviceState.disconnected
? '设备未连接'
: status == DeviceState.connecting
? '正在搜索信号...'
: 'ID: 884-X9-01',
style: TextStyle(
fontSize: 12,
fontFamily: 'monospace',
color: Colors.white.withOpacity(0.6),
),
),
],
),
// Connect Button
GestureDetector(
onTap: status == DeviceState.connected ? onDisconnect : onConnect,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
color: status == DeviceState.connected
? Colors.white.withOpacity(0.1)
: status == DeviceState.connecting
? Colors.white.withOpacity(0.2)
: Colors.white,
borderRadius: BorderRadius.circular(20),
border: status == DeviceState.connected
? Border.all(color: Colors.white.withOpacity(0.1))
: null,
),
child: Text(
status == DeviceState.connected
? '断开'
: status == DeviceState.connecting
? '连接中'
: '连接设备',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: status == DeviceState.connected
? Colors.white.withOpacity(0.7)
: const Color(0xFF2E1065),
),
),
),
),
],
),
// Battery Info
if (status == DeviceState.connected) ...[
const SizedBox(height: 16),
Row(
children: [
const Icon(LucideIcons.battery, size: 16, color: Color(0xFF34D399)),
const SizedBox(width: 8),
const Text(
'85%',
style: TextStyle(
fontSize: 14,
fontFamily: 'monospace',
color: Colors.white,
),
),
],
),
],
],
),
],
),
);
}
}
// Mode Button Widget
class _ModeButton extends StatelessWidget {
final String title;
final String subtitle;
final IconData icon;
final Color iconColor;
final bool enabled;
final VoidCallback onTap;
const _ModeButton({
required this.title,
required this.subtitle,
required this.icon,
required this.iconColor,
required this.enabled,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: enabled ? onTap : null,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 300),
opacity: enabled ? 1.0 : 0.5,
child: Container(
height: 120,
padding: const EdgeInsets.symmetric(horizontal: 24),
decoration: BoxDecoration(
color: Colors.white.withOpacity(enabled ? 0.1 : 0.05),
borderRadius: BorderRadius.circular(24),
border: Border.all(
color: Colors.white.withOpacity(enabled ? 0.2 : 0.05),
),
),
child: Row(
children: [
Container(
width: 56,
height: 56,
decoration: BoxDecoration(
color: iconColor.withOpacity(0.2),
shape: BoxShape.circle,
),
child: Icon(icon, size: 28, color: iconColor),
),
const SizedBox(width: 20),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 4),
Text(
subtitle,
style: TextStyle(
fontSize: 12,
color: Colors.white.withOpacity(0.5),
),
),
],
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,205 @@
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:lucide_icons/lucide_icons.dart';
class FreeControlScreen extends StatefulWidget {
const FreeControlScreen({super.key});
@override
State<FreeControlScreen> createState() => _FreeControlScreenState();
}
class _FreeControlScreenState extends State<FreeControlScreen> {
double _intensity = 0;
bool _isClimax = false;
void _handleInteraction(Offset localPosition, double height) {
final relativeY = 1 - (localPosition.dy / height).clamp(0.0, 1.0);
setState(() => _intensity = (relativeY * 100).roundToDouble());
}
void _handleClimax() {
if (_isClimax) return;
setState(() {
_isClimax = true;
_intensity = 100;
});
HapticFeedback.heavyImpact();
Future.delayed(const Duration(seconds: 3), () {
if (mounted) {
setState(() {
_isClimax = false;
_intensity = 20;
});
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF2E1065),
body: SafeArea(
child: Column(
children: [
// Header
Padding(
padding: const EdgeInsets.fromLTRB(8, 8, 16, 16),
child: Row(
children: [
IconButton(
onPressed: () => Navigator.of(context).pop(),
icon: Icon(LucideIcons.chevronLeft, color: Colors.white.withOpacity(0.7)),
),
const Text(
'自由操控',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
],
),
),
// Control Area
Expanded(
child: Center(
child: LayoutBuilder(
builder: (context, constraints) {
final controlHeight = constraints.maxHeight * 0.65;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onPanUpdate: (details) {
_handleInteraction(details.localPosition, controlHeight);
},
onTapDown: (details) {
_handleInteraction(details.localPosition, controlHeight);
},
child: Container(
width: 180,
height: controlHeight,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(32),
border: Border.all(color: Colors.white.withOpacity(0.2)),
),
child: Stack(
children: [
// Fill Level
Align(
alignment: Alignment.bottomCenter,
child: AnimatedContainer(
duration: const Duration(milliseconds: 50),
width: double.infinity,
height: controlHeight * (_intensity / 100),
decoration: BoxDecoration(
color: _isClimax
? Colors.red.withOpacity(0.5)
: const Color(0xFFC084FC).withOpacity(0.5),
borderRadius: BorderRadius.circular(32),
),
),
),
// Labels
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text(
'MAX',
style: TextStyle(
fontSize: 10,
fontFamily: 'monospace',
color: Colors.white.withOpacity(0.4),
),
),
),
Center(
child: Text(
_intensity.toInt().toString(),
style: TextStyle(
fontSize: 48,
fontWeight: FontWeight.bold,
fontFamily: 'monospace',
color: _isClimax ? Colors.red[200] : Colors.white,
),
),
),
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Text(
'OFF',
style: TextStyle(
fontSize: 10,
fontFamily: 'monospace',
color: Colors.white.withOpacity(0.4),
),
),
),
],
),
],
),
),
),
const SizedBox(height: 16),
Text(
'上下滑动触控板以控制强度',
style: TextStyle(
fontSize: 12,
fontFamily: 'monospace',
letterSpacing: 1,
color: Colors.white.withOpacity(0.6),
),
),
],
);
},
),
),
),
// Climax Button
Padding(
padding: const EdgeInsets.fromLTRB(24, 0, 24, 40),
child: GestureDetector(
onTap: _handleClimax,
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 18),
decoration: BoxDecoration(
color: _isClimax ? Colors.red : Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: _isClimax ? Colors.red : Colors.red.withOpacity(0.5),
),
),
child: Center(
child: Text(
_isClimax ? 'MAX OUTPUT...' : '一键爆发',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
letterSpacing: 2,
color: _isClimax ? Colors.white : Colors.red[300],
),
),
),
),
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,264 @@
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:fl_chart/fl_chart.dart';
class PatternControlScreen extends StatefulWidget {
const PatternControlScreen({super.key});
@override
State<PatternControlScreen> createState() => _PatternControlScreenState();
}
class _PatternControlScreenState extends State<PatternControlScreen> {
String? _activePattern;
double _globalIntensity = 50;
List<FlSpot> _waveData = List.generate(20, (i) => FlSpot(i.toDouble(), 10));
Timer? _waveTimer;
static const List<Map<String, dynamic>> _patterns = [
{'id': 'pulse', 'name': '脉冲跳动', 'icon': LucideIcons.activity, 'color': Color(0xFFC084FC)},
{'id': 'wave', 'name': '深海潮汐', 'icon': LucideIcons.waves, 'color': Color(0xFF60A5FA)},
{'id': 'climb', 'name': '登峰造极', 'icon': LucideIcons.rotateCw, 'color': Color(0xFF34D399)},
{'id': 'storm', 'name': '雷雨风暴', 'icon': LucideIcons.zap, 'color': Color(0xFFFBBF24)},
{'id': 'chaos', 'name': '随机漫步', 'icon': LucideIcons.sliders, 'color': Color(0xFFF472B6)},
{'id': 'sos', 'name': 'SOS', 'icon': LucideIcons.power, 'color': Color(0xFFF87171)},
];
@override
void initState() {
super.initState();
_startWaveAnimation();
}
void _startWaveAnimation() {
_waveTimer?.cancel();
_waveTimer = Timer.periodic(const Duration(milliseconds: 100), (_) {
if (!mounted) return;
setState(() {
final random = Random();
_waveData = [
..._waveData.sublist(1),
FlSpot(
19,
_activePattern != null
? random.nextDouble() * _globalIntensity + 20
: 10,
),
];
// Update x coordinates
for (int i = 0; i < _waveData.length; i++) {
_waveData[i] = FlSpot(i.toDouble(), _waveData[i].y);
}
});
});
}
@override
void dispose() {
_waveTimer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF2E1065),
body: SafeArea(
child: Column(
children: [
// Header
Padding(
padding: const EdgeInsets.fromLTRB(8, 8, 16, 16),
child: Row(
children: [
IconButton(
onPressed: () => Navigator.of(context).pop(),
icon: Icon(LucideIcons.chevronLeft, color: Colors.white.withOpacity(0.7)),
),
const Text(
'波形控制',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
],
),
),
// Wave Chart
Container(
height: 140,
margin: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(24),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: LineChart(
LineChartData(
gridData: const FlGridData(show: false),
titlesData: const FlTitlesData(show: false),
borderData: FlBorderData(show: false),
minX: 0,
maxX: 19,
minY: 0,
maxY: 100,
lineBarsData: [
LineChartBarData(
spots: _waveData,
isCurved: true,
curveSmoothness: 0.3,
color: const Color(0xFFE9D5FF),
barWidth: 3,
isStrokeCapRound: true,
dotData: const FlDotData(show: false),
belowBarData: BarAreaData(
show: true,
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
const Color(0xFFC084FC).withOpacity(0.6),
const Color(0xFFC084FC).withOpacity(0),
],
),
),
),
],
lineTouchData: const LineTouchData(enabled: false),
),
),
),
const SizedBox(height: 24),
// Intensity Slider
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'强度',
style: TextStyle(
fontSize: 12,
letterSpacing: 2,
color: Colors.white.withOpacity(0.6),
),
),
Text(
'${_globalIntensity.toInt()}%',
style: TextStyle(
fontSize: 12,
letterSpacing: 2,
color: Colors.white.withOpacity(0.6),
),
),
],
),
const SizedBox(height: 8),
SliderTheme(
data: SliderThemeData(
trackHeight: 8,
thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 10),
overlayShape: const RoundSliderOverlayShape(overlayRadius: 20),
activeTrackColor: const Color(0xFFC084FC),
inactiveTrackColor: Colors.white.withOpacity(0.2),
thumbColor: Colors.white,
),
child: Slider(
value: _globalIntensity,
min: 0,
max: 100,
onChanged: (value) => setState(() => _globalIntensity = value),
),
),
],
),
),
const SizedBox(height: 24),
// Pattern Grid
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: GridView.builder(
padding: const EdgeInsets.only(bottom: 24),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
childAspectRatio: 1.5,
),
itemCount: _patterns.length,
itemBuilder: (context, index) {
final pattern = _patterns[index];
final isActive = _activePattern == pattern['id'];
return GestureDetector(
onTap: () {
setState(() {
_activePattern = isActive ? null : pattern['id'] as String;
});
HapticFeedback.selectionClick();
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
decoration: BoxDecoration(
color: isActive ? Colors.white : Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: isActive ? Colors.white : Colors.white.withOpacity(0.1),
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: isActive
? const Color(0xFF2E1065).withOpacity(0.1)
: Colors.white.withOpacity(0.05),
shape: BoxShape.circle,
),
child: Icon(
pattern['icon'] as IconData,
size: 18,
color: isActive
? const Color(0xFF2E1065)
: pattern['color'] as Color,
),
),
const SizedBox(height: 8),
Text(
pattern['name'] as String,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: isActive ? const Color(0xFF2E1065) : Colors.white.withOpacity(0.6),
),
),
],
),
),
);
},
),
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,306 @@
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:go_router/go_router.dart';
import '../../data/mock_data.dart';
import '../../models/character.dart';
import '../../widgets/tab_content_layout.dart';
class DiscoveryScreen extends StatefulWidget {
const DiscoveryScreen({super.key});
@override
State<DiscoveryScreen> createState() => _DiscoveryScreenState();
}
class _DiscoveryScreenState extends State<DiscoveryScreen> {
String _activeFilter = 'all';
final List<Map<String, String>> _filters = [
{'id': 'all', 'label': '全部'},
{'id': 'gentle', 'label': '温柔治愈'},
{'id': 'dom', 'label': '主导强势'},
{'id': 'wild', 'label': '反差/猎奇'},
{'id': 'voice', 'label': '语音陪聊'},
{'id': 'scenario', 'label': '场景扮演'},
{'id': 'exclusive', 'label': '会员限定'},
];
List<Character> get _filteredCharacters {
if (_activeFilter == 'all') return mockCharacters;
return mockCharacters.where((c) {
final tags = c.tags.join('');
if (_activeFilter == 'gentle') return tags.contains('治愈') || tags.contains('温顺') || tags.contains('医疗');
if (_activeFilter == 'dom') return tags.contains('强势') || tags.contains('调教') || tags.contains('指令');
if (_activeFilter == 'wild') return tags.contains('病娇') || tags.contains('神秘') || tags.contains('不稳定') || tags.contains('极乐');
if (_activeFilter == 'exclusive') return c.isLocked;
return false;
}).toList();
}
@override
Widget build(BuildContext context) {
const double bottomNavHeight = 90;
return TabContentLayout(
child: Column(
children: [
// 1. Sticky Filter Bar (simulated)
SizedBox(
height: 50,
child: ListView.separated(
padding: const EdgeInsets.symmetric(horizontal: 16),
scrollDirection: Axis.horizontal,
itemCount: _filters.length,
separatorBuilder: (_, __) => const SizedBox(width: 12),
itemBuilder: (context, index) {
final filter = _filters[index];
final isActive = _activeFilter == filter['id'];
return Center(
child: GestureDetector(
onTap: () => setState(() => _activeFilter = filter['id']!),
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
decoration: BoxDecoration(
color: isActive ? Colors.white : Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: isActive ? Colors.white : Colors.white.withOpacity(0.1),
),
),
child: Text(
filter['label']!,
style: TextStyle(
fontSize: 14,
fontWeight: isActive ? FontWeight.bold : FontWeight.normal,
color: isActive ? const Color(0xFF2E1065) : Colors.white.withOpacity(0.7),
),
),
),
),
);
},
),
),
// 2. Grid Layout
Expanded(
child: _filteredCharacters.isEmpty
? Center(child: Text('暂无匹配角色', style: TextStyle(color: Colors.white.withOpacity(0.5))))
: GridView.builder(
padding: const EdgeInsets.fromLTRB(16, 16, 16, bottomNavHeight + 20),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 3 / 4,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
),
itemCount: _filteredCharacters.length,
itemBuilder: (context, index) {
final char = _filteredCharacters[index];
return _CharacterCard(
character: char,
onTap: () {
if (!char.isLocked) {
context.push('/interaction/${char.id}');
}
},
)
.animate()
.fadeIn(duration: 400.ms, delay: (index * 100).ms)
.scale(begin: const Offset(0.9, 0.9));
},
),
),
],
),
);
}
}
class _CharacterCard extends StatelessWidget {
final Character character;
final VoidCallback onTap;
const _CharacterCard({
required this.character,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3),
blurRadius: 15,
offset: const Offset(0, 6),
),
BoxShadow(
color: const Color(0xFFA855F7).withOpacity(0.1),
blurRadius: 20,
offset: const Offset(0, 0),
),
],
),
child: Stack(
fit: StackFit.expand,
children: [
// Clipped content area - ensures all elements respect border radius
ClipRRect(
borderRadius: BorderRadius.circular(24),
child: Stack(
fit: StackFit.expand,
children: [
// Background Image
Image.network(
character.avatar,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
color: Colors.black26,
child: const Center(child: CircularProgressIndicator(strokeWidth: 2)),
);
},
),
// Gradient Overlay
Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.transparent,
Colors.transparent,
Color(0xFF2E1065), // Deep purple at bottom
],
stops: [0.0, 0.5, 1.0],
),
),
),
// Top Left: Popularity/Compatibility Badge
if (!character.isLocked)
Positioned(
top: 12,
left: 12,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.4),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(LucideIcons.flame, size: 12, color: Color(0xFFF472B6)),
const SizedBox(width: 4),
Text(
'${character.compatibility}%',
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
fontFamily: 'monospace',
color: Colors.white,
),
),
],
),
),
),
// Top Right: Lock Icon
if (character.isLocked)
Positioned(
top: 12,
right: 12,
child: Container(
width: 32,
height: 32,
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.4),
shape: BoxShape.circle,
border: Border.all(color: Colors.white.withOpacity(0.2)),
),
child: const Icon(LucideIcons.lock, size: 14, color: Colors.white70),
),
),
// Bottom Content
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
character.name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white,
shadows: [Shadow(color: Colors.black45, blurRadius: 2, offset: Offset(0, 1))],
),
),
const SizedBox(height: 4),
Wrap(
spacing: 4,
runSpacing: 4,
children: character.tags.take(2).map((tag) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.12),
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: Colors.white.withOpacity(0.25),
width: 0.8,
),
),
child: Text(
tag,
style: const TextStyle(fontSize: 10, color: Colors.white),
),
);
}).toList(),
),
],
),
),
),
],
),
),
// Border Overlay - Outside ClipRRect to ensure full visibility
Positioned.fill(
child: IgnorePointer(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
border: Border.all(
color: Colors.white.withOpacity(0.15),
width: 1.2,
),
),
),
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,231 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:go_router/go_router.dart';
import '../../models/character.dart';
import '../../models/message.dart';
import '../../data/mock_data.dart';
import 'voice_mode_overlay.dart';
class InteractionScreen extends StatefulWidget {
final String characterId;
const InteractionScreen({super.key, required this.characterId});
@override
State<InteractionScreen> createState() => _InteractionScreenState();
}
class _InteractionScreenState extends State<InteractionScreen> {
late Character _character;
final List<Message> _messages = List.from(mockMessages);
final TextEditingController _controller = TextEditingController();
bool _isVoiceMode = false;
@override
void initState() {
super.initState();
_character = mockCharacters.firstWhere(
(c) => c.id == widget.characterId,
orElse: () => mockCharacters.first,
);
}
void _sendMessage() {
if (_controller.text.trim().isEmpty) return;
final newUserMsg = Message(
id: DateTime.now().toString(),
text: _controller.text,
sender: MessageSender.user,
type: MessageType.text,
timestamp: DateTime.now()
);
setState(() {
_messages.add(newUserMsg);
_controller.clear();
});
// Mock AI Reply
Future.delayed(const Duration(seconds: 2), () {
if (!mounted) return;
final newAiMsg = Message(
id: DateTime.now().toString(),
text: '我收到了你的信号: "${newUserMsg.text}"。这让我感觉很好...',
sender: MessageSender.ai,
type: MessageType.text,
timestamp: DateTime.now()
);
setState(() {
_messages.add(newAiMsg);
});
});
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
flexibleSpace: ClipRRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(color: Colors.black.withOpacity(0.5)),
),
),
leading: IconButton(
icon: const Icon(LucideIcons.arrowLeft, color: Colors.white),
onPressed: () => context.pop(),
),
title: Row(
children: [
CircleAvatar(
backgroundImage: NetworkImage(_character.avatar),
radius: 16,
),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(_character.name, style: const TextStyle(fontSize: 16, color: Colors.white)),
Row(
children: [
Container(width: 6, height: 6, decoration: const BoxDecoration(color: Color(0xFF10B981), shape: BoxShape.circle)),
const SizedBox(width: 4),
Text('Online', style: TextStyle(fontSize: 10, color: Colors.white.withOpacity(0.7))),
],
)
],
)
],
),
actions: [
IconButton(icon: const Icon(LucideIcons.moreVertical, color: Colors.white), onPressed: () {}),
],
),
body: Container(
decoration: const BoxDecoration(
color: Color(0xFF2E1065),
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFF2E1065), Color(0xFF0F172A)],
)
),
child: Column(
children: [
Expanded(
child: ListView.separated(
padding: const EdgeInsets.only(top: 120, bottom: 20, left: 16, right: 16),
itemCount: _messages.length,
separatorBuilder: (_, __) => const SizedBox(height: 16),
itemBuilder: (context, index) {
final msg = _messages[index];
final isMe = msg.sender == MessageSender.user;
return Align(
alignment: isMe ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.75),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: isMe ? const Color(0xFFA855F7) : Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(16),
topRight: const Radius.circular(16),
bottomLeft: isMe ? const Radius.circular(16) : const Radius.circular(2),
bottomRight: isMe ? const Radius.circular(2) : const Radius.circular(16),
),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Text(
msg.text,
style: const TextStyle(color: Colors.white, fontSize: 14, height: 1.4),
),
),
);
},
),
),
// Input Area
ClipRRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 20, sigmaY: 20),
child: Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom + 10,
top: 10,
left: 16,
right: 16
),
color: Colors.black.withOpacity(0.4),
child: Row(
children: [
GestureDetector(
onTap: () => setState(() => _isVoiceMode = true),
child: Container(
width: 44,
height: 44,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white.withOpacity(0.1),
border: Border.all(color: Colors.white.withOpacity(0.1))
),
child: const Icon(LucideIcons.phone, color: Colors.white, size: 20),
),
),
const SizedBox(width: 8),
Expanded(
child: TextField(
controller: _controller,
style: const TextStyle(color: Colors.white),
decoration: InputDecoration(
filled: true,
fillColor: Colors.white.withOpacity(0.05),
hintText: 'Type a message...',
hintStyle: TextStyle(color: Colors.white.withOpacity(0.3)),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(24),
borderSide: BorderSide.none,
),
contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
),
),
),
const SizedBox(width: 8),
GestureDetector(
onTap: _sendMessage,
child: Container(
width: 44,
height: 44,
decoration: const BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(colors: [Color(0xFFA855F7), Color(0xFFEC4899)])
),
child: const Icon(LucideIcons.send, color: Colors.white, size: 20),
),
)
],
),
),
),
)
],
), // Column
), // Container
), // Scaffold
if (_isVoiceMode)
VoiceModeOverlay(
character: _character,
onClose: () => setState(() => _isVoiceMode = false),
),
],
);
}
}

View File

@@ -0,0 +1,263 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
import '../../models/character.dart';
class VoiceModeOverlay extends StatefulWidget {
final Character character;
final VoidCallback onClose;
const VoiceModeOverlay({
super.key,
required this.character,
required this.onClose,
});
@override
State<VoiceModeOverlay> createState() => _VoiceModeOverlayState();
}
class _VoiceModeOverlayState extends State<VoiceModeOverlay> with SingleTickerProviderStateMixin {
bool _isMicMuted = false;
bool _isSpeakerOn = true;
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this, duration: const Duration(seconds: 2))
..repeat(reverse: true);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Positioned.fill(
child: Material(
type: MaterialType.transparency,
child: Container(
color: const Color(0xFF2E1065),
child: Stack(
children: [
// Background Image with Blur
Positioned.fill(
child: Image.network(
widget.character.avatar,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Container(color: const Color(0xFF2E1065));
},
),
),
Positioned.fill(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 20, sigmaY: 20),
child: Container(
color: const Color(0xFF2E1065).withOpacity(0.8),
),
),
),
// Main Content
SafeArea(
child: Column(
children: [
// Header
Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
onPressed: widget.onClose,
icon: const Icon(LucideIcons.chevronLeft, color: Colors.white70),
style: IconButton.styleFrom(
backgroundColor: Colors.white.withOpacity(0.1),
),
),
],
),
),
const Spacer(),
// Character Info & Status
Column(
children: [
Text(
widget.character.name,
style: const TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
_isMicMuted ? 'Mic Muted' : 'Listening...',
style: TextStyle(
color: Colors.white.withOpacity(0.6),
fontSize: 12,
letterSpacing: 2,
fontWeight: FontWeight.w500,
),
),
],
),
const SizedBox(height: 48),
// Avatar pulsing animation
SizedBox(
width: 200,
height: 200,
child: Stack(
alignment: Alignment.center,
children: [
if (!_isMicMuted)
AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Container(
width: 200 * (0.8 + 0.2 * _controller.value),
height: 200 * (0.8 + 0.2 * _controller.value),
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Colors.white.withOpacity(0.2 * (1 - _controller.value)),
width: 1,
),
),
);
},
),
Container(
width: 160,
height: 160,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white.withOpacity(0.3), width: 2),
boxShadow: [
BoxShadow(
color: const Color(0xFFC084FC).withOpacity(0.5),
blurRadius: 30,
spreadRadius: 0
)
],
image: DecorationImage(
image: NetworkImage(widget.character.avatar),
fit: BoxFit.cover,
),
),
),
],
),
),
const Spacer(),
// Waveform (Simulated)
SizedBox(
height: 32,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: List.generate(5, (index) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 2.0),
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Container(
width: 4,
height: _isMicMuted
? 4
: 10 + (20 * (index % 2 == 0 ? _controller.value : 1 - _controller.value)),
decoration: BoxDecoration(
color: Colors.white.withOpacity(_isMicMuted ? 0.2 : 0.8),
borderRadius: BorderRadius.circular(2),
),
);
}
),
);
}),
),
),
const SizedBox(height: 48),
// Bottom Controls
Padding(
padding: const EdgeInsets.symmetric(horizontal: 48.0, vertical: 32.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Mic Toggle
IconButton(
onPressed: () => setState(() => _isMicMuted = !_isMicMuted),
icon: Icon(_isMicMuted ? LucideIcons.micOff : LucideIcons.mic),
iconSize: 24,
style: IconButton.styleFrom(
backgroundColor: _isMicMuted
? Colors.white
: Colors.white.withOpacity(0.1),
foregroundColor: _isMicMuted
? const Color(0xFF2E1065)
: Colors.white,
padding: const EdgeInsets.all(16),
minimumSize: const Size(64, 64),
),
),
// End Call
IconButton(
onPressed: widget.onClose,
icon: const Icon(LucideIcons.phoneOff),
iconSize: 32,
style: IconButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
padding: const EdgeInsets.all(20),
minimumSize: const Size(80, 80),
),
),
// Speaker Toggle
IconButton(
onPressed: () => setState(() => _isSpeakerOn = !_isSpeakerOn),
icon: Icon(
_isSpeakerOn ? LucideIcons.volume2 : LucideIcons.volumeX
),
iconSize: 24,
style: IconButton.styleFrom(
backgroundColor: _isSpeakerOn
? Colors.white.withOpacity(0.1)
: Colors.white.withOpacity(0.05),
foregroundColor: _isSpeakerOn
? Colors.white
: Colors.white.withOpacity(0.5),
padding: const EdgeInsets.all(16),
minimumSize: const Size(64, 64),
),
),
],
),
),
],
),
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,325 @@
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:go_router/go_router.dart';
import '../../data/mock_data.dart';
import '../../models/scenario.dart';
import '../../widgets/tab_content_layout.dart';
class LibraryScreen extends StatefulWidget {
const LibraryScreen({super.key});
@override
State<LibraryScreen> createState() => _LibraryScreenState();
}
class _LibraryScreenState extends State<LibraryScreen> {
String _activeCategory = '全部';
final List<String> _categories = ['全部', '职场', '邻家', '科幻', 'ASMR'];
List<Scenario> get _filteredScenarios {
if (_activeCategory == '全部') return mockScenarios;
return mockScenarios.where((s) =>
s.category == _activeCategory || s.tags.contains(_activeCategory)
).toList();
}
Color _getIntensityColor(String intensity) {
switch (intensity) {
case 'Low':
return const Color(0xFF34D399); // Emerald
case 'Medium':
return const Color(0xFF60A5FA); // Blue
case 'High':
return const Color(0xFFFBBF24); // Amber
case 'Extreme':
return const Color(0xFFF472B6); // Pink
default:
return Colors.white.withOpacity(0.5);
}
}
@override
Widget build(BuildContext context) {
const double bottomNavHeight = 90;
return TabContentLayout(
child: Column(
children: [
// Filter Bar
SizedBox(
height: 50,
child: ListView.separated(
padding: const EdgeInsets.symmetric(horizontal: 16),
scrollDirection: Axis.horizontal,
itemCount: _categories.length,
separatorBuilder: (_, __) => const SizedBox(width: 12),
itemBuilder: (context, index) {
final category = _categories[index];
final isActive = _activeCategory == category;
return Center(
child: GestureDetector(
onTap: () => setState(() => _activeCategory = category),
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
decoration: BoxDecoration(
gradient: isActive
? const LinearGradient(
colors: [Color(0xFFC084FC), Color(0xFFF472B6)],
)
: null,
color: isActive ? null : Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: isActive ? Colors.transparent : Colors.white.withOpacity(0.1),
),
boxShadow: isActive
? [
BoxShadow(
color: const Color(0xFFC084FC).withOpacity(0.4),
blurRadius: 15,
),
]
: null,
),
child: Text(
category,
style: TextStyle(
fontSize: 14,
fontWeight: isActive ? FontWeight.bold : FontWeight.normal,
color: isActive ? Colors.white : Colors.white.withOpacity(0.7),
),
),
),
),
);
},
),
),
const SizedBox(height: 8),
// Scenario List
Expanded(
child: _filteredScenarios.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(LucideIcons.activity, size: 32, color: Colors.white.withOpacity(0.5)),
const SizedBox(height: 8),
Text(
'暂无相关剧本',
style: TextStyle(fontSize: 12, color: Colors.white.withOpacity(0.5)),
),
],
),
)
: ListView.builder(
padding: EdgeInsets.fromLTRB(16, 8, 16, bottomNavHeight + 20),
itemCount: _filteredScenarios.length,
itemBuilder: (context, index) {
final scenario = _filteredScenarios[index];
return _ScenarioCard(
scenario: scenario,
intensityColor: _getIntensityColor(scenario.intensity),
onTap: () {
if (!scenario.isLocked) {
context.push('/player/${scenario.id}');
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('需要积分解锁此高级内容'),
backgroundColor: Color(0xFF4C1D95),
),
);
}
},
)
.animate()
.fadeIn(duration: 400.ms, delay: (index * 80).ms)
.slideX(begin: 0.05, end: 0);
},
),
),
],
),
);
}
}
class _ScenarioCard extends StatelessWidget {
final Scenario scenario;
final Color intensityColor;
final VoidCallback onTap;
const _ScenarioCard({
required this.scenario,
required this.intensityColor,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFF4C1D95).withOpacity(0.2),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Row(
children: [
// Left: Thumbnail
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.white.withOpacity(0.1)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
clipBehavior: Clip.antiAlias,
child: Stack(
fit: StackFit.expand,
children: [
Image.network(
scenario.cover,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
color: Colors.black26,
child: const Center(
child: CircularProgressIndicator(strokeWidth: 2),
),
);
},
),
if (scenario.isLocked)
Container(
color: const Color(0xFF2E1065).withOpacity(0.6),
child: const Center(
child: Icon(LucideIcons.lock, size: 16, color: Colors.white70),
),
),
],
),
),
const SizedBox(width: 16),
// Center: Info
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Category & Intensity
Row(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.circular(4),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Text(
scenario.category,
style: TextStyle(
fontSize: 10,
fontFamily: 'monospace',
letterSpacing: 1,
color: Colors.white.withOpacity(0.9),
),
),
),
const SizedBox(width: 8),
Icon(LucideIcons.zap, size: 10, color: intensityColor),
const SizedBox(width: 2),
Text(
scenario.intensity,
style: TextStyle(
fontSize: 9,
fontWeight: FontWeight.bold,
color: intensityColor,
),
),
],
),
const SizedBox(height: 6),
// Title
Text(
scenario.title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 6),
// Tags
Wrap(
spacing: 6,
children: scenario.tags.take(3).map((tag) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(4),
border: Border.all(color: Colors.white.withOpacity(0.05)),
),
child: Text(
'#$tag',
style: TextStyle(
fontSize: 10,
color: Colors.white.withOpacity(0.7),
),
),
);
}).toList(),
),
],
),
),
// Right: Play Button
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: scenario.isLocked
? Colors.white.withOpacity(0.05)
: const Color(0xFFC084FC).withOpacity(0.2),
),
child: Center(
child: Icon(
scenario.isLocked ? LucideIcons.lock : LucideIcons.play,
size: scenario.isLocked ? 16 : 18,
color: scenario.isLocked
? Colors.white.withOpacity(0.3)
: const Color(0xFFC084FC),
),
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,179 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../widgets/status_bar.dart';
import '../../providers/device_provider.dart';
import '../../widgets/glass_bottom_nav.dart';
class MainScreen extends ConsumerStatefulWidget {
final StatefulNavigationShell navigationShell;
const MainScreen({
super.key,
required this.navigationShell,
});
@override
ConsumerState<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends ConsumerState<MainScreen> {
void _goBranch(int index) {
widget.navigationShell.goBranch(
index,
initialLocation: index == widget.navigationShell.currentIndex,
);
}
Map<String, String> _getPageTitle(int index) {
switch (index) {
case 0:
return {'title': '专属推荐', 'subtitle': '基于偏好生成的私密匹配列表'};
case 1:
return {'title': '剧本馆', 'subtitle': '沉浸式感官体验库'};
case 2:
return {'title': '手动实验室', 'subtitle': '实时触觉反馈控制'};
case 3:
return {'title': '个人中心', 'subtitle': 'ID: 884-291-00X'};
default:
return {'title': 'Wei AI', 'subtitle': ''};
}
}
@override
Widget build(BuildContext context) {
final pageMeta = _getPageTitle(widget.navigationShell.currentIndex);
return Container(
decoration: const BoxDecoration(
color: Color(0xFF2E1065), // Fallback
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFF2E1065), // #2e1065
Color(0xFF4C1D95), // #4c1d95
Color(0xFF831843), // #831843
],
stops: [0.0, 0.4, 1.0],
),
),
child: Stack(
children: [
// Radial Gradient 1 (Top Left)
Positioned(
top: -100,
left: -100,
child: Container(
width: 500, // Larger size for smoother gradient
height: 500,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: RadialGradient(
colors: [
const Color(0xFFA855F7).withOpacity(0.4), // rgba(168, 85, 247, 0.4)
Colors.transparent,
],
),
),
),
),
// Radial Gradient 2 (Top Right)
Positioned(
top: -100,
right: -100,
child: Container(
width: 500,
height: 500,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: RadialGradient(
colors: [
const Color(0xFFEC4899).withOpacity(0.3), // rgba(236, 72, 153, 0.3)
Colors.transparent,
],
),
),
),
),
// Scaffold
Scaffold(
backgroundColor: Colors.transparent,
extendBody: true, // Important for glass nav
extendBodyBehindAppBar: false, // Body starts below AppBar naturally
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
titleSpacing: 20,
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
const Icon(LucideIcons.hexagon, size: 12, color: Colors.white70),
const SizedBox(width: 6),
Text(
'WEI AI',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
letterSpacing: 2,
color: Colors.white.withOpacity(0.8),
),
),
],
),
const SizedBox(height: 4),
Text(
pageMeta['title']!,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
shadows: [Shadow(color: Colors.black26, offset: Offset(0, 2), blurRadius: 4)]
)
),
],
),
centerTitle: false,
toolbarHeight: 90, // Increased toolbar height to accommodate subtitle
actions: [
Padding(
padding: const EdgeInsets.only(right: 20.0, top: 20), // Adjusted top padding to align with title
child: StatusBar(
status: ref.watch(deviceProvider),
onTap: () {
ref.read(deviceProvider.notifier).toggleConnection();
},
),
),
],
bottom: pageMeta['subtitle']!.isNotEmpty ? PreferredSize(
preferredSize: const Size.fromHeight(20),
child: Padding(
padding: const EdgeInsets.only(left: 20, bottom: 10),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
pageMeta['subtitle']!,
style: TextStyle(fontSize: 10, color: Colors.white.withOpacity(0.6)),
),
),
),
) : null,
),
body: widget.navigationShell,
bottomNavigationBar: GlassBottomNav(
currentIndex: widget.navigationShell.currentIndex,
onTap: _goBranch,
),
),
],
),
);
}
}

View File

@@ -0,0 +1,515 @@
import 'dart:async';
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:fl_chart/fl_chart.dart';
import '../../data/mock_data.dart';
import '../../models/scenario.dart';
class ScriptPlayerScreen extends StatefulWidget {
final String scenarioId;
const ScriptPlayerScreen({super.key, required this.scenarioId});
@override
State<ScriptPlayerScreen> createState() => _ScriptPlayerScreenState();
}
class _ScriptPlayerScreenState extends State<ScriptPlayerScreen>
with TickerProviderStateMixin {
bool _isPlaying = true;
double _progress = 0;
bool _isDragging = false;
bool _showAlert = false;
Timer? _timer;
late Scenario _scenario;
late List<FlSpot> _waveData;
@override
void initState() {
super.initState();
_scenario = mockScenarios.firstWhere(
(s) => s.id == widget.scenarioId,
orElse: () => mockScenarios.first,
);
_generateWaveData();
_startPlayback();
}
void _generateWaveData() {
// Generate 101 data points (0 to 100) for realistic wave
final random = Random(42); // Fixed seed for consistency
_waveData = List.generate(101, (i) {
double hz;
if (i < 20) {
// Warmup
hz = 10 + (i * 1.5) + (random.nextDouble() * 5);
} else if (i < 50) {
// Plateau/Tease
hz = 40 + sin(i * 0.5) * 10 + (random.nextDouble() * 10);
} else if (i < 85) {
// Build up to climax
hz = 70 + (i - 50) * 0.8 + (random.nextDouble() * 15);
} else {
// Cooldown
hz = 90 - ((i - 85) * 5) + (random.nextDouble() * 5);
}
return FlSpot(i.toDouble(), hz.clamp(0, 100));
});
}
void _startPlayback() {
_timer?.cancel();
_timer = Timer.periodic(const Duration(milliseconds: 50), (timer) {
if (!_isPlaying || _isDragging) return;
setState(() {
if (_progress >= 100) {
_isPlaying = false;
timer.cancel();
} else {
_progress += 0.1;
}
});
});
}
void _handleEmergencyStop() {
setState(() {
_isPlaying = false;
_showAlert = true;
});
Future.delayed(const Duration(milliseconds: 1500), () {
if (mounted) {
Navigator.of(context).pop();
}
});
}
int get _activeLineIndex {
for (int i = 0; i < dialogueScript.length; i++) {
final nextLine = i + 1 < dialogueScript.length ? dialogueScript[i + 1] : null;
if (_progress >= dialogueScript[i].time &&
(nextLine == null || _progress < nextLine.time)) {
return i;
}
}
return 0;
}
double get _currentHz {
final index = _progress.floor().clamp(0, 100);
return _waveData[index].y;
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onDoubleTap: _handleEmergencyStop,
child: Scaffold(
backgroundColor: const Color(0xFF2E1065),
body: Stack(
children: [
// Background Visual
Positioned.fill(
child: Opacity(
opacity: 0.4,
child: Stack(
fit: StackFit.expand,
children: [
Image.network(
_scenario.cover,
fit: BoxFit.cover,
),
BackdropFilter(
filter: ImageFilter.blur(sigmaX: 20, sigmaY: 20),
child: Container(color: Colors.transparent),
),
Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0x992E1065),
Color(0xCC2E1065),
Color(0xFF2E1065),
],
stops: [0.0, 0.5, 1.0],
),
),
),
],
),
),
),
// Emergency Overlay
if (_showAlert)
Positioned.fill(
child: Container(
color: const Color(0xF02E1065),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
LucideIcons.alertTriangle,
size: 64,
color: const Color(0xFFF43F5E),
),
const SizedBox(height: 16),
const Text(
'EMERGENCY STOP',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Color(0xFFF43F5E),
letterSpacing: 4,
),
),
const SizedBox(height: 8),
const Text(
'系统已强制中断',
style: TextStyle(fontSize: 14, color: Colors.white70),
),
],
),
),
),
// Main Content
SafeArea(
child: Column(
children: [
// Header
_buildHeader(),
// Central Lyrics
Expanded(child: _buildLyrics()),
// Bottom Controls
_buildControls(),
],
),
),
],
),
),
);
}
Widget _buildHeader() {
return Padding(
padding: const EdgeInsets.fromLTRB(24, 16, 24, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_scenario.title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 4),
Text(
'${_scenario.category} SCENARIO',
style: const TextStyle(
fontSize: 10,
letterSpacing: 1,
color: Color(0xFFC084FC),
),
),
],
),
GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white.withOpacity(0.1),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: const Icon(LucideIcons.x, size: 20, color: Colors.white70),
),
),
],
),
);
}
Widget _buildLyrics() {
// 简单展示:只显示当前行附近的几行
return Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 上面 2 行
for (int i = _activeLineIndex - 2; i < _activeLineIndex; i++)
if (i >= 0)
_buildLyricLine(i, isActive: false, distance: _activeLineIndex - i),
// 当前行
_buildLyricLine(_activeLineIndex, isActive: true, distance: 0),
// 下面 2 行
for (int i = _activeLineIndex + 1; i <= _activeLineIndex + 2; i++)
if (i < dialogueScript.length)
_buildLyricLine(i, isActive: false, distance: i - _activeLineIndex),
],
),
),
);
}
Widget _buildLyricLine(int index, {required bool isActive, required int distance}) {
final opacity = isActive ? 1.0 : (distance > 1 ? 0.2 : 0.5);
final scale = isActive ? 1.0 : 0.9;
return AnimatedContainer(
duration: const Duration(milliseconds: 400),
height: isActive ? 80 : 56,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 400),
opacity: opacity,
child: Transform.scale(
scale: scale,
child: Center(
child: Text(
dialogueScript[index].text,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: isActive ? 20 : 16,
fontWeight: isActive ? FontWeight.bold : FontWeight.w400,
color: isActive ? Colors.white : const Color(0xFFCBD5E1),
shadows: isActive
? [
const Shadow(
color: Color(0x99C084FC),
blurRadius: 20,
),
]
: null,
),
),
),
),
),
);
}
Widget _buildControls() {
return Container(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 24),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
const Color(0xFF2E1065),
const Color(0xFF2E1065),
const Color(0xFF2E1065).withOpacity(0),
],
stops: const [0.0, 0.7, 1.0],
),
),
child: Column(
children: [
// Wave Chart
SizedBox(
height: 100,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: LineChart(
LineChartData(
gridData: const FlGridData(show: false),
titlesData: const FlTitlesData(show: false),
borderData: FlBorderData(show: false),
minX: 0,
maxX: 100,
minY: 0,
maxY: 100,
lineBarsData: [
LineChartBarData(
spots: _waveData,
isCurved: true,
curveSmoothness: 0.3,
gradient: const LinearGradient(
colors: [Color(0xFFC084FC), Color(0xFFF472B6)],
),
barWidth: 3,
isStrokeCapRound: true,
dotData: const FlDotData(show: false),
belowBarData: BarAreaData(
show: true,
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
const Color(0xFFC084FC).withOpacity(0.5),
const Color(0xFFC084FC).withOpacity(0),
],
),
),
),
],
lineTouchData: const LineTouchData(enabled: false),
extraLinesData: ExtraLinesData(
verticalLines: [
VerticalLine(
x: _progress,
color: Colors.white.withOpacity(0.3),
strokeWidth: 1,
dashArray: [4, 4],
),
],
),
),
),
),
),
const SizedBox(height: 16),
// Control Bar
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Row(
children: [
// Play/Pause Button
GestureDetector(
onTap: () {
setState(() {
_isPlaying = !_isPlaying;
if (_isPlaying && _progress >= 100) {
_progress = 0;
}
});
},
child: Container(
width: 48,
height: 48,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.white.withOpacity(0.3),
blurRadius: 20,
),
],
),
child: Icon(
_isPlaying ? LucideIcons.pause : LucideIcons.play,
size: 20,
color: const Color(0xFF2E1065),
),
),
),
const SizedBox(width: 20),
// Progress Info & Slider
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'DEVICE INTENSITY',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
letterSpacing: 1,
color: Color(0xFFCBD5E1),
),
),
const SizedBox(height: 2),
Text(
'伸缩频率: ${max(1, (_progress / 10).ceil())}',
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
fontFamily: 'monospace',
color: Color(0xFFF472B6),
),
),
],
),
Text(
'${(_progress * 1.5).toStringAsFixed(0)}s / ${_scenario.duration}',
style: const TextStyle(
fontSize: 10,
fontFamily: 'monospace',
color: Color(0xFFCBD5E1),
),
),
],
),
const SizedBox(height: 8),
// Progress Slider
SliderTheme(
data: SliderThemeData(
trackHeight: 4,
thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 8),
overlayShape: const RoundSliderOverlayShape(overlayRadius: 16),
activeTrackColor: const Color(0xFFF472B6),
inactiveTrackColor: Colors.white.withOpacity(0.2),
thumbColor: Colors.white,
),
child: Slider(
value: _progress.clamp(0, 100),
min: 0,
max: 100,
onChangeStart: (_) => _isDragging = true,
onChangeEnd: (_) => _isDragging = false,
onChanged: (value) {
setState(() {
_progress = value;
});
},
),
),
],
),
),
],
),
),
const SizedBox(height: 16),
// Emergency Stop Hint
Text(
'双击屏幕紧急停止',
style: TextStyle(
fontSize: 10,
letterSpacing: 2,
color: Colors.white.withOpacity(0.4),
),
),
],
),
);
}
}

View File

@@ -0,0 +1,134 @@
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
class DeviceManagerScreen extends StatelessWidget {
const DeviceManagerScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF2E1065),
body: SafeArea(
child: Column(
children: [
_buildHeader(context),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildSectionTitle('已连接设备'),
const SizedBox(height: 12),
_buildDeviceCard(name: 'Link-X Pro', id: '884-X9-01', battery: 85, isConnected: true),
const SizedBox(height: 24),
_buildSectionTitle('历史设备'),
const SizedBox(height: 12),
_buildDeviceCard(name: 'Link-S Mini', id: '772-M3-02', battery: 0, isConnected: false),
const SizedBox(height: 24),
_buildAddDeviceButton(),
],
),
),
),
],
),
),
);
}
Widget _buildHeader(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(8, 8, 16, 16),
child: Row(
children: [
IconButton(
onPressed: () => Navigator.of(context).pop(),
icon: Icon(LucideIcons.chevronLeft, color: Colors.white.withOpacity(0.7)),
),
const Text('我的设备', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white)),
],
),
);
}
Widget _buildSectionTitle(String title) {
return Text(title, style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold, letterSpacing: 2, color: Colors.white.withOpacity(0.4)));
}
Widget _buildDeviceCard({required String name, required String id, required int battery, required bool isConnected}) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Icon(LucideIcons.bluetooth, size: 24, color: isConnected ? const Color(0xFF34D399) : Colors.white.withOpacity(0.5)),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(name, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white)),
const SizedBox(height: 2),
Text('ID: $id', style: TextStyle(fontSize: 12, fontFamily: 'monospace', color: Colors.white.withOpacity(0.5))),
],
),
],
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: isConnected ? const Color(0xFF34D399).withOpacity(0.2) : Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Text(
isConnected ? '已连接' : '未连接',
style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold, color: isConnected ? const Color(0xFF34D399) : Colors.white.withOpacity(0.5)),
),
),
],
),
if (isConnected) ...[
const SizedBox(height: 16),
Row(
children: [
Icon(LucideIcons.battery, size: 16, color: const Color(0xFF34D399)),
const SizedBox(width: 8),
Text('$battery%', style: const TextStyle(fontSize: 14, fontFamily: 'monospace', color: Colors.white)),
],
),
],
],
),
);
}
Widget _buildAddDeviceButton() {
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 16),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white.withOpacity(0.2)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(LucideIcons.plus, size: 20, color: Colors.white.withOpacity(0.7)),
const SizedBox(width: 8),
Text('添加新设备', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white.withOpacity(0.7))),
],
),
);
}
}

View File

@@ -0,0 +1,118 @@
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
class HelpScreen extends StatelessWidget {
const HelpScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF2E1065),
body: SafeArea(
child: Column(
children: [
_buildHeader(context),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildSectionTitle('常见问题'),
const SizedBox(height: 12),
_buildFaqItem(question: '如何连接设备?', answer: '进入手动实验室页面,点击"连接设备"按钮,开启蓝牙后按照提示操作。'),
_buildFaqItem(question: '积分如何获取?', answer: '可通过充值购买或完成日常任务获得积分奖励。'),
_buildFaqItem(question: '如何升级会员?', answer: '在个人中心点击订阅管理,选择合适的会员方案进行升级。'),
const SizedBox(height: 24),
_buildSectionTitle('联系我们'),
const SizedBox(height: 12),
_buildContactItem(icon: LucideIcons.messageCircle, title: '在线客服', subtitle: '工作时间: 9:00-21:00'),
const SizedBox(height: 12),
_buildContactItem(icon: LucideIcons.mail, title: '邮箱反馈', subtitle: 'support@weiai.com'),
const SizedBox(height: 24),
_buildFeedbackButton(),
],
),
),
),
],
),
),
);
}
Widget _buildHeader(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(8, 8, 16, 16),
child: Row(
children: [
IconButton(onPressed: () => Navigator.of(context).pop(), icon: Icon(LucideIcons.chevronLeft, color: Colors.white.withOpacity(0.7))),
const Text('帮助与反馈', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white)),
],
),
);
}
Widget _buildSectionTitle(String title) {
return Text(title, style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold, letterSpacing: 2, color: Colors.white.withOpacity(0.4)));
}
Widget _buildFaqItem({required String question, required String answer}) {
return Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(color: Colors.white.withOpacity(0.05), borderRadius: BorderRadius.circular(16), border: Border.all(color: Colors.white.withOpacity(0.1))),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(LucideIcons.helpCircle, size: 16, color: const Color(0xFFC084FC)),
const SizedBox(width: 8),
Expanded(child: Text(question, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: Colors.white))),
],
),
const SizedBox(height: 8),
Text(answer, style: TextStyle(fontSize: 13, color: Colors.white.withOpacity(0.6), height: 1.5)),
],
),
);
}
Widget _buildContactItem({required IconData icon, required String title, required String subtitle}) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(color: Colors.white.withOpacity(0.05), borderRadius: BorderRadius.circular(16), border: Border.all(color: Colors.white.withOpacity(0.1))),
child: Row(
children: [
Container(padding: const EdgeInsets.all(10), decoration: BoxDecoration(color: Colors.white.withOpacity(0.05), borderRadius: BorderRadius.circular(12)), child: Icon(icon, size: 20, color: Colors.white.withOpacity(0.7))),
const SizedBox(width: 14),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500, color: Colors.white)),
const SizedBox(height: 2),
Text(subtitle, style: TextStyle(fontSize: 12, color: Colors.white.withOpacity(0.5))),
],
),
],
),
);
}
Widget _buildFeedbackButton() {
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 16),
decoration: BoxDecoration(gradient: const LinearGradient(colors: [Color(0xFFC084FC), Color(0xFFF472B6)]), borderRadius: BorderRadius.circular(16)),
child: const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(LucideIcons.send, size: 18, color: Colors.white),
SizedBox(width: 8),
Text('提交反馈', style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold, color: Colors.white)),
],
),
);
}
}

View File

@@ -0,0 +1,119 @@
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
class PrivacyScreen extends StatefulWidget {
const PrivacyScreen({super.key});
@override
State<PrivacyScreen> createState() => _PrivacyScreenState();
}
class _PrivacyScreenState extends State<PrivacyScreen> {
bool _dataEncryption = true;
bool _anonymousMode = false;
bool _shareAnalytics = true;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF2E1065),
body: SafeArea(
child: Column(
children: [
_buildHeader(),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildSecurityStatus(),
const SizedBox(height: 24),
_buildSectionTitle('隐私设置'),
const SizedBox(height: 12),
_buildSwitchTile(icon: LucideIcons.lock, title: '端到端加密', subtitle: '所有数据均已加密保护', value: _dataEncryption, onChanged: (v) => setState(() => _dataEncryption = v)),
const SizedBox(height: 12),
_buildSwitchTile(icon: LucideIcons.eyeOff, title: '匿名模式', subtitle: '隐藏个人信息和活动记录', value: _anonymousMode, onChanged: (v) => setState(() => _anonymousMode = v)),
const SizedBox(height: 12),
_buildSwitchTile(icon: LucideIcons.barChart2, title: '分享使用数据', subtitle: '帮助我们改进产品体验', value: _shareAnalytics, onChanged: (v) => setState(() => _shareAnalytics = v)),
],
),
),
),
],
),
),
);
}
Widget _buildHeader() {
return Padding(
padding: const EdgeInsets.fromLTRB(8, 8, 16, 16),
child: Row(
children: [
IconButton(onPressed: () => Navigator.of(context).pop(), icon: Icon(LucideIcons.chevronLeft, color: Colors.white.withOpacity(0.7))),
const Text('隐私安全', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white)),
],
),
);
}
Widget _buildSecurityStatus() {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: const Color(0xFF34D399).withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: const Color(0xFF34D399).withOpacity(0.3)),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(color: const Color(0xFF34D399).withOpacity(0.2), shape: BoxShape.circle),
child: const Icon(LucideIcons.shieldCheck, size: 24, color: Color(0xFF34D399)),
),
const SizedBox(width: 16),
const Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('账户安全', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF34D399))),
SizedBox(height: 4),
Text('您的账户处于安全状态', style: TextStyle(fontSize: 12, color: Colors.white70)),
],
),
),
],
),
);
}
Widget _buildSectionTitle(String title) {
return Text(title, style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold, letterSpacing: 2, color: Colors.white.withOpacity(0.4)));
}
Widget _buildSwitchTile({required IconData icon, required String title, required String subtitle, required bool value, required ValueChanged<bool> onChanged}) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(color: Colors.white.withOpacity(0.05), borderRadius: BorderRadius.circular(16), border: Border.all(color: Colors.white.withOpacity(0.1))),
child: Row(
children: [
Container(padding: const EdgeInsets.all(10), decoration: BoxDecoration(color: Colors.white.withOpacity(0.05), borderRadius: BorderRadius.circular(12)), child: Icon(icon, size: 20, color: Colors.white.withOpacity(0.7))),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500, color: Colors.white)),
const SizedBox(height: 2),
Text(subtitle, style: TextStyle(fontSize: 12, color: Colors.white.withOpacity(0.5))),
],
),
),
Switch(value: value, onChanged: onChanged, activeColor: const Color(0xFFC084FC)),
],
),
);
}
}

View File

@@ -0,0 +1,404 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:lucide_icons/lucide_icons.dart';
import '../../widgets/tab_content_layout.dart';
class ProfileScreen extends StatelessWidget {
const ProfileScreen({super.key});
@override
Widget build(BuildContext context) {
const double bottomNavHeight = 90;
return TabContentLayout(
child: SingleChildScrollView(
padding: EdgeInsets.fromLTRB(16, 8, 16, bottomNavHeight + 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// User Header
_buildUserHeader(context),
const SizedBox(height: 24),
// Stats Grid
_buildStatsGrid(),
const SizedBox(height: 20),
// Joy Points Card
_buildJoyPointsCard(context),
const SizedBox(height: 24),
// Menu List
_buildMenuSection(context),
const SizedBox(height: 24),
// Logout Button
_buildLogoutButton(),
const SizedBox(height: 16),
// Version
Center(
child: Text(
'Wei AI v2.5.0 (Neon Edition)',
style: TextStyle(
fontSize: 10,
fontFamily: 'monospace',
color: Colors.white.withOpacity(0.3),
),
),
),
],
),
),
);
}
Widget _buildUserHeader(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
// Avatar
Container(
padding: const EdgeInsets.all(2),
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: const LinearGradient(
colors: [Color(0xFFC084FC), Color(0xFFF472B6)],
),
),
child: Container(
width: 64,
height: 64,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white.withOpacity(0.2), width: 2),
image: const DecorationImage(
image: NetworkImage('https://api.dicebear.com/7.x/avataaars/png?seed=commander'),
fit: BoxFit.cover,
),
),
),
),
const SizedBox(width: 16),
// User Info
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Commander',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 4),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(LucideIcons.crown, size: 12, color: const Color(0xFFFBBF24)),
const SizedBox(width: 4),
const Text(
'LV.4 黑金会员',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.bold,
color: Colors.white,
letterSpacing: 1,
),
),
],
),
),
],
),
],
),
// Settings Button
GestureDetector(
onTap: () => context.push('/profile/settings'),
child: Container(
width: 44,
height: 44,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
shape: BoxShape.circle,
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Icon(LucideIcons.settings, size: 20, color: Colors.white.withOpacity(0.7)),
),
),
],
);
}
Widget _buildStatsGrid() {
final stats = [
{'label': '互动时长', 'val': '42', 'unit': 'h'},
{'label': '亲密指数', 'val': '85', 'unit': '%'},
{'label': '解锁剧本', 'val': '12', 'unit': ''},
];
return Row(
children: stats.map((stat) {
return Expanded(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 4),
padding: const EdgeInsets.symmetric(vertical: 16),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
stat['val']!,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
fontFamily: 'monospace',
color: Colors.white,
),
),
Padding(
padding: const EdgeInsets.only(bottom: 2),
child: Text(
stat['unit']!,
style: TextStyle(
fontSize: 10,
color: Colors.white.withOpacity(0.6),
),
),
),
],
),
const SizedBox(height: 4),
Text(
stat['label']!,
style: TextStyle(
fontSize: 10,
letterSpacing: 1,
color: Colors.white.withOpacity(0.5),
),
),
],
),
),
);
}).toList(),
);
}
Widget _buildJoyPointsCard(BuildContext context) {
return GestureDetector(
onTap: () => context.push('/profile/topup'),
child: Container(
height: 120,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
gradient: const LinearGradient(
colors: [Color(0xFF7C3AED), Color(0xFF9333EA), Color(0xFFDB2777)],
),
boxShadow: [
BoxShadow(
color: const Color(0xFFC084FC).withOpacity(0.4),
blurRadius: 30,
offset: const Offset(0, 10),
),
],
),
child: Padding(
padding: const EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
children: [
Icon(LucideIcons.zap, size: 16, color: Colors.white.withOpacity(0.9)),
const SizedBox(width: 6),
Text(
'JOY POINTS',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.bold,
letterSpacing: 2,
color: Colors.white.withOpacity(0.9),
),
),
],
),
const SizedBox(height: 4),
const Text(
'2,450',
style: TextStyle(
fontSize: 36,
fontWeight: FontWeight.bold,
fontFamily: 'monospace',
color: Colors.white,
),
),
],
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24),
),
child: const Text(
'立即充值',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Color(0xFF9333EA),
),
),
),
],
),
),
),
);
}
Widget _buildMenuSection(BuildContext context) {
final menuItems = [
{'id': 'device', 'icon': LucideIcons.bluetooth, 'label': '我的设备', 'sub': 'Link-X Pro', 'route': '/profile/device'},
{'id': 'sub', 'icon': LucideIcons.creditCard, 'label': '订阅管理', 'sub': '', 'route': '/profile/subscription'},
{'id': 'privacy', 'icon': LucideIcons.shield, 'label': '隐私安全', 'sub': '', 'route': '/profile/privacy'},
{'id': 'help', 'icon': LucideIcons.helpCircle, 'label': '帮助与反馈', 'sub': '', 'route': '/profile/help'},
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 4, bottom: 8),
child: Text(
'GENERAL',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.bold,
letterSpacing: 2,
color: Colors.white.withOpacity(0.4),
),
),
),
...menuItems.map((item) => _buildMenuItem(
context,
icon: item['icon'] as IconData,
label: item['label'] as String,
sub: item['sub'] as String,
route: item['route'] as String,
)),
],
);
}
Widget _buildMenuItem(BuildContext context, {
required IconData icon,
required String label,
required String sub,
required String route,
}) {
return GestureDetector(
onTap: () => context.push(route),
child: Container(
margin: const EdgeInsets.only(bottom: 10),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, size: 20, color: Colors.white.withOpacity(0.7)),
),
const SizedBox(width: 14),
Text(
label,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
],
),
Row(
children: [
if (sub.isNotEmpty)
Text(
sub,
style: TextStyle(
fontSize: 12,
color: Colors.white.withOpacity(0.5),
),
),
const SizedBox(width: 8),
Icon(LucideIcons.chevronRight, size: 18, color: Colors.white.withOpacity(0.3)),
],
),
],
),
),
);
}
Widget _buildLogoutButton() {
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 16),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.red.withOpacity(0.3)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(LucideIcons.logOut, size: 18, color: Colors.red[400]),
const SizedBox(width: 8),
Text(
'退出登录',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.red[400],
),
),
],
),
);
}
}

View File

@@ -0,0 +1,231 @@
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
class SettingsScreen extends StatefulWidget {
const SettingsScreen({super.key});
@override
State<SettingsScreen> createState() => _SettingsScreenState();
}
class _SettingsScreenState extends State<SettingsScreen> {
bool _notificationsEnabled = true;
bool _darkMode = true;
bool _hapticFeedback = true;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF2E1065),
body: SafeArea(
child: Column(
children: [
_buildHeader(),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildSectionTitle('通知'),
_buildSwitchTile(
icon: LucideIcons.bell,
title: '推送通知',
subtitle: '接收消息和活动提醒',
value: _notificationsEnabled,
onChanged: (v) => setState(() => _notificationsEnabled = v),
),
const SizedBox(height: 20),
_buildSectionTitle('显示'),
_buildSwitchTile(
icon: LucideIcons.moon,
title: '深色模式',
subtitle: '始终使用深色主题',
value: _darkMode,
onChanged: (v) => setState(() => _darkMode = v),
),
const SizedBox(height: 20),
_buildSectionTitle('体验'),
_buildSwitchTile(
icon: LucideIcons.smartphone,
title: '触觉反馈',
subtitle: '操作时产生震动反馈',
value: _hapticFeedback,
onChanged: (v) => setState(() => _hapticFeedback = v),
),
const SizedBox(height: 20),
_buildSectionTitle('存储'),
_buildActionTile(
icon: LucideIcons.trash2,
title: '清除缓存',
subtitle: '已使用 128 MB',
onTap: () {},
),
const SizedBox(height: 20),
_buildSectionTitle('关于'),
_buildInfoTile(
icon: LucideIcons.info,
title: '版本',
value: 'v2.5.0',
),
],
),
),
),
],
),
),
);
}
Widget _buildHeader() {
return Padding(
padding: const EdgeInsets.fromLTRB(8, 8, 16, 16),
child: Row(
children: [
IconButton(
onPressed: () => Navigator.of(context).pop(),
icon: Icon(LucideIcons.chevronLeft, color: Colors.white.withOpacity(0.7)),
),
const Text(
'系统设置',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white),
),
],
),
);
}
Widget _buildSectionTitle(String title) {
return Padding(
padding: const EdgeInsets.only(left: 4, bottom: 12),
child: Text(
title,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
letterSpacing: 2,
color: Colors.white.withOpacity(0.4),
),
),
);
}
Widget _buildSwitchTile({
required IconData icon,
required String title,
required String subtitle,
required bool value,
required ValueChanged<bool> onChanged,
}) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, size: 20, color: Colors.white.withOpacity(0.7)),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500, color: Colors.white)),
const SizedBox(height: 2),
Text(subtitle, style: TextStyle(fontSize: 12, color: Colors.white.withOpacity(0.5))),
],
),
),
Switch(
value: value,
onChanged: onChanged,
activeColor: const Color(0xFFC084FC),
),
],
),
);
}
Widget _buildActionTile({
required IconData icon,
required String title,
required String subtitle,
required VoidCallback onTap,
}) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, size: 20, color: Colors.white.withOpacity(0.7)),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500, color: Colors.white)),
const SizedBox(height: 2),
Text(subtitle, style: TextStyle(fontSize: 12, color: Colors.white.withOpacity(0.5))),
],
),
),
Icon(LucideIcons.chevronRight, size: 18, color: Colors.white.withOpacity(0.3)),
],
),
),
);
}
Widget _buildInfoTile({
required IconData icon,
required String title,
required String value,
}) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, size: 20, color: Colors.white.withOpacity(0.7)),
),
const SizedBox(width: 14),
Expanded(child: Text(title, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500, color: Colors.white))),
Text(value, style: TextStyle(fontSize: 14, fontFamily: 'monospace', color: Colors.white.withOpacity(0.5))),
],
),
);
}
}

View File

@@ -0,0 +1,120 @@
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
class SubscriptionScreen extends StatelessWidget {
const SubscriptionScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF2E1065),
body: SafeArea(
child: Column(
children: [
_buildHeader(context),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildCurrentPlan(),
const SizedBox(height: 24),
_buildSectionTitle('升级方案'),
const SizedBox(height: 12),
_buildPlanCard(name: '月度会员', price: '¥29/月', features: ['无限互动时长', '优先匹配', '专属剧本'], isRecommended: false),
const SizedBox(height: 12),
_buildPlanCard(name: '年度会员', price: '¥199/年', features: ['月度会员全部权益', '额外赠送500积分', '新功能优先体验'], isRecommended: true),
],
),
),
),
],
),
),
);
}
Widget _buildHeader(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(8, 8, 16, 16),
child: Row(
children: [
IconButton(onPressed: () => Navigator.of(context).pop(), icon: Icon(LucideIcons.chevronLeft, color: Colors.white.withOpacity(0.7))),
const Text('订阅管理', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white)),
],
),
);
}
Widget _buildCurrentPlan() {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: const LinearGradient(colors: [Color(0xFF7C3AED), Color(0xFF9333EA)]),
borderRadius: BorderRadius.circular(20),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(LucideIcons.crown, size: 20, color: const Color(0xFFFBBF24)),
const SizedBox(width: 8),
const Text('当前订阅', style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold, letterSpacing: 1, color: Colors.white70)),
],
),
const SizedBox(height: 8),
const Text('黑金会员 LV.4', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.white)),
const SizedBox(height: 8),
Text('有效期至 2025-12-31', style: TextStyle(fontSize: 12, color: Colors.white.withOpacity(0.7))),
],
),
);
}
Widget _buildSectionTitle(String title) {
return Text(title, style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold, letterSpacing: 2, color: Colors.white.withOpacity(0.4)));
}
Widget _buildPlanCard({required String name, required String price, required List<String> features, required bool isRecommended}) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: isRecommended ? const Color(0xFFC084FC) : Colors.white.withOpacity(0.1), width: isRecommended ? 2 : 1),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(name, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white)),
if (isRecommended)
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(color: const Color(0xFFC084FC), borderRadius: BorderRadius.circular(8)),
child: const Text('推荐', style: TextStyle(fontSize: 10, fontWeight: FontWeight.bold, color: Colors.white)),
),
],
),
const SizedBox(height: 4),
Text(price, style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, fontFamily: 'monospace', color: Color(0xFFC084FC))),
const SizedBox(height: 12),
...features.map((f) => Padding(
padding: const EdgeInsets.only(bottom: 6),
child: Row(
children: [
Icon(LucideIcons.check, size: 14, color: const Color(0xFF34D399)),
const SizedBox(width: 8),
Text(f, style: TextStyle(fontSize: 13, color: Colors.white.withOpacity(0.7))),
],
),
)),
],
),
);
}
}

View File

@@ -0,0 +1,150 @@
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
class TopupScreen extends StatefulWidget {
const TopupScreen({super.key});
@override
State<TopupScreen> createState() => _TopupScreenState();
}
class _TopupScreenState extends State<TopupScreen> {
int _selectedPackage = 1;
final List<Map<String, dynamic>> _packages = [
{'points': 100, 'price': 6, 'bonus': 0},
{'points': 500, 'price': 28, 'bonus': 50},
{'points': 1000, 'price': 50, 'bonus': 150},
{'points': 3000, 'price': 128, 'bonus': 600},
];
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF2E1065),
body: SafeArea(
child: Column(
children: [
_buildHeader(),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildBalanceCard(),
const SizedBox(height: 24),
_buildSectionTitle('选择套餐'),
const SizedBox(height: 12),
..._packages.asMap().entries.map((e) => _buildPackageCard(e.key, e.value)),
const SizedBox(height: 24),
_buildPayButton(),
],
),
),
),
],
),
),
);
}
Widget _buildHeader() {
return Padding(
padding: const EdgeInsets.fromLTRB(8, 8, 16, 16),
child: Row(
children: [
IconButton(
onPressed: () => Navigator.of(context).pop(),
icon: Icon(LucideIcons.chevronLeft, color: Colors.white.withOpacity(0.7)),
),
const Text('立即充值', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white)),
],
),
);
}
Widget _buildBalanceCard() {
return Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
gradient: const LinearGradient(colors: [Color(0xFF7C3AED), Color(0xFF9333EA), Color(0xFFDB2777)]),
borderRadius: BorderRadius.circular(24),
),
child: Row(
children: [
Icon(LucideIcons.zap, size: 32, color: Colors.white.withOpacity(0.9)),
const SizedBox(width: 16),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('当前余额', style: TextStyle(fontSize: 12, color: Colors.white.withOpacity(0.8))),
const SizedBox(height: 4),
const Text('2,450', style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold, fontFamily: 'monospace', color: Colors.white)),
],
),
],
),
);
}
Widget _buildSectionTitle(String title) {
return Text(
title,
style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold, letterSpacing: 2, color: Colors.white.withOpacity(0.4)),
);
}
Widget _buildPackageCard(int index, Map<String, dynamic> pkg) {
final isSelected = _selectedPackage == index;
return GestureDetector(
onTap: () => setState(() => _selectedPackage = index),
child: Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isSelected ? Colors.white : Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: isSelected ? Colors.white : Colors.white.withOpacity(0.1), width: isSelected ? 2 : 1),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Icon(LucideIcons.zap, size: 20, color: isSelected ? const Color(0xFF9333EA) : const Color(0xFFC084FC)),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${pkg['points']} 积分',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: isSelected ? const Color(0xFF2E1065) : Colors.white),
),
if (pkg['bonus'] > 0)
Text('+${pkg['bonus']} 赠送', style: TextStyle(fontSize: 12, color: isSelected ? const Color(0xFF9333EA) : const Color(0xFFC084FC))),
],
),
],
),
Text('¥${pkg['price']}', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, fontFamily: 'monospace', color: isSelected ? const Color(0xFF2E1065) : Colors.white)),
],
),
),
);
}
Widget _buildPayButton() {
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 18),
decoration: BoxDecoration(
gradient: const LinearGradient(colors: [Color(0xFFC084FC), Color(0xFFF472B6)]),
borderRadius: BorderRadius.circular(20),
),
child: const Center(
child: Text('确认支付', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white)),
),
);
}
}

View File

@@ -0,0 +1,98 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
class GlassBottomNav extends StatelessWidget {
final int currentIndex;
final Function(int) onTap;
const GlassBottomNav({
super.key,
required this.currentIndex,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 20, sigmaY: 20),
child: Container(
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.2), // bg-black/20
border: Border(top: BorderSide(color: Colors.white.withOpacity(0.1))),
),
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom + 12,
top: 12,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildNavItem(0, LucideIcons.compass, '发现'),
_buildNavItem(1, LucideIcons.playCircle, '剧本'),
_buildNavItem(2, LucideIcons.radio, '操控'),
_buildNavItem(3, LucideIcons.user, '我的'),
],
),
),
),
);
}
Widget _buildNavItem(int index, IconData icon, String label) {
bool isActive = currentIndex == index;
return GestureDetector(
onTap: () => onTap(index),
behavior: HitTestBehavior.opaque,
child: SizedBox(
width: 60,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Stack(
alignment: Alignment.center,
children: [
if (isActive)
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.white.withOpacity(0.1),
blurRadius: 10,
spreadRadius: 5,
)
]
),
),
AnimatedScale(
scale: isActive ? 1.1 : 1.0,
duration: const Duration(milliseconds: 300),
child: Icon(
icon,
size: 26,
color: isActive ? Colors.white : Colors.white.withOpacity(0.5),
shadows: isActive ? [const BoxShadow(color: Colors.white, blurRadius: 10)] : null,
),
),
],
),
const SizedBox(height: 4),
Text(
label,
style: TextStyle(
fontSize: 10,
color: isActive ? Colors.white : Colors.white.withOpacity(0.4),
fontWeight: FontWeight.w500,
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,82 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
import '../models/device_status.dart';
import '../config/theme.dart';
class StatusBar extends StatelessWidget {
final DeviceStatus status;
final VoidCallback? onTap;
const StatusBar({
super.key,
required this.status,
this.onTap,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
height: 32, // h-7 is ~28px, adjusted for touch target
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
color: const Color(0xFF1A1625).withOpacity(0.8),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
// Connection Status Icon
if (status.connected) ...[
// TODO: Add ping animation if needed using flutter_animate
const Icon(LucideIcons.bluetooth, size: 14, color: AppTheme.neonGreen),
] else ...[
Container(
width: 6,
height: 6,
decoration: const BoxDecoration(color: Colors.grey, shape: BoxShape.circle),
),
],
const SizedBox(width: 8),
Container(width: 1, height: 10, color: Colors.white.withOpacity(0.1)),
const SizedBox(width: 8),
// Battery Status
if (status.connected) ...[
Text(
'${status.battery.floor()}%',
style: TextStyle(
fontFamily: 'monospace',
fontSize: 12,
fontWeight: FontWeight.bold,
color: status.battery < 20 ? Theme.of(context).colorScheme.error : Colors.white,
),
),
const SizedBox(width: 4),
Icon(
LucideIcons.zap,
size: 12,
color: status.battery < 20 ? Theme.of(context).colorScheme.error : AppTheme.neonPurple,
),
] else ...[
const Text(
'未连接',
style: TextStyle(fontSize: 10, color: Colors.grey),
),
],
],
),
),
),
),
);
}
}

View File

@@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
class TabContentLayout extends StatelessWidget {
final Widget child;
final bool applyTopPadding;
final bool applyBottomPadding;
const TabContentLayout({
super.key,
required this.child,
this.applyTopPadding = true,
this.applyBottomPadding = false, // Usually managed solely by the glass nav height inside the list, but let's see.
});
@override
Widget build(BuildContext context) {
// Since extendBodyBehindAppBar is true, we don't need extra padding
// The body naturally starts from top, but we just need minimal spacing
return Padding(
padding: EdgeInsets.only(
top: 0, // Content starts naturally below AppBar
// We don't necessarily pad bottom here if we want content to scroll behind bottom nav,
// but for fixed elements (like Discovery Filter bar), we might need structure.
),
child: child,
);
}
}

7
wei_ai_app/macos/.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
# Flutter-related
**/Flutter/ephemeral/
**/Pods/
# Xcode-related
**/dgph
**/xcuserdata/

View File

@@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"

View File

@@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"

View File

@@ -0,0 +1,12 @@
//
// Generated file. Do not edit.
//
import FlutterMacOS
import Foundation
import path_provider_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
}

42
wei_ai_app/macos/Podfile Normal file
View File

@@ -0,0 +1,42 @@
platform :osx, '10.15'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_macos_podfile_setup
target 'Runner' do
use_frameworks!
flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_macos_build_settings(target)
end
end

View File

@@ -0,0 +1,705 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXAggregateTarget section */
33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
isa = PBXAggregateTarget;
buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
buildPhases = (
33CC111E2044C6BF0003C045 /* ShellScript */,
);
dependencies = (
);
name = "Flutter Assemble";
productName = FLX;
};
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 33CC10EC2044A3C60003C045;
remoteInfo = Runner;
};
33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 33CC111A2044C6BA0003C045;
remoteInfo = FLX;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
33CC110E2044A8840003C045 /* Bundle Framework */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Bundle Framework";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
33CC10ED2044A3C60003C045 /* wei_ai_app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "wei_ai_app.app"; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = "<group>"; };
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = "<group>"; };
33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = "<group>"; };
33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = "<group>"; };
33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = "<group>"; };
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
331C80D2294CF70F00263BE5 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
33CC10EA2044A3C60003C045 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
331C80D6294CF71000263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
331C80D7294CF71000263BE5 /* RunnerTests.swift */,
);
path = RunnerTests;
sourceTree = "<group>";
};
33BA886A226E78AF003329D5 /* Configs */ = {
isa = PBXGroup;
children = (
33E5194F232828860026EE4D /* AppInfo.xcconfig */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
);
path = Configs;
sourceTree = "<group>";
};
33CC10E42044A3C60003C045 = {
isa = PBXGroup;
children = (
33FAB671232836740065AC1E /* Runner */,
33CEB47122A05771004F2AC0 /* Flutter */,
331C80D6294CF71000263BE5 /* RunnerTests */,
33CC10EE2044A3C60003C045 /* Products */,
D73912EC22F37F3D000D13A0 /* Frameworks */,
);
sourceTree = "<group>";
};
33CC10EE2044A3C60003C045 /* Products */ = {
isa = PBXGroup;
children = (
33CC10ED2044A3C60003C045 /* wei_ai_app.app */,
331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
33CC11242044D66E0003C045 /* Resources */ = {
isa = PBXGroup;
children = (
33CC10F22044A3C60003C045 /* Assets.xcassets */,
33CC10F42044A3C60003C045 /* MainMenu.xib */,
33CC10F72044A3C60003C045 /* Info.plist */,
);
name = Resources;
path = ..;
sourceTree = "<group>";
};
33CEB47122A05771004F2AC0 /* Flutter */ = {
isa = PBXGroup;
children = (
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
);
path = Flutter;
sourceTree = "<group>";
};
33FAB671232836740065AC1E /* Runner */ = {
isa = PBXGroup;
children = (
33CC10F02044A3C60003C045 /* AppDelegate.swift */,
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
33E51913231747F40026EE4D /* DebugProfile.entitlements */,
33E51914231749380026EE4D /* Release.entitlements */,
33CC11242044D66E0003C045 /* Resources */,
33BA886A226E78AF003329D5 /* Configs */,
);
path = Runner;
sourceTree = "<group>";
};
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
isa = PBXGroup;
children = (
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
331C80D4294CF70F00263BE5 /* RunnerTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
331C80D1294CF70F00263BE5 /* Sources */,
331C80D2294CF70F00263BE5 /* Frameworks */,
331C80D3294CF70F00263BE5 /* Resources */,
);
buildRules = (
);
dependencies = (
331C80DA294CF71000263BE5 /* PBXTargetDependency */,
);
name = RunnerTests;
productName = RunnerTests;
productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
33CC10EC2044A3C60003C045 /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
33CC10E92044A3C60003C045 /* Sources */,
33CC10EA2044A3C60003C045 /* Frameworks */,
33CC10EB2044A3C60003C045 /* Resources */,
33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */,
);
buildRules = (
);
dependencies = (
33CC11202044C79F0003C045 /* PBXTargetDependency */,
);
name = Runner;
productName = Runner;
productReference = 33CC10ED2044A3C60003C045 /* wei_ai_app.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
33CC10E52044A3C60003C045 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C80D4294CF70F00263BE5 = {
CreatedOnToolsVersion = 14.0;
TestTargetID = 33CC10EC2044A3C60003C045;
};
33CC10EC2044A3C60003C045 = {
CreatedOnToolsVersion = 9.2;
LastSwiftMigration = 1100;
ProvisioningStyle = Automatic;
SystemCapabilities = {
com.apple.Sandbox = {
enabled = 1;
};
};
};
33CC111A2044C6BA0003C045 = {
CreatedOnToolsVersion = 9.2;
ProvisioningStyle = Manual;
};
};
};
buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 33CC10E42044A3C60003C045;
productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
33CC10EC2044A3C60003C045 /* Runner */,
331C80D4294CF70F00263BE5 /* RunnerTests */,
33CC111A2044C6BA0003C045 /* Flutter Assemble */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
331C80D3294CF70F00263BE5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
33CC10EB2044A3C60003C045 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3399D490228B24CF009A79C7 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
};
33CC111E2044C6BF0003C045 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
Flutter/ephemeral/FlutterInputs.xcfilelist,
);
inputPaths = (
Flutter/ephemeral/tripwire,
);
outputFileListPaths = (
Flutter/ephemeral/FlutterOutputs.xcfilelist,
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
331C80D1294CF70F00263BE5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
33CC10E92044A3C60003C045 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 33CC10EC2044A3C60003C045 /* Runner */;
targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;
};
33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
33CC10F52044A3C60003C045 /* Base */,
);
name = MainMenu.xib;
path = Runner;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
331C80DB294CF71000263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.weiai.weiAiApp.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/wei_ai_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/wei_ai_app";
};
name = Debug;
};
331C80DC294CF71000263BE5 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.weiai.weiAiApp.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/wei_ai_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/wei_ai_app";
};
name = Release;
};
331C80DD294CF71000263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.weiai.weiAiApp.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/wei_ai_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/wei_ai_app";
};
name = Profile;
};
338D0CE9231458BD00FA5F75 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
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_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
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_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
};
name = Profile;
};
338D0CEA231458BD00FA5F75 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};
name = Profile;
};
338D0CEB231458BD00FA5F75 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Profile;
};
33CC10F92044A3C60003C045 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
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_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
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_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
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_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
33CC10FA2044A3C60003C045 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
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_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
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_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
};
name = Release;
};
33CC10FC2044A3C60003C045 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
33CC10FD2044A3C60003C045 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};
name = Release;
};
33CC111C2044C6BA0003C045 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
33CC111D2044C6BA0003C045 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C80DB294CF71000263BE5 /* Debug */,
331C80DC294CF71000263BE5 /* Release */,
331C80DD294CF71000263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
33CC10F92044A3C60003C045 /* Debug */,
33CC10FA2044A3C60003C045 /* Release */,
338D0CE9231458BD00FA5F75 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
33CC10FC2044A3C60003C045 /* Debug */,
33CC10FD2044A3C60003C045 /* Release */,
338D0CEA231458BD00FA5F75 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
isa = XCConfigurationList;
buildConfigurations = (
33CC111C2044C6BA0003C045 /* Debug */,
33CC111D2044C6BA0003C045 /* Release */,
338D0CEB231458BD00FA5F75 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 33CC10E52044A3C60003C045 /* Project object */;
}

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "wei_ai_app.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "wei_ai_app.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C80D4294CF70F00263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "wei_ai_app.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "wei_ai_app.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>

Some files were not shown because too many files have changed in this diff Show More