init: v1.1.0+2 baseline

This commit is contained in:
Ubuntu 2026-02-17 16:10:18 +08:00
commit bb645e8428
159 changed files with 8630 additions and 0 deletions

43
.gitignore vendored Normal file
View File

@ -0,0 +1,43 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

45
.metadata Normal file
View File

@ -0,0 +1,45 @@
# 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: "dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: android
create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: ios
create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: linux
create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: macos
create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: web
create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: windows
create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
# 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
README.md Normal file
View File

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

28
analysis_options.yaml Normal file
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

13
android/.gitignore vendored Normal file
View File

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

44
android/app/build.gradle Normal file
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.banxiang.banxiang_app"
compileSdk = 35
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.banxiang.banxiang_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.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="伴享"
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.banxiang.banxiang_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>

20
android/build.gradle Normal file
View File

@ -0,0 +1,20 @@
allprojects {
repositories {
maven { url "https://maven.aliyun.com/repository/google" }
google()
maven { url "https://maven.aliyun.com/repository/central" }
mavenCentral()
}
}
rootProject.buildDir = "../build"
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

View File

@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true

View File

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.3-all.zip

30
android/settings.gradle Normal file
View File

@ -0,0 +1,30 @@
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}()
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
maven { url = "https://maven.aliyun.com/repository/google" }
maven { url = "https://maven.aliyun.com/repository/central" }
maven { url = "https://maven.aliyun.com/repository/public" }
maven { url "https://maven.aliyun.com/repository/google" }
google()
maven { url "https://maven.aliyun.com/repository/central" }
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.1.0" apply false
id "org.jetbrains.kotlin.android" version "1.8.22" apply false
}
include ":app"

34
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>12.0</string>
</dict>
</plist>

View File

@ -0,0 +1 @@
#include "Generated.xcconfig"

View File

@ -0,0 +1 @@
#include "Generated.xcconfig"

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 = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
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.banxiang.banxiangApp;
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.banxiang.banxiangApp.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.banxiang.banxiangApp.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.banxiang.banxiangApp.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 = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
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.banxiang.banxiangApp;
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.banxiang.banxiangApp;
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,98 @@
<?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"
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"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
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>

49
ios/Runner/Info.plist Normal file
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>Banxiang 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>banxiang_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.
}
}

48
lib/main.dart Normal file
View File

@ -0,0 +1,48 @@
import 'package:flutter/material.dart';
import 'screens/login_screen.dart';
import 'screens/activity_list_screen.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('auth_token');
final isLoggedIn = token != null && token.isNotEmpty;
runApp(BanxiangApp(isLoggedIn: isLoggedIn));
}
class BanxiangApp extends StatelessWidget {
final bool isLoggedIn;
const BanxiangApp({required this.isLoggedIn});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '伴享',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Color(0xFFFF6B35),
scaffoldBackgroundColor: const Color(0xFFFFF8F0),
appBarTheme: AppBarTheme(
backgroundColor: Colors.white,
foregroundColor: Color(0xFF333333),
elevation: 0,
),
bottomNavigationBarTheme: BottomNavigationBarThemeData(
selectedItemColor: Color(0xFFFF6B35),
unselectedItemColor: Color(0xFF999999),
backgroundColor: Colors.white,
type: BottomNavigationBarType.fixed,
),
colorScheme: ColorScheme.fromSeed(
seedColor: Color(0xFFFF6B35),
primary: Color(0xFFFF6B35),
secondary: Color(0xFFFFB84D),
),
),
home: isLoggedIn ? ActivityListScreen() : LoginScreen(),
);
}
}

84
lib/main.dart.bak Normal file
View File

@ -0,0 +1,84 @@
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'theme.dart';
import 'services/auth_service.dart';
import 'screens/login_screen.dart';
import 'screens/main_screen.dart';
void main() {
runApp(const BanxiangApp());
}
class BanxiangApp extends StatelessWidget {
const BanxiangApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '伴享',
theme: AppTheme.lightTheme,
debugShowCheckedModeBanner: false,
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [Locale('zh', 'CN')],
locale: const Locale('zh', 'CN'),
home: const SplashScreen(),
);
}
}
class SplashScreen extends StatefulWidget {
const SplashScreen({super.key});
@override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
@override
void initState() {
super.initState();
_checkAuth();
}
void _checkAuth() async {
await Future.delayed(const Duration(seconds: 2));
final loggedIn = await AuthService.checkLoginStatus();
if (mounted) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => loggedIn ? const MainScreen() : const LoginScreen()),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 100, height: 100,
decoration: BoxDecoration(
color: const Color(0xFF1976D2),
borderRadius: BorderRadius.circular(24),
),
child: const Icon(Icons.elderly, size: 56, color: Colors.white),
),
const SizedBox(height: 24),
const Text('伴享', style: TextStyle(fontSize: 36, fontWeight: FontWeight.bold, color: Color(0xFF333333))),
const SizedBox(height: 8),
const Text('银发生活社交平台', style: TextStyle(fontSize: 16, color: Color(0xFF999999))),
const SizedBox(height: 48),
const SizedBox(width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2)),
],
),
),
);
}
}

23
lib/models/activity.dart Normal file
View File

@ -0,0 +1,23 @@
class Activity {
String id;
String title;
String category;
DateTime time;
String location;
int maxParticipants;
int currentParticipants;
String creatorName;
String description;
Activity({
required this.id,
required this.title,
required this.category,
required this.time,
required this.location,
required this.maxParticipants,
this.currentParticipants = 0,
required this.creatorName,
this.description = '',
});
}

49
lib/models/hospital.dart Normal file
View File

@ -0,0 +1,49 @@
class Hospital {
String id;
String name;
String address;
List<String> departments;
Hospital({
required this.id,
required this.name,
required this.address,
required this.departments,
});
}
class Doctor {
String id;
String name;
String department;
String title;
String fee;
Doctor({
required this.id,
required this.name,
required this.department,
required this.title,
required this.fee,
});
}
class Appointment {
String id;
String hospitalName;
String doctorName;
String department;
DateTime time;
String status;
String fee;
Appointment({
required this.id,
required this.hospitalName,
required this.doctorName,
required this.department,
required this.time,
required this.status,
required this.fee,
});
}

11
lib/models/message.dart Normal file
View File

@ -0,0 +1,11 @@
class Message {
String text;
bool isUser;
DateTime time;
Message({
required this.text,
required this.isUser,
DateTime? time,
}) : time = time ?? DateTime.now();
}

21
lib/models/user.dart Normal file
View File

@ -0,0 +1,21 @@
class User {
String? id;
String phone;
String? nickname;
String? avatar;
int? birthYear;
String? gender;
String? city;
List<String> interests;
User({
this.id,
required this.phone,
this.nickname,
this.avatar,
this.birthYear,
this.gender,
this.city,
this.interests = const [],
});
}

View File

