This commit is contained in:
2026-02-27 21:12:56 +08:00
commit a878084cbb
233 changed files with 22988 additions and 0 deletions

45
.gitignore vendored Normal file
View File

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

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: "582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536
base_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536
- platform: android
create_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536
base_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536
- platform: ios
create_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536
base_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536
- platform: linux
create_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536
base_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536
- platform: macos
create_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536
base_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536
- platform: web
create_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536
base_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536
- platform: windows
create_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536
base_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536
# 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'

17
README.md Normal file
View File

@@ -0,0 +1,17 @@
# mesh_drop_flutter
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:
- [Learn Flutter](https://docs.flutter.dev/get-started/learn-flutter)
- [Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Flutter learning resources](https://docs.flutter.dev/reference/learning-resources)
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

14
android/.gitignore vendored Normal file
View File

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

View File

@@ -0,0 +1,44 @@
plugins {
id("com.android.application")
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
}
android {
namespace = "com.example.mesh_drop_flutter"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.mesh_drop_flutter"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
}
}
}
flutter {
source = "../.."
}

View File

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

View File

@@ -0,0 +1,45 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="mesh_drop_flutter"
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.example.mesh_drop_flutter
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>

24
android/build.gradle.kts Normal file
View File

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

View File

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

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip

View File

@@ -0,0 +1,26 @@
pluginManagement {
val flutterSdkPath =
run {
val properties = java.util.Properties()
file("local.properties").inputStream().use { properties.load(it) }
val flutterSdkPath = properties.getProperty("flutter.sdk")
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
flutterSdkPath
}
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.11.1" apply false
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
}
include(":app")

3
flutter_rust_bridge.yaml Normal file
View File

@@ -0,0 +1,3 @@
rust_input: crate::api
rust_root: rust/
dart_output: lib/backend

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,24 @@
<?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>
</dict>
</plist>

View File

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

View File

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

View File

@@ -0,0 +1,620 @@
// !$*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 */; };
7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7884E8672EC3CC0400C636F2 /* SceneDelegate.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>"; };
7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.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 */,
7884E8672EC3CC0400C636F2 /* SceneDelegate.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 */,
7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 97C146ED1CF9000F007C117D /* Runner */;
targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.meshDropFlutter;
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.example.meshDropFlutter.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.example.meshDropFlutter.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.example.meshDropFlutter.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.meshDropFlutter;
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.example.meshDropFlutter;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C8088294A63A400263BE5 /* Debug */,
331C8089294A63A400263BE5 /* Release */,
331C808A294A63A400263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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>

70
ios/Runner/Info.plist Normal file
View File

@@ -0,0 +1,70 @@
<?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>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Mesh Drop Flutter</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>mesh_drop_flutter</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>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>flutter</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</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>
</dict>
</plist>

View File

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

View File

@@ -0,0 +1,6 @@
import Flutter
import UIKit
class SceneDelegate: FlutterSceneDelegate {
}

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

61
lib/app/app.dart Normal file
View File

@@ -0,0 +1,61 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../backend/event.dart';
import 'features/peers/controller/peers_controller.dart';
import 'features/transfer/controller/transfers_controller.dart';
import 'navigation/home_shell.dart';
import 'sync/backend_event_sync.dart';
import 'theme/app_theme.dart';
import 'theme/theme_mode_controller.dart';
class MeshDropApp extends ConsumerWidget {
const MeshDropApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final themeMode = ref.watch(themeModeControllerProvider);
ref.listen<AsyncValue<AppEvent>>(backendEventSyncProvider, (
previous,
next,
) {
next.whenData((event) {
switch (event) {
case AppEvent_PeerConnectOrUpdated(:final peer):
ref.read(peersControllerProvider.notifier).upsertPeer(peer);
case AppEvent_PeerDisconnected(:final id):
ref.read(peersControllerProvider.notifier).removePeer(id);
case AppEvent_TransferAdded(:final transfer):
ref
.read(transfersControllerProvider.notifier)
.upsertTransfer(transfer);
case AppEvent_TransferStatusChanged(:final transfer):
ref
.read(transfersControllerProvider.notifier)
.upsertTransfer(transfer);
case AppEvent_TransferRemoved(:final id):
ref.read(transfersControllerProvider.notifier).removeTransfer(id);
case AppEvent_TransferClear():
ref
.read(transfersControllerProvider.notifier)
.clearTransfersLocal();
case AppEvent_TransferProgressChanged(:final id, :final progress):
final currentBytes = progress < 0 ? 0.0 : progress;
ref
.read(transfersControllerProvider.notifier)
.updateProgress(id, currentBytes);
}
});
});
return MaterialApp(
title: 'Mesh Drop',
debugShowCheckedModeBanner: false,
themeMode: themeMode,
theme: AppTheme.lightTheme,
darkTheme: AppTheme.darkTheme,
home: const HomeShell(),
);
}
}

View File

@@ -0,0 +1,246 @@
import 'dart:io';
import 'package:desktop_drop/desktop_drop.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import '../../../backend/api/commands.dart' as commands;
import '../../../backend/discovery/model.dart';
class SendFilesModal extends StatefulWidget {
const SendFilesModal({
super.key,
required this.peer,
required this.targetIp,
this.initialPaths = const [],
});
final Peer peer;
final String targetIp;
final List<String> initialPaths;
@override
State<SendFilesModal> createState() => _SendFilesModalState();
}
class _SendFilesModalState extends State<SendFilesModal> {
late final List<String> _paths = [...widget.initialPaths];
bool _dragging = false;
Future<void> _submit() async {
if (_paths.isEmpty) {
return;
}
final pending = List<String>.from(_paths);
if (mounted) {
Navigator.of(context).pop();
}
for (final path in pending) {
final entityType = FileSystemEntity.typeSync(path);
if (entityType == FileSystemEntityType.directory) {
commands.sendFolder(
target: widget.peer,
targetIp: widget.targetIp,
folderPath: path,
);
} else {
commands.sendFile(
target: widget.peer,
targetIp: widget.targetIp,
filePath: path,
);
}
}
}
Future<void> _pickFiles() async {
final result = await FilePicker.platform.pickFiles(allowMultiple: true);
if (result == null || !mounted) {
return;
}
final selected = result.paths.whereType<String>().where(
(p) => p.isNotEmpty,
);
setState(() {
for (final path in selected) {
if (!_paths.contains(path)) {
_paths.add(path);
}
}
});
}
Future<void> _pickFolder() async {
final folder = await FilePicker.platform.getDirectoryPath();
if (folder == null || folder.isEmpty || !mounted) {
return;
}
setState(() {
if (!_paths.contains(folder)) {
_paths.add(folder);
}
});
}
bool get _enableDragDrop =>
Platform.isLinux || Platform.isWindows || Platform.isMacOS;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return AlertDialog(
title: Text('发送文件给 ${widget.peer.name}'),
content: SizedBox(
width: 640,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (_enableDragDrop)
DropTarget(
onDragEntered: (_) => setState(() => _dragging = true),
onDragExited: (_) => setState(() => _dragging = false),
onDragDone: (details) {
setState(() {
_dragging = false;
_paths.addAll(
details.files
.map((f) => f.path)
.where((p) => p.isNotEmpty)
.where((p) => !_paths.contains(p)),
);
});
},
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: _dragging
? theme.colorScheme.primary
: theme.colorScheme.outlineVariant,
width: _dragging ? 2 : 1,
),
color: theme.colorScheme.surfaceContainerHighest.withValues(
alpha: 0.35,
),
),
child: Column(
children: [
Icon(
_dragging
? Icons.file_download_done_rounded
: Icons.file_upload_rounded,
size: 36,
),
const SizedBox(height: 8),
Text(_dragging ? '松手即可添加文件' : '将文件或文件夹拖到这里'),
],
),
),
)
else
Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(color: theme.colorScheme.outlineVariant),
color: theme.colorScheme.surfaceContainerHighest.withValues(
alpha: 0.35,
),
),
child: const Column(
children: [
Icon(Icons.file_upload_rounded, size: 36),
SizedBox(height: 8),
],
),
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: OutlinedButton.icon(
onPressed: _pickFiles,
icon: const Icon(Icons.attach_file_rounded),
label: const Text('选择文件'),
),
),
const SizedBox(width: 8),
Expanded(
child: OutlinedButton.icon(
onPressed: _pickFolder,
icon: const Icon(Icons.folder_open_rounded),
label: const Text('选择文件夹'),
),
),
],
),
const SizedBox(height: 12),
ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 260),
child: _paths.isEmpty
? const Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 24),
child: Text('暂无待发送文件'),
),
)
: ListView.separated(
shrinkWrap: true,
itemCount: _paths.length,
separatorBuilder: (_, _) => const Divider(height: 1),
itemBuilder: (context, index) {
final path = _paths[index];
final isDirectory =
FileSystemEntity.typeSync(path) ==
FileSystemEntityType.directory;
return ListTile(
dense: true,
leading: Icon(
isDirectory
? Icons.folder_copy_rounded
: Icons.insert_drive_file_rounded,
),
title: Text(
path.split(Platform.pathSeparator).last,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
subtitle: Text(
path,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
trailing: IconButton(
icon: const Icon(Icons.delete_outline_rounded),
onPressed: () =>
setState(() => _paths.removeAt(index)),
),
);
},
),
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('取消'),
),
FilledButton.icon(
onPressed: _paths.isEmpty ? null : _submit,
icon: const Icon(Icons.send_rounded),
label: Text('发送 (${_paths.length})'),
),
],
);
}
}

View File

@@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
import '../../../backend/api/commands.dart' as commands;
import '../../../backend/discovery/model.dart';
class SendTextModal extends StatefulWidget {
const SendTextModal({super.key, required this.peer, required this.targetIp});
final Peer peer;
final String targetIp;
@override
State<SendTextModal> createState() => _SendTextModalState();
}
class _SendTextModalState extends State<SendTextModal> {
final _controller = TextEditingController();
Future<void> _submit() async {
final text = _controller.text.trim();
if (text.isEmpty) {
return;
}
if (mounted) {
Navigator.of(context).pop();
}
commands.sendText(
target: widget.peer,
targetIp: widget.targetIp,
text: text,
);
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text('发送文本给 ${widget.peer.name}'),
content: SizedBox(
width: 540,
child: TextField(
controller: _controller,
maxLines: 8,
minLines: 4,
decoration: const InputDecoration(
hintText: '输入你想发送的文本内容',
border: OutlineInputBorder(),
),
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('取消'),
),
FilledButton.icon(
onPressed: _submit,
icon: const Icon(Icons.send_rounded),
label: const Text('发送'),
),
],
);
}
}

View File

@@ -0,0 +1,50 @@
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../../../../backend/api/commands.dart' as commands;
import '../../../../backend/discovery/model.dart';
part 'peers_controller.g.dart';
@riverpod
class PeersController extends _$PeersController {
@override
Future<List<Peer>> build() async {
return commands.getPeers();
}
Future<void> refresh() async {
state = const AsyncLoading();
state = await AsyncValue.guard(commands.getPeers);
}
void upsertPeer(Peer peer) {
final current = state.asData?.value ?? const <Peer>[];
final index = current.indexWhere((item) => item.id == peer.id);
if (index < 0) {
state = AsyncData([...current, peer]);
return;
}
final old = current[index];
if (old == peer) {
return;
}
final next = [...current];
next[index] = peer;
state = AsyncData(next);
}
void removePeer(String id) {
final current = state.asData?.value;
if (current == null || current.isEmpty) {
return;
}
final next = current.where((item) => item.id != id).toList(growable: false);
if (next.length != current.length) {
state = AsyncData(next);
}
}
}

View File

@@ -0,0 +1,54 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'peers_controller.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
@ProviderFor(PeersController)
final peersControllerProvider = PeersControllerProvider._();
final class PeersControllerProvider
extends $AsyncNotifierProvider<PeersController, List<Peer>> {
PeersControllerProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'peersControllerProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$peersControllerHash();
@$internal
@override
PeersController create() => PeersController();
}
String _$peersControllerHash() => r'd6d108b39274dd5de380523373f115d2e4c10e0e';
abstract class _$PeersController extends $AsyncNotifier<List<Peer>> {
FutureOr<List<Peer>> build();
@$mustCallSuper
@override
void runBuild() {
final ref = this.ref as $Ref<AsyncValue<List<Peer>>, List<Peer>>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<AsyncValue<List<Peer>>, List<Peer>>,
AsyncValue<List<Peer>>,
Object?,
Object?
>;
element.handleCreate(ref, build);
}
}

View File

@@ -0,0 +1,73 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../controller/peers_controller.dart';
import '../widgets/peer_card.dart';
import '../../../shared/widgets/empty_state.dart';
class PeersPage extends ConsumerWidget {
const PeersPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final peersAsync = ref.watch(peersControllerProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Peers Discovery'),
actions: [
IconButton(
tooltip: '刷新',
onPressed: () =>
ref.read(peersControllerProvider.notifier).refresh(),
icon: const Icon(Icons.refresh_rounded),
),
],
),
body: peersAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, _) => AppEmptyState(
icon: Icons.cloud_off_rounded,
title: '发现失败',
message: error.toString(),
action: FilledButton.icon(
onPressed: () =>
ref.read(peersControllerProvider.notifier).refresh(),
icon: const Icon(Icons.refresh_rounded),
label: const Text('重试'),
),
),
data: (peers) {
if (peers.isEmpty) {
return AppEmptyState(
icon: Icons.wifi_tethering,
title: '扫描中',
message: '请确认局域网连接和防火墙配置。',
);
}
final width = MediaQuery.sizeOf(context).width;
final columns = width >= 1320
? 4
: width >= 1100
? 3
: width >= 720
? 2
: 1;
return GridView.builder(
padding: const EdgeInsets.all(16),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: columns,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
childAspectRatio: 1.36,
),
itemCount: peers.length,
itemBuilder: (context, index) => PeerCard(peer: peers[index]),
);
},
),
);
}
}

View File

@@ -0,0 +1,266 @@
import 'dart:io';
import 'package:desktop_drop/desktop_drop.dart';
import 'package:flutter/material.dart';
import '../../../../backend/api/commands.dart' as commands;
import '../../../../backend/discovery/model.dart';
import '../../modals/send_files_modal.dart';
import '../../modals/send_text_modal.dart';
class PeerCard extends StatefulWidget {
const PeerCard({super.key, required this.peer});
final Peer peer;
@override
State<PeerCard> createState() => _PeerCardState();
}
class _PeerCardState extends State<PeerCard> {
bool _dragging = false;
String? get _targetIp {
if (widget.peer.routes.isEmpty) {
return null;
}
return widget.peer.routes.values.first.ip;
}
Future<void> _openTextModal() async {
final targetIp = _targetIp;
if (targetIp == null || !mounted) {
return;
}
await showDialog<void>(
context: context,
builder: (_) => SendTextModal(peer: widget.peer, targetIp: targetIp),
);
}
Future<void> _openFilesModal(List<String> files) async {
final targetIp = _targetIp;
if (targetIp == null || !mounted) {
return;
}
await showDialog<void>(
context: context,
builder: (_) => SendFilesModal(
peer: widget.peer,
targetIp: targetIp,
initialPaths: files,
),
);
}
bool get _enableDragDrop =>
Platform.isLinux || Platform.isWindows || Platform.isMacOS;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final targetIp = _targetIp;
final card = Card(
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18),
side: BorderSide(
color: _dragging
? theme.colorScheme.primary
: theme.colorScheme.outlineVariant,
width: _dragging ? 2 : 1,
),
),
child: Padding(
padding: const EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
CircleAvatar(
backgroundColor: theme.colorScheme.primaryContainer,
child: Icon(
_iconForOs(widget.peer.os),
color: theme.colorScheme.onPrimaryContainer,
),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.peer.name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.titleMedium,
),
Text(
targetIp ?? '无路由',
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
],
),
),
],
),
const SizedBox(height: 10),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
Chip(
avatar: const Icon(Icons.shield_rounded, size: 16),
label: Text(widget.peer.enableTls ? 'TLS on' : 'TLS off'),
),
if (widget.peer.trustMismatch)
const Chip(
avatar: Icon(Icons.warning_amber_rounded, size: 16),
label: Text('Trust Mismatch'),
),
FutureBuilder<bool>(
future: commands.isTrusted(peerId: widget.peer.id),
builder: (context, snapshot) {
final trusted = snapshot.data ?? false;
return Chip(
avatar: Icon(
trusted
? Icons.verified_user_rounded
: Icons.gpp_maybe_rounded,
size: 16,
),
label: Text(trusted ? 'Trusted' : 'Untrusted'),
);
},
),
Chip(
avatar: const Icon(Icons.hub_rounded, size: 16),
label: Text('${widget.peer.routes.length} routes'),
),
],
),
const Spacer(),
if (_enableDragDrop && _dragging)
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: theme.colorScheme.primaryContainer.withValues(
alpha: 0.55,
),
),
child: const Center(child: Text('释放后发送文件')),
),
Row(
children: [
Expanded(
child: FilledButton.tonalIcon(
onPressed: targetIp == null ? null : _openTextModal,
icon: const Icon(Icons.chat_bubble_rounded),
label: const Text('文本'),
),
),
const SizedBox(width: 8),
Expanded(
child: FilledButton.icon(
onPressed: targetIp == null
? null
: () => _openFilesModal(const []),
icon: const Icon(Icons.upload_file_rounded),
label: const Text('文件/文件夹'),
),
),
],
),
const SizedBox(height: 8),
FutureBuilder<bool>(
future: commands.isTrusted(peerId: widget.peer.id),
builder: (context, snapshot) {
final trusted = snapshot.data ?? false;
return SizedBox(
width: double.infinity,
child: OutlinedButton.icon(
onPressed: () async {
if (trusted) {
await commands.untrustPeer(peerId: widget.peer.id);
} else {
await commands.trustPeer(peerId: widget.peer.id);
}
if (mounted) {
setState(() {});
}
},
icon: Icon(
trusted
? Icons.verified_user_rounded
: Icons.gpp_maybe_rounded,
),
label: Text(trusted ? '取消信任' : '设为信任设备'),
),
);
},
),
if (targetIp == null)
Padding(
padding: const EdgeInsets.only(top: 8),
child: Text(
'该设备暂无可用传输路由',
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.error,
),
),
),
],
),
),
);
if (!_enableDragDrop) {
return card;
}
return DropTarget(
onDragEntered: (_) => setState(() => _dragging = true),
onDragExited: (_) => setState(() => _dragging = false),
onDragDone: (details) {
final files = details.files
.map((f) => f.path)
.where((e) => e.isNotEmpty)
.toList();
setState(() => _dragging = false);
if (files.isNotEmpty) {
_openFilesModal(files);
}
},
child: card,
);
}
IconData _iconForOs(String os) {
final normalized = os.toLowerCase();
if (normalized.contains('windows')) {
return Icons.laptop_windows_rounded;
}
if (normalized.contains('mac') || normalized.contains('darwin')) {
return Icons.laptop_mac_rounded;
}
if (normalized.contains('linux')) {
return Icons.computer_rounded;
}
if (normalized.contains('android')) {
return Icons.phone_android_rounded;
}
if (normalized.contains('ios') || normalized.contains('iphone')) {
return Icons.phone_iphone_rounded;
}
return Platform.isAndroid
? Icons.devices_rounded
: Icons.device_hub_rounded;
}
}

View File

@@ -0,0 +1,99 @@
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../../../../backend/api/commands.dart' as commands;
part 'settings_controller.g.dart';
class SettingsState {
const SettingsState({
required this.hostname,
required this.savePath,
required this.autoAccept,
required this.saveHistory,
required this.enableTls,
});
final String hostname;
final String savePath;
final bool autoAccept;
final bool saveHistory;
final bool enableTls;
SettingsState copyWith({
String? hostname,
String? savePath,
bool? autoAccept,
bool? saveHistory,
bool? enableTls,
}) {
return SettingsState(
hostname: hostname ?? this.hostname,
savePath: savePath ?? this.savePath,
autoAccept: autoAccept ?? this.autoAccept,
saveHistory: saveHistory ?? this.saveHistory,
enableTls: enableTls ?? this.enableTls,
);
}
}
@riverpod
class SettingsController extends _$SettingsController {
@override
Future<SettingsState> build() async {
final values = await Future.wait([
commands.getHostname(),
commands.getSavePath(),
commands.getAutoAccept(),
commands.getSaveHistory(),
commands.getEnableTls(),
]);
return SettingsState(
hostname: values[0] as String,
savePath: values[1] as String,
autoAccept: values[2] as bool,
saveHistory: values[3] as bool,
enableTls: values[4] as bool,
);
}
Future<void> updateHostname(String value) async {
await commands.setHostname(hostname: value);
final current = state.value;
if (current != null) {
state = AsyncData(current.copyWith(hostname: value));
}
}
Future<void> updateSavePath(String value) async {
await commands.setSavePath(savePath: value);
final current = state.value;
if (current != null) {
state = AsyncData(current.copyWith(savePath: value));
}
}
Future<void> updateAutoAccept(bool value) async {
await commands.setAutoAccept(autoAccept: value);
final current = state.value;
if (current != null) {
state = AsyncData(current.copyWith(autoAccept: value));
}
}
Future<void> updateSaveHistory(bool value) async {
await commands.setSaveHistory(saveHistory: value);
final current = state.value;
if (current != null) {
state = AsyncData(current.copyWith(saveHistory: value));
}
}
Future<void> updateEnableTls(bool value) async {
await commands.setEnableTls(enableTls: value);
final current = state.value;
if (current != null) {
state = AsyncData(current.copyWith(enableTls: value));
}
}
}

View File

@@ -0,0 +1,55 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'settings_controller.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
@ProviderFor(SettingsController)
final settingsControllerProvider = SettingsControllerProvider._();
final class SettingsControllerProvider
extends $AsyncNotifierProvider<SettingsController, SettingsState> {
SettingsControllerProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'settingsControllerProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$settingsControllerHash();
@$internal
@override
SettingsController create() => SettingsController();
}
String _$settingsControllerHash() =>
r'9a56637cd6a41c05c9c35b78430ff9fd9f9affe6';
abstract class _$SettingsController extends $AsyncNotifier<SettingsState> {
FutureOr<SettingsState> build();
@$mustCallSuper
@override
void runBuild() {
final ref = this.ref as $Ref<AsyncValue<SettingsState>, SettingsState>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<AsyncValue<SettingsState>, SettingsState>,
AsyncValue<SettingsState>,
Object?,
Object?
>;
element.handleCreate(ref, build);
}
}

View File