@ -0,0 +1,487 @@
import 'package:flutter/material.dart';
import 'package:carousel_slider/carousel_slider.dart';
class ActivityDetailScreen extends StatefulWidget {
final Map<String, dynamic> activity;
ActivityDetailScreen({required this.activity});
@override
_ActivityDetailScreenState createState() => _ActivityDetailScreenState();
}
class _ActivityDetailScreenState extends State<ActivityDetailScreen> {
final TextEditingController _commentController = TextEditingController();
bool _isLiked = false;
int _likeCount = 0;
int _currentImageIndex = 0;
//
final List<Map<String, dynamic>> _comments = [
{
'userName': '张阿姨',
'avatar': 'https://i.pravatar.cc/150?img=1',
'content': '这个活动真不错,我也想参加!',
'time': '2小时前',
'likes': 5,
},
{
'userName': '李大爷',
'avatar': 'https://i.pravatar.cc/150?img=2',
'content': '上周参加过,很有意思',
'time': '5小时前',
'likes': 3,
},
{
'userName': '王阿姨',
'avatar': 'https://i.pravatar.cc/150?img=3',
'content': '地点在哪里呀?',
'time': '1天前',
'likes': 2,
},
];
@override
void initState() {
super.initState();
_likeCount = (widget.activity['participants'] as int) * 2;
}
void _toggleLike() {
setState(() {
_isLiked = !_isLiked;
_likeCount += _isLiked ? 1 : -1;
});
}
void _postComment() {
if (_commentController.text.trim().isEmpty) {
return;
}
setState(() {
_comments.insert(0, {
'userName': '',
'avatar': 'https://i.pravatar.cc/150?img=10',
'content': _commentController.text,
'time': '刚刚',
'likes': 0,
});
});
_commentController.clear();
FocusScope.of(context).unfocus();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('评论成功!'),
backgroundColor: Color(0xFFFF6B35),
duration: Duration(seconds: 1),
),
);
}
@override
Widget build(BuildContext context) {
//
final images = [
widget.activity['image'],
'https://picsum.photos/seed/${widget.activity['title'].hashCode + 1}/400/300',
'https://picsum.photos/seed/${widget.activity['title'].hashCode + 2}/400/300',
];
return Scaffold(
backgroundColor: const Color(0xFFFFF8F0),
body: CustomScrollView(
slivers: [
//
SliverAppBar(
expandedHeight: 400,
pinned: true,
backgroundColor: Colors.white,
iconTheme: IconThemeData(color: Colors.white),
flexibleSpace: FlexibleSpaceBar(
background: Stack(
children: [
CarouselSlider(
options: CarouselOptions(
height: 400,
viewportFraction: 1.0,
onPageChanged: (index, reason) {
setState(() {
_currentImageIndex = index;
});
},
),
items: images.map((imageUrl) {
return Container(
width: double.infinity,
color: Colors.grey[200],
child: Stack(
children: [
Center(
child: Icon(Icons.image, size: 60, color: Colors.grey[400]),
),
Image.network(
imageUrl,
width: double.infinity,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Container(
color: Colors.grey[200],
child: Center(
child: Icon(Icons.image, size: 60, color: Colors.grey[400]),
),
);
},
),
],
),
);
}).toList(),
),
//
Positioned(
bottom: 20,
left: 0,
right: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: images.asMap().entries.map((entry) {
return Container(
width: 8,
height: 8,
margin: EdgeInsets.symmetric(horizontal: 4),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _currentImageIndex == entry.key
? Colors.white
: Colors.white.withOpacity(0.4),
),
);
}).toList(),
),
),
],
),
),
),
//
SliverToBoxAdapter(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
Text(
widget.activity['title'],
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Color(0xFF333333),
),
),
SizedBox(height: 12),
//
Row(
children: [
CircleAvatar(
radius: 20,
backgroundImage: NetworkImage('https://i.pravatar.cc/150?img=5'),
),
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'活动发起人',
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 14,
),
),
Text(
'2天前',
style: TextStyle(
fontSize: 12,
color: Color(0xFF999999),
),
),
],
),
),
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFFFF6B35),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 8),
),
child: Text('关注'),
),
],
),
SizedBox(height: 16),
//
Text(
'这是一个非常有趣的活动,欢迎大家一起来参加!活动内容丰富多彩,适合所有年龄段的朋友。我们将在${widget.activity['location']}举行,期待您的到来!',
style: TextStyle(
fontSize: 15,
color: Color(0xFF333333),
height: 1.6,
),
),
SizedBox(height: 16),
//
Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: Color(0xFFFFF5F0),
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
Row(
children: [
Icon(Icons.location_on, size: 18, color: Color(0xFFFF6B35)),
SizedBox(width: 8),
Text(
widget.activity['location'],
style: TextStyle(fontSize: 14),
),
],
),
SizedBox(height: 8),
Row(
children: [
Icon(Icons.people, size: 18, color: Color(0xFFFF6B35)),
SizedBox(width: 8),
Text(
'已有 ${widget.activity['participants']} 人报名',
style: TextStyle(fontSize: 14),
),
],
),
],
),
),
],
),
),
Divider(thickness: 8, color: Color(0xFFF5F5F5)),
//
Padding(
padding: EdgeInsets.all(16),
child: Row(
children: [
Text(
'评论',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
SizedBox(width: 8),
Text(
'${_comments.length}',
style: TextStyle(
fontSize: 14,
color: Color(0xFF999999),
),
),
],
),
),
],
),
),
//
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
final comment = _comments[index];
return _buildCommentItem(comment);
},
childCount: _comments.length,
),
),
SliverToBoxAdapter(
child: SizedBox(height: 80),
),
],
),
//
bottomNavigationBar: _buildBottomBar(),
);
}
Widget _buildCommentItem(Map<String, dynamic> comment) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CircleAvatar(
radius: 18,
backgroundImage: NetworkImage(comment['avatar']),
),
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
comment['userName'],
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 14,
),
),
SizedBox(height: 4),
Text(
comment['content'],
style: TextStyle(
fontSize: 14,
color: Color(0xFF333333),
height: 1.4,
),
),
SizedBox(height: 8),
Row(
children: [
Text(
comment['time'],
style: TextStyle(
fontSize: 12,
color: Color(0xFF999999),
),
),
SizedBox(width: 16),
Icon(Icons.favorite_border, size: 14, color: Color(0xFF999999)),
SizedBox(width: 4),
Text(
'${comment['likes']}',
style: TextStyle(
fontSize: 12,
color: Color(0xFF999999),
),
),
],
),
],
),
),
],
),
);
}
Widget _buildBottomBar() {
return Container(
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: Offset(0, -2),
),
],
),
child: SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
children: [
//
Expanded(
child: Container(
height: 40,
decoration: BoxDecoration(
color: Color(0xFFF5F5F5),
borderRadius: BorderRadius.circular(20),
),
child: TextField(
controller: _commentController,
decoration: InputDecoration(
hintText: '说点什么...',
hintStyle: TextStyle(fontSize: 14, color: Color(0xFF999999)),
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
),
onSubmitted: (_) => _postComment(),
),
),
),
SizedBox(width: 12),
//
GestureDetector(
onTap: _toggleLike,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
_isLiked ? Icons.favorite : Icons.favorite_border,
color: _isLiked ? Colors.red : Color(0xFF999999),
size: 24,
),
Text(
'$_likeCount',
style: TextStyle(fontSize: 12, color: Color(0xFF999999)),
),
],
),
),
SizedBox(width: 16),
//
ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('报名成功!'),
backgroundColor: Color(0xFFFF6B35),
),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFFFF6B35),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 10),
),
child: Text('报名'),
),
],
),
),
),
);
}
@override
void dispose() {
_commentController.dispose();
super.dispose();
}
}

View File

@ -0,0 +1,317 @@
import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'publish_screen.dart';
import 'activity_detail_screen.dart';
import 'dart:math';
class ActivityListScreen extends StatefulWidget {
@override
_ActivityListScreenState createState() => _ActivityListScreenState();
}
class _ActivityListScreenState extends State<ActivityListScreen> {
int _selectedIndex = 0;
//
final List<Map<String, dynamic>> activities = [
{
'title': '周末公园太极拳',
'location': '人民公园',
'participants': 12,
'image': 'https://picsum.photos/seed/1/400/300',
},
{
'title': '社区书法班开课啦',
'location': '社区活动中心',
'participants': 8,
'image': 'https://picsum.photos/seed/2/400/500',
},
{
'title': '广场舞队招新',
'location': '中央广场',
'participants': 25,
'image': 'https://picsum.photos/seed/3/400/400',
},
{
'title': '老年摄影兴趣小组',
'location': '文化馆',
'participants': 15,
'image': 'https://picsum.photos/seed/4/400/350',
},
{
'title': '周三棋牌活动',
'location': '社区会所',
'participants': 20,
'image': 'https://picsum.photos/seed/5/400/450',
},
{
'title': '健康养生讲座',
'location': '图书馆',
'participants': 30,
'image': 'https://picsum.photos/seed/6/400/380',
},
];
void _onItemTapped(int index) {
if (index == 2) {
// -
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PublishScreen()),
);
return;
}
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFFFF8F0),
appBar: AppBar(
title: Text('伴享', style: TextStyle(fontWeight: FontWeight.bold)),
centerTitle: true,
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.notifications_none),
onPressed: () {},
),
],
),
body: _buildBody(),
bottomNavigationBar: _buildBottomNavigationBar(),
floatingActionButton: _buildFloatingActionButton(),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
Widget _buildBody() {
return RefreshIndicator(
onRefresh: () async {
await Future.delayed(Duration(seconds: 1));
setState(() {});
},
child: Padding(
padding: EdgeInsets.all(8),
child: MasonryGridView.count(
crossAxisCount: 2,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
itemCount: activities.length,
itemBuilder: (context, index) {
return _buildActivityCard(activities[index]);
},
),
),
);
}
Widget _buildActivityCard(Map<String, dynamic> activity) {
final random = Random(activity['title'].hashCode);
final height = 200.0 + random.nextInt(100);
return GestureDetector(
onTap: () {
//
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ActivityDetailScreen(activity: activity),
),
);
},
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.08),
blurRadius: 8,
offset: Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
ClipRRect(
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
child: Container(
height: height,
width: double.infinity,
color: Colors.grey[200],
child: Stack(
children: [
Center(
child: Icon(Icons.image, size: 40, color: Colors.grey[400]),
),
Image.network(
activity['image'],
height: height,
width: double.infinity,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Container(
color: Colors.grey[200],
child: Center(
child: Icon(Icons.image, size: 40, color: Colors.grey[400]),
),
);
},
),
],
),
),
),
//
Padding(
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
activity['title'],
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF333333),
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 8),
Row(
children: [
Icon(Icons.location_on, size: 14, color: Color(0xFF999999)),
SizedBox(width: 4),
Expanded(
child: Text(
activity['location'],
style: TextStyle(
fontSize: 12,
color: Color(0xFF999999),
),
overflow: TextOverflow.ellipsis,
),
),
],
),
SizedBox(height: 4),
Row(
children: [
Icon(Icons.people, size: 14, color: Color(0xFF999999)),
SizedBox(width: 4),
Text(
'${activity['participants']}人报名',
style: TextStyle(
fontSize: 12,
color: Color(0xFF999999),
),
),
],
),
],
),
),
],
),
),
);
}
Widget _buildBottomNavigationBar() {
return Container(
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: Offset(0, -2),
),
],
),
child: SafeArea(
child: Container(
height: 60,
child: Row(
children: [
_buildNavItem(0, Icons.home, '首页'),
_buildNavItem(1, Icons.people, '好友'),
SizedBox(width: 60), //
_buildNavItem(3, Icons.message, '消息'),
_buildNavItem(4, Icons.person, '我的'),
],
),
),
),
);
}
Widget _buildNavItem(int index, IconData icon, String label) {
final isSelected = _selectedIndex == index;
return Expanded(
child: GestureDetector(
onTap: () => _onItemTapped(index),
behavior: HitTestBehavior.opaque,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
icon,
color: isSelected ? Color(0xFFFF6B35) : Color(0xFF999999),
size: 24,
),
SizedBox(height: 4),
Text(
label,
style: TextStyle(
fontSize: 11,
color: isSelected ? Color(0xFFFF6B35) : Color(0xFF999999),
),
),
],
),
),
);
}
Widget _buildFloatingActionButton() {
return Container(
width: 56,
height: 56,
margin: EdgeInsets.only(top: 30),
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: [Color(0xFFFF6B35), Color(0xFFFFB84D)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: Color(0xFFFF6B35).withOpacity(0.4),
blurRadius: 12,
offset: Offset(0, 4),
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () => _onItemTapped(2),
customBorder: CircleBorder(),
child: Icon(Icons.add, color: Colors.white, size: 28),
),
),
);
}
}

View File