@@ -0,0 +1,161 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../theme/theme_mode_controller.dart';
import '../controller/settings_controller.dart';
class SettingsPage extends ConsumerWidget {
const SettingsPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final settingsAsync = ref.watch(settingsControllerProvider);
final themeMode = ref.watch(themeModeControllerProvider);
return Scaffold(
appBar: AppBar(title: const Text('Settings')),
body: settingsAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, _) => Center(child: Text(error.toString())),
data: (settings) {
return ListView(
padding: const EdgeInsets.all(16),
children: [
Card(
child: Column(
children: [
ListTile(
leading: const Icon(Icons.brightness_6_rounded),
title: const Text('主题模式'),
subtitle: Text(themeMode.name),
trailing: SegmentedButton<ThemeMode>(
segments: const [
ButtonSegment(
value: ThemeMode.light,
icon: Icon(Icons.light_mode_rounded),
label: Text('Light'),
),
ButtonSegment(
value: ThemeMode.dark,
icon: Icon(Icons.dark_mode_rounded),
label: Text('Dark'),
),
ButtonSegment(
value: ThemeMode.system,
icon: Icon(Icons.settings_suggest_rounded),
label: Text('System'),
),
],
selected: {themeMode},
onSelectionChanged: (selection) => ref
.read(themeModeControllerProvider.notifier)
.setThemeMode(selection.first),
),
),
],
),
),
const SizedBox(height: 12),
Card(
child: Column(
children: [
ListTile(
leading: const Icon(Icons.badge_rounded),
title: const Text('Hostname'),
subtitle: Text(settings.hostname),
trailing: IconButton(
icon: const Icon(Icons.edit_rounded),
onPressed: () => _editText(
context: context,
title: 'Hostname',
initial: settings.hostname,
onSubmit: (value) => ref
.read(settingsControllerProvider.notifier)
.updateHostname(value),
),
),
),
const Divider(height: 1),
ListTile(
leading: const Icon(Icons.folder_rounded),
title: const Text('默认保存路径'),
subtitle: Text(settings.savePath),
trailing: IconButton(
icon: const Icon(Icons.edit_rounded),
onPressed: () => _editText(
context: context,
title: '保存路径',
initial: settings.savePath,
onSubmit: (value) => ref
.read(settingsControllerProvider.notifier)
.updateSavePath(value),
),
),
),
const Divider(height: 1),
SwitchListTile.adaptive(
secondary: const Icon(Icons.auto_mode_rounded),
title: const Text('自动接收'),
value: settings.autoAccept,
onChanged: (v) => ref
.read(settingsControllerProvider.notifier)
.updateAutoAccept(v),
),
SwitchListTile.adaptive(
secondary: const Icon(Icons.history_rounded),
title: const Text('保存历史记录'),
value: settings.saveHistory,
onChanged: (v) => ref
.read(settingsControllerProvider.notifier)
.updateSaveHistory(v),
),
SwitchListTile.adaptive(
secondary: const Icon(Icons.shield_rounded),
title: const Text('启用 TLS'),
value: settings.enableTls,
onChanged: (v) => ref
.read(settingsControllerProvider.notifier)
.updateEnableTls(v),
),
],
),
),
],
);
},
),
);
}
Future<void> _editText({
required BuildContext context,
required String title,
required String initial,
required Future<void> Function(String value) onSubmit,
}) async {
final controller = TextEditingController(text: initial);
final confirmed = await showDialog<bool>(
context: context,
builder: (_) {
return AlertDialog(
title: Text(title),
content: TextField(controller: controller),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text('取消'),
),
FilledButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text('保存'),
),
],
);
},
);
if (confirmed == true) {
await onSubmit(controller.text.trim());
}
}
}

View File

@@ -0,0 +1,127 @@
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../../../../backend/api/commands.dart' as commands;
import '../../../../backend/transfer/model.dart';
part 'transfers_controller.g.dart';
@riverpod
class TransfersController extends _$TransfersController {
@override
Future<List<Transfer>> build() async {
return commands.getTransfers();
}
Future<void> refresh() async {
state = const AsyncLoading();
state = await AsyncValue.guard(commands.getTransfers);
}
void updateProgress(String id, double currentBytes) {
final current = state.asData?.value;
if (current == null || current.isEmpty) {
return;
}
final normalized = currentBytes < 0 ? 0.0 : currentBytes;
var changed = false;
final next = current
.map((item) {
if (item.id != id) {
return item;
}
if ((item.progress - normalized).abs() < 0.001) {
return item;
}
changed = true;
return Transfer(
id: item.id,
createTime: item.createTime,
sender: item.sender,
senderIp: item.senderIp,
fileName: item.fileName,
fileSize: item.fileSize,
savePath: item.savePath,
status: item.status,
type: item.type,
contentType: item.contentType,
text: item.text,
errorMsg: item.errorMsg,
token: item.token,
progress: normalized,
lastReadTime: item.lastReadTime,
speed: item.speed,
);
})
.toList(growable: false);
if (changed) {
state = AsyncData(next);
}
}
void upsertTransfer(Transfer transfer) {
final current = state.asData?.value ?? const <Transfer>[];
final index = current.indexWhere((item) => item.id == transfer.id);
if (index < 0) {
state = AsyncData([...current, transfer]);
return;
}
final old = current[index];
if (old == transfer) {
return;
}
final next = [...current];
next[index] = transfer;
state = AsyncData(next);
}
void removeTransfer(String id) {
final current = state.asData?.value;
if (current == null || current.isEmpty) {
return;
}
final next = current.where((item) => item.id != id).toList(growable: false);
if (next.length != current.length) {
state = AsyncData(next);
}
}
void clearTransfersLocal() {
state = const AsyncData(<Transfer>[]);
}
Future<void> cancel(String id) async {
await commands.cancelTransfer(id: id);
}
Future<void> accept(String id, String path) async {
await commands.resolvePendingRequest(id: id, accept: true, path: path);
}
Future<void> reject(String id) async {
await commands.resolvePendingRequest(id: id, accept: false, path: '');
}
Future<void> delete(String id) async {
await commands.deleteTransfer(id: id);
}
Future<void> clearCompleted() async {
final current = state.asData?.value ?? const <Transfer>[];
final hasCompleted = current.any(
(item) => item.status is TransferStatus_Completed,
);
if (!hasCompleted) {
return;
}
await commands.clearTransfers();
}
}

View File

@@ -0,0 +1,55 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'transfers_controller.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
@ProviderFor(TransfersController)
final transfersControllerProvider = TransfersControllerProvider._();
final class TransfersControllerProvider
extends $AsyncNotifierProvider<TransfersController, List<Transfer>> {
TransfersControllerProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'transfersControllerProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$transfersControllerHash();
@$internal
@override
TransfersController create() => TransfersController();
}
String _$transfersControllerHash() =>
r'4bb376e37746360ad323d2428d4fcfc4b1e37aa7';
abstract class _$TransfersController extends $AsyncNotifier<List<Transfer>> {
FutureOr<List<Transfer>> build();
@$mustCallSuper
@override
void runBuild() {
final ref = this.ref as $Ref<AsyncValue<List<Transfer>>, List<Transfer>>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<AsyncValue<List<Transfer>>, List<Transfer>>,
AsyncValue<List<Transfer>>,
Object?,
Object?
>;
element.handleCreate(ref, build);
}
}

View File

@@ -0,0 +1,98 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../../backend/transfer/model.dart';
import '../../../shared/widgets/empty_state.dart';
import '../../settings/controller/settings_controller.dart';
import '../controller/transfers_controller.dart';
import '../widgets/transfer_item.dart';
class TransferPage extends ConsumerWidget {
const TransferPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final transfersAsync = ref.watch(transfersControllerProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Transfer Queue'),
actions: [
IconButton(
tooltip: '清理已完成',
onPressed: () =>
ref.read(transfersControllerProvider.notifier).clearCompleted(),
icon: const Icon(Icons.cleaning_services_rounded),
),
IconButton(
tooltip: '刷新',
onPressed: () =>
ref.read(transfersControllerProvider.notifier).refresh(),
icon: const Icon(Icons.refresh_rounded),
),
],
),
body: transfersAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, _) => AppEmptyState(
icon: Icons.warning_amber_rounded,
title: '加载失败',
message: error.toString(),
),
data: (transfers) {
if (transfers.isEmpty) {
return const AppEmptyState(
icon: Icons.inbox_rounded,
title: '暂无传输记录',
message: '发起或接收一次文件后,会在这里看到记录。',
);
}
return ListView.separated(
padding: const EdgeInsets.all(16),
itemBuilder: (context, index) {
final item = transfers[index];
final isSender = item.type == TransferType.send;
final isReceiver = item.type == TransferType.receive;
final canCancel =
(isSender &&
(item.status is TransferStatus_Pending ||
item.status is TransferStatus_Active)) ||
(isReceiver && item.status is TransferStatus_Active);
return TransferItem(
transfer: item,
onCancel: canCancel
? () => ref
.read(transfersControllerProvider.notifier)
.cancel(item.id)
: null,
onAccept: isReceiver && item.status is TransferStatus_Pending
? () async {
final settings = await ref.read(
settingsControllerProvider.future,
);
await ref
.read(transfersControllerProvider.notifier)
.accept(item.id, settings.savePath);
}
: null,
onReject: isReceiver && item.status is TransferStatus_Pending
? () => ref
.read(transfersControllerProvider.notifier)
.reject(item.id)
: null,
onDelete: () => ref
.read(transfersControllerProvider.notifier)
.delete(item.id),
);
},
separatorBuilder: (_, _) => const SizedBox(height: 12),
itemCount: transfers.length,
);
},
),
);
}
}

View File