@ -0,0 +1,164 @@
import 'package:flutter/material.dart';
import '../models/message.dart';
import '../services/ai_service.dart';
class AIChatScreen extends StatefulWidget {
@override
State<AIChatScreen> createState() => _AIChatScreenState();
}
class _AIChatScreenState extends State<AIChatScreen> {
final List<Message> _messages = [
Message(
text: '您好!我是伴享智能助手,有什么可以帮您的吗?',
isUser: false,
),
];
final _textController = TextEditingController();
final _scrollController = ScrollController();
void _sendMessage() {
if (_textController.text.trim().isEmpty) return;
final newMessages = AIService.mockChat(_textController.text);
setState(() {
_messages.addAll(newMessages);
});
_textController.clear();
//
Future.delayed(Duration(milliseconds: 100), () {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: Duration(milliseconds: 300),
curve: Curves.easeOut,
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('AI智能助手'),
),
body: Column(
children: [
//
Container(
height: 60,
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: ListView(
scrollDirection: Axis.horizontal,
children: [
_buildQuickButton('附近活动'),
_buildQuickButton('我要挂号'),
_buildQuickButton('帮我买菜'),
],
),
),
Divider(height: 1),
//
Expanded(
child: ListView.builder(
controller: _scrollController,
padding: EdgeInsets.all(16),
itemCount: _messages.length,
itemBuilder: (context, index) {
final message = _messages[index];
return _buildMessageBubble(message);
},
),
),
//
Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 4,
offset: Offset(0, -2),
),
],
),
child: Row(
children: [
//
IconButton(
icon: Icon(Icons.mic, color: Color(0xFF333333)),
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('语音功能开发中')),
);
},
),
//
Expanded(
child: TextField(
controller: _textController,
decoration: InputDecoration(
hintText: '输入您的问题...',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(24),
),
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
onSubmitted: (_) => _sendMessage(),
),
),
SizedBox(width: 8),
//
IconButton(
icon: Icon(Icons.send, color: Color(0xFF333333)),
onPressed: _sendMessage,
),
],
),
),
],
),
);
}
Widget _buildQuickButton(String text) {
return Padding(
padding: EdgeInsets.only(right: 8),
child: OutlinedButton(
onPressed: () {
_textController.text = text;
_sendMessage();
},
style: OutlinedButton.styleFrom(
side: BorderSide(color: Color(0xFF333333)),
),
child: Text(text, style: TextStyle(color: Color(0xFF333333))),
),
);
}
Widget _buildMessageBubble(Message message) {
return Align(
alignment: message.isUser ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
margin: EdgeInsets.only(bottom: 12),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.7),
decoration: BoxDecoration(
color: message.isUser ? Color(0xFF333333) : Color(0xFFF0F0F0),
borderRadius: BorderRadius.circular(16),
),
child: Text(
message.text,
style: TextStyle(
color: message.isUser ? Colors.white : Color(0xFF333333),
fontSize: 16,
),
),
),
);
}
}

View File

@ -0,0 +1,164 @@
import 'package:flutter/material.dart';
import '../models/hospital.dart';
import '../services/medical_service.dart';
import 'payment_screen.dart';
class BookingScreen extends StatefulWidget {
final Hospital hospital;
final Doctor doctor;
const BookingScreen({
required this.hospital,
required this.doctor,
});
@override
State<BookingScreen> createState() => _BookingScreenState();
}
class _BookingScreenState extends State<BookingScreen> {
DateTime _selectedDate = DateTime.now().add(Duration(days: 1));
String _selectedTime = '上午';
final _nameController = TextEditingController();
final _phoneController = TextEditingController();
void _submit() {
if (_nameController.text.isEmpty || _phoneController.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('请填写就诊人信息')),
);
return;
}
final appointment = Appointment(
id: DateTime.now().toString(),
hospitalName: widget.hospital.name,
doctorName: widget.doctor.name,
department: widget.doctor.department,
time: _selectedDate,
status: '待支付',
fee: widget.doctor.fee,
);
MedicalService.addAppointment(appointment);
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => PaymentScreen(appointment: appointment)),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('预约挂号')),
body: SingleChildScrollView(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('医生信息', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500)),
SizedBox(height: 12),
Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
_buildRow('医院', widget.hospital.name),
_buildRow('科室', widget.doctor.department),
_buildRow('医生', widget.doctor.name),
_buildRow('职称', widget.doctor.title),
_buildRow('挂号费', '¥${widget.doctor.fee}'),
],
),
),
),
SizedBox(height: 24),
Text('选择时间', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500)),
SizedBox(height: 12),
Row(
children: [
Expanded(
child: Text('${_selectedDate.month}${_selectedDate.day}'),
),
TextButton(
onPressed: () async {
final date = await showDatePicker(
context: context,
initialDate: _selectedDate,
firstDate: DateTime.now(),
lastDate: DateTime.now().add(Duration(days: 30)),
);
if (date != null) setState(() => _selectedDate = date);
},
child: Text('选择日期'),
),
],
),
Row(
children: [
Radio(
value: '上午',
groupValue: _selectedTime,
onChanged: (val) => setState(() => _selectedTime = val!),
),
Text('上午'),
SizedBox(width: 20),
Radio(
value: '下午',
groupValue: _selectedTime,
onChanged: (val) => setState(() => _selectedTime = val!),
),
Text('下午'),
],
),
SizedBox(height: 24),
Text('就诊人信息', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500)),
SizedBox(height: 12),
TextField(
controller: _nameController,
decoration: InputDecoration(
labelText: '姓名',
border: OutlineInputBorder(),
),
),
SizedBox(height: 12),
TextField(
controller: _phoneController,
keyboardType: TextInputType.phone,
decoration: InputDecoration(
labelText: '手机号',
border: OutlineInputBorder(),
),
),
SizedBox(height: 40),
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
onPressed: _submit,
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF333333),
foregroundColor: Colors.white,
),
child: Text('提交订单', style: TextStyle(fontSize: 16)),
),
),
],
),
),
);
}
Widget _buildRow(String label, String value) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
Text('$label', style: TextStyle(color: Colors.grey)),
Text(value),
],
),
);
}
}

View File

@ -0,0 +1,124 @@
import 'package:flutter/material.dart';
import '../models/activity.dart';
import '../services/activity_service.dart';
class CreateActivityScreen extends StatefulWidget {
@override
State<CreateActivityScreen> createState() => _CreateActivityScreenState();
}
class _CreateActivityScreenState extends State<CreateActivityScreen> {
final _titleController = TextEditingController();
final _locationController = TextEditingController();
final _descController = TextEditingController();
String _selectedCategory = '太极';
int _maxParticipants = 10;
DateTime _selectedDate = DateTime.now().add(Duration(days: 1));
TimeOfDay _selectedTime = TimeOfDay(hour: 9, minute: 0);
void _submit() {
if (_titleController.text.isEmpty || _locationController.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('请填写完整信息')),
);
return;
}
final activity = Activity(
id: DateTime.now().toString(),
title: _titleController.text,
category: _selectedCategory,
time: DateTime(
_selectedDate.year,
_selectedDate.month,
_selectedDate.day,
_selectedTime.hour,
_selectedTime.minute,
),
location: _locationController.text,
maxParticipants: _maxParticipants,
creatorName: '',
description: _descController.text,
);
ActivityService.addActivity(activity);
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('活动创建成功')),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('发起活动')),
body: ListView(
padding: EdgeInsets.all(16),
children: [
TextField(
controller: _titleController,
decoration: InputDecoration(
labelText: '活动名称',
border: OutlineInputBorder(),
),
),
SizedBox(height: 16),
DropdownButtonFormField<String>(
value: _selectedCategory,
decoration: InputDecoration(
labelText: '活动类型',
border: OutlineInputBorder(),
),
items: ActivityService.categories
.skip(1)
.map((c) => DropdownMenuItem(value: c, child: Text(c)))
.toList(),
onChanged: (val) => setState(() => _selectedCategory = val!),
),
SizedBox(height: 16),
TextField(
controller: _locationController,
decoration: InputDecoration(
labelText: '活动地点',
border: OutlineInputBorder(),
),
),
SizedBox(height: 16),
ListTile(
title: Text('人数限制'),
trailing: DropdownButton<int>(
value: _maxParticipants,
items: [5, 8, 10, 12, 15]
.map((n) => DropdownMenuItem(value: n, child: Text('$n')))
.toList(),
onChanged: (val) => setState(() => _maxParticipants = val!),
),
contentPadding: EdgeInsets.zero,
),
SizedBox(height: 16),
TextField(
controller: _descController,
maxLines: 3,
decoration: InputDecoration(
labelText: '活动描述(选填)',
border: OutlineInputBorder(),
),
),
SizedBox(height: 40),
SizedBox(
height: 50,
child: ElevatedButton(
onPressed: _submit,
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF333333),
foregroundColor: Colors.white,
),
child: Text('发布活动', style: TextStyle(fontSize: 16)),
),
),
],
),
);
}
}

View File

@ -0,0 +1,50 @@
import 'package:flutter/material.dart';
import '../models/hospital.dart';
import '../services/medical_service.dart';
import 'doctor_list_screen.dart';
class DepartmentScreen extends StatelessWidget {
final Hospital hospital;
const DepartmentScreen({required this.hospital});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('选择科室')),
body: GridView.builder(
padding: EdgeInsets.all(16),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
),
itemCount: hospital.departments.length,
itemBuilder: (context, index) {
final department = hospital.departments[index];
return Card(
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => DoctorListScreen(
hospital: hospital,
department: department,
),
),
);
},
child: Center(
child: Text(
department,
style: TextStyle(fontSize: 16),
),
),
),
);
},
),
);
}
}

View File

@ -0,0 +1,83 @@
import 'package:flutter/material.dart';
import '../models/hospital.dart';
import '../services/medical_service.dart';
import 'booking_screen.dart';
class DoctorListScreen extends StatelessWidget {
final Hospital hospital;
final String department;
const DoctorListScreen({
required this.hospital,
required this.department,
});
@override
Widget build(BuildContext context) {
final doctors = MedicalService.getDoctorsByDepartment(department);
return Scaffold(
appBar: AppBar(title: Text('$department - 选择医生')),
body: doctors.isEmpty
? Center(child: Text('暂无医生'))
: ListView.builder(
padding: EdgeInsets.all(16),
itemCount: doctors.length,
itemBuilder: (context, index) {
final doctor = doctors[index];
return Card(
margin: EdgeInsets.only(bottom: 12),
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => BookingScreen(
hospital: hospital,
doctor: doctor,
),
),
);
},
child: Padding(
padding: EdgeInsets.all(16),
child: Row(
children: [
CircleAvatar(
radius: 30,
child: Text(doctor.name[0]),
),
SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
doctor.name,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
),
SizedBox(height: 4),
Text(doctor.title, style: TextStyle(color: Colors.grey)),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('挂号费', style: TextStyle(fontSize: 12, color: Colors.grey)),
Text(
'¥${doctor.fee}',
style: TextStyle(fontSize: 18, color: Color(0xFF333333)),
),
],
),
],
),
),
),
);
},
),
);
}
}