@@ -0,0 +1,282 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../../../backend/transfer/model.dart';
class TransferItem extends StatelessWidget {
const TransferItem({
super.key,
required this.transfer,
this.onAccept,
this.onReject,
this.onCancel,
this.onDelete,
});
final Transfer transfer;
final VoidCallback? onAccept;
final VoidCallback? onReject;
final VoidCallback? onCancel;
final VoidCallback? onDelete;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Card(
child: Padding(
padding: const EdgeInsets.all(14),
child: Column(
children: [
Row(
children: [
CircleAvatar(
backgroundColor: theme.colorScheme.secondaryContainer,
child: Icon(
_iconForContentType(transfer.contentType),
color: theme.colorScheme.onSecondaryContainer,
),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
transfer.fileName.isNotEmpty
? transfer.fileName
: '文本消息',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.titleMedium,
),
Text(
'${transfer.sender.name} · ${transfer.type.name}',
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
],
),
),
_StatusChip(status: transfer.status),
],
),
const SizedBox(height: 12),
LinearProgressIndicator(
value: _progressOrNull(transfer),
color: _progressColor(theme, transfer.status),
),
const SizedBox(height: 10),
Row(
children: [
Expanded(
child: Text(
'进度 ${_progressPercent(transfer).toStringAsFixed(0)}% 大小 ${_formatSize(transfer.fileSize)} 速度 ${_formatSpeed(transfer.speed)}',
style: theme.textTheme.bodySmall,
),
),
Wrap(spacing: 8, children: _buildActions(context)),
],
),
],
),
),
);
}
List<Widget> _buildActions(BuildContext context) {
final actions = <Widget>[];
if (transfer.status is TransferStatus_Pending) {
if (onReject != null) {
actions.add(
OutlinedButton.icon(
onPressed: onReject,
icon: const Icon(Icons.close_rounded),
label: const Text('拒绝'),
),
);
}
if (onAccept != null) {
actions.add(
FilledButton.icon(
onPressed: onAccept,
icon: const Icon(Icons.check_rounded),
label: const Text('接收'),
),
);
}
return actions;
}
if ((transfer.status is TransferStatus_Active ||
transfer.status is TransferStatus_Accepted) &&
onCancel != null) {
actions.add(
OutlinedButton.icon(
onPressed: onCancel,
icon: const Icon(Icons.stop_circle_outlined),
label: const Text('取消'),
),
);
}
final canPreviewText =
transfer.type == TransferType.receive &&
transfer.contentType == ContentType.text &&
transfer.status is TransferStatus_Completed;
if (canPreviewText) {
actions.add(
IconButton(
tooltip: '复制文本',
onPressed: () async {
final messenger = ScaffoldMessenger.of(context);
await Clipboard.setData(ClipboardData(text: transfer.text));
messenger.showSnackBar(const SnackBar(content: Text('文本已复制')));
},
icon: const Icon(Icons.copy_rounded),
),
);
actions.add(
IconButton(
tooltip: '查看文本',
onPressed: () {
showDialog<void>(
context: context,
builder: (_) => AlertDialog(
title: const Text('接收文本内容'),
content: SizedBox(
width: 560,
child: SingleChildScrollView(
child: SelectableText(transfer.text),
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('关闭'),
),
],
),
);
},
icon: const Icon(Icons.visibility_rounded),
),
);
}
if ((transfer.status is TransferStatus_Completed ||
transfer.status is TransferStatus_Error ||
transfer.status is TransferStatus_Canceled) &&
onDelete != null) {
actions.add(
IconButton(
tooltip: '删除记录',
onPressed: onDelete,
icon: const Icon(Icons.delete_outline_rounded),
),
);
}
return actions;
}
IconData _iconForContentType(ContentType contentType) {
return switch (contentType) {
ContentType.file => Icons.insert_drive_file_rounded,
ContentType.folder => Icons.folder_zip_rounded,
ContentType.text => Icons.text_snippet_rounded,
};
}
double? _progressOrNull(Transfer transfer) {
if (transfer.status is TransferStatus_Pending) {
return null;
}
return _progressFraction(transfer);
}
Color _progressColor(ThemeData theme, TransferStatus status) {
return switch (status) {
TransferStatus_Completed() => theme.colorScheme.primary,
TransferStatus_Active() ||
TransferStatus_Accepted() => theme.colorScheme.tertiary,
TransferStatus_Rejected() ||
TransferStatus_Error() => theme.colorScheme.error,
TransferStatus_Canceled() => theme.colorScheme.outline,
TransferStatus_Pending() => theme.colorScheme.secondary,
};
}
String _formatSpeed(double? bytesPerSec) {
if (bytesPerSec == null || bytesPerSec <= 0) {
return '--';
}
return '${_formatSize(bytesPerSec)}/s';
}
String _formatSize(double bytes) {
if (bytes <= 0) {
return '0 B';
}
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
var size = bytes;
var i = 0;
while (size >= 1024 && i < units.length - 1) {
size /= 1024;
i++;
}
final fixed = size >= 100 ? 0 : (size >= 10 ? 1 : 2);
return '${size.toStringAsFixed(fixed)} ${units[i]}';
}
double _progressedBytes(Transfer transfer) {
final current = transfer.progress;
if (current <= 0) {
return 0;
}
if (transfer.fileSize <= 0) {
return current;
}
return current.clamp(0, transfer.fileSize).toDouble();
}
double _progressFraction(Transfer transfer) {
if (transfer.fileSize <= 0) {
return 0;
}
return (_progressedBytes(transfer) / transfer.fileSize)
.clamp(0, 1)
.toDouble();
}
double _progressPercent(Transfer transfer) {
return _progressFraction(transfer) * 100;
}
}
class _StatusChip extends StatelessWidget {
const _StatusChip({required this.status});
final TransferStatus status;
@override
Widget build(BuildContext context) {
final label = switch (status) {
TransferStatus_Pending() => 'Pending',
TransferStatus_Accepted() => 'Accepted',
TransferStatus_Rejected() => 'Rejected',
TransferStatus_Completed() => 'Completed',
TransferStatus_Error() => 'Error',
TransferStatus_Canceled() => 'Canceled',
TransferStatus_Active() => 'Active',
};
return Chip(label: Text(label));
}
}

View File

@@ -0,0 +1,83 @@
import 'package:flutter/material.dart';
import '../features/peers/pages/peers_page.dart';
import '../features/settings/pages/settings_page.dart';
import '../features/transfer/pages/transfer_page.dart';
enum AppTab { peers, transfer, settings }
class HomeShell extends StatefulWidget {
const HomeShell({super.key});
@override
State<HomeShell> createState() => _HomeShellState();
}
class _HomeShellState extends State<HomeShell> {
AppTab _current = AppTab.peers;
void _onSelect(int index) {
setState(() {
_current = AppTab.values[index];
});
}
@override
Widget build(BuildContext context) {
final isWide = MediaQuery.sizeOf(context).width >= 920;
final pages = const [PeersPage(), TransferPage(), SettingsPage()];
if (isWide) {
return Scaffold(
body: Row(
children: [
NavigationRail(
selectedIndex: _current.index,
onDestinationSelected: _onSelect,
labelType: NavigationRailLabelType.all,
minWidth: 84,
destinations: const [
NavigationRailDestination(
icon: Icon(Icons.radar_rounded),
label: Text('Peers'),
),
NavigationRailDestination(
icon: Icon(Icons.sync_alt_rounded),
label: Text('Transfer'),
),
NavigationRailDestination(
icon: Icon(Icons.tune_rounded),
label: Text('Settings'),
),
],
),
const VerticalDivider(width: 1),
Expanded(child: pages[_current.index]),
],
),
);
}
return Scaffold(
body: pages[_current.index],
bottomNavigationBar: NavigationBar(
selectedIndex: _current.index,
onDestinationSelected: _onSelect,
destinations: const [
NavigationDestination(
icon: Icon(Icons.radar_rounded),
label: 'Peers',
),
NavigationDestination(
icon: Icon(Icons.sync_alt_rounded),
label: 'Transfer',
),
NavigationDestination(
icon: Icon(Icons.tune_rounded),
label: 'Settings',
),
],
),
);
}
}

View File

@@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
class AppEmptyState extends StatelessWidget {
const AppEmptyState({
super.key,
required this.icon,
required this.title,
required this.message,
this.action,
});
final IconData icon;
final String title;
final String message;
final Widget? action;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Center(
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, size: 52, color: theme.colorScheme.primary),
const SizedBox(height: 14),
Text(title, style: theme.textTheme.titleLarge),
const SizedBox(height: 8),
Text(
message,
textAlign: TextAlign.center,
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
if (action != null) ...[const SizedBox(height: 16), action!],
],
),
),
);
}
}

View File

@@ -0,0 +1,11 @@
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../../backend/api/commands.dart' as commands;
import '../../backend/event.dart';
part 'backend_event_sync.g.dart';
@Riverpod(keepAlive: true)
Stream<AppEvent> backendEventSync(Ref ref) {
return commands.createEventStream();
}

View File

@@ -0,0 +1,44 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'backend_event_sync.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
@ProviderFor(backendEventSync)
final backendEventSyncProvider = BackendEventSyncProvider._();
final class BackendEventSyncProvider
extends
$FunctionalProvider<AsyncValue<AppEvent>, AppEvent, Stream<AppEvent>>
with $FutureModifier<AppEvent>, $StreamProvider<AppEvent> {
BackendEventSyncProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'backendEventSyncProvider',
isAutoDispose: false,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$backendEventSyncHash();
@$internal
@override
$StreamProviderElement<AppEvent> $createElement($ProviderPointer pointer) =>
$StreamProviderElement(pointer);
@override
Stream<AppEvent> create(Ref ref) {
return backendEventSync(ref);
}
}
String _$backendEventSyncHash() => r'98ada20a035b92e209fca4c366faf7f41f412160';

View File

@@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
final class AppTheme {
const AppTheme._();
static ThemeData get lightTheme {
const seed = Color(0xFF4F46E5);
final colorScheme = ColorScheme.fromSeed(
seedColor: seed,
brightness: Brightness.light,
);
return ThemeData(
useMaterial3: true,
colorScheme: colorScheme,
scaffoldBackgroundColor: const Color(0xFFF7F8FC),
cardTheme: CardThemeData(
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
),
chipTheme: ChipThemeData(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(999)),
),
);
}
static ThemeData get darkTheme {
const seed = Color(0xFF8B8BFF);
final colorScheme = ColorScheme.fromSeed(
seedColor: seed,
brightness: Brightness.dark,
);
return ThemeData(
useMaterial3: true,
colorScheme: colorScheme,
scaffoldBackgroundColor: const Color(0xFF0B1020),
cardTheme: CardThemeData(
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
),
chipTheme: ChipThemeData(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(999)),
),
);
}
}

View File

@@ -0,0 +1,21 @@
import 'package:flutter/material.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'theme_mode_controller.g.dart';
@riverpod
class ThemeModeController extends _$ThemeModeController {
@override
ThemeMode build() => ThemeMode.system;
void setThemeMode(ThemeMode mode) {
state = mode;
}
void toggle() {
state = switch (state) {
ThemeMode.dark => ThemeMode.light,
ThemeMode.light || ThemeMode.system => ThemeMode.dark,
};
}
}

View File

@@ -0,0 +1,63 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'theme_mode_controller.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
@ProviderFor(ThemeModeController)
final themeModeControllerProvider = ThemeModeControllerProvider._();
final class ThemeModeControllerProvider
extends $NotifierProvider<ThemeModeController, ThemeMode> {
ThemeModeControllerProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'themeModeControllerProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$themeModeControllerHash();
@$internal
@override
ThemeModeController create() => ThemeModeController();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(ThemeMode value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<ThemeMode>(value),
);
}
}
String _$themeModeControllerHash() =>
r'96d7617273bf6319cb57844c94190c8514dc0b36';
abstract class _$ThemeModeController extends $Notifier<ThemeMode> {
ThemeMode build();
@$mustCallSuper
@override
void runBuild() {
final ref = this.ref as $Ref<ThemeMode, ThemeMode>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<ThemeMode, ThemeMode>,
ThemeMode,
Object?,
Object?
>;
element.handleCreate(ref, build);
}
}

View File

@@ -0,0 +1,107 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../discovery/model.dart';
import '../event.dart';
import '../frb_generated.dart';
import '../transfer/model.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
Stream<AppEvent> createEventStream() =>
RustLib.instance.api.crateApiCommandsCreateEventStream();
Future<String> getSavePath() =>
RustLib.instance.api.crateApiCommandsGetSavePath();
Future<void> setSavePath({required String savePath}) =>
RustLib.instance.api.crateApiCommandsSetSavePath(savePath: savePath);
Future<void> setHostname({required String hostname}) =>
RustLib.instance.api.crateApiCommandsSetHostname(hostname: hostname);
Future<String> getHostname() =>
RustLib.instance.api.crateApiCommandsGetHostname();
Future<bool> getAutoAccept() =>
RustLib.instance.api.crateApiCommandsGetAutoAccept();
Future<void> setAutoAccept({required bool autoAccept}) =>
RustLib.instance.api.crateApiCommandsSetAutoAccept(autoAccept: autoAccept);
Future<bool> getSaveHistory() =>
RustLib.instance.api.crateApiCommandsGetSaveHistory();
Future<void> setSaveHistory({required bool saveHistory}) => RustLib.instance.api
.crateApiCommandsSetSaveHistory(saveHistory: saveHistory);
Future<bool> getEnableTls() =>
RustLib.instance.api.crateApiCommandsGetEnableTls();
Future<void> setEnableTls({required bool enableTls}) =>
RustLib.instance.api.crateApiCommandsSetEnableTls(enableTls: enableTls);
Future<List<Peer>> getPeers() =>
RustLib.instance.api.crateApiCommandsGetPeers();
Future<void> sendFile({
required Peer target,
required String targetIp,
required String filePath,
}) => RustLib.instance.api.crateApiCommandsSendFile(
target: target,
targetIp: targetIp,
filePath: filePath,
);
Future<void> sendText({
required Peer target,
required String targetIp,
required String text,
}) => RustLib.instance.api.crateApiCommandsSendText(
target: target,
targetIp: targetIp,
text: text,
);
Future<void> sendFolder({
required Peer target,
required String targetIp,
required String folderPath,
}) => RustLib.instance.api.crateApiCommandsSendFolder(
target: target,
targetIp: targetIp,
folderPath: folderPath,
);
Future<List<Transfer>> getTransfers() =>
RustLib.instance.api.crateApiCommandsGetTransfers();
Future<void> resolvePendingRequest({
required String id,
required bool accept,
required String path,
}) => RustLib.instance.api.crateApiCommandsResolvePendingRequest(
id: id,
accept: accept,
path: path,
);
Future<void> cancelTransfer({required String id}) =>
RustLib.instance.api.crateApiCommandsCancelTransfer(id: id);
Future<void> deleteTransfer({required String id}) =>
RustLib.instance.api.crateApiCommandsDeleteTransfer(id: id);
Future<void> clearTransfers() =>
RustLib.instance.api.crateApiCommandsClearTransfers();
Future<bool> isTrusted({required String peerId}) =>
RustLib.instance.api.crateApiCommandsIsTrusted(peerId: peerId);
Future<void> trustPeer({required String peerId}) =>
RustLib.instance.api.crateApiCommandsTrustPeer(peerId: peerId);
Future<void> untrustPeer({required String peerId}) =>
RustLib.instance.api.crateApiCommandsUntrustPeer(peerId: peerId);