View File

@ -0,0 +1,222 @@
import 'package:flutter/material.dart';
import '../models/activity.dart';
import '../services/activity_service.dart';
import '../utils/constants.dart';
import 'activity_detail_screen.dart';
import 'create_activity_screen.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
String _selectedCategory = '全部';
String _timeFilter = '全部';
List<Activity> _activities = [];
bool _loading = true;
@override
void initState() {
super.initState();
_loadActivities();
}
void _loadActivities() {
setState(() {
_activities = ActivityService.getMockActivities();
_loading = false;
});
}
List<Activity> get _filteredActivities {
return _activities.where((a) {
if (_selectedCategory != '全部' && a.category != _selectedCategory) return false;
return true;
}).toList();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.location_on, size: 18, color: Color(0xFF1976D2)),
const SizedBox(width: 4),
const Text('成都', style: TextStyle(fontSize: 16, color: Color(0xFF666666))),
const SizedBox(width: 16),
Expanded(
child: Container(
height: 36,
decoration: BoxDecoration(
color: const Color(0xFFF5F5F5),
borderRadius: BorderRadius.circular(18),
),
child: const Row(
children: [
SizedBox(width: 12),
Icon(Icons.search, size: 20, color: Color(0xFF999999)),
SizedBox(width: 8),
Text('搜索活动', style: TextStyle(fontSize: 14, color: Color(0xFF999999))),
],
),
),
),
],
),
),
body: _loading
? const Center(child: CircularProgressIndicator())
: RefreshIndicator(
onRefresh: () async => _loadActivities(),
child: CustomScrollView(
slivers: [
// Category chips
SliverToBoxAdapter(
child: SizedBox(
height: 48,
child: ListView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 16),
children: AppConstants.activityCategories.map((cat) {
final selected = _selectedCategory == cat;
return Padding(
padding: const EdgeInsets.only(right: 8),
child: ChoiceChip(
label: Text(cat, style: TextStyle(fontSize: 14, color: selected ? Colors.white : const Color(0xFF666666))),
selected: selected,
selectedColor: const Color(0xFF1976D2),
backgroundColor: const Color(0xFFF5F5F5),
onSelected: (_) => setState(() => _selectedCategory = cat),
),
);
}).toList(),
),
),
),
// Time filter
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
children: ['全部', '今天', '明天', '本周'].map((t) {
final selected = _timeFilter == t;
return Padding(
padding: const EdgeInsets.only(right: 12),
child: GestureDetector(
onTap: () => setState(() => _timeFilter = t),
child: Text(t, style: TextStyle(
fontSize: 16,
color: selected ? const Color(0xFF1976D2) : const Color(0xFF999999),
fontWeight: selected ? FontWeight.w600 : FontWeight.normal,
)),
),
);
}).toList(),
),
),
),
// Activity list
SliverList(
delegate: SliverChildBuilderDelegate(
(ctx, i) => _ActivityCard(
activity: _filteredActivities[i],
onTap: () => Navigator.push(context, MaterialPageRoute(
builder: (_) => ActivityDetailScreen(activity: _filteredActivities[i]),
)),
),
childCount: _filteredActivities.length,
),
),
const SliverToBoxAdapter(child: SizedBox(height: 80)),
],
),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const CreateActivityScreen())),
icon: const Icon(Icons.add),
label: const Text('发起活动', style: TextStyle(fontSize: 16)),
backgroundColor: const Color(0xFF1976D2),
foregroundColor: Colors.white,
),
);
}
}
class _ActivityCard extends StatelessWidget {
final Activity activity;
final VoidCallback onTap;
const _ActivityCard({required this.activity, required this.onTap});
String _categoryEmoji(String cat) {
const map = {'太极':'🧘','茶话会':'🍵','书法':'✍️','摄影':'📷','舞蹈':'💃','户外徒步':'🥾','手工':'🎨','唱歌':'🎵','棋牌':'♟️','读书':'📚','晨练':'🏃'};
return map[cat] ?? '🎯';
}
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(16),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(_categoryEmoji(activity.category), style: const TextStyle(fontSize: 24)),
const SizedBox(width: 8),
Expanded(child: Text(activity.title, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600), maxLines: 1, overflow: TextOverflow.ellipsis)),
],
),
const SizedBox(height: 12),
Row(
children: [
const Icon(Icons.access_time, size: 16, color: Color(0xFF999999)),
const SizedBox(width: 4),
Text(activity.timeDisplay, style: const TextStyle(fontSize: 16, color: Color(0xFF666666))),
const SizedBox(width: 16),
const Icon(Icons.location_on_outlined, size: 16, color: Color(0xFF999999)),
const SizedBox(width: 4),
Text(activity.distance != null ? '距你${activity.distance}km' : '', style: const TextStyle(fontSize: 16, color: Color(0xFF666666))),
],
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: Text(
'已报名 ${activity.currentParticipants}/${activity.maxParticipants}',
style: TextStyle(
fontSize: 16,
color: activity.isFull ? const Color(0xFFF44336) : const Color(0xFF4CAF50),
fontWeight: FontWeight.w500,
),
),
),
SizedBox(
height: 40,
child: ElevatedButton(
onPressed: activity.isFull ? null : () {},
style: ElevatedButton.styleFrom(
minimumSize: const Size(80, 40),
padding: const EdgeInsets.symmetric(horizontal: 20),
),
child: Text(activity.isFull ? '已满员' : '报名', style: const TextStyle(fontSize: 16)),
),
),
],
),
],
),
),
),
);
}
}

View File

@ -0,0 +1,63 @@
import 'package:flutter/material.dart';
import '../models/hospital.dart';
import '../services/medical_service.dart';
import 'department_screen.dart';
class HospitalListScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('在线挂号')),
body: ListView.builder(
padding: EdgeInsets.all(16),
itemCount: MedicalService.mockHospitals.length,
itemBuilder: (context, index) {
final hospital = MedicalService.mockHospitals[index];
return Card(
margin: EdgeInsets.only(bottom: 12),
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => DepartmentScreen(hospital: hospital),
),
);
},
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
hospital.name,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
),
SizedBox(height: 8),
Row(
children: [
Icon(Icons.location_on, size: 16, color: Colors.grey),
SizedBox(width: 4),
Text(hospital.address, style: TextStyle(color: Colors.grey)),
],
),
SizedBox(height: 8),
Wrap(
spacing: 8,
children: hospital.departments
.map((d) => Chip(
label: Text(d, style: TextStyle(fontSize: 12)),
padding: EdgeInsets.zero,
))
.toList(),
),
],
),
),
),
);
},
),
);
}
}

View File

@ -0,0 +1,96 @@
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'profile_setup_screen.dart';
import 'activity_list_screen.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _phoneController = TextEditingController();
final _codeController = TextEditingController();
bool _codeSent = false;
void _sendCode() {
if (_phoneController.text.length == 11) {
setState(() => _codeSent = true);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('验证码已发送测试码123456')),
);
}
}
void _login() async {
if (_codeController.text == '123456') {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('auth_token', 'mock_token_${DateTime.now().millisecondsSinceEpoch}');
await prefs.setString('phone', _phoneController.text);
final isNewUser = prefs.getString('user_nickname') == null;
if (mounted) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => isNewUser ? ProfileSetupScreen() : ActivityListScreen()),
);
}
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('验证码错误')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFFFF8F0),
appBar: AppBar(title: Text('登录 / 注册')),
body: Padding(
padding: EdgeInsets.all(24),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('欢迎使用伴享', style: TextStyle(fontSize: 28, fontWeight: FontWeight.w300)),
SizedBox(height: 60),
TextField(
controller: _phoneController,
keyboardType: TextInputType.phone,
maxLength: 11,
decoration: InputDecoration(
labelText: '手机号',
border: OutlineInputBorder(),
),
),
SizedBox(height: 20),
if (_codeSent) ...[
TextField(
controller: _codeController,
keyboardType: TextInputType.number,
maxLength: 6,
decoration: InputDecoration(
labelText: '验证码',
border: OutlineInputBorder(),
),
),
SizedBox(height: 20),
],
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
onPressed: _codeSent ? _login : _sendCode,
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF333333),
foregroundColor: Colors.white,
),
child: Text(_codeSent ? '登录' : '获取验证码', style: TextStyle(fontSize: 16)),
),
),
],
),
),
);
}
}

View File

@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import 'home_screen.dart';
import 'services_screen.dart';
import 'ai_chat_screen.dart';
import 'messages_screen.dart';
import 'profile_screen.dart';
class MainScreen extends StatefulWidget {
const MainScreen({super.key});
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int _currentIndex = 0;
final _screens = const [
HomeScreen(),
ServicesScreen(),
AiChatScreen(),
MessagesScreen(),
ProfileScreen(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(index: _currentIndex, children: _screens),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (i) => setState(() => _currentIndex = i),
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home_outlined), activeIcon: Icon(Icons.home), label: '首页'),
BottomNavigationBarItem(icon: Icon(Icons.grid_view_outlined), activeIcon: Icon(Icons.grid_view), label: '服务'),
BottomNavigationBarItem(icon: Icon(Icons.smart_toy_outlined), activeIcon: Icon(Icons.smart_toy), label: 'AI管家'),
BottomNavigationBarItem(icon: Icon(Icons.message_outlined), activeIcon: Icon(Icons.message), label: '消息'),
BottomNavigationBarItem(icon: Icon(Icons.person_outline), activeIcon: Icon(Icons.person), label: '我的'),
],
),
);
}
}

View File