View File

@@ -0,0 +1,72 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../frb_generated.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
class Peer {
final String id;
final String name;
final Map<String, RouteState> routes;
final int port;
final String os;
final String publicKey;
final bool trustMismatch;
final bool enableTls;
const Peer({
required this.id,
required this.name,
required this.routes,
required this.port,
required this.os,
required this.publicKey,
required this.trustMismatch,
required this.enableTls,
});
@override
int get hashCode =>
id.hashCode ^
name.hashCode ^
routes.hashCode ^
port.hashCode ^
os.hashCode ^
publicKey.hashCode ^
trustMismatch.hashCode ^
enableTls.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Peer &&
runtimeType == other.runtimeType &&
id == other.id &&
name == other.name &&
routes == other.routes &&
port == other.port &&
os == other.os &&
publicKey == other.publicKey &&
trustMismatch == other.trustMismatch &&
enableTls == other.enableTls;
}
class RouteState {
final String ip;
final double lastSeen;
const RouteState({required this.ip, required this.lastSeen});
@override
int get hashCode => ip.hashCode ^ lastSeen.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is RouteState &&
runtimeType == other.runtimeType &&
ip == other.ip &&
lastSeen == other.lastSeen;
}

34
lib/backend/event.dart Normal file
View File

@@ -0,0 +1,34 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import 'discovery/model.dart';
import 'frb_generated.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
import 'transfer/model.dart';
part 'event.freezed.dart';
@freezed
sealed class AppEvent with _$AppEvent {
const AppEvent._();
const factory AppEvent.transferStatusChanged({required Transfer transfer}) =
AppEvent_TransferStatusChanged;
const factory AppEvent.transferProgressChanged({
required String id,
required double progress,
required double total,
required double speed,
}) = AppEvent_TransferProgressChanged;
const factory AppEvent.peerConnectOrUpdated({required Peer peer}) =
AppEvent_PeerConnectOrUpdated;
const factory AppEvent.peerDisconnected({required String id}) =
AppEvent_PeerDisconnected;
const factory AppEvent.transferAdded({required Transfer transfer}) =
AppEvent_TransferAdded;
const factory AppEvent.transferRemoved({required String id}) =
AppEvent_TransferRemoved;
const factory AppEvent.transferClear() = AppEvent_TransferClear;
}

View File

@@ -0,0 +1,638 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'event.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$AppEvent {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppEvent);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'AppEvent()';
}
}
/// @nodoc
class $AppEventCopyWith<$Res> {
$AppEventCopyWith(AppEvent _, $Res Function(AppEvent) __);
}
/// Adds pattern-matching-related methods to [AppEvent].
extension AppEventPatterns on AppEvent {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>({TResult Function( AppEvent_TransferStatusChanged value)? transferStatusChanged,TResult Function( AppEvent_TransferProgressChanged value)? transferProgressChanged,TResult Function( AppEvent_PeerConnectOrUpdated value)? peerConnectOrUpdated,TResult Function( AppEvent_PeerDisconnected value)? peerDisconnected,TResult Function( AppEvent_TransferAdded value)? transferAdded,TResult Function( AppEvent_TransferRemoved value)? transferRemoved,TResult Function( AppEvent_TransferClear value)? transferClear,required TResult orElse(),}){
final _that = this;
switch (_that) {
case AppEvent_TransferStatusChanged() when transferStatusChanged != null:
return transferStatusChanged(_that);case AppEvent_TransferProgressChanged() when transferProgressChanged != null:
return transferProgressChanged(_that);case AppEvent_PeerConnectOrUpdated() when peerConnectOrUpdated != null:
return peerConnectOrUpdated(_that);case AppEvent_PeerDisconnected() when peerDisconnected != null:
return peerDisconnected(_that);case AppEvent_TransferAdded() when transferAdded != null:
return transferAdded(_that);case AppEvent_TransferRemoved() when transferRemoved != null:
return transferRemoved(_that);case AppEvent_TransferClear() when transferClear != null:
return transferClear(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>({required TResult Function( AppEvent_TransferStatusChanged value) transferStatusChanged,required TResult Function( AppEvent_TransferProgressChanged value) transferProgressChanged,required TResult Function( AppEvent_PeerConnectOrUpdated value) peerConnectOrUpdated,required TResult Function( AppEvent_PeerDisconnected value) peerDisconnected,required TResult Function( AppEvent_TransferAdded value) transferAdded,required TResult Function( AppEvent_TransferRemoved value) transferRemoved,required TResult Function( AppEvent_TransferClear value) transferClear,}){
final _that = this;
switch (_that) {
case AppEvent_TransferStatusChanged():
return transferStatusChanged(_that);case AppEvent_TransferProgressChanged():
return transferProgressChanged(_that);case AppEvent_PeerConnectOrUpdated():
return peerConnectOrUpdated(_that);case AppEvent_PeerDisconnected():
return peerDisconnected(_that);case AppEvent_TransferAdded():
return transferAdded(_that);case AppEvent_TransferRemoved():
return transferRemoved(_that);case AppEvent_TransferClear():
return transferClear(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>({TResult? Function( AppEvent_TransferStatusChanged value)? transferStatusChanged,TResult? Function( AppEvent_TransferProgressChanged value)? transferProgressChanged,TResult? Function( AppEvent_PeerConnectOrUpdated value)? peerConnectOrUpdated,TResult? Function( AppEvent_PeerDisconnected value)? peerDisconnected,TResult? Function( AppEvent_TransferAdded value)? transferAdded,TResult? Function( AppEvent_TransferRemoved value)? transferRemoved,TResult? Function( AppEvent_TransferClear value)? transferClear,}){
final _that = this;
switch (_that) {
case AppEvent_TransferStatusChanged() when transferStatusChanged != null:
return transferStatusChanged(_that);case AppEvent_TransferProgressChanged() when transferProgressChanged != null:
return transferProgressChanged(_that);case AppEvent_PeerConnectOrUpdated() when peerConnectOrUpdated != null:
return peerConnectOrUpdated(_that);case AppEvent_PeerDisconnected() when peerDisconnected != null:
return peerDisconnected(_that);case AppEvent_TransferAdded() when transferAdded != null:
return transferAdded(_that);case AppEvent_TransferRemoved() when transferRemoved != null:
return transferRemoved(_that);case AppEvent_TransferClear() when transferClear != null:
return transferClear(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function( Transfer transfer)? transferStatusChanged,TResult Function( String id, double progress, double total, double speed)? transferProgressChanged,TResult Function( Peer peer)? peerConnectOrUpdated,TResult Function( String id)? peerDisconnected,TResult Function( Transfer transfer)? transferAdded,TResult Function( String id)? transferRemoved,TResult Function()? transferClear,required TResult orElse(),}) {final _that = this;
switch (_that) {
case AppEvent_TransferStatusChanged() when transferStatusChanged != null:
return transferStatusChanged(_that.transfer);case AppEvent_TransferProgressChanged() when transferProgressChanged != null:
return transferProgressChanged(_that.id,_that.progress,_that.total,_that.speed);case AppEvent_PeerConnectOrUpdated() when peerConnectOrUpdated != null:
return peerConnectOrUpdated(_that.peer);case AppEvent_PeerDisconnected() when peerDisconnected != null:
return peerDisconnected(_that.id);case AppEvent_TransferAdded() when transferAdded != null:
return transferAdded(_that.transfer);case AppEvent_TransferRemoved() when transferRemoved != null:
return transferRemoved(_that.id);case AppEvent_TransferClear() when transferClear != null:
return transferClear();case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function( Transfer transfer) transferStatusChanged,required TResult Function( String id, double progress, double total, double speed) transferProgressChanged,required TResult Function( Peer peer) peerConnectOrUpdated,required TResult Function( String id) peerDisconnected,required TResult Function( Transfer transfer) transferAdded,required TResult Function( String id) transferRemoved,required TResult Function() transferClear,}) {final _that = this;
switch (_that) {
case AppEvent_TransferStatusChanged():
return transferStatusChanged(_that.transfer);case AppEvent_TransferProgressChanged():
return transferProgressChanged(_that.id,_that.progress,_that.total,_that.speed);case AppEvent_PeerConnectOrUpdated():
return peerConnectOrUpdated(_that.peer);case AppEvent_PeerDisconnected():
return peerDisconnected(_that.id);case AppEvent_TransferAdded():
return transferAdded(_that.transfer);case AppEvent_TransferRemoved():
return transferRemoved(_that.id);case AppEvent_TransferClear():
return transferClear();}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function( Transfer transfer)? transferStatusChanged,TResult? Function( String id, double progress, double total, double speed)? transferProgressChanged,TResult? Function( Peer peer)? peerConnectOrUpdated,TResult? Function( String id)? peerDisconnected,TResult? Function( Transfer transfer)? transferAdded,TResult? Function( String id)? transferRemoved,TResult? Function()? transferClear,}) {final _that = this;
switch (_that) {
case AppEvent_TransferStatusChanged() when transferStatusChanged != null:
return transferStatusChanged(_that.transfer);case AppEvent_TransferProgressChanged() when transferProgressChanged != null:
return transferProgressChanged(_that.id,_that.progress,_that.total,_that.speed);case AppEvent_PeerConnectOrUpdated() when peerConnectOrUpdated != null:
return peerConnectOrUpdated(_that.peer);case AppEvent_PeerDisconnected() when peerDisconnected != null:
return peerDisconnected(_that.id);case AppEvent_TransferAdded() when transferAdded != null:
return transferAdded(_that.transfer);case AppEvent_TransferRemoved() when transferRemoved != null:
return transferRemoved(_that.id);case AppEvent_TransferClear() when transferClear != null:
return transferClear();case _:
return null;
}
}
}
/// @nodoc
class AppEvent_TransferStatusChanged extends AppEvent {
const AppEvent_TransferStatusChanged({required this.transfer}): super._();
final Transfer transfer;
/// Create a copy of AppEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$AppEvent_TransferStatusChangedCopyWith<AppEvent_TransferStatusChanged> get copyWith => _$AppEvent_TransferStatusChangedCopyWithImpl<AppEvent_TransferStatusChanged>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppEvent_TransferStatusChanged&&(identical(other.transfer, transfer) || other.transfer == transfer));
}
@override
int get hashCode => Object.hash(runtimeType,transfer);
@override
String toString() {
return 'AppEvent.transferStatusChanged(transfer: $transfer)';
}
}
/// @nodoc
abstract mixin class $AppEvent_TransferStatusChangedCopyWith<$Res> implements $AppEventCopyWith<$Res> {
factory $AppEvent_TransferStatusChangedCopyWith(AppEvent_TransferStatusChanged value, $Res Function(AppEvent_TransferStatusChanged) _then) = _$AppEvent_TransferStatusChangedCopyWithImpl;
@useResult
$Res call({
Transfer transfer
});
}
/// @nodoc
class _$AppEvent_TransferStatusChangedCopyWithImpl<$Res>
implements $AppEvent_TransferStatusChangedCopyWith<$Res> {
_$AppEvent_TransferStatusChangedCopyWithImpl(this._self, this._then);
final AppEvent_TransferStatusChanged _self;
final $Res Function(AppEvent_TransferStatusChanged) _then;
/// Create a copy of AppEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? transfer = null,}) {
return _then(AppEvent_TransferStatusChanged(
transfer: null == transfer ? _self.transfer : transfer // ignore: cast_nullable_to_non_nullable
as Transfer,
));
}
}
/// @nodoc
class AppEvent_TransferProgressChanged extends AppEvent {
const AppEvent_TransferProgressChanged({required this.id, required this.progress, required this.total, required this.speed}): super._();
final String id;
final double progress;
final double total;
final double speed;
/// Create a copy of AppEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$AppEvent_TransferProgressChangedCopyWith<AppEvent_TransferProgressChanged> get copyWith => _$AppEvent_TransferProgressChangedCopyWithImpl<AppEvent_TransferProgressChanged>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppEvent_TransferProgressChanged&&(identical(other.id, id) || other.id == id)&&(identical(other.progress, progress) || other.progress == progress)&&(identical(other.total, total) || other.total == total)&&(identical(other.speed, speed) || other.speed == speed));
}
@override
int get hashCode => Object.hash(runtimeType,id,progress,total,speed);
@override
String toString() {
return 'AppEvent.transferProgressChanged(id: $id, progress: $progress, total: $total, speed: $speed)';
}
}
/// @nodoc
abstract mixin class $AppEvent_TransferProgressChangedCopyWith<$Res> implements $AppEventCopyWith<$Res> {
factory $AppEvent_TransferProgressChangedCopyWith(AppEvent_TransferProgressChanged value, $Res Function(AppEvent_TransferProgressChanged) _then) = _$AppEvent_TransferProgressChangedCopyWithImpl;
@useResult
$Res call({
String id, double progress, double total, double speed
});
}
/// @nodoc
class _$AppEvent_TransferProgressChangedCopyWithImpl<$Res>
implements $AppEvent_TransferProgressChangedCopyWith<$Res> {
_$AppEvent_TransferProgressChangedCopyWithImpl(this._self, this._then);
final AppEvent_TransferProgressChanged _self;
final $Res Function(AppEvent_TransferProgressChanged) _then;
/// Create a copy of AppEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? id = null,Object? progress = null,Object? total = null,Object? speed = null,}) {
return _then(AppEvent_TransferProgressChanged(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,progress: null == progress ? _self.progress : progress // ignore: cast_nullable_to_non_nullable
as double,total: null == total ? _self.total : total // ignore: cast_nullable_to_non_nullable
as double,speed: null == speed ? _self.speed : speed // ignore: cast_nullable_to_non_nullable
as double,
));
}
}
/// @nodoc
class AppEvent_PeerConnectOrUpdated extends AppEvent {
const AppEvent_PeerConnectOrUpdated({required this.peer}): super._();
final Peer peer;
/// Create a copy of AppEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$AppEvent_PeerConnectOrUpdatedCopyWith<AppEvent_PeerConnectOrUpdated> get copyWith => _$AppEvent_PeerConnectOrUpdatedCopyWithImpl<AppEvent_PeerConnectOrUpdated>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppEvent_PeerConnectOrUpdated&&(identical(other.peer, peer) || other.peer == peer));
}
@override
int get hashCode => Object.hash(runtimeType,peer);
@override
String toString() {
return 'AppEvent.peerConnectOrUpdated(peer: $peer)';
}
}
/// @nodoc
abstract mixin class $AppEvent_PeerConnectOrUpdatedCopyWith<$Res> implements $AppEventCopyWith<$Res> {
factory $AppEvent_PeerConnectOrUpdatedCopyWith(AppEvent_PeerConnectOrUpdated value, $Res Function(AppEvent_PeerConnectOrUpdated) _then) = _$AppEvent_PeerConnectOrUpdatedCopyWithImpl;
@useResult
$Res call({
Peer peer
});
}
/// @nodoc
class _$AppEvent_PeerConnectOrUpdatedCopyWithImpl<$Res>
implements $AppEvent_PeerConnectOrUpdatedCopyWith<$Res> {
_$AppEvent_PeerConnectOrUpdatedCopyWithImpl(this._self, this._then);
final AppEvent_PeerConnectOrUpdated _self;
final $Res Function(AppEvent_PeerConnectOrUpdated) _then;
/// Create a copy of AppEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? peer = null,}) {
return _then(AppEvent_PeerConnectOrUpdated(
peer: null == peer ? _self.peer : peer // ignore: cast_nullable_to_non_nullable
as Peer,
));
}
}
/// @nodoc
class AppEvent_PeerDisconnected extends AppEvent {
const AppEvent_PeerDisconnected({required this.id}): super._();
final String id;
/// Create a copy of AppEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$AppEvent_PeerDisconnectedCopyWith<AppEvent_PeerDisconnected> get copyWith => _$AppEvent_PeerDisconnectedCopyWithImpl<AppEvent_PeerDisconnected>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppEvent_PeerDisconnected&&(identical(other.id, id) || other.id == id));
}
@override
int get hashCode => Object.hash(runtimeType,id);
@override
String toString() {
return 'AppEvent.peerDisconnected(id: $id)';
}
}
/// @nodoc
abstract mixin class $AppEvent_PeerDisconnectedCopyWith<$Res> implements $AppEventCopyWith<$Res> {
factory $AppEvent_PeerDisconnectedCopyWith(AppEvent_PeerDisconnected value, $Res Function(AppEvent_PeerDisconnected) _then) = _$AppEvent_PeerDisconnectedCopyWithImpl;
@useResult
$Res call({
String id
});
}
/// @nodoc
class _$AppEvent_PeerDisconnectedCopyWithImpl<$Res>
implements $AppEvent_PeerDisconnectedCopyWith<$Res> {
_$AppEvent_PeerDisconnectedCopyWithImpl(this._self, this._then);
final AppEvent_PeerDisconnected _self;
final $Res Function(AppEvent_PeerDisconnected) _then;
/// Create a copy of AppEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? id = null,}) {
return _then(AppEvent_PeerDisconnected(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class AppEvent_TransferAdded extends AppEvent {
const AppEvent_TransferAdded({required this.transfer}): super._();
final Transfer transfer;
/// Create a copy of AppEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$AppEvent_TransferAddedCopyWith<AppEvent_TransferAdded> get copyWith => _$AppEvent_TransferAddedCopyWithImpl<AppEvent_TransferAdded>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppEvent_TransferAdded&&(identical(other.transfer, transfer) || other.transfer == transfer));
}
@override
int get hashCode => Object.hash(runtimeType,transfer);
@override
String toString() {
return 'AppEvent.transferAdded(transfer: $transfer)';
}
}
/// @nodoc
abstract mixin class $AppEvent_TransferAddedCopyWith<$Res> implements $AppEventCopyWith<$Res> {
factory $AppEvent_TransferAddedCopyWith(AppEvent_TransferAdded value, $Res Function(AppEvent_TransferAdded) _then) = _$AppEvent_TransferAddedCopyWithImpl;
@useResult
$Res call({
Transfer transfer
});
}
/// @nodoc
class _$AppEvent_TransferAddedCopyWithImpl<$Res>
implements $AppEvent_TransferAddedCopyWith<$Res> {
_$AppEvent_TransferAddedCopyWithImpl(this._self, this._then);
final AppEvent_TransferAdded _self;
final $Res Function(AppEvent_TransferAdded) _then;
/// Create a copy of AppEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? transfer = null,}) {
return _then(AppEvent_TransferAdded(
transfer: null == transfer ? _self.transfer : transfer // ignore: cast_nullable_to_non_nullable
as Transfer,
));
}
}
/// @nodoc
class AppEvent_TransferRemoved extends AppEvent {
const AppEvent_TransferRemoved({required this.id}): super._();
final String id;
/// Create a copy of AppEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$AppEvent_TransferRemovedCopyWith<AppEvent_TransferRemoved> get copyWith => _$AppEvent_TransferRemovedCopyWithImpl<AppEvent_TransferRemoved>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppEvent_TransferRemoved&&(identical(other.id, id) || other.id == id));
}
@override
int get hashCode => Object.hash(runtimeType,id);
@override
String toString() {
return 'AppEvent.transferRemoved(id: $id)';
}
}
/// @nodoc
abstract mixin class $AppEvent_TransferRemovedCopyWith<$Res> implements $AppEventCopyWith<$Res> {
factory $AppEvent_TransferRemovedCopyWith(AppEvent_TransferRemoved value, $Res Function(AppEvent_TransferRemoved) _then) = _$AppEvent_TransferRemovedCopyWithImpl;
@useResult
$Res call({
String id
});
}
/// @nodoc
class _$AppEvent_TransferRemovedCopyWithImpl<$Res>
implements $AppEvent_TransferRemovedCopyWith<$Res> {
_$AppEvent_TransferRemovedCopyWithImpl(this._self, this._then);
final AppEvent_TransferRemoved _self;
final $Res Function(AppEvent_TransferRemoved) _then;
/// Create a copy of AppEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? id = null,}) {
return _then(AppEvent_TransferRemoved(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class AppEvent_TransferClear extends AppEvent {
const AppEvent_TransferClear(): super._();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppEvent_TransferClear);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'AppEvent.transferClear()';
}
}
// dart format on

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,303 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field
import 'api/commands.dart';
import 'dart:async';
import 'dart:convert';
import 'dart:ffi' as ffi;
import 'discovery/model.dart';
import 'event.dart';
import 'frb_generated.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_io.dart';
import 'transfer/model.dart';
abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
RustLibApiImplPlatform({
required super.handler,
required super.wire,
required super.generalizedFrbRustBinding,
required super.portManager,
});
@protected
AnyhowException dco_decode_AnyhowException(dynamic raw);
@protected
Map<String, RouteState> dco_decode_Map_String_route_state_None(dynamic raw);
@protected
RustStreamSink<AppEvent> dco_decode_StreamSink_app_event_Sse(dynamic raw);
@protected
String dco_decode_String(dynamic raw);
@protected
AppEvent dco_decode_app_event(dynamic raw);
@protected
bool dco_decode_bool(dynamic raw);
@protected
Peer dco_decode_box_autoadd_peer(dynamic raw);
@protected
Transfer dco_decode_box_autoadd_transfer(dynamic raw);
@protected
CanceledBy dco_decode_canceled_by(dynamic raw);
@protected
ContentType dco_decode_content_type(dynamic raw);
@protected
double dco_decode_f_64(dynamic raw);
@protected
int dco_decode_i_32(dynamic raw);
@protected
PlatformInt64 dco_decode_i_64(dynamic raw);
@protected
List<Peer> dco_decode_list_peer(dynamic raw);
@protected
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
@protected
List<(String, RouteState)> dco_decode_list_record_string_route_state(
dynamic raw,
);
@protected
List<Transfer> dco_decode_list_transfer(dynamic raw);
@protected
Peer dco_decode_peer(dynamic raw);
@protected
(String, RouteState) dco_decode_record_string_route_state(dynamic raw);
@protected
RouteState dco_decode_route_state(dynamic raw);
@protected
Transfer dco_decode_transfer(dynamic raw);
@protected
TransferStatus dco_decode_transfer_status(dynamic raw);
@protected
TransferType dco_decode_transfer_type(dynamic raw);
@protected
int dco_decode_u_16(dynamic raw);
@protected
int dco_decode_u_8(dynamic raw);
@protected
void dco_decode_unit(dynamic raw);
@protected
AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer);
@protected
Map<String, RouteState> sse_decode_Map_String_route_state_None(
SseDeserializer deserializer,
);
@protected
RustStreamSink<AppEvent> sse_decode_StreamSink_app_event_Sse(
SseDeserializer deserializer,
);
@protected
String sse_decode_String(SseDeserializer deserializer);
@protected
AppEvent sse_decode_app_event(SseDeserializer deserializer);
@protected
bool sse_decode_bool(SseDeserializer deserializer);
@protected
Peer sse_decode_box_autoadd_peer(SseDeserializer deserializer);
@protected
Transfer sse_decode_box_autoadd_transfer(SseDeserializer deserializer);
@protected
CanceledBy sse_decode_canceled_by(SseDeserializer deserializer);
@protected
ContentType sse_decode_content_type(SseDeserializer deserializer);
@protected
double sse_decode_f_64(SseDeserializer deserializer);
@protected
int sse_decode_i_32(SseDeserializer deserializer);
@protected
PlatformInt64 sse_decode_i_64(SseDeserializer deserializer);
@protected
List<Peer> sse_decode_list_peer(SseDeserializer deserializer);
@protected
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
@protected
List<(String, RouteState)> sse_decode_list_record_string_route_state(
SseDeserializer deserializer,
);
@protected
List<Transfer> sse_decode_list_transfer(SseDeserializer deserializer);
@protected
Peer sse_decode_peer(SseDeserializer deserializer);
@protected
(String, RouteState) sse_decode_record_string_route_state(
SseDeserializer deserializer,
);
@protected
RouteState sse_decode_route_state(SseDeserializer deserializer);
@protected
Transfer sse_decode_transfer(SseDeserializer deserializer);
@protected
TransferStatus sse_decode_transfer_status(SseDeserializer deserializer);
@protected
TransferType sse_decode_transfer_type(SseDeserializer deserializer);
@protected
int sse_decode_u_16(SseDeserializer deserializer);
@protected
int sse_decode_u_8(SseDeserializer deserializer);
@protected
void sse_decode_unit(SseDeserializer deserializer);
@protected
void sse_encode_AnyhowException(
AnyhowException self,
SseSerializer serializer,
);
@protected
void sse_encode_Map_String_route_state_None(
Map<String, RouteState> self,
SseSerializer serializer,
);
@protected
void sse_encode_StreamSink_app_event_Sse(
RustStreamSink<AppEvent> self,
SseSerializer serializer,
);
@protected
void sse_encode_String(String self, SseSerializer serializer);
@protected
void sse_encode_app_event(AppEvent self, SseSerializer serializer);
@protected
void sse_encode_bool(bool self, SseSerializer serializer);
@protected
void sse_encode_box_autoadd_peer(Peer self, SseSerializer serializer);
@protected
void sse_encode_box_autoadd_transfer(Transfer self, SseSerializer serializer);
@protected
void sse_encode_canceled_by(CanceledBy self, SseSerializer serializer);
@protected
void sse_encode_content_type(ContentType self, SseSerializer serializer);
@protected
void sse_encode_f_64(double self, SseSerializer serializer);
@protected
void sse_encode_i_32(int self, SseSerializer serializer);
@protected
void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer);
@protected
void sse_encode_list_peer(List<Peer> self, SseSerializer serializer);
@protected
void sse_encode_list_prim_u_8_strict(
Uint8List self,
SseSerializer serializer,
);
@protected
void sse_encode_list_record_string_route_state(
List<(String, RouteState)> self,
SseSerializer serializer,
);
@protected
void sse_encode_list_transfer(List<Transfer> self, SseSerializer serializer);
@protected
void sse_encode_peer(Peer self, SseSerializer serializer);
@protected
void sse_encode_record_string_route_state(
(String, RouteState) self,
SseSerializer serializer,
);
@protected
void sse_encode_route_state(RouteState self, SseSerializer serializer);
@protected
void sse_encode_transfer(Transfer self, SseSerializer serializer);
@protected
void sse_encode_transfer_status(
TransferStatus self,
SseSerializer serializer,
);
@protected
void sse_encode_transfer_type(TransferType self, SseSerializer serializer);
@protected
void sse_encode_u_16(int self, SseSerializer serializer);
@protected
void sse_encode_u_8(int self, SseSerializer serializer);
@protected
void sse_encode_unit(void self, SseSerializer serializer);
}
// Section: wire_class
class RustLibWire implements BaseWire {
factory RustLibWire.fromExternalLibrary(ExternalLibrary lib) =>
RustLibWire(lib.ffiDynamicLibrary);
/// Holds the symbol lookup function.
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
_lookup;
/// The symbols are looked up in [dynamicLibrary].
RustLibWire(ffi.DynamicLibrary dynamicLibrary)
: _lookup = dynamicLibrary.lookup;
}