@ -0,0 +1,236 @@
import 'package:flutter/material.dart';
class MedicalScreen extends StatelessWidget {
const MedicalScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('医疗健康')),
body: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Hospital selection
const Text('选择医院', style: TextStyle(fontSize: 20, fontWeight: FontWeight.w600)),
const SizedBox(height: 12),
_hospitalCard(context, '四川大学华西医院', '三甲', '距您5.2km', '028-85422114'),
_hospitalCard(context, '四川省人民医院', '三甲', '距您3.8km', '028-87394243'),
_hospitalCard(context, '成都市第一人民医院', '三甲', '距您2.1km', '028-86660016'),
const SizedBox(height: 24),
// Departments
const Text('常见科室', style: TextStyle(fontSize: 20, fontWeight: FontWeight.w600)),
const SizedBox(height: 12),
Wrap(
spacing: 12, runSpacing: 12,
children: ['心内科', '骨科', '消化内科', '神经内科', '内分泌科', '眼科', '耳鼻喉科', '皮肤科'].map((dept) {
return GestureDetector(
onTap: () => _showDoctors(context, dept),
child: Container(
width: (MediaQuery.of(context).size.width - 64) / 4,
height: 64,
decoration: BoxDecoration(
color: const Color(0xFFF5F5F5),
borderRadius: BorderRadius.circular(12),
),
child: Center(child: Text(dept, style: const TextStyle(fontSize: 14), textAlign: TextAlign.center)),
),
);
}).toList(),
),
const SizedBox(height: 24),
// Online consultation
const Text('在线问诊', style: TextStyle(fontSize: 20, fontWeight: FontWeight.w600)),
const SizedBox(height: 12),
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.chat, color: Color(0xFF4CAF50)),
SizedBox(width: 8),
Text('图文问诊', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600)),
Spacer(),
Text('¥19.9/次', style: TextStyle(fontSize: 16, color: Color(0xFFF44336), fontWeight: FontWeight.w600)),
],
),
const SizedBox(height: 8),
const Text('描述症状上传照片专业医生2小时内回复', style: TextStyle(fontSize: 14, color: Color(0xFF999999))),
const SizedBox(height: 12),
SizedBox(
width: double.infinity, height: 44,
child: ElevatedButton(
onPressed: () => _showConsultationDialog(context),
style: ElevatedButton.styleFrom(backgroundColor: const Color(0xFF4CAF50)),
child: const Text('开始问诊'),
),
),
],
),
),
),
const SizedBox(height: 12),
const Center(
child: Text('⚠️ 仅供参考,不构成医疗建议', style: TextStyle(fontSize: 12, color: Color(0xFF999999))),
),
],
),
),
);
}
Widget _hospitalCard(BuildContext context, String name, String level, String distance, String phone) {
return Card(
margin: const EdgeInsets.only(bottom: 8),
child: ListTile(
onTap: () => _showDoctors(context, '心内科'),
leading: CircleAvatar(backgroundColor: const Color(0xFFE8F5E9), child: Text(level, style: const TextStyle(fontSize: 12, color: Color(0xFF4CAF50)))),
title: Text(name, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
subtitle: Text('$distance 📞 $phone', style: const TextStyle(fontSize: 13, color: Color(0xFF999999))),
trailing: const Icon(Icons.chevron_right),
),
);
}
void _showDoctors(BuildContext context, String dept) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
builder: (ctx) => DraggableScrollableSheet(
initialChildSize: 0.7, minChildSize: 0.4, maxChildSize: 0.9,
expand: false,
builder: (_, controller) => Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Text('$dept - 医生列表', style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
),
Expanded(
child: ListView(
controller: controller,
padding: const EdgeInsets.symmetric(horizontal: 16),
children: [
_doctorTile(ctx, '王教授', '主任医师', 4.9, '高血压、冠心病', '50'),
_doctorTile(ctx, '李医生', '副主任医师', 4.7, '心律失常、心力衰竭', '35'),
_doctorTile(ctx, '张医生', '主治医师', 4.5, '心肌病、心脏瓣膜病', '25'),
],
),
),
],
),
),
);
}
Widget _doctorTile(BuildContext context, String name, String title, double rating, String specialty, String fee) {
return Card(
margin: const EdgeInsets.only(bottom: 12),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const CircleAvatar(radius: 24, backgroundColor: Color(0xFFE3F2FD), child: Icon(Icons.person, color: Color(0xFF1976D2))),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(children: [
Text(name, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600)),
const SizedBox(width: 8),
Text(title, style: const TextStyle(fontSize: 14, color: Color(0xFF999999))),
]),
Row(children: [
const Icon(Icons.star, size: 16, color: Color(0xFFFFC107)),
Text(' $rating', style: const TextStyle(fontSize: 14, color: Color(0xFF666666))),
]),
],
),
const Spacer(),
Text('¥$fee', style: const TextStyle(fontSize: 18, color: Color(0xFFF44336), fontWeight: FontWeight.w600)),
],
),
const SizedBox(height: 8),
Text('擅长:$specialty', style: const TextStyle(fontSize: 14, color: Color(0xFF666666))),
const SizedBox(height: 12),
Row(
children: [
_slotChip('明天 9:00'),
const SizedBox(width: 8),
_slotChip('后天 14:00'),
const SizedBox(width: 8),
_slotChip('周五 9:00'),
],
),
],
),
),
);
}
Widget _slotChip(String text) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration(
color: const Color(0xFFE3F2FD),
borderRadius: BorderRadius.circular(8),
),
child: Text(text, style: const TextStyle(fontSize: 13, color: Color(0xFF1976D2))),
);
}
void _showConsultationDialog(BuildContext context) {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('在线问诊'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const TextField(
maxLines: 3,
decoration: InputDecoration(hintText: '请描述您的症状...', hintStyle: TextStyle(fontSize: 16)),
),
const SizedBox(height: 12),
const Text('支持上传最多3张图片', style: TextStyle(fontSize: 14, color: Color(0xFF999999))),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(3, (i) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Container(
width: 60, height: 60,
decoration: BoxDecoration(
color: const Color(0xFFF5F5F5),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: const Color(0xFFDDDDDD)),
),
child: const Icon(Icons.add_photo_alternate, color: Color(0xFF999999)),
),
)),
),
],
),
actions: [
TextButton(onPressed: () => Navigator.pop(ctx), child: const Text('取消')),
ElevatedButton(
onPressed: () {
Navigator.pop(ctx);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('问诊已提交医生将在2小时内回复', style: TextStyle(fontSize: 16)), backgroundColor: Color(0xFF4CAF50)),
);
},
child: const Text('支付 ¥19.9'),
),
],
),
);
}
}

View File

@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
class MessagesScreen extends StatelessWidget {
const MessagesScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('消息')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_notificationTile(Icons.event, '活动提醒', '您报名的「太极晨练」明天7:00开始', '10分钟前', const Color(0xFF1976D2)),
_notificationTile(Icons.check_circle, '报名成功', '您已成功报名「周末茶话会」', '1小时前', const Color(0xFF4CAF50)),
_notificationTile(Icons.payment, '支付通知', '挂号费¥50已支付成功', '昨天', const Color(0xFFFF9800)),
_notificationTile(Icons.chat, '问诊回复', '王医生已回复您的问诊', '昨天', const Color(0xFF9C27B0)),
_notificationTile(Icons.campaign, '系统通知', '欢迎使用伴享!完善资料赢取积分', '2天前', const Color(0xFF607D8B)),
],
),
);
}
Widget _notificationTile(IconData icon, String title, String content, String time, Color color) {
return Card(
margin: const EdgeInsets.only(bottom: 8),
child: ListTile(
leading: CircleAvatar(backgroundColor: color.withOpacity(0.1), child: Icon(icon, color: color)),
title: Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
subtitle: Text(content, style: const TextStyle(fontSize: 14, color: Color(0xFF666666))),
trailing: Text(time, style: const TextStyle(fontSize: 12, color: Color(0xFF999999))),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
);
}
}

View File

@ -0,0 +1,137 @@
import 'package:flutter/material.dart';
import '../models/hospital.dart';
import '../services/medical_service.dart';
import 'payment_screen.dart';
class OrderListScreen extends StatefulWidget {
@override
State<OrderListScreen> createState() => _OrderListScreenState();
}
class _OrderListScreenState extends State<OrderListScreen> {
@override
Widget build(BuildContext context) {
final orders = MedicalService.appointments;
return Scaffold(
appBar: AppBar(title: Text('我的订单')),
body: orders.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.inbox, size: 80, color: Colors.grey[300]),
SizedBox(height: 16),
Text('暂无订单', style: TextStyle(color: Colors.grey)),
],
),
)
: ListView.builder(
padding: EdgeInsets.all(16),
itemCount: orders.length,
itemBuilder: (context, index) {
final order = orders[index];
return Card(
margin: EdgeInsets.only(bottom: 12),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
order.hospitalName,
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
),
_buildStatusChip(order.status),
],
),
SizedBox(height: 12),
_buildInfoRow(Icons.medical_services, '${order.department} - ${order.doctorName}'),
_buildInfoRow(
Icons.access_time,
'${order.time.month}${order.time.day}',
),
_buildInfoRow(Icons.attach_money, '¥${order.fee}'),
SizedBox(height: 12),
if (order.status == '待支付')
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
setState(() {
order.status = '已取消';
});
},
child: Text('取消订单'),
),
SizedBox(width: 8),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => PaymentScreen(appointment: order),
),
).then((_) => setState(() {}));
},
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF333333),
foregroundColor: Colors.white,
),
child: Text('去支付'),
),
],
),
],
),
),
);
},
),
);
}
Widget _buildStatusChip(String status) {
Color color;
switch (status) {
case '待支付':
color = Colors.orange;
break;
case '已支付':
case '待就诊':
color = Colors.green;
break;
case '已完成':
color = Colors.blue;
break;
case '已取消':
color = Colors.grey;
break;
default:
color = Colors.grey;
}
return Chip(
label: Text(status, style: TextStyle(color: Colors.white, fontSize: 12)),
backgroundColor: color,
padding: EdgeInsets.zero,
);
}
Widget _buildInfoRow(IconData icon, String text) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
Icon(icon, size: 16, color: Colors.grey),
SizedBox(width: 8),
Text(text, style: TextStyle(color: Colors.grey[700])),
],
),
);
}
}

View File