View File

@@ -0,0 +1,303 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field
// Static analysis wrongly picks the IO variant, thus ignore this
// ignore_for_file: argument_type_not_assignable
import 'api/commands.dart';
import 'dart:async';
import 'dart:convert';
import 'discovery/model.dart';
import 'event.dart';
import 'frb_generated.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_web.dart';
import 'transfer/model.dart';
abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
RustLibApiImplPlatform({
required super.handler,
required super.wire,
required super.generalizedFrbRustBinding,
required super.portManager,
});
@protected
AnyhowException dco_decode_AnyhowException(dynamic raw);
@protected
Map<String, RouteState> dco_decode_Map_String_route_state_None(dynamic raw);
@protected
RustStreamSink<AppEvent> dco_decode_StreamSink_app_event_Sse(dynamic raw);
@protected
String dco_decode_String(dynamic raw);
@protected
AppEvent dco_decode_app_event(dynamic raw);
@protected
bool dco_decode_bool(dynamic raw);
@protected
Peer dco_decode_box_autoadd_peer(dynamic raw);
@protected
Transfer dco_decode_box_autoadd_transfer(dynamic raw);
@protected
CanceledBy dco_decode_canceled_by(dynamic raw);
@protected
ContentType dco_decode_content_type(dynamic raw);
@protected
double dco_decode_f_64(dynamic raw);
@protected
int dco_decode_i_32(dynamic raw);
@protected
PlatformInt64 dco_decode_i_64(dynamic raw);
@protected
List<Peer> dco_decode_list_peer(dynamic raw);
@protected
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
@protected
List<(String, RouteState)> dco_decode_list_record_string_route_state(
dynamic raw,
);
@protected
List<Transfer> dco_decode_list_transfer(dynamic raw);
@protected
Peer dco_decode_peer(dynamic raw);
@protected
(String, RouteState) dco_decode_record_string_route_state(dynamic raw);
@protected
RouteState dco_decode_route_state(dynamic raw);
@protected
Transfer dco_decode_transfer(dynamic raw);
@protected
TransferStatus dco_decode_transfer_status(dynamic raw);
@protected
TransferType dco_decode_transfer_type(dynamic raw);
@protected
int dco_decode_u_16(dynamic raw);
@protected
int dco_decode_u_8(dynamic raw);
@protected
void dco_decode_unit(dynamic raw);
@protected
AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer);
@protected
Map<String, RouteState> sse_decode_Map_String_route_state_None(
SseDeserializer deserializer,
);
@protected
RustStreamSink<AppEvent> sse_decode_StreamSink_app_event_Sse(
SseDeserializer deserializer,
);
@protected
String sse_decode_String(SseDeserializer deserializer);
@protected
AppEvent sse_decode_app_event(SseDeserializer deserializer);
@protected
bool sse_decode_bool(SseDeserializer deserializer);
@protected
Peer sse_decode_box_autoadd_peer(SseDeserializer deserializer);
@protected
Transfer sse_decode_box_autoadd_transfer(SseDeserializer deserializer);
@protected
CanceledBy sse_decode_canceled_by(SseDeserializer deserializer);
@protected
ContentType sse_decode_content_type(SseDeserializer deserializer);
@protected
double sse_decode_f_64(SseDeserializer deserializer);
@protected
int sse_decode_i_32(SseDeserializer deserializer);
@protected
PlatformInt64 sse_decode_i_64(SseDeserializer deserializer);
@protected
List<Peer> sse_decode_list_peer(SseDeserializer deserializer);
@protected
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
@protected
List<(String, RouteState)> sse_decode_list_record_string_route_state(
SseDeserializer deserializer,
);
@protected
List<Transfer> sse_decode_list_transfer(SseDeserializer deserializer);
@protected
Peer sse_decode_peer(SseDeserializer deserializer);
@protected
(String, RouteState) sse_decode_record_string_route_state(
SseDeserializer deserializer,
);
@protected
RouteState sse_decode_route_state(SseDeserializer deserializer);
@protected
Transfer sse_decode_transfer(SseDeserializer deserializer);
@protected
TransferStatus sse_decode_transfer_status(SseDeserializer deserializer);
@protected
TransferType sse_decode_transfer_type(SseDeserializer deserializer);
@protected
int sse_decode_u_16(SseDeserializer deserializer);
@protected
int sse_decode_u_8(SseDeserializer deserializer);
@protected
void sse_decode_unit(SseDeserializer deserializer);
@protected
void sse_encode_AnyhowException(
AnyhowException self,
SseSerializer serializer,
);
@protected
void sse_encode_Map_String_route_state_None(
Map<String, RouteState> self,
SseSerializer serializer,
);
@protected
void sse_encode_StreamSink_app_event_Sse(
RustStreamSink<AppEvent> self,
SseSerializer serializer,
);
@protected
void sse_encode_String(String self, SseSerializer serializer);
@protected
void sse_encode_app_event(AppEvent self, SseSerializer serializer);
@protected
void sse_encode_bool(bool self, SseSerializer serializer);
@protected
void sse_encode_box_autoadd_peer(Peer self, SseSerializer serializer);
@protected
void sse_encode_box_autoadd_transfer(Transfer self, SseSerializer serializer);
@protected
void sse_encode_canceled_by(CanceledBy self, SseSerializer serializer);
@protected
void sse_encode_content_type(ContentType self, SseSerializer serializer);
@protected
void sse_encode_f_64(double self, SseSerializer serializer);
@protected
void sse_encode_i_32(int self, SseSerializer serializer);
@protected
void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer);
@protected
void sse_encode_list_peer(List<Peer> self, SseSerializer serializer);
@protected
void sse_encode_list_prim_u_8_strict(
Uint8List self,
SseSerializer serializer,
);
@protected
void sse_encode_list_record_string_route_state(
List<(String, RouteState)> self,
SseSerializer serializer,
);
@protected
void sse_encode_list_transfer(List<Transfer> self, SseSerializer serializer);
@protected
void sse_encode_peer(Peer self, SseSerializer serializer);
@protected
void sse_encode_record_string_route_state(
(String, RouteState) self,
SseSerializer serializer,
);
@protected
void sse_encode_route_state(RouteState self, SseSerializer serializer);
@protected
void sse_encode_transfer(Transfer self, SseSerializer serializer);
@protected
void sse_encode_transfer_status(
TransferStatus self,
SseSerializer serializer,
);
@protected
void sse_encode_transfer_type(TransferType self, SseSerializer serializer);
@protected
void sse_encode_u_16(int self, SseSerializer serializer);
@protected
void sse_encode_u_8(int self, SseSerializer serializer);
@protected
void sse_encode_unit(void self, SseSerializer serializer);
}
// Section: wire_class
class RustLibWire implements BaseWire {
RustLibWire.fromExternalLibrary(ExternalLibrary lib);
}
@JS('wasm_bindgen')
external RustLibWasmModule get wasmModule;
@JS()
@anonymous
extension type RustLibWasmModule._(JSObject _) implements JSObject {}

View File

@@ -0,0 +1,109 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../discovery/model.dart';
import '../frb_generated.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
part 'model.freezed.dart';
enum CanceledBy { sender, receiver }
enum ContentType { file, text, folder }
class Transfer {
final String id;
final double createTime;
final Peer sender;
final String senderIp;
final String fileName;
final double fileSize;
final String savePath;
final TransferStatus status;
final TransferType type;
final ContentType contentType;
final String text;
final String errorMsg;
final String token;
final double progress;
final PlatformInt64 lastReadTime;
final double speed;
const Transfer({
required this.id,
required this.createTime,
required this.sender,
required this.senderIp,
required this.fileName,
required this.fileSize,
required this.savePath,
required this.status,
required this.type,
required this.contentType,
required this.text,
required this.errorMsg,
required this.token,
required this.progress,
required this.lastReadTime,
required this.speed,
});
@override
int get hashCode =>
id.hashCode ^
createTime.hashCode ^
sender.hashCode ^
senderIp.hashCode ^
fileName.hashCode ^
fileSize.hashCode ^
savePath.hashCode ^
status.hashCode ^
type.hashCode ^
contentType.hashCode ^
text.hashCode ^
errorMsg.hashCode ^
token.hashCode ^
progress.hashCode ^
lastReadTime.hashCode ^
speed.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Transfer &&
runtimeType == other.runtimeType &&
id == other.id &&
createTime == other.createTime &&
sender == other.sender &&
senderIp == other.senderIp &&
fileName == other.fileName &&
fileSize == other.fileSize &&
savePath == other.savePath &&
status == other.status &&
type == other.type &&
contentType == other.contentType &&
text == other.text &&
errorMsg == other.errorMsg &&
token == other.token &&
progress == other.progress &&
lastReadTime == other.lastReadTime &&
speed == other.speed;
}
@freezed
sealed class TransferStatus with _$TransferStatus {
const TransferStatus._();
const factory TransferStatus.pending() = TransferStatus_Pending;
const factory TransferStatus.accepted() = TransferStatus_Accepted;
const factory TransferStatus.rejected() = TransferStatus_Rejected;
const factory TransferStatus.completed() = TransferStatus_Completed;
const factory TransferStatus.error() = TransferStatus_Error;
const factory TransferStatus.canceled(CanceledBy field0) =
TransferStatus_Canceled;
const factory TransferStatus.active() = TransferStatus_Active;
}
enum TransferType { send, receive }

View File

@@ -0,0 +1,462 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$TransferStatus {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TransferStatus);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'TransferStatus()';
}
}
/// @nodoc
class $TransferStatusCopyWith<$Res> {
$TransferStatusCopyWith(TransferStatus _, $Res Function(TransferStatus) __);
}
/// Adds pattern-matching-related methods to [TransferStatus].
extension TransferStatusPatterns on TransferStatus {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>({TResult Function( TransferStatus_Pending value)? pending,TResult Function( TransferStatus_Accepted value)? accepted,TResult Function( TransferStatus_Rejected value)? rejected,TResult Function( TransferStatus_Completed value)? completed,TResult Function( TransferStatus_Error value)? error,TResult Function( TransferStatus_Canceled value)? canceled,TResult Function( TransferStatus_Active value)? active,required TResult orElse(),}){
final _that = this;
switch (_that) {
case TransferStatus_Pending() when pending != null:
return pending(_that);case TransferStatus_Accepted() when accepted != null:
return accepted(_that);case TransferStatus_Rejected() when rejected != null:
return rejected(_that);case TransferStatus_Completed() when completed != null:
return completed(_that);case TransferStatus_Error() when error != null:
return error(_that);case TransferStatus_Canceled() when canceled != null:
return canceled(_that);case TransferStatus_Active() when active != null:
return active(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>({required TResult Function( TransferStatus_Pending value) pending,required TResult Function( TransferStatus_Accepted value) accepted,required TResult Function( TransferStatus_Rejected value) rejected,required TResult Function( TransferStatus_Completed value) completed,required TResult Function( TransferStatus_Error value) error,required TResult Function( TransferStatus_Canceled value) canceled,required TResult Function( TransferStatus_Active value) active,}){
final _that = this;
switch (_that) {
case TransferStatus_Pending():
return pending(_that);case TransferStatus_Accepted():
return accepted(_that);case TransferStatus_Rejected():
return rejected(_that);case TransferStatus_Completed():
return completed(_that);case TransferStatus_Error():
return error(_that);case TransferStatus_Canceled():
return canceled(_that);case TransferStatus_Active():
return active(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>({TResult? Function( TransferStatus_Pending value)? pending,TResult? Function( TransferStatus_Accepted value)? accepted,TResult? Function( TransferStatus_Rejected value)? rejected,TResult? Function( TransferStatus_Completed value)? completed,TResult? Function( TransferStatus_Error value)? error,TResult? Function( TransferStatus_Canceled value)? canceled,TResult? Function( TransferStatus_Active value)? active,}){
final _that = this;
switch (_that) {
case TransferStatus_Pending() when pending != null:
return pending(_that);case TransferStatus_Accepted() when accepted != null:
return accepted(_that);case TransferStatus_Rejected() when rejected != null:
return rejected(_that);case TransferStatus_Completed() when completed != null:
return completed(_that);case TransferStatus_Error() when error != null:
return error(_that);case TransferStatus_Canceled() when canceled != null:
return canceled(_that);case TransferStatus_Active() when active != null:
return active(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function()? pending,TResult Function()? accepted,TResult Function()? rejected,TResult Function()? completed,TResult Function()? error,TResult Function( CanceledBy field0)? canceled,TResult Function()? active,required TResult orElse(),}) {final _that = this;
switch (_that) {
case TransferStatus_Pending() when pending != null:
return pending();case TransferStatus_Accepted() when accepted != null:
return accepted();case TransferStatus_Rejected() when rejected != null:
return rejected();case TransferStatus_Completed() when completed != null:
return completed();case TransferStatus_Error() when error != null:
return error();case TransferStatus_Canceled() when canceled != null:
return canceled(_that.field0);case TransferStatus_Active() when active != null:
return active();case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function() pending,required TResult Function() accepted,required TResult Function() rejected,required TResult Function() completed,required TResult Function() error,required TResult Function( CanceledBy field0) canceled,required TResult Function() active,}) {final _that = this;
switch (_that) {
case TransferStatus_Pending():
return pending();case TransferStatus_Accepted():
return accepted();case TransferStatus_Rejected():
return rejected();case TransferStatus_Completed():
return completed();case TransferStatus_Error():
return error();case TransferStatus_Canceled():
return canceled(_that.field0);case TransferStatus_Active():
return active();}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function()? pending,TResult? Function()? accepted,TResult? Function()? rejected,TResult? Function()? completed,TResult? Function()? error,TResult? Function( CanceledBy field0)? canceled,TResult? Function()? active,}) {final _that = this;
switch (_that) {
case TransferStatus_Pending() when pending != null:
return pending();case TransferStatus_Accepted() when accepted != null:
return accepted();case TransferStatus_Rejected() when rejected != null:
return rejected();case TransferStatus_Completed() when completed != null:
return completed();case TransferStatus_Error() when error != null:
return error();case TransferStatus_Canceled() when canceled != null:
return canceled(_that.field0);case TransferStatus_Active() when active != null:
return active();case _:
return null;
}
}
}
/// @nodoc
class TransferStatus_Pending extends TransferStatus {
const TransferStatus_Pending(): super._();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TransferStatus_Pending);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'TransferStatus.pending()';
}
}
/// @nodoc
class TransferStatus_Accepted extends TransferStatus {
const TransferStatus_Accepted(): super._();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TransferStatus_Accepted);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'TransferStatus.accepted()';
}
}
/// @nodoc
class TransferStatus_Rejected extends TransferStatus {
const TransferStatus_Rejected(): super._();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TransferStatus_Rejected);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'TransferStatus.rejected()';
}
}
/// @nodoc
class TransferStatus_Completed extends TransferStatus {
const TransferStatus_Completed(): super._();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TransferStatus_Completed);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'TransferStatus.completed()';
}
}
/// @nodoc
class TransferStatus_Error extends TransferStatus {
const TransferStatus_Error(): super._();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TransferStatus_Error);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'TransferStatus.error()';
}
}
/// @nodoc
class TransferStatus_Canceled extends TransferStatus {
const TransferStatus_Canceled(this.field0): super._();
final CanceledBy field0;
/// Create a copy of TransferStatus
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$TransferStatus_CanceledCopyWith<TransferStatus_Canceled> get copyWith => _$TransferStatus_CanceledCopyWithImpl<TransferStatus_Canceled>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TransferStatus_Canceled&&(identical(other.field0, field0) || other.field0 == field0));
}
@override
int get hashCode => Object.hash(runtimeType,field0);
@override
String toString() {
return 'TransferStatus.canceled(field0: $field0)';
}
}
/// @nodoc
abstract mixin class $TransferStatus_CanceledCopyWith<$Res> implements $TransferStatusCopyWith<$Res> {
factory $TransferStatus_CanceledCopyWith(TransferStatus_Canceled value, $Res Function(TransferStatus_Canceled) _then) = _$TransferStatus_CanceledCopyWithImpl;
@useResult
$Res call({
CanceledBy field0
});
}
/// @nodoc
class _$TransferStatus_CanceledCopyWithImpl<$Res>
implements $TransferStatus_CanceledCopyWith<$Res> {
_$TransferStatus_CanceledCopyWithImpl(this._self, this._then);
final TransferStatus_Canceled _self;
final $Res Function(TransferStatus_Canceled) _then;
/// Create a copy of TransferStatus
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? field0 = null,}) {
return _then(TransferStatus_Canceled(
null == field0 ? _self.field0 : field0 // ignore: cast_nullable_to_non_nullable
as CanceledBy,
));
}
}
/// @nodoc
class TransferStatus_Active extends TransferStatus {
const TransferStatus_Active(): super._();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TransferStatus_Active);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'TransferStatus.active()';
}
}
// dart format on

11
lib/main.dart Normal file
View File

@@ -0,0 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'app/app.dart';
import 'backend/frb_generated.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await RustLib.init();
runApp(const ProviderScope(child: MeshDropApp()));
}

1
linux/.gitignore vendored Normal file
View File

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

128
linux/CMakeLists.txt Normal file
View File

@@ -0,0 +1,128 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.13)
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 "mesh_drop_flutter")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "com.example.mesh_drop_flutter")
# 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)
# Application build; see runner/CMakeLists.txt.
add_subdirectory("runner")
# 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 <desktop_drop/desktop_drop_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) desktop_drop_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopDropPlugin");
desktop_drop_plugin_register_with_registrar(desktop_drop_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_

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