@ -0,0 +1,192 @@
import 'package:flutter/material.dart';
import '../models/hospital.dart';
class PaymentScreen extends StatefulWidget {
final Appointment appointment;
const PaymentScreen({required this.appointment});
@override
State<PaymentScreen> createState() => _PaymentScreenState();
}
class _PaymentScreenState extends State<PaymentScreen> {
String _selectedMethod = '微信支付';
bool _isProcessing = false;
void _pay() async {
setState(() => _isProcessing = true);
//
await Future.delayed(Duration(seconds: 2));
setState(() {
_isProcessing = false;
widget.appointment.status = '待就诊';
});
if (mounted) {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
title: Row(
children: [
Icon(Icons.check_circle, color: Colors.green, size: 32),
SizedBox(width: 12),
Text('支付成功'),
],
),
content: Text('您的预约已确认,请按时就诊'),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
Navigator.pop(context);
},
child: Text('确定'),
),
],
),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('支付')),
body: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
//
Container(
padding: EdgeInsets.all(16),
color: Colors.grey[50],
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('订单信息', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
SizedBox(height: 12),
_buildRow('医院', widget.appointment.hospitalName),
_buildRow('科室', widget.appointment.department),
_buildRow('医生', widget.appointment.doctorName),
_buildRow(
'就诊时间',
'${widget.appointment.time.month}${widget.appointment.time.day}',
),
Divider(height: 24),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('合计金额', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500)),
Text(
'¥${widget.appointment.fee}',
style: TextStyle(fontSize: 24, color: Colors.red, fontWeight: FontWeight.bold),
),
],
),
],
),
),
SizedBox(height: 20),
//
Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('支付方式', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
SizedBox(height: 12),
_buildPaymentMethod('微信支付', Icons.wechat),
_buildPaymentMethod('支付宝', Icons.account_balance_wallet),
],
),
),
],
),
),
),
//
Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 4,
offset: Offset(0, -2),
),
],
),
child: SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
onPressed: _isProcessing ? null : _pay,
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF333333),
foregroundColor: Colors.white,
),
child: _isProcessing
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
),
SizedBox(width: 12),
Text('支付中...'),
],
)
: Text('确认支付 ¥${widget.appointment.fee}', style: TextStyle(fontSize: 16)),
),
),
),
],
),
);
}
Widget _buildRow(String label, String value) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
Text('$label', style: TextStyle(color: Colors.grey)),
Text(value),
],
),
);
}
Widget _buildPaymentMethod(String name, IconData icon) {
final selected = _selectedMethod == name;
return Card(
child: InkWell(
onTap: () => setState(() => _selectedMethod = name),
child: Padding(
padding: EdgeInsets.all(16),
child: Row(
children: [
Icon(icon, size: 32, color: selected ? Color(0xFF333333) : Colors.grey),
SizedBox(width: 16),
Expanded(child: Text(name, style: TextStyle(fontSize: 16))),
if (selected)
Icon(Icons.check_circle, color: Color(0xFF333333)),
],
),
),
),
);
}
}

View File

@ -0,0 +1,103 @@
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class ProfileScreen extends StatefulWidget {
@override
State<ProfileScreen> createState() => _ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen> {
final _nicknameController = TextEditingController();
String? _selectedCity;
List<String> _selectedInterests = [];
final _cities = ['北京', '上海', '广州', '深圳', '成都', '重庆'];
final _interests = ['太极', '晨练', '书法', '摄影', '舞蹈', '旅游', '茶艺', '手工', '唱歌', '棋牌'];
@override
void initState() {
super.initState();
_loadProfile();
}
void _loadProfile() async {
final prefs = await SharedPreferences.getInstance();
setState(() {
_nicknameController.text = prefs.getString('nickname') ?? '';
_selectedCity = prefs.getString('city');
_selectedInterests = prefs.getStringList('interests') ?? [];
});
}
void _save() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('nickname', _nicknameController.text);
if (_selectedCity != null) await prefs.setString('city', _selectedCity!);
await prefs.setStringList('interests', _selectedInterests);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('保存成功')),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('个人资料')),
body: ListView(
padding: EdgeInsets.all(16),
children: [
TextField(
controller: _nicknameController,
decoration: InputDecoration(
labelText: '昵称',
border: OutlineInputBorder(),
),
),
SizedBox(height: 20),
DropdownButtonFormField<String>(
value: _selectedCity,
decoration: InputDecoration(
labelText: '居住城市',
border: OutlineInputBorder(),
),
items: _cities.map((city) => DropdownMenuItem(value: city, child: Text(city))).toList(),
onChanged: (val) => setState(() => _selectedCity = val),
),
SizedBox(height: 20),
Text('兴趣爱好最多5个', style: TextStyle(fontSize: 16)),
SizedBox(height: 10),
Wrap(
spacing: 8,
children: _interests.map((interest) {
final selected = _selectedInterests.contains(interest);
return FilterChip(
label: Text(interest),
selected: selected,
onSelected: (val) {
setState(() {
if (val && _selectedInterests.length < 5) {
_selectedInterests.add(interest);
} else {
_selectedInterests.remove(interest);
}
});
},
);
}).toList(),
),
SizedBox(height: 40),
ElevatedButton(
onPressed: _save,
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF333333),
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 16),
),
child: Text('保存', style: TextStyle(fontSize: 16)),
),
],
),
);
}
}

View File

@ -0,0 +1,195 @@
import 'package:flutter/material.dart';
import '../services/auth_service.dart';
import '../utils/constants.dart';
import 'activity_list_screen.dart';
class ProfileSetupScreen extends StatefulWidget {
const ProfileSetupScreen({super.key});
@override
State<ProfileSetupScreen> createState() => _ProfileSetupScreenState();
}
class _ProfileSetupScreenState extends State<ProfileSetupScreen> {
final _pageController = PageController();
int _currentStep = 0;
final _nicknameController = TextEditingController();
String? _gender;
int? _birthYear;
final Set<String> _selectedInterests = {};
void _nextStep() {
if (_currentStep < 2) {
setState(() => _currentStep++);
_pageController.animateToPage(_currentStep, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);
} else {
_finishSetup();
}
}
void _finishSetup() async {
await AuthService.updateProfile(
nickname: _nicknameController.text.trim().isEmpty ? null : _nicknameController.text.trim(),
birthYear: _birthYear,
gender: _gender,
interests: _selectedInterests.toList(),
);
if (mounted) {
Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => ActivityListScreen()));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('完善资料 (${_currentStep + 1}/3)'),
actions: [
TextButton(
onPressed: _finishSetup,
child: const Text('跳过', style: TextStyle(fontSize: 16)),
),
],
),
body: PageView(
controller: _pageController,
physics: const NeverScrollableScrollPhysics(),
children: [
// Step 1: Nickname
Padding(
padding: const EdgeInsets.all(32),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('给自己取个昵称吧', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
const Text('其他人将通过昵称认识您', style: TextStyle(fontSize: 16, color: Color(0xFF999999))),
const SizedBox(height: 32),
Center(
child: GestureDetector(
onTap: () {},
child: CircleAvatar(
radius: 50,
backgroundColor: const Color(0xFFE3F2FD),
child: const Icon(Icons.camera_alt, size: 32, color: Color(0xFF1976D2)),
),
),
),
const SizedBox(height: 8),
const Center(child: Text('点击设置头像', style: TextStyle(fontSize: 14, color: Color(0xFF999999)))),
const SizedBox(height: 24),
TextField(
controller: _nicknameController,
style: const TextStyle(fontSize: 20),
maxLength: 20,
decoration: const InputDecoration(hintText: '输入您的昵称', counterText: ''),
),
const Spacer(),
SizedBox(width: double.infinity, height: 52, child: ElevatedButton(onPressed: _nextStep, child: const Text('下一步'))),
],
),
),
// Step 2: Birth year + Gender
Padding(
padding: const EdgeInsets.all(32),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('基本信息', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
const SizedBox(height: 32),
const Text('性别', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600)),
const SizedBox(height: 12),
Row(
children: [
Expanded(child: _genderButton('', 'male', Icons.male)),
const SizedBox(width: 16),
Expanded(child: _genderButton('', 'female', Icons.female)),
],
),
const SizedBox(height: 32),
const Text('出生年份', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600)),
const SizedBox(height: 12),
DropdownButtonFormField<int>(
value: _birthYear,
style: const TextStyle(fontSize: 18, color: Color(0xFF333333)),
decoration: const InputDecoration(hintText: '选择出生年份'),
items: List.generate(50, (i) => 1945 + i)
.map((y) => DropdownMenuItem(value: y, child: Text('$y年')))
.toList(),
onChanged: (v) => setState(() => _birthYear = v),
),
const Spacer(),
SizedBox(width: double.infinity, height: 52, child: ElevatedButton(onPressed: _nextStep, child: const Text('下一步'))),
],
),
),
// Step 3: Interests
Padding(
padding: const EdgeInsets.all(32),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('选择您的兴趣', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
const Text('至少选1个最多5个', style: TextStyle(fontSize: 16, color: Color(0xFF999999))),
const SizedBox(height: 24),
Wrap(
spacing: 12, runSpacing: 12,
children: AppConstants.interestTags.map((tag) {
final selected = _selectedInterests.contains(tag);
return ChoiceChip(
label: Text(tag, style: TextStyle(fontSize: 16, color: selected ? Colors.white : const Color(0xFF666666))),
selected: selected,
selectedColor: const Color(0xFF1976D2),
backgroundColor: const Color(0xFFF5F5F5),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
onSelected: (s) {
setState(() {
if (s && _selectedInterests.length < 5) {
_selectedInterests.add(tag);
} else {
_selectedInterests.remove(tag);
}
});
},
);
}).toList(),
),
const Spacer(),
SizedBox(
width: double.infinity, height: 52,
child: ElevatedButton(
onPressed: _selectedInterests.isNotEmpty ? _nextStep : null,
child: const Text('完成'),
),
),
],
),
),
],
),
);
}
Widget _genderButton(String label, String value, IconData icon) {
final selected = _gender == value;
return GestureDetector(
onTap: () => setState(() => _gender = value),
child: Container(
height: 64,
decoration: BoxDecoration(
color: selected ? const Color(0xFFE3F2FD) : Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: selected ? const Color(0xFF1976D2) : const Color(0xFFDDDDDD), width: selected ? 2 : 1),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, color: selected ? const Color(0xFF1976D2) : const Color(0xFF999999), size: 28),
const SizedBox(width: 8),
Text(label, style: TextStyle(fontSize: 18, color: selected ? const Color(0xFF1976D2) : const Color(0xFF666666), fontWeight: selected ? FontWeight.w600 : FontWeight.normal)),
],
),
),
);
}
}

View File

@ -0,0 +1,355 @@
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
class PublishScreen extends StatefulWidget {
@override
_PublishScreenState createState() => _PublishScreenState();
}
class _PublishScreenState extends State<PublishScreen> {
final TextEditingController _titleController = TextEditingController();
final TextEditingController _contentController = TextEditingController();
final TextEditingController _locationController = TextEditingController();
final ImagePicker _picker = ImagePicker();
List<XFile> _images = [];
XFile? _video;
bool _isVideo = false;
Future<void> _pickImages() async {
if (_isVideo) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('已选择视频,不能再选择图片')),
);
return;
}
if (_images.length >= 9) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('最多只能选择9张图片')),
);
return;
}
final List<XFile> selectedImages = await _picker.pickMultiImage();
if (selectedImages.isNotEmpty) {
setState(() {
int availableSlots = 9 - _images.length;
_images.addAll(selectedImages.take(availableSlots));
});
}
}
Future<void> _pickVideo() async {
if (_images.isNotEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('已选择图片,不能再选择视频')),
);
return;
}
final XFile? video = await _picker.pickVideo(source: ImageSource.gallery);
if (video != null) {
setState(() {
_video = video;
_isVideo = true;
});
}
}
void _removeImage(int index) {
setState(() {
_images.removeAt(index);
});
}
void _removeVideo() {
setState(() {
_video = null;
_isVideo = false;
});
}
void _publish() {
if (_titleController.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('请输入标题')),
);
return;
}
if (_images.isEmpty && _video == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('请至少添加一张图片或一个视频')),
);
return;
}
// TODO:
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('发布成功!'),
backgroundColor: Color(0xFFFF6B35),
),
);
Navigator.pop(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFFFF8F0),
appBar: AppBar(
title: Text('发布活动'),
actions: [
TextButton(
onPressed: _publish,
child: Text(
'发布',
style: TextStyle(
color: Color(0xFFFF6B35),
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
],
),
body: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
TextField(
controller: _titleController,
decoration: InputDecoration(
hintText: '输入活动标题...',
border: InputBorder.none,
hintStyle: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.grey[400],
),
),
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
maxLines: 1,
),
Divider(),
SizedBox(height: 16),
//
TextField(
controller: _contentController,
decoration: InputDecoration(
hintText: '分享活动详情...',
border: InputBorder.none,
hintStyle: TextStyle(color: Colors.grey[400]),
),
maxLines: 5,
),
SizedBox(height: 20),
// /
_buildMediaGrid(),
SizedBox(height: 20),
//
Row(
children: [
Icon(Icons.location_on, color: Color(0xFFFF6B35)),
SizedBox(width: 8),
Expanded(
child: TextField(
controller: _locationController,
decoration: InputDecoration(
hintText: '添加位置',
border: InputBorder.none,
hintStyle: TextStyle(color: Colors.grey[400]),
),
),
),
],
),
Divider(),
],
),
),
),
);
}
Widget _buildMediaGrid() {
return Wrap(
spacing: 8,
runSpacing: 8,
children: [
//
if (!_isVideo)
..._images.asMap().entries.map((entry) {
int index = entry.key;
XFile image = entry.value;
return _buildImageItem(image, index);
}).toList(),
//
if (_isVideo && _video != null)
_buildVideoItem(_video!),
//
if ((_images.length < 9 && !_isVideo) || (_images.isEmpty && !_isVideo))
_buildAddButton(),
],
);
}
Widget _buildImageItem(XFile image, int index) {
return Stack(
children: [
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
image: DecorationImage(
image: FileImage(File(image.path)),
fit: BoxFit.cover,
),
),
),
Positioned(
top: 4,
right: 4,
child: GestureDetector(
onTap: () => _removeImage(index),
child: Container(
width: 24,
height: 24,
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
shape: BoxShape.circle,
),
child: Icon(
Icons.close,
color: Colors.white,
size: 16,
),
),
),
),
],
);
}
Widget _buildVideoItem(XFile video) {
return Stack(
children: [
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Colors.black,
),
child: Center(
child: Icon(
Icons.play_circle_outline,
color: Colors.white,
size: 40,
),
),
),
Positioned(
top: 4,
right: 4,
child: GestureDetector(
onTap: _removeVideo,
child: Container(
width: 24,
height: 24,
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
shape: BoxShape.circle,
),
child: Icon(
Icons.close,
color: Colors.white,
size: 16,
),
),
),
),
],
);
}
Widget _buildAddButton() {
return GestureDetector(
onTap: () {
showModalBottomSheet(
context: context,
builder: (context) {
return SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: Icon(Icons.image, color: Color(0xFFFF6B35)),
title: Text('选择图片'),
onTap: () {
Navigator.pop(context);
_pickImages();
},
),
ListTile(
leading: Icon(Icons.videocam, color: Color(0xFFFF6B35)),
title: Text('选择视频'),
onTap: () {
Navigator.pop(context);
_pickVideo();
},
),
],
),
);
},
);
},
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey[300]!),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.add, size: 32, color: Colors.grey[400]),
SizedBox(height: 4),
Text(
'添加',
style: TextStyle(fontSize: 12, color: Colors.grey[400]),
),
],
),
),
);
}
@override
void dispose() {
_titleController.dispose();
_contentController.dispose();
_locationController.dispose();
super.dispose();
}
}

View File

@ -0,0 +1,86 @@
import 'package:flutter/material.dart';
import 'medical_screen.dart';
class ServicesScreen extends StatelessWidget {
const ServicesScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('生活服务')),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('服务分类', style: TextStyle(fontSize: 20, fontWeight: FontWeight.w600)),
const SizedBox(height: 16),
Row(
children: [
_serviceCard(context, '🏥', '医疗健康', true, () {
Navigator.push(context, MaterialPageRoute(builder: (_) => const MedicalScreen()));
}),
const SizedBox(width: 12),
_serviceCard(context, '🏠', '家政服务', false, null),
const SizedBox(width: 12),
_serviceCard(context, '🛒', '生活消费', false, null),
const SizedBox(width: 12),
_serviceCard(context, '🔒', '更多', false, null),
],
),
const SizedBox(height: 32),
// Quick access
const Text('快捷入口', style: TextStyle(fontSize: 20, fontWeight: FontWeight.w600)),
const SizedBox(height: 16),
_quickAccess(context, Icons.local_hospital, '在线挂号', '预约三甲医院专家号', () {
Navigator.push(context, MaterialPageRoute(builder: (_) => const MedicalScreen()));
}),
_quickAccess(context, Icons.chat_bubble_outline, '在线问诊', '¥19.9/次 专业医生解答', () {
Navigator.push(context, MaterialPageRoute(builder: (_) => const MedicalScreen()));
}),
],
),
),
);
}
Widget _serviceCard(BuildContext context, String emoji, String label, bool active, VoidCallback? onTap) {
return Expanded(
child: GestureDetector(
onTap: active ? onTap : () {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('$label即将开放', style: const TextStyle(fontSize: 16))));
},
child: Container(
height: 100,
decoration: BoxDecoration(
color: active ? const Color(0xFFE3F2FD) : const Color(0xFFF5F5F5),
borderRadius: BorderRadius.circular(12),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(emoji, style: const TextStyle(fontSize: 28)),
const SizedBox(height: 8),
Text(label, style: TextStyle(fontSize: 13, color: active ? const Color(0xFF1976D2) : const Color(0xFF999999))),
if (!active) const Text('即将开放', style: TextStyle(fontSize: 11, color: Color(0xFFCCCCCC))),
],
),
),
),
);
}
Widget _quickAccess(BuildContext context, IconData icon, String title, String subtitle, VoidCallback onTap) {
return Card(
margin: const EdgeInsets.only(bottom: 12),
child: ListTile(
onTap: onTap,
leading: CircleAvatar(backgroundColor: const Color(0xFFE3F2FD), child: Icon(icon, color: const Color(0xFF1976D2))),
title: Text(title, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600)),
subtitle: Text(subtitle, style: const TextStyle(fontSize: 14, color: Color(0xFF999999))),
trailing: const Icon(Icons.chevron_right),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
);
}
}

View File

@ -0,0 +1,54 @@
import '../models/activity.dart';
class ActivityService {
static List<Activity> mockActivities = [
Activity(
id: '1',
title: '人民公园晨练太极',
category: '太极',
time: DateTime.now().add(Duration(days: 1, hours: 6)),
location: '人民公园东门',
maxParticipants: 15,
currentParticipants: 8,
creatorName: '张大爷',
description: '每周三次的太极晨练,欢迎新朋友加入',
),
Activity(
id: '2',
title: '锦江畔摄影采风',
category: '摄影',
time: DateTime.now().add(Duration(days: 2, hours: 14)),
location: '锦江绿道',
maxParticipants: 10,
currentParticipants: 5,
creatorName: '李阿姨',
description: '春天来了,一起拍拍花儿',
),
Activity(
id: '3',
title: '茶馆品茶会',
category: '茶艺',
time: DateTime.now().add(Duration(days: 3, hours: 15)),
location: '宽窄巷子茶馆',
maxParticipants: 8,
currentParticipants: 3,
creatorName: '王老师',
description: '品茶聊天,结交新朋友',
),
];
static List<String> categories = [
'全部', '太极', '晨练', '书法', '摄影', '舞蹈', '旅游', '茶艺', '手工', '唱歌', '棋牌'
];
static List<Activity> getActivities({String? category}) {
if (category == null || category == '全部') {
return mockActivities;
}
return mockActivities.where((a) => a.category == category).toList();
}
static void addActivity(Activity activity) {
mockActivities.add(activity);
}
}

View File

@ -0,0 +1,27 @@
import '../models/message.dart';
class AIService {
static List<Message> mockChat(String userInput) {
String reply;
//
if (userInput.contains('活动') || userInput.contains('附近')) {
reply = '好的!我帮您查看附近的活动,您可以点击"活动广场"查看详情';
} else if (userInput.contains('挂号') || userInput.contains('医院')) {
reply = '挂号功能正在开发中,敬请期待!';
} else if (userInput.contains('买菜') || userInput.contains('购物')) {
reply = '生鲜配送功能正在开发中,敬请期待!';
} else if (userInput.contains('你好') || userInput.contains('您好')) {
reply = '您好!我是您的智能助手,有什么可以帮您的吗?';
} else if (userInput.contains('谢谢')) {
reply = '不客气!随时为您服务';
} else {
reply = '我理解您的意思了。目前我还在学习中,这个问题我会尽快帮您解决!';
}
return [
Message(text: userInput, isUser: true),
Message(text: reply, isUser: false),
];
}
}

View File

@ -0,0 +1,80 @@
import 'package:shared_preferences/shared_preferences.dart';
import '../models/user.dart';
class AuthService {
static const _tokenKey = 'auth_token';
static const _phoneKey = 'user_phone';
static const _nicknameKey = 'user_nickname';
static User? _currentUser;
static User? get currentUser => _currentUser;
static bool get isLoggedIn => _currentUser != null;
static Future<bool> checkLoginStatus() async {
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString(_tokenKey);
if (token != null) {
_currentUser = User(
id: '1',
phone: prefs.getString(_phoneKey) ?? '',
nickname: prefs.getString(_nicknameKey),
city: '成都',
);
return true;
}
return false;
}
static Future<Map<String, dynamic>> sendCode(String phone) async {
// Mock: always succeed
await Future.delayed(const Duration(seconds: 1));
return {'success': true, 'expiresIn': 120};
}
static Future<Map<String, dynamic>> login(String phone, String code) async {
await Future.delayed(const Duration(seconds: 1));
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_tokenKey, 'mock_token_${DateTime.now().millisecondsSinceEpoch}');
await prefs.setString(_phoneKey, phone);
_currentUser = User(
id: '1',
phone: phone,
city: '成都',
);
return {
'success': true,
'isNewUser': true,
'user': _currentUser,
};
}
static Future<void> updateProfile({
String? nickname,
int? birthYear,
String? gender,
String? city,
List<String>? interests,
}) async {
final prefs = await SharedPreferences.getInstance();
if (nickname != null) {
await prefs.setString(_nicknameKey, nickname);
}
_currentUser = User(
id: _currentUser?.id ?? '1',
phone: _currentUser?.phone ?? '',
nickname: nickname ?? _currentUser?.nickname,
birthYear: birthYear ?? _currentUser?.birthYear,
gender: gender ?? _currentUser?.gender,
city: city ?? _currentUser?.city ?? '成都',
interests: interests ?? _currentUser?.interests ?? [],
);
}
static Future<void> logout() async {
final prefs = await SharedPreferences.getInstance();
await prefs.clear();
_currentUser = null;
}
}

View File

@ -0,0 +1,58 @@
import '../models/hospital.dart';
class MedicalService {
static List<Hospital> mockHospitals = [
Hospital(
id: '1',
name: '成都市第一人民医院',
address: '锦江区红星路一段',
departments: ['内科', '外科', '骨科', '心内科', '呼吸科'],
),
Hospital(
id: '2',
name: '四川省人民医院',
address: '青羊区一环路西二段',
departments: ['内科', '外科', '神经内科', '消化科', '肿瘤科'],
),
Hospital(
id: '3',
name: '华西医院',
address: '武侯区国学巷',
departments: ['内科', '外科', '骨科', '眼科', '耳鼻喉科'],
),
];
static List<Doctor> mockDoctors = [
Doctor(
id: '1',
name: '张医生',
department: '内科',
title: '主任医师',
fee: '50',
),
Doctor(
id: '2',
name: '李医生',
department: '外科',
title: '副主任医师',
fee: '40',
),
Doctor(
id: '3',
name: '王医生',
department: '骨科',
title: '主治医师',
fee: '30',
),
];
static List<Appointment> appointments = [];
static List<Doctor> getDoctorsByDepartment(String department) {
return mockDoctors.where((d) => d.department == department).toList();
}
static void addAppointment(Appointment appointment) {
appointments.add(appointment);
}
}

79
lib/theme.dart Normal file
View File

@ -0,0 +1,79 @@
import 'package:flutter/material.dart';
class AppTheme {
static ThemeData get lightTheme {
return ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF1976D2),
brightness: Brightness.light,
primary: const Color(0xFF1976D2),
secondary: const Color(0xFF4CAF50),
surface: Colors.white,
),
scaffoldBackgroundColor: const Color(0xFFFAFAFA),
appBarTheme: const AppBarTheme(
backgroundColor: Colors.white,
foregroundColor: Color(0xFF333333),
elevation: 0,
centerTitle: true,
titleTextStyle: TextStyle(
color: Color(0xFF333333),
fontSize: 22,
fontWeight: FontWeight.w600,
),
),
cardTheme: CardTheme(
color: Colors.white,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: const BorderSide(color: Color(0xFFEEEEEE), width: 1),
),
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF1976D2),
foregroundColor: Colors.white,
minimumSize: const Size(double.infinity, 52),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
textStyle: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
),
),
textTheme: const TextTheme(
headlineLarge: TextStyle(fontSize: 28, fontWeight: FontWeight.bold, color: Color(0xFF333333)),
headlineMedium: TextStyle(fontSize: 22, fontWeight: FontWeight.w600, color: Color(0xFF333333)),
bodyLarge: TextStyle(fontSize: 18, color: Color(0xFF333333)),
bodyMedium: TextStyle(fontSize: 16, color: Color(0xFF666666)),
labelLarge: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: Colors.white,
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Color(0xFFDDDDDD)),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Color(0xFFDDDDDD)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Color(0xFF1976D2), width: 2),
),
hintStyle: const TextStyle(fontSize: 18, color: Color(0xFFBBBBBB)),
),
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
backgroundColor: Colors.white,
selectedItemColor: Color(0xFF1976D2),
unselectedItemColor: Color(0xFF999999),
selectedLabelStyle: TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
unselectedLabelStyle: TextStyle(fontSize: 14),
type: BottomNavigationBarType.fixed,
),
useMaterial3: true,
);
}
}

18
lib/utils/constants.dart Normal file
View File

@ -0,0 +1,18 @@
class AppConstants {
static const String appName = '伴享';
static const String appSlogan = '银发生活社交平台';
static const String apiBaseUrl = 'https://api.banxiang.com/api/v1';
// Activity categories
static const List<String> activityCategories = [
'全部', '晨练', '太极', '茶话会', '书法', '摄影',
'舞蹈', '户外徒步', '手工', '唱歌', '棋牌', '读书', '其他',
];
// Interest tags
static const List<String> interestTags = [
'太极', '书法', '摄影', '舞蹈', '唱歌', '棋牌',
'读书', '钓鱼', '园艺', '烹饪', '旅行', '手工',
'徒步', '游泳', '乒乓球', '广场舞',
];
}

1
linux/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
flutter/ephemeral

145
linux/CMakeLists.txt Normal file
View File

@ -0,0 +1,145 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)
# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
set(BINARY_NAME "banxiang_app")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "com.banxiang.banxiang_app")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(SET CMP0063 NEW)
# Load bundled libraries from the lib/ directory relative to the binary.
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Root filesystem for cross-building.
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
endif()
# Define build configuration options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
# Compilation settings that should be applied to most targets.
#
# Be cautious about adding new options here, as plugins use this function by
# default. In most cases, you should add new options to specific targets instead
# of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
endfunction()
# Flutter library and tool build rules.
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR})
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
# Define the application target. To change its name, change BINARY_NAME above,
# not the value here, or `flutter run` will no longer work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)
# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})
# Add dependency libraries. Add any application-specific dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)
# Only the install-generated bundle's copy of the executable will launch
# correctly, since the resources must in the right relative locations. To avoid
# people trying to run the unbundled copy, put it in a subdirectory instead of
# the default top-level location.
set_target_properties(${BINARY_NAME}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# By default, "installing" just makes a relocatable bundle in the build
# directory.
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
# Start with a clean build bundle directory every time.
install(CODE "
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
" COMPONENT Runtime)
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
install(FILES "${bundled_library}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endforeach(bundled_library)
# Copy the native assets provided by the build.dart from all packages.
set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/")
install(DIRECTORY "${NATIVE_ASSETS_DIR}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()

View File

@ -0,0 +1,88 @@
# This file controls Flutter-level build steps. It should not be edited.
cmake_minimum_required(VERSION 3.10)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)
# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.
# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
# which isn't available in 3.10.
function(list_prepend LIST_NAME PREFIX)
set(NEW_LIST "")
foreach(element ${${LIST_NAME}})
list(APPEND NEW_LIST "${PREFIX}${element}")
endforeach(element)
set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
endfunction()
# === Flutter Library ===
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"fl_basic_message_channel.h"
"fl_binary_codec.h"
"fl_binary_messenger.h"
"fl_dart_project.h"
"fl_engine.h"
"fl_json_message_codec.h"
"fl_json_method_codec.h"
"fl_message_codec.h"
"fl_method_call.h"
"fl_method_channel.h"
"fl_method_codec.h"
"fl_method_response.h"
"fl_plugin_registrar.h"
"fl_plugin_registry.h"
"fl_standard_message_codec.h"
"fl_standard_method_codec.h"
"fl_string_codec.h"
"fl_value.h"
"fl_view.h"
"flutter_linux.h"
)
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
target_link_libraries(flutter INTERFACE
PkgConfig::GTK
PkgConfig::GLIB
PkgConfig::GIO
)
add_dependencies(flutter flutter_assemble)
# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/_phony_
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
)

View File

@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
#include <file_selector_linux/file_selector_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
}

View File

@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter_linux/flutter_linux.h>
// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_

View File

@ -0,0 +1,24 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

6
linux/main.cc Normal file
View File

@ -0,0 +1,6 @@
#include "my_application.h"
int main(int argc, char** argv) {
g_autoptr(MyApplication) app = my_application_new();
return g_application_run(G_APPLICATION(app), argc, argv);
}

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