diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4314849
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,44 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e042c98
--- /dev/null
+++ b/README.md
@@ -0,0 +1,119 @@
+# Connect
+
+> Connect a zecure open-source instant-image sharing, full-featured AI app based on 5 blockchains.
+
+Actually there is no AI or Blockchain or any other dump hyped stuff included into this app. The
+purpose of this app is first off all to create a usable, good looking and important of all
+end-to-end encrypted alternative to S[redacted]chat. As I love to schare images with friends but not
+a strange company.
+
+## Features
+
+This app was started because of the three main features I missed out by popular alternatives.
+
+### Three to rule them all.
+
+1. Security by design: No one except your device can access your data.
+2. Privacy by design: The server only knows your username and public key.
+3. User-friendliness: Decide for your own :)
+
+> "It's the combination that makes the difference. Only a combination of privacy, security and user-friendliness can make a application good. Focusing on just one of these three areas can mean that everyone loses out in the end." ~ Myself 2019
+
+### But there is even more!
+
+4. Decentralized: Everyone can host an server. The usernames are like your email USERNAME@YOURSERVER
+5. Simple to use: What is a Public key? If you don't know this it is okay you don't have to to use Connect!
+
+> "Its like when S[redacted]chat and email had a modern baby" ~ Myself 2024
+
+
+## Roadmap
+
+The development of this App has the following stages:
+
+1. **Working POC**: As this is my first App ever the first step is to learn App development. During
+this step I try everything I can do as a non experienced App developer to make working POC App which
+is by design secure meaning while I learn new things I always try to think how this could be
+attacked and how I could protect it. But as I have no experience at all I depend on libraries who
+look secure. This step include:
+
+ 1. Create a working POC and an private invitation code
+ 2. Test the POC with friends
+ 3. Improve and fix bugs
+ 4. Repeat steps 2. and 3. until I have a usable App
+
+2. **Bug Bounty**: This is probably the most fun part. The GitHub Repository will become open-source
+and I make public releases so others can [install](https://github.com/ImranR98/Obtainium) and test
+the app as well. After I published the App the following steps will happen.
+ 1. I try to hack my own App using the free Android Hacking Course from
+ [heytree.io](https://app.hextree.io/map/android/android-continent). And if I find something will
+ create a nice write-up.
+ 2. Add the new invitation code "BUGBOUNTY" so reale smart people (not
+ like me) can try to hack the app. And if they find something and create a small POC there is a
+ small Bug Bounty of 50€.
+
+3. **Pre-Releasing and Scaling**: If I ever come to this point the App will be first released on
+[droidy-fy](https://droidify.eu.org/) to test on a larger scale.
+
+4. **Release**: This will probably never happen, but if it will, I will release the App on Google
+Play and maybe Apple App Store for a couple of €€ as this requires a paid developer account :/. The
+version on droid-fy will be free for ever to encourage people to switch to open source apps which.
+
+5. **Becoming Rich**: I will add an ChatBot into the App. Then I rename the App into ConnectAI add
+misleading advertisement on the website that the app is smart as it has an AI and then sell the app
+for $7 trillion to become rich.
+
+## Decentralized
+
+The idea behind this idea is that the server does not know anything about the user. As the server
+is not able to read the messages but only see the receiver of it the only purpose is to forward
+the message. So like for emails the user can specify in his username to which server he belongs to.
+
+
+The username is then build up with the following informations:
+
+`username@fqdn:port`
+
+The username is unique for the corresponding server. The second part contains the fully qualified
+domain name where the server is reachable. It is also possible to specify a custom port. The
+default port are the HTTP and TLS ports.
+
+
+### Thoughts
+When the user is adding another user they could exchange the servers public key which the user then
+reports to their own home sever. The home server then can authenticate the other server. But what
+is the thread here, the server are not able to see the messages nor change it as the client does an
+integrity check. An attacker could drop a messages and send an ACK as this is unauthenticated.
+
+## Donating
+
+As this is currently just a stupid project which does not work or serves a real use case even I
+would not donate anything to it. But there is another cool way: If you are on the search for a really
+cheap hosting provider which I use for my self for over 8 years you could use the following voucher
+to get 5€ off `36nc15747855300` (contact me to get a 30% off voucher). I get 10% from the affiliate program which
+helps me cover my hosting costs :)
+
+## Self-hosting
+
+It is possible to host the server yourself and then modify the server in your app. But be aware it
+is not planned to sync the different servers so you can only add people when their are on the same
+server! As in my understanding this is not supported by the Signal protocol, but please proof me
+wrong and at a PR :)
+
+A possible idea could be to add multiple servers in app but this would then require multiple
+parallel open websocket connection to check for new messages. As before if you want that feature
+create a PR :)
+
+
+## Bug-Bounty
+
+This app offers a Bug Bounty as I really love the Idea of others hacking my app. Depending on the
+criticality the bounty can go up to 50€ (more later when there are donation or I actually make some
+money which I probably never will do :/).
+
+## Credits
+
+As this is my first Android app, I used different examples where I copied the code design decisions.
+To give them credits and maybe help others they are listed below:
+
+- [Database handling (model, provider and usage)](https://github.com/alextekartik/flutter_app_example/blob/master/notepad_sqflite/)
\ No newline at end of file
diff --git a/analysis_options.yaml b/analysis_options.yaml
new file mode 100644
index 0000000..0d29021
--- /dev/null
+++ b/analysis_options.yaml
@@ -0,0 +1,28 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+linter:
+ # The lint rules applied to this project can be customized in the
+ # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+ # included above or to enable additional rules. A list of all available lints
+ # and their documentation is published at https://dart.dev/lints.
+ #
+ # Instead of disabling a lint rule for the entire project in the
+ # section below, it can also be suppressed for a single line of code
+ # or a specific dart file by using the `// ignore: name_of_lint` and
+ # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+ # producing the lint.
+ rules:
+ # avoid_print: false # Uncomment to disable the `avoid_print` rule
+ # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/android/.gitignore b/android/.gitignore
new file mode 100644
index 0000000..55afd91
--- /dev/null
+++ b/android/.gitignore
@@ -0,0 +1,13 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/to/reference-keystore
+key.properties
+**/*.keystore
+**/*.jks
diff --git a/android/app/build.gradle b/android/app/build.gradle
new file mode 100644
index 0000000..93add39
--- /dev/null
+++ b/android/app/build.gradle
@@ -0,0 +1,45 @@
+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.connect"
+ compileSdk = flutter.compileSdkVersion
+ //ndkVersion = flutter.ndkVersion
+ ndkVersion = "25.1.8937393"
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_17
+ targetCompatibility JavaVersion.VERSION_17
+ }
+
+ kotlinOptions {
+ jvmTarget = 17
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId = "eu.tsmr.connect"
+ // You can update the following values to match your application needs.
+ // For more information, see: https://flutter.dev/to/review-gradle-config.
+ minSdk = flutter.minSdkVersion
+ targetSdk = flutter.targetSdkVersion
+ versionCode = flutter.versionCode
+ versionName = flutter.versionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig = signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source = "../.."
+}
diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro
new file mode 100644
index 0000000..d0e0fbc
--- /dev/null
+++ b/android/app/proguard-rules.pro
@@ -0,0 +1 @@
+-keep class net.sqlcipher.** { *; }
\ No newline at end of file
diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000..399f698
--- /dev/null
+++ b/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..42de359
--- /dev/null
+++ b/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/kotlin/com/example/connect/MainActivity.kt b/android/app/src/main/kotlin/com/example/connect/MainActivity.kt
new file mode 100644
index 0000000..ba3d3c2
--- /dev/null
+++ b/android/app/src/main/kotlin/com/example/connect/MainActivity.kt
@@ -0,0 +1,5 @@
+package com.example.connect
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity()
diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 0000000..f74085f
--- /dev/null
+++ b/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 0000000..304732f
--- /dev/null
+++ b/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..db77bb4
Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-hdpi/launcher_icon.png b/android/app/src/main/res/mipmap-hdpi/launcher_icon.png
new file mode 100644
index 0000000..06a81f5
Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/launcher_icon.png differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..17987b7
Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-mdpi/launcher_icon.png b/android/app/src/main/res/mipmap-mdpi/launcher_icon.png
new file mode 100644
index 0000000..d2aecdd
Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/launcher_icon.png differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..09d4391
Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png
new file mode 100644
index 0000000..ea0ca0f
Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d5f1c8d
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png
new file mode 100644
index 0000000..c72472a
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4d6372e
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png
new file mode 100644
index 0000000..b509ceb
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png differ
diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 0000000..06952be
--- /dev/null
+++ b/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..cb1ef88
--- /dev/null
+++ b/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 0000000..399f698
--- /dev/null
+++ b/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/android/build.gradle b/android/build.gradle
new file mode 100644
index 0000000..d2ffbff
--- /dev/null
+++ b/android/build.gradle
@@ -0,0 +1,18 @@
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.buildDir = "../build"
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+ project.evaluationDependsOn(":app")
+}
+
+tasks.register("clean", Delete) {
+ delete rootProject.buildDir
+}
diff --git a/android/gradle.properties b/android/gradle.properties
new file mode 100644
index 0000000..2597170
--- /dev/null
+++ b/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..22041e5
--- /dev/null
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
\ No newline at end of file
diff --git a/android/settings.gradle b/android/settings.gradle
new file mode 100644
index 0000000..b5e1b3f
--- /dev/null
+++ b/android/settings.gradle
@@ -0,0 +1,25 @@
+pluginManagement {
+ def flutterSdkPath = {
+ def properties = new Properties()
+ file("local.properties").withInputStream { properties.load(it) }
+ def flutterSdkPath = properties.getProperty("flutter.sdk")
+ assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+ return flutterSdkPath
+ }()
+
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
+
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+plugins {
+ id "dev.flutter.flutter-plugin-loader" version "1.0.0"
+ id "com.android.application" version "8.3.2" apply false
+ id "org.jetbrains.kotlin.android" version "2.0.20" apply false
+}
+
+include ":app"
diff --git a/assets/images/2.0x/flutter_logo.png b/assets/images/2.0x/flutter_logo.png
new file mode 100644
index 0000000..b65164d
Binary files /dev/null and b/assets/images/2.0x/flutter_logo.png differ
diff --git a/assets/images/3.0x/flutter_logo.png b/assets/images/3.0x/flutter_logo.png
new file mode 100644
index 0000000..97e5dc9
Binary files /dev/null and b/assets/images/3.0x/flutter_logo.png differ
diff --git a/assets/images/flutter_logo.png b/assets/images/flutter_logo.png
new file mode 100644
index 0000000..b5c6ca7
Binary files /dev/null and b/assets/images/flutter_logo.png differ
diff --git a/assets/images/logo.png b/assets/images/logo.png
new file mode 100644
index 0000000..c685be2
Binary files /dev/null and b/assets/images/logo.png differ
diff --git a/assets/images/logo/android/play_store_512.png b/assets/images/logo/android/play_store_512.png
new file mode 100644
index 0000000..2213192
Binary files /dev/null and b/assets/images/logo/android/play_store_512.png differ
diff --git a/assets/images/logo/android/res/mipmap-anydpi-v26/ic_launcher.xml b/assets/images/logo/android/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..345888d
--- /dev/null
+++ b/assets/images/logo/android/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/images/logo/android/res/mipmap-hdpi/ic_launcher.png b/assets/images/logo/android/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..92b6c05
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/assets/images/logo/android/res/mipmap-hdpi/ic_launcher_background.png b/assets/images/logo/android/res/mipmap-hdpi/ic_launcher_background.png
new file mode 100644
index 0000000..f60a087
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-hdpi/ic_launcher_background.png differ
diff --git a/assets/images/logo/android/res/mipmap-hdpi/ic_launcher_foreground.png b/assets/images/logo/android/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..5756a07
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/assets/images/logo/android/res/mipmap-hdpi/ic_launcher_monochrome.png b/assets/images/logo/android/res/mipmap-hdpi/ic_launcher_monochrome.png
new file mode 100644
index 0000000..44047db
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-hdpi/ic_launcher_monochrome.png differ
diff --git a/assets/images/logo/android/res/mipmap-mdpi/ic_launcher.png b/assets/images/logo/android/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..b98fd2d
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/assets/images/logo/android/res/mipmap-mdpi/ic_launcher_background.png b/assets/images/logo/android/res/mipmap-mdpi/ic_launcher_background.png
new file mode 100644
index 0000000..5ac52b8
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-mdpi/ic_launcher_background.png differ
diff --git a/assets/images/logo/android/res/mipmap-mdpi/ic_launcher_foreground.png b/assets/images/logo/android/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..192abe0
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/assets/images/logo/android/res/mipmap-mdpi/ic_launcher_monochrome.png b/assets/images/logo/android/res/mipmap-mdpi/ic_launcher_monochrome.png
new file mode 100644
index 0000000..4f71405
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-mdpi/ic_launcher_monochrome.png differ
diff --git a/assets/images/logo/android/res/mipmap-xhdpi/ic_launcher.png b/assets/images/logo/android/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..0e2bec2
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/assets/images/logo/android/res/mipmap-xhdpi/ic_launcher_background.png b/assets/images/logo/android/res/mipmap-xhdpi/ic_launcher_background.png
new file mode 100644
index 0000000..ed3244f
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-xhdpi/ic_launcher_background.png differ
diff --git a/assets/images/logo/android/res/mipmap-xhdpi/ic_launcher_foreground.png b/assets/images/logo/android/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..ce3ad26
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/assets/images/logo/android/res/mipmap-xhdpi/ic_launcher_monochrome.png b/assets/images/logo/android/res/mipmap-xhdpi/ic_launcher_monochrome.png
new file mode 100644
index 0000000..6d68579
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-xhdpi/ic_launcher_monochrome.png differ
diff --git a/assets/images/logo/android/res/mipmap-xxhdpi/ic_launcher.png b/assets/images/logo/android/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..6bdb7a8
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/assets/images/logo/android/res/mipmap-xxhdpi/ic_launcher_background.png b/assets/images/logo/android/res/mipmap-xxhdpi/ic_launcher_background.png
new file mode 100644
index 0000000..72222b9
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-xxhdpi/ic_launcher_background.png differ
diff --git a/assets/images/logo/android/res/mipmap-xxhdpi/ic_launcher_foreground.png b/assets/images/logo/android/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..2d1f04c
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/assets/images/logo/android/res/mipmap-xxhdpi/ic_launcher_monochrome.png b/assets/images/logo/android/res/mipmap-xxhdpi/ic_launcher_monochrome.png
new file mode 100644
index 0000000..60be356
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-xxhdpi/ic_launcher_monochrome.png differ
diff --git a/assets/images/logo/android/res/mipmap-xxxhdpi/ic_launcher.png b/assets/images/logo/android/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..1ecbb80
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/assets/images/logo/android/res/mipmap-xxxhdpi/ic_launcher_background.png b/assets/images/logo/android/res/mipmap-xxxhdpi/ic_launcher_background.png
new file mode 100644
index 0000000..f5654c9
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-xxxhdpi/ic_launcher_background.png differ
diff --git a/assets/images/logo/android/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/assets/images/logo/android/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..7b4839a
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/assets/images/logo/android/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/assets/images/logo/android/res/mipmap-xxxhdpi/ic_launcher_monochrome.png
new file mode 100644
index 0000000..58861ac
Binary files /dev/null and b/assets/images/logo/android/res/mipmap-xxxhdpi/ic_launcher_monochrome.png differ
diff --git a/assets/images/logo/ios/AppIcon-20@2x.png b/assets/images/logo/ios/AppIcon-20@2x.png
new file mode 100644
index 0000000..947f94b
Binary files /dev/null and b/assets/images/logo/ios/AppIcon-20@2x.png differ
diff --git a/assets/images/logo/ios/AppIcon-20@2x~ipad.png b/assets/images/logo/ios/AppIcon-20@2x~ipad.png
new file mode 100644
index 0000000..947f94b
Binary files /dev/null and b/assets/images/logo/ios/AppIcon-20@2x~ipad.png differ
diff --git a/assets/images/logo/ios/AppIcon-20@3x.png b/assets/images/logo/ios/AppIcon-20@3x.png
new file mode 100644
index 0000000..c32e369
Binary files /dev/null and b/assets/images/logo/ios/AppIcon-20@3x.png differ
diff --git a/assets/images/logo/ios/AppIcon-20~ipad.png b/assets/images/logo/ios/AppIcon-20~ipad.png
new file mode 100644
index 0000000..4d16182
Binary files /dev/null and b/assets/images/logo/ios/AppIcon-20~ipad.png differ
diff --git a/assets/images/logo/ios/AppIcon-29.png b/assets/images/logo/ios/AppIcon-29.png
new file mode 100644
index 0000000..8fa6ef4
Binary files /dev/null and b/assets/images/logo/ios/AppIcon-29.png differ
diff --git a/assets/images/logo/ios/AppIcon-29@2x.png b/assets/images/logo/ios/AppIcon-29@2x.png
new file mode 100644
index 0000000..4d1fec9
Binary files /dev/null and b/assets/images/logo/ios/AppIcon-29@2x.png differ
diff --git a/assets/images/logo/ios/AppIcon-29@2x~ipad.png b/assets/images/logo/ios/AppIcon-29@2x~ipad.png
new file mode 100644
index 0000000..4d1fec9
Binary files /dev/null and b/assets/images/logo/ios/AppIcon-29@2x~ipad.png differ
diff --git a/assets/images/logo/ios/AppIcon-29@3x.png b/assets/images/logo/ios/AppIcon-29@3x.png
new file mode 100644
index 0000000..1dadbe0
Binary files /dev/null and b/assets/images/logo/ios/AppIcon-29@3x.png differ
diff --git a/assets/images/logo/ios/AppIcon-29~ipad.png b/assets/images/logo/ios/AppIcon-29~ipad.png
new file mode 100644
index 0000000..8fa6ef4
Binary files /dev/null and b/assets/images/logo/ios/AppIcon-29~ipad.png differ
diff --git a/assets/images/logo/ios/AppIcon-40@2x.png b/assets/images/logo/ios/AppIcon-40@2x.png
new file mode 100644
index 0000000..b0fe379
Binary files /dev/null and b/assets/images/logo/ios/AppIcon-40@2x.png differ
diff --git a/assets/images/logo/ios/AppIcon-40@2x~ipad.png b/assets/images/logo/ios/AppIcon-40@2x~ipad.png
new file mode 100644
index 0000000..b0fe379
Binary files /dev/null and b/assets/images/logo/ios/AppIcon-40@2x~ipad.png differ
diff --git a/assets/images/logo/ios/AppIcon-40@3x.png b/assets/images/logo/ios/AppIcon-40@3x.png
new file mode 100644
index 0000000..07fafe4
Binary files /dev/null and b/assets/images/logo/ios/AppIcon-40@3x.png differ
diff --git a/assets/images/logo/ios/AppIcon-40~ipad.png b/assets/images/logo/ios/AppIcon-40~ipad.png
new file mode 100644
index 0000000..947f94b
Binary files /dev/null and b/assets/images/logo/ios/AppIcon-40~ipad.png differ
diff --git a/assets/images/logo/ios/AppIcon-60@2x~car.png b/assets/images/logo/ios/AppIcon-60@2x~car.png
new file mode 100644
index 0000000..07fafe4
Binary files /dev/null and b/assets/images/logo/ios/AppIcon-60@2x~car.png differ
diff --git a/assets/images/logo/ios/AppIcon-60@3x~car.png b/assets/images/logo/ios/AppIcon-60@3x~car.png
new file mode 100644
index 0000000..06e300f
Binary files /dev/null and b/assets/images/logo/ios/AppIcon-60@3x~car.png differ
diff --git a/assets/images/logo/ios/AppIcon-83.5@2x~ipad.png b/assets/images/logo/ios/AppIcon-83.5@2x~ipad.png
new file mode 100644
index 0000000..73e50e9
Binary files /dev/null and b/assets/images/logo/ios/AppIcon-83.5@2x~ipad.png differ
diff --git a/assets/images/logo/ios/AppIcon@2x.png b/assets/images/logo/ios/AppIcon@2x.png
new file mode 100644
index 0000000..07fafe4
Binary files /dev/null and b/assets/images/logo/ios/AppIcon@2x.png differ
diff --git a/assets/images/logo/ios/AppIcon@2x~ipad.png b/assets/images/logo/ios/AppIcon@2x~ipad.png
new file mode 100644
index 0000000..9cb8132
Binary files /dev/null and b/assets/images/logo/ios/AppIcon@2x~ipad.png differ
diff --git a/assets/images/logo/ios/AppIcon@3x.png b/assets/images/logo/ios/AppIcon@3x.png
new file mode 100644
index 0000000..06e300f
Binary files /dev/null and b/assets/images/logo/ios/AppIcon@3x.png differ
diff --git a/assets/images/logo/ios/AppIcon~ios-marketing.png b/assets/images/logo/ios/AppIcon~ios-marketing.png
new file mode 100644
index 0000000..9a6dbc7
Binary files /dev/null and b/assets/images/logo/ios/AppIcon~ios-marketing.png differ
diff --git a/assets/images/logo/ios/AppIcon~ipad.png b/assets/images/logo/ios/AppIcon~ipad.png
new file mode 100644
index 0000000..42aeac7
Binary files /dev/null and b/assets/images/logo/ios/AppIcon~ipad.png differ
diff --git a/assets/images/logo/ios/Contents.json b/assets/images/logo/ios/Contents.json
new file mode 100644
index 0000000..bd04914
--- /dev/null
+++ b/assets/images/logo/ios/Contents.json
@@ -0,0 +1,134 @@
+{
+ "images": [
+ {
+ "filename": "AppIcon@2x.png",
+ "idiom": "iphone",
+ "scale": "2x",
+ "size": "60x60"
+ },
+ {
+ "filename": "AppIcon@3x.png",
+ "idiom": "iphone",
+ "scale": "3x",
+ "size": "60x60"
+ },
+ {
+ "filename": "AppIcon~ipad.png",
+ "idiom": "ipad",
+ "scale": "1x",
+ "size": "76x76"
+ },
+ {
+ "filename": "AppIcon@2x~ipad.png",
+ "idiom": "ipad",
+ "scale": "2x",
+ "size": "76x76"
+ },
+ {
+ "filename": "AppIcon-83.5@2x~ipad.png",
+ "idiom": "ipad",
+ "scale": "2x",
+ "size": "83.5x83.5"
+ },
+ {
+ "filename": "AppIcon-40@2x.png",
+ "idiom": "iphone",
+ "scale": "2x",
+ "size": "40x40"
+ },
+ {
+ "filename": "AppIcon-40@3x.png",
+ "idiom": "iphone",
+ "scale": "3x",
+ "size": "40x40"
+ },
+ {
+ "filename": "AppIcon-40~ipad.png",
+ "idiom": "ipad",
+ "scale": "1x",
+ "size": "40x40"
+ },
+ {
+ "filename": "AppIcon-40@2x~ipad.png",
+ "idiom": "ipad",
+ "scale": "2x",
+ "size": "40x40"
+ },
+ {
+ "filename": "AppIcon-20@2x.png",
+ "idiom": "iphone",
+ "scale": "2x",
+ "size": "20x20"
+ },
+ {
+ "filename": "AppIcon-20@3x.png",
+ "idiom": "iphone",
+ "scale": "3x",
+ "size": "20x20"
+ },
+ {
+ "filename": "AppIcon-20~ipad.png",
+ "idiom": "ipad",
+ "scale": "1x",
+ "size": "20x20"
+ },
+ {
+ "filename": "AppIcon-20@2x~ipad.png",
+ "idiom": "ipad",
+ "scale": "2x",
+ "size": "20x20"
+ },
+ {
+ "filename": "AppIcon-29.png",
+ "idiom": "iphone",
+ "scale": "1x",
+ "size": "29x29"
+ },
+ {
+ "filename": "AppIcon-29@2x.png",
+ "idiom": "iphone",
+ "scale": "2x",
+ "size": "29x29"
+ },
+ {
+ "filename": "AppIcon-29@3x.png",
+ "idiom": "iphone",
+ "scale": "3x",
+ "size": "29x29"
+ },
+ {
+ "filename": "AppIcon-29~ipad.png",
+ "idiom": "ipad",
+ "scale": "1x",
+ "size": "29x29"
+ },
+ {
+ "filename": "AppIcon-29@2x~ipad.png",
+ "idiom": "ipad",
+ "scale": "2x",
+ "size": "29x29"
+ },
+ {
+ "filename": "AppIcon-60@2x~car.png",
+ "idiom": "car",
+ "scale": "2x",
+ "size": "60x60"
+ },
+ {
+ "filename": "AppIcon-60@3x~car.png",
+ "idiom": "car",
+ "scale": "3x",
+ "size": "60x60"
+ },
+ {
+ "filename": "AppIcon~ios-marketing.png",
+ "idiom": "ios-marketing",
+ "scale": "1x",
+ "size": "1024x1024"
+ }
+ ],
+ "info": {
+ "author": "iconkitchen",
+ "version": 1
+ }
+}
\ No newline at end of file
diff --git a/assets/images/logo/logo.png b/assets/images/logo/logo.png
new file mode 100644
index 0000000..2213192
Binary files /dev/null and b/assets/images/logo/logo.png differ
diff --git a/assets/images/logo/web/README.txt b/assets/images/logo/web/README.txt
new file mode 100644
index 0000000..3bdd02f
--- /dev/null
+++ b/assets/images/logo/web/README.txt
@@ -0,0 +1,18 @@
+Add this to your HTML
:
+
+
+
+
+Add this to your app's manifest.json:
+
+ ...
+ {
+ "icons": [
+ { "src": "/favicon.ico", "type": "image/x-icon", "sizes": "16x16 32x32" },
+ { "src": "/icon-192.png", "type": "image/png", "sizes": "192x192" },
+ { "src": "/icon-512.png", "type": "image/png", "sizes": "512x512" },
+ { "src": "/icon-192-maskable.png", "type": "image/png", "sizes": "192x192", "purpose": "maskable" },
+ { "src": "/icon-512-maskable.png", "type": "image/png", "sizes": "512x512", "purpose": "maskable" }
+ ]
+ }
+ ...
diff --git a/assets/images/logo/web/apple-touch-icon.png b/assets/images/logo/web/apple-touch-icon.png
new file mode 100644
index 0000000..06e300f
Binary files /dev/null and b/assets/images/logo/web/apple-touch-icon.png differ
diff --git a/assets/images/logo/web/favicon.ico b/assets/images/logo/web/favicon.ico
new file mode 100644
index 0000000..d5ee75c
Binary files /dev/null and b/assets/images/logo/web/favicon.ico differ
diff --git a/assets/images/logo/web/icon-192-maskable.png b/assets/images/logo/web/icon-192-maskable.png
new file mode 100644
index 0000000..ff700c5
Binary files /dev/null and b/assets/images/logo/web/icon-192-maskable.png differ
diff --git a/assets/images/logo/web/icon-192.png b/assets/images/logo/web/icon-192.png
new file mode 100644
index 0000000..d41d311
Binary files /dev/null and b/assets/images/logo/web/icon-192.png differ
diff --git a/assets/images/logo/web/icon-512-maskable.png b/assets/images/logo/web/icon-512-maskable.png
new file mode 100644
index 0000000..2213192
Binary files /dev/null and b/assets/images/logo/web/icon-512-maskable.png differ
diff --git a/assets/images/logo/web/icon-512.png b/assets/images/logo/web/icon-512.png
new file mode 100644
index 0000000..efda500
Binary files /dev/null and b/assets/images/logo/web/icon-512.png differ
diff --git a/assets/images/shield-solid.svg b/assets/images/shield-solid.svg
new file mode 100644
index 0000000..c6e82e1
--- /dev/null
+++ b/assets/images/shield-solid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ios/.gitignore b/ios/.gitignore
new file mode 100644
index 0000000..7a7f987
--- /dev/null
+++ b/ios/.gitignore
@@ -0,0 +1,34 @@
+**/dgph
+*.mode1v3
+*.mode2v3
+*.moved-aside
+*.pbxuser
+*.perspectivev3
+**/*sync/
+.sconsign.dblite
+.tags*
+**/.vagrant/
+**/DerivedData/
+Icon?
+**/Pods/
+**/.symlinks/
+profile
+xcuserdata
+**/.generated/
+Flutter/App.framework
+Flutter/Flutter.framework
+Flutter/Flutter.podspec
+Flutter/Generated.xcconfig
+Flutter/ephemeral/
+Flutter/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!default.mode1v3
+!default.mode2v3
+!default.pbxuser
+!default.perspectivev3
diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 0000000..7c56964
--- /dev/null
+++ b/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ App
+ CFBundleIdentifier
+ io.flutter.flutter.app
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ App
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ MinimumOSVersion
+ 12.0
+
+
diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig
new file mode 100644
index 0000000..ec97fc6
--- /dev/null
+++ b/ios/Flutter/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "Generated.xcconfig"
diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig
new file mode 100644
index 0000000..c4855bf
--- /dev/null
+++ b/ios/Flutter/Release.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "Generated.xcconfig"
diff --git a/ios/Podfile b/ios/Podfile
new file mode 100644
index 0000000..c9339a0
--- /dev/null
+++ b/ios/Podfile
@@ -0,0 +1,41 @@
+# Uncomment this line to define a global platform for your project
+# platform :ios, '12.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_ios_podfile_setup
+
+target 'Runner' do
+ flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+ target 'RunnerTests' do
+ inherit! :search_paths
+ end
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_ios_build_settings(target)
+ end
+end
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..b485680
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,616 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 54;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 97C146E61CF9000F007C117D /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 97C146ED1CF9000F007C117D;
+ remoteInfo = Runner;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
+ 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 = ""; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 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 = "";
+ };
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ 331C8082294A63A400263BE5 /* RunnerTests */,
+ );
+ sourceTree = "";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ 331C8081294A63A400263BE5 /* RunnerTests.xctest */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 331C8080294A63A400263BE5 /* RunnerTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
+ buildPhases = (
+ 331C807D294A63A400263BE5 /* Sources */,
+ 331C807F294A63A400263BE5 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 331C8086294A63A400263BE5 /* PBXTargetDependency */,
+ );
+ name = RunnerTests;
+ productName = RunnerTests;
+ productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = YES;
+ LastUpgradeCheck = 1510;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 331C8080294A63A400263BE5 = {
+ CreatedOnToolsVersion = 14.0;
+ TestTargetID = 97C146ED1CF9000F007C117D;
+ };
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ LastSwiftMigration = 1100;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 97C146E51CF9000F007C117D;
+ productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 97C146ED1CF9000F007C117D /* Runner */,
+ 331C8080294A63A400263BE5 /* RunnerTests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 331C807F294A63A400263BE5 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 97C146EC1CF9000F007C117D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 331C807D294A63A400263BE5 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 97C146EA1CF9000F007C117D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 97C146ED1CF9000F007C117D /* Runner */;
+ targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C146FB1CF9000F007C117D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Profile;
+ };
+ 249021D4217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ 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.connect;
+ 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.connect.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.connect.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.connect.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 = AppIcon;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.connect;
+ 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.connect;
+ 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 */;
+}
diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..8e3ca5d
--- /dev/null
+++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift
new file mode 100644
index 0000000..6266644
--- /dev/null
+++ b/ios/Runner/AppDelegate.swift
@@ -0,0 +1,13 @@
+import Flutter
+import UIKit
+
+@main
+@objc class AppDelegate: FlutterAppDelegate {
+ override func application(
+ _ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+ ) -> Bool {
+ GeneratedPluginRegistrant.register(with: self)
+ return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+ }
+}
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..d0d98aa
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1 @@
+{"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":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"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":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@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"}}
\ No newline at end of file
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 0000000..7030e7f
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 0000000..aad73ee
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 0000000..6155983
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 0000000..9d67b03
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 0000000..07cff37
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 0000000..c1c790f
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 0000000..22ca802
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 0000000..6155983
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 0000000..baa889e
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 0000000..604603d
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png
new file mode 100644
index 0000000..97f65d5
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png
new file mode 100644
index 0000000..9e5aaea
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png
new file mode 100644
index 0000000..e09f1bc
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png
new file mode 100644
index 0000000..18c463a
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 0000000..604603d
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 0000000..29535b1
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png
new file mode 100644
index 0000000..06a81f5
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png
new file mode 100644
index 0000000..c72472a
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 0000000..a7a6f67
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 0000000..81d8674
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 0000000..9539c82
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 0000000..0bedcf2
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 0000000..89c2725
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f2e259c
--- /dev/null
+++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..f3c2851
--- /dev/null
+++ b/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
new file mode 100644
index 0000000..78131c2
--- /dev/null
+++ b/ios/Runner/Info.plist
@@ -0,0 +1,53 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ Connect
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ connect
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ CADisableMinimumFrameDurationOnPhone
+
+ UIApplicationSupportsIndirectInputEvents
+
+ NSCameraUsageDescription
+ Why? What is the purpose of the app?
+ NSMicrophoneUsageDescription
+ Explanation on why the microphone access is needed.
+
+
diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 0000000..308a2a5
--- /dev/null
+++ b/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1 @@
+#import "GeneratedPluginRegistrant.h"
diff --git a/ios/RunnerTests/RunnerTests.swift b/ios/RunnerTests/RunnerTests.swift
new file mode 100644
index 0000000..86a7c3b
--- /dev/null
+++ b/ios/RunnerTests/RunnerTests.swift
@@ -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.
+ }
+
+}
diff --git a/l10n.yaml b/l10n.yaml
new file mode 100644
index 0000000..d480072
--- /dev/null
+++ b/l10n.yaml
@@ -0,0 +1,3 @@
+arb-dir: lib/src/localization
+template-arb-file: app_en.arb
+output-localization-file: app_localizations.dart
diff --git a/lib/main.dart b/lib/main.dart
new file mode 100644
index 0000000..4bfc817
--- /dev/null
+++ b/lib/main.dart
@@ -0,0 +1,66 @@
+import 'package:camera/camera.dart';
+import 'package:connect/src/providers/api_provider.dart';
+import 'package:connect/src/providers/db_provider.dart';
+import 'package:flutter/material.dart';
+import 'package:logging/logging.dart';
+
+import 'src/app.dart';
+import 'src/settings/settings_controller.dart';
+import 'src/settings/settings_service.dart';
+
+late DbProvider dbProvider;
+late ApiProvider apiProvider;
+
+void main() async {
+ // Set up the SettingsController, which will glue user settings to multiple
+ // Flutter Widgets.
+ final settingsController = SettingsController(SettingsService());
+
+ // Load the user's preferred theme while the splash screen is displayed.
+ // This prevents a sudden theme change when the app is first displayed.
+ await settingsController.loadSettings();
+
+ // Ensure that plugin services are initialized so that `availableCameras()`
+ // can be called before `runApp()`
+ WidgetsFlutterBinding.ensureInitialized();
+
+ // check if release build or debug build
+ final kDebugMode = true;
+
+ Logger.root.level = Level.ALL; // defaults to Level.INFO
+ Logger.root.onRecord.listen((record) {
+ if (kDebugMode) {
+ // ignore: avoid_print
+ print('${record.level.name}: ${record.time}: ${record.message}');
+ }
+ });
+
+ var cameras = await availableCameras();
+
+ // Create or open the database
+ dbProvider = DbProvider();
+ await dbProvider.ready;
+
+ // Create an option to select different servers.
+ var apiUrl = "ws://api.theconnectapp.de/v0/";
+ if (kDebugMode) {
+ // Overwrite the domain in your local network so you can test the app locally
+ apiUrl = "ws://debug.theconnectapp.de:3030/v0/";
+ }
+
+ apiProvider = ApiProvider(apiUrl: apiUrl);
+
+ // TODO: Open the connection in the background so the app launch is not delayed.
+ await apiProvider.connect();
+
+ // Workmanager.executeTask((task, inputData) async {
+ // await _HomeState().manager();
+ // print('Background Services are Working!');//This is Working
+ // return true;
+ // });
+
+ // Run the app and pass in the SettingsController. The app listens to the
+ // SettingsController for changes, then passes it further down to the
+ // SettingsView.
+ runApp(MyApp(settingsController: settingsController, cameras: cameras));
+}
diff --git a/lib/src/app.dart b/lib/src/app.dart
new file mode 100644
index 0000000..1bcd956
--- /dev/null
+++ b/lib/src/app.dart
@@ -0,0 +1,85 @@
+import 'package:camera/camera.dart';
+import 'views/home_view.dart';
+import 'views/register_view.dart';
+import 'utils.dart';
+import 'package:flutter/material.dart';
+// import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+// import 'package:flutter_localizations/flutter_localizations.dart';
+
+import 'settings/settings_controller.dart';
+
+/// The Widget that configures your application.
+class MyApp extends StatefulWidget {
+ const MyApp(
+ {super.key, required this.settingsController, required this.cameras});
+
+ final SettingsController settingsController;
+ final List cameras;
+
+ @override
+ State createState() => _MyAppState();
+}
+
+class _MyAppState extends State {
+ Future _isUserCreated = isUserCreated();
+
+ @override
+ Widget build(BuildContext context) {
+ // Glue the SettingsController to the MaterialApp.
+ //
+ // The ListenableBuilder Widget listens to the SettingsController for changes.
+ // Whenever the user updates their settings, the MaterialApp is rebuilt.
+ return ListenableBuilder(
+ listenable: widget.settingsController,
+ builder: (BuildContext context, Widget? child) {
+ return MaterialApp(
+ restorationScopeId: 'app',
+ // localizationsDelegates: const [
+ // AppLocalizations.delegate,
+ // GlobalMaterialLocalizations.delegate,
+ // GlobalWidgetsLocalizations.delegate,
+ // GlobalCupertinoLocalizations.delegate,
+ // ],
+ supportedLocales: const [
+ Locale('en', ''), // English, no country code
+ ],
+ onGenerateTitle: (BuildContext context) => "Connect!",
+ theme: ThemeData(
+ colorScheme:
+ ColorScheme.fromSeed(seedColor: const Color(0xFF57CC99)),
+ inputDecorationTheme:
+ const InputDecorationTheme(border: OutlineInputBorder())),
+ darkTheme: ThemeData.dark().copyWith(
+ colorScheme: ColorScheme.fromSeed(
+ brightness: Brightness.dark, // <-- the only line added
+ seedColor: const Color(0xFF57CC99)),
+ inputDecorationTheme:
+ const InputDecorationTheme(border: OutlineInputBorder()),
+ ),
+ themeMode: widget.settingsController.themeMode,
+ home: FutureBuilder(
+ future: _isUserCreated,
+ builder: (context, snapshot) {
+ if (snapshot.hasData) {
+ return snapshot.data!
+ ? HomeView(
+ settingsController: widget.settingsController,
+ cameras: widget.cameras)
+ : RegisterView(callbackOnSuccess: () {
+ _isUserCreated = isUserCreated();
+ setState(() {});
+ });
+ } else {
+ return Center(
+ child: SizedBox(
+ width: 60,
+ height: 60,
+ child: CircularProgressIndicator(),
+ ));
+ }
+ }),
+ );
+ },
+ );
+ }
+}
diff --git a/lib/src/localization/app_en.arb b/lib/src/localization/app_en.arb
new file mode 100644
index 0000000..241eb64
--- /dev/null
+++ b/lib/src/localization/app_en.arb
@@ -0,0 +1,7 @@
+{
+ "appTitle": "connect",
+ "@@locale": "en",
+ "@appTitle": {
+ "description": "The title of the application"
+ }
+}
\ No newline at end of file
diff --git a/lib/src/model/identity_key_store_model.dart b/lib/src/model/identity_key_store_model.dart
new file mode 100644
index 0000000..313c2be
--- /dev/null
+++ b/lib/src/model/identity_key_store_model.dart
@@ -0,0 +1,34 @@
+import 'dart:typed_data';
+
+import 'package:cv/cv.dart';
+
+class DbSignalIdentityKeyStore extends CvModelBase {
+ static const tableName = "signal_identity_key_store";
+
+ static const columnDeviceId = "device_id";
+ final deviceId = CvField(columnDeviceId);
+
+ static const columnName = "name";
+ final name = CvField(columnName);
+
+ static const columnIdentityKey = "identity_key";
+ final identityKey = CvField(columnIdentityKey);
+
+ static const columnCreatedAt = "created_at";
+ final createdAt = CvField(columnCreatedAt);
+
+ static String getCreateTableString() {
+ return """
+ CREATE TABLE $tableName (
+ $columnDeviceId INTEGER NOT NULL,
+ $columnName TEXT NOT NULL,
+ $columnIdentityKey BLOB NOT NULL,
+ $columnCreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY ($columnDeviceId, $columnName)
+ )
+ """;
+ }
+
+ @override
+ List get fields => [deviceId, name, identityKey, createdAt];
+}
diff --git a/lib/src/model/model_constants.dart b/lib/src/model/model_constants.dart
new file mode 100644
index 0000000..ad5aa79
--- /dev/null
+++ b/lib/src/model/model_constants.dart
@@ -0,0 +1,5 @@
+const String dbName = 'connect.db';
+
+const int kVersion1 = 1;
+
+String tableLibSignal = 'LibSignal';
diff --git a/lib/src/model/pre_key_model.dart b/lib/src/model/pre_key_model.dart
new file mode 100644
index 0000000..2f66de4
--- /dev/null
+++ b/lib/src/model/pre_key_model.dart
@@ -0,0 +1,29 @@
+import 'dart:typed_data';
+import 'package:cv/cv.dart';
+
+class DbSignalPreKeyStore extends CvModelBase {
+ static const tableName = "signal_pre_key_store";
+
+ static const columnPreKeyId = "pre_key_id";
+ final preKeyId = CvField(columnPreKeyId);
+
+ static const columnPreKey = "pre_key";
+ final preKey = CvField(columnPreKey);
+
+ static const columnCreatedAt = "created_at";
+ final createdAt = CvField(columnCreatedAt);
+
+ static String getCreateTableString() {
+ return """
+ CREATE TABLE $tableName (
+ $columnPreKeyId INTEGER NOT NULL,
+ $columnPreKey BLOB NOT NULL,
+ $columnCreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY ($columnPreKeyId)
+ )
+ """;
+ }
+
+ @override
+ List get fields => [preKeyId, preKey, createdAt];
+}
diff --git a/lib/src/model/sender_key_store_model.dart b/lib/src/model/sender_key_store_model.dart
new file mode 100644
index 0000000..792bce8
--- /dev/null
+++ b/lib/src/model/sender_key_store_model.dart
@@ -0,0 +1,29 @@
+import 'dart:typed_data';
+import 'package:cv/cv.dart';
+
+class DbSignalSenderKeyStore extends CvModelBase {
+ static const tableName = "signal_sender_key_store";
+
+ static const columnSenderKeyName = "sender_key_name";
+ final senderKeyName = CvField(columnSenderKeyName);
+
+ static const columnSenderKey = "sender_key";
+ final senderKey = CvField(columnSenderKey);
+
+ static const columnCreatedAt = "created_at";
+ final createdAt = CvField(columnCreatedAt);
+
+ static String getCreateTableString() {
+ return """
+ CREATE TABLE $tableName (
+ $columnSenderKeyName TEXT NOT NULL,
+ $columnSenderKey BLOB NOT NULL,
+ $columnCreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY ($columnSenderKeyName)
+ )
+ """;
+ }
+
+ @override
+ List get fields => [senderKeyName, senderKey, createdAt];
+}
diff --git a/lib/src/model/session_store_model.dart b/lib/src/model/session_store_model.dart
new file mode 100644
index 0000000..8e05417
--- /dev/null
+++ b/lib/src/model/session_store_model.dart
@@ -0,0 +1,33 @@
+import 'dart:typed_data';
+import 'package:cv/cv.dart';
+
+class DbSignalSessionStore extends CvModelBase {
+ static const tableName = "signal_session_store";
+
+ static const columnDeviceId = "device_id";
+ final deviceId = CvField(columnDeviceId);
+
+ static const columnName = "name";
+ final name = CvField(columnName);
+
+ static const columnSessionRecord = "session_record";
+ final sessionRecord = CvField(columnSessionRecord);
+
+ static const columnCreatedAt = "created_at";
+ final createdAt = CvField(columnCreatedAt);
+
+ static String getCreateTableString() {
+ return """
+ CREATE TABLE $tableName (
+ $columnDeviceId INTEGER NOT NULL,
+ $columnName TEXT NOT NULL,
+ $columnSessionRecord BLOB NOT NULL,
+ $columnCreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY ($columnDeviceId, $columnName)
+ )
+ """;
+ }
+
+ @override
+ List get fields => [deviceId, name, sessionRecord, createdAt];
+}
diff --git a/lib/src/model/user.dart b/lib/src/model/user.dart
new file mode 100644
index 0000000..2655eaf
--- /dev/null
+++ b/lib/src/model/user.dart
@@ -0,0 +1,27 @@
+import 'dart:typed_data';
+import 'package:json_annotation/json_annotation.dart';
+part 'user.g.dart';
+
+class Uint8ListConverter implements JsonConverter> {
+ const Uint8ListConverter();
+ @override
+ Uint8List fromJson(List json) {
+ return Uint8List.fromList(json);
+ }
+
+ @override
+ List toJson(Uint8List object) {
+ return object.toList();
+ }
+}
+
+@JsonSerializable()
+class User {
+ const User({required this.username, required this.identityKeyPairU8List, required this.registrationId});
+ final String username;
+ final int registrationId;
+ @Uint8ListConverter()
+ final Uint8List identityKeyPairU8List;
+ factory User.fromJson(Map json) => _$UserFromJson(json);
+ Map toJson() => _$UserToJson(this);
+}
diff --git a/lib/src/model/user.g.dart b/lib/src/model/user.g.dart
new file mode 100644
index 0000000..4662cb0
--- /dev/null
+++ b/lib/src/model/user.g.dart
@@ -0,0 +1,21 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'user.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+User _$UserFromJson(Map json) => User(
+ username: json['username'] as String,
+ identityKeyPairU8List: const Uint8ListConverter()
+ .fromJson(json['identityKeyPairU8List'] as List),
+ registrationId: (json['registrationId'] as num).toInt(),
+ );
+
+Map _$UserToJson(User instance) => {
+ 'username': instance.username,
+ 'registrationId': instance.registrationId,
+ 'identityKeyPairU8List':
+ const Uint8ListConverter().toJson(instance.identityKeyPairU8List),
+ };
diff --git a/lib/src/providers/api_provider.dart b/lib/src/providers/api_provider.dart
new file mode 100644
index 0000000..4f0e282
--- /dev/null
+++ b/lib/src/providers/api_provider.dart
@@ -0,0 +1,144 @@
+import 'dart:collection';
+import 'dart:convert';
+import 'dart:ffi';
+import 'dart:math';
+
+import 'package:logging/logging.dart';
+import 'package:web_socket_channel/web_socket_channel.dart';
+
+class ApiProvider {
+ ApiProvider({required this.apiUrl});
+
+ final String apiUrl;
+ final log = Logger("connect::ApiProvider");
+ final HashMap> _callbacks = HashMap();
+
+ final HashMap?> messagesV0 = HashMap();
+
+ WebSocketChannel? _channel;
+
+ Future connect() async {
+ if (_channel != null && _channel!.closeCode != null) {
+ return true;
+ }
+ log.info("Trying to connect to the backend!");
+ try {
+ var channel = WebSocketChannel.connect(
+ Uri.parse(apiUrl),
+ );
+ _channel = channel;
+ _channel!.stream.listen(_onData);
+ await _channel!.ready;
+ log.info("Websocket is connected!");
+ return true;
+ } on WebSocketChannelException catch (e) {
+ log.shout("Error: $e");
+ return false;
+ }
+ }
+
+ void _onData(dynamic msgJson) {
+ try {
+ Map msg = jsonDecode(msgJson);
+ if (!msg.containsKey("v")) {
+ log.shout("Got invalid data from server!");
+ return;
+ }
+ if (msg["v"] != "0") {
+ log.shout("Server msg not supported by client!\n $msgJson");
+ return;
+ }
+ if (!msg.containsKey("seq") || !msg.containsKey("kind")) {
+ log.shout("Invalid server msg. No seq number or kind given: $msgJson!");
+ return;
+ }
+ if (msg["kind"] == "Response") {
+ if (messagesV0[msg["seq"]] != null) {
+ log.shout("Seq no unknown: $msgJson!");
+ return;
+ }
+ messagesV0[msg["seq"]] = msg;
+ }
+ } catch (e) {
+ log.shout("Error parsing the servers message: $e");
+ }
+ }
+
+ // void _reconnect() {}
+
+ void addNotifier(String messageType, Function callBackFunction) {
+ if (!_callbacks.containsKey(messageType)) {
+ _callbacks[messageType] = [];
+ }
+ _callbacks[messageType]!.add(callBackFunction);
+ }
+
+ // TODO: There must be a smarter move to do that :/
+ Future?> _waitForResponse(Uint64 seq) async {
+ final startTime = DateTime.now();
+
+ final timeout = Duration(seconds: 5);
+
+ while (true) {
+ if (messagesV0[seq] != null) {
+ final tmp = messagesV0[seq];
+ messagesV0.remove(seq);
+ return tmp;
+ }
+ if (DateTime.now().difference(startTime) > timeout) {
+ log.shout("Timeout for message $seq");
+ return null;
+ }
+ await Future.delayed(Duration(milliseconds: 1));
+ }
+ }
+
+ Future?> _sendRequestV0(
+ Map request) async {
+ var seq = Random().nextInt(0xFFFFFFFFFFFFFF) as Uint64;
+ while (!messagesV0.containsKey(seq)) {
+ seq = Random().nextInt(0xFFFFFFFFFFFFFF) as Uint64;
+ }
+ request["v"] = "0";
+ request["seq"] = seq;
+ final requestJson = jsonEncode(request);
+
+ // check if it is connected to the backend. if not try to reconnect.
+ if (!await connect()) {
+ return null;
+ }
+
+ // reserve seq no
+ messagesV0[seq] = null;
+ _channel!.sink.add(requestJson);
+
+ return await _waitForResponse(seq);
+ }
+
+ String? _getErrorMsg(Map msg) {
+ if (msg.containsKey("Ok")) {
+ return null;
+ }
+ if (msg.containsKey("Error")) {
+ if (msg["Error"] != null) {
+ if (msg["Error"].containsKey("AlertUser")) {
+ return msg["Error"]["AlertUser"];
+ }
+ }
+ return "There was an unknown error :/";
+ }
+ return null;
+ }
+
+ Future handshakeCheckRegister(
+ String username, String inviteCode) async {
+ final resp = await _sendRequestV0({
+ "kind": "Handshake",
+ "CheckRegister": {"username": username, "inviteCode": inviteCode}
+ });
+ if (resp == null) {
+ return "Server is not reachable!";
+ }
+ return _getErrorMsg(resp);
+ }
+}
diff --git a/lib/src/providers/db_provider.dart b/lib/src/providers/db_provider.dart
new file mode 100644
index 0000000..a07812e
--- /dev/null
+++ b/lib/src/providers/db_provider.dart
@@ -0,0 +1,83 @@
+import 'dart:async';
+import 'dart:math';
+import 'package:connect/src/model/identity_key_store_model.dart';
+import 'package:connect/src/model/model_constants.dart';
+import 'package:connect/src/model/pre_key_model.dart';
+import 'package:connect/src/model/sender_key_store_model.dart';
+import 'package:connect/src/model/session_store_model.dart';
+import 'package:connect/src/utils.dart';
+import 'package:sqflite_sqlcipher/sqflite.dart';
+
+class DbProvider {
+ Database? db;
+
+ Future openPath(String path) async {
+ // Read the password for the database from the secure storage. If there is no, then create a
+ // new cryptographically secure random password with 32 bytes and store them in the secure storage.
+ // So if someone dumps the app for example using a tool like Cellebrite including the apps
+ // content he is not able to access the databases content.
+ // (https://signal.org/blog/cellebrite-and-clickbait/)
+ //
+ // CHECK: Does this actually improve the security or is this just security through obscurity and
+ // can be removed as it does increase complexity?
+ // Current thoughts: As the database password is at some point in the memory the attacker could
+ // just dump it. Questions here: What capabilities must the attacker have to do that?
+
+ final storage = getSecureStorage();
+ var password = await storage.read(key: "sqflite_database_password");
+ if (password == null) {
+ var secureRandom = Random.secure();
+ password = "";
+ for (var i = 0; i < 32; i++) {
+ password = "$password${String.fromCharCode(secureRandom.nextInt(256))}";
+ }
+ await storage.write(key: "sqflite_database_password", value: password);
+ }
+
+ db = await openDatabase(path, password: password, version: kVersion1,
+ onCreate: (db, version) async {
+ await _createDb(db);
+ }, onUpgrade: (db, oldVersion, newVersion) async {
+ if (oldVersion < kVersion1) {
+ await _createDb(db);
+ }
+ });
+ }
+
+ Future get ready async {
+ if (db == null) {
+ await open();
+ }
+ return db;
+ }
+
+ Future _createDb(Database db) async {
+ await db.execute('DROP TABLE If EXISTS ${DbSignalSessionStore.tableName}');
+ await db.execute(DbSignalSessionStore.getCreateTableString());
+
+ await db.execute('DROP TABLE If EXISTS ${DbSignalPreKeyStore.tableName}');
+ await db.execute(DbSignalPreKeyStore.getCreateTableString());
+
+ await db
+ .execute('DROP TABLE If EXISTS ${DbSignalSenderKeyStore.tableName}');
+ await db.execute(DbSignalSenderKeyStore.getCreateTableString());
+
+ await db
+ .execute('DROP TABLE If EXISTS ${DbSignalIdentityKeyStore.tableName}');
+ await db.execute(DbSignalIdentityKeyStore.getCreateTableString());
+ }
+
+ Future open() async {
+ await openPath(await fixPath(dbName));
+ }
+
+ Future fixPath(String path) async => path;
+
+ Future close() async {
+ await db!.close();
+ }
+
+ // Future deleteDb() async {
+ // await deleteDatabase(await fixPath(dbName));
+ // }
+}
diff --git a/lib/src/settings/settings_controller.dart b/lib/src/settings/settings_controller.dart
new file mode 100644
index 0000000..e32c0df
--- /dev/null
+++ b/lib/src/settings/settings_controller.dart
@@ -0,0 +1,50 @@
+import 'package:flutter/material.dart';
+
+import 'settings_service.dart';
+
+/// A class that many Widgets can interact with to read user settings, update
+/// user settings, or listen to user settings changes.
+///
+/// Controllers glue Data Services to Flutter Widgets. The SettingsController
+/// uses the SettingsService to store and retrieve user settings.
+class SettingsController with ChangeNotifier {
+ SettingsController(this._settingsService);
+
+ // Make SettingsService a private variable so it is not used directly.
+ final SettingsService _settingsService;
+
+ // Make ThemeMode a private variable so it is not updated directly without
+ // also persisting the changes with the SettingsService.
+ late ThemeMode _themeMode;
+
+ // Allow Widgets to read the user's preferred ThemeMode.
+ ThemeMode get themeMode => _themeMode;
+
+ /// Load the user's settings from the SettingsService. It may load from a
+ /// local database or the internet. The controller only knows it can load the
+ /// settings from the service.
+ Future loadSettings() async {
+ _themeMode = await _settingsService.themeMode();
+
+ // Important! Inform listeners a change has occurred.
+ notifyListeners();
+ }
+
+ /// Update and persist the ThemeMode based on the user's selection.
+ Future updateThemeMode(ThemeMode? newThemeMode) async {
+ if (newThemeMode == null) return;
+
+ // Do not perform any work if new and old ThemeMode are identical
+ if (newThemeMode == _themeMode) return;
+
+ // Otherwise, store the new ThemeMode in memory
+ _themeMode = newThemeMode;
+
+ // Important! Inform listeners a change has occurred.
+ notifyListeners();
+
+ // Persist the changes to a local database or the internet using the
+ // SettingService.
+ await _settingsService.updateThemeMode(newThemeMode);
+ }
+}
diff --git a/lib/src/settings/settings_service.dart b/lib/src/settings/settings_service.dart
new file mode 100644
index 0000000..6f94dc3
--- /dev/null
+++ b/lib/src/settings/settings_service.dart
@@ -0,0 +1,17 @@
+import 'package:flutter/material.dart';
+
+/// A service that stores and retrieves user settings.
+///
+/// By default, this class does not persist user settings. If you'd like to
+/// persist the user settings locally, use the shared_preferences package. If
+/// you'd like to store settings on a web server, use the http package.
+class SettingsService {
+ /// Loads the User's preferred ThemeMode from local or remote storage.
+ Future themeMode() async => ThemeMode.system;
+
+ /// Persists the user's preferred ThemeMode to local or remote storage.
+ Future updateThemeMode(ThemeMode theme) async {
+ // Use the shared_preferences package to persist settings locally or the
+ // http package to persist settings over the network.
+ }
+}
diff --git a/lib/src/settings/settings_view.dart b/lib/src/settings/settings_view.dart
new file mode 100644
index 0000000..9dd36bd
--- /dev/null
+++ b/lib/src/settings/settings_view.dart
@@ -0,0 +1,49 @@
+import 'package:flutter/material.dart';
+
+import 'settings_controller.dart';
+
+/// Displays the various settings that can be customized by the user.
+///
+/// When a user changes a setting, the SettingsController is updated and
+/// Widgets that listen to the SettingsController are rebuilt.
+class SettingsView extends StatelessWidget {
+ const SettingsView({required this.controller});
+
+ final SettingsController controller;
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('Settings'),
+ ),
+ body: Padding(
+ padding: const EdgeInsets.all(16),
+ // Glue the SettingsController to the theme selection DropdownButton.
+ //
+ // When a user selects a theme from the dropdown list, the
+ // SettingsController is updated, which rebuilds the MaterialApp.
+ child: DropdownButton(
+ // Read the selected themeMode from the controller
+ value: controller.themeMode,
+ // Call the updateThemeMode method any time the user selects a theme.
+ onChanged: controller.updateThemeMode,
+ items: const [
+ DropdownMenuItem(
+ value: ThemeMode.system,
+ child: Text('System Theme'),
+ ),
+ DropdownMenuItem(
+ value: ThemeMode.light,
+ child: Text('Light Theme'),
+ ),
+ DropdownMenuItem(
+ value: ThemeMode.dark,
+ child: Text('Dark Theme'),
+ )
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/src/signal/connect_identitiy_key_store.dart b/lib/src/signal/connect_identitiy_key_store.dart
new file mode 100644
index 0000000..7a7ab9e
--- /dev/null
+++ b/lib/src/signal/connect_identitiy_key_store.dart
@@ -0,0 +1,68 @@
+import 'dart:typed_data';
+import 'package:collection/collection.dart';
+import 'package:connect/main.dart';
+import 'package:connect/src/model/identity_key_store_model.dart';
+import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
+
+bool eq(List? list1, List? list2) =>
+ ListEquality().equals(list1, list2);
+
+// make it easier to read
+typedef DB = DbSignalIdentityKeyStore;
+
+class ConnectIdentityKeyStore extends IdentityKeyStore {
+ ConnectIdentityKeyStore(this.identityKeyPair, this.localRegistrationId);
+
+ final IdentityKeyPair identityKeyPair;
+ final int localRegistrationId;
+
+ @override
+ Future getIdentity(SignalProtocolAddress address) async {
+ var dbIdentityKey = (await dbProvider.db!.query(DB.tableName,
+ columns: [DB.columnIdentityKey],
+ where: '${DB.columnDeviceId} = ? AND ${DB.columnName} = ?',
+ whereArgs: [address.getDeviceId(), address.getName()]));
+ if (dbIdentityKey.isEmpty) {
+ return null;
+ }
+ Uint8List identityKey = dbIdentityKey.first.cast()[DB.columnIdentityKey];
+ return IdentityKey.fromBytes(identityKey, 0);
+ }
+
+ @override
+ Future getIdentityKeyPair() async => identityKeyPair;
+
+ @override
+ Future getLocalRegistrationId() async => localRegistrationId;
+
+ @override
+ Future isTrustedIdentity(SignalProtocolAddress address,
+ IdentityKey? identityKey, Direction? direction) async {
+ final trusted = await getIdentity(address);
+ if (identityKey == null) {
+ return false;
+ }
+ return trusted == null || eq(trusted.serialize(), identityKey.serialize());
+ }
+
+ @override
+ Future saveIdentity(
+ SignalProtocolAddress address, IdentityKey? identityKey) async {
+ if (identityKey == null) {
+ return false;
+ }
+ if (await getIdentity(address) == null) {
+ await dbProvider.db!.insert(DB.tableName, {
+ DB.columnDeviceId: address.getDeviceId(),
+ DB.columnName: address.getName(),
+ DB.columnIdentityKey: identityKey.serialize()
+ });
+ } else {
+ await dbProvider.db!.update(
+ DB.tableName, {DB.columnIdentityKey: identityKey.serialize()},
+ where: '${DB.columnDeviceId} = ? AND ${DB.columnName} = ?',
+ whereArgs: [address.getDeviceId(), address.getName()]);
+ }
+ return true;
+ }
+}
diff --git a/lib/src/signal/connect_pre_key_store.dart b/lib/src/signal/connect_pre_key_store.dart
new file mode 100644
index 0000000..9f982b1
--- /dev/null
+++ b/lib/src/signal/connect_pre_key_store.dart
@@ -0,0 +1,49 @@
+import 'dart:typed_data';
+import 'package:connect/main.dart';
+import 'package:connect/src/model/pre_key_model.dart';
+import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
+
+typedef DB = DbSignalPreKeyStore;
+
+class ConnectPreKeyStore extends PreKeyStore {
+ @override
+ Future containsPreKey(int preKeyId) async {
+ final dbPreKey = await dbProvider.db!.query(DB.tableName,
+ columns: [DB.columnPreKey],
+ where: '${DB.columnPreKeyId} = ?',
+ whereArgs: [preKeyId]);
+ return dbPreKey.isNotEmpty;
+ }
+
+ @override
+ Future loadPreKey(int preKeyId) async {
+ final dbPreKey = await dbProvider.db!.query(DB.tableName,
+ columns: [DB.columnPreKey],
+ where: '${DB.columnPreKeyId} = ?',
+ whereArgs: [preKeyId]);
+ if (dbPreKey.isEmpty) {
+ throw InvalidKeyIdException('No such preKey record! - $preKeyId');
+ }
+ Uint8List preKey = dbPreKey.first.cast()[DB.columnPreKey];
+ return PreKeyRecord.fromBuffer(preKey);
+ }
+
+ @override
+ Future removePreKey(int preKeyId) async {
+ await dbProvider.db!.delete(DB.tableName,
+ where: '${DB.columnPreKeyId} = ?',
+ whereArgs: [DB.columnPreKeyId]);
+ }
+
+ @override
+ Future storePreKey(int preKeyId, PreKeyRecord record) async {
+ if (await containsPreKey(preKeyId)) {
+ await dbProvider.db!.insert(DB.tableName,
+ {DB.columnPreKeyId: preKeyId, DB.columnPreKey: record.serialize()});
+ } else {
+ await dbProvider.db!.update(
+ DB.tableName, {DB.columnPreKey: record.serialize()},
+ where: '${DB.columnPreKeyId} = ?', whereArgs: [preKeyId]);
+ }
+ }
+}
diff --git a/lib/src/signal/connect_sender_key_store.dart b/lib/src/signal/connect_sender_key_store.dart
new file mode 100644
index 0000000..9671f78
--- /dev/null
+++ b/lib/src/signal/connect_sender_key_store.dart
@@ -0,0 +1,31 @@
+import 'dart:typed_data';
+import 'package:connect/main.dart';
+import 'package:connect/src/model/sender_key_store_model.dart';
+import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
+
+typedef DB = DbSignalSenderKeyStore;
+
+class ConnectSenderKeyStore extends SenderKeyStore {
+ @override
+ Future loadSenderKey(SenderKeyName senderKeyName) async {
+ final dbSenderKey = await dbProvider.db!.query(DB.tableName,
+ columns: [DB.columnSenderKey],
+ where: '${DB.columnSenderKeyName} = ?',
+ whereArgs: [senderKeyName.serialize()]);
+ if (dbSenderKey.isEmpty) {
+ throw InvalidKeyIdException(
+ 'No such sender key record! - $senderKeyName');
+ }
+ Uint8List preKey = dbSenderKey.first.cast()[DB.columnSenderKey];
+ return SenderKeyRecord.fromSerialized(preKey);
+ }
+
+ @override
+ Future storeSenderKey(
+ SenderKeyName senderKeyName, SenderKeyRecord record) async {
+ await dbProvider.db!.insert(DB.tableName, {
+ DB.columnSenderKeyName: senderKeyName.serialize(),
+ DB.columnSenderKey: record.serialize()
+ });
+ }
+}
diff --git a/lib/src/signal/connect_session_store.dart b/lib/src/signal/connect_session_store.dart
new file mode 100644
index 0000000..1ece6ee
--- /dev/null
+++ b/lib/src/signal/connect_session_store.dart
@@ -0,0 +1,73 @@
+import 'dart:typed_data';
+import 'package:connect/main.dart';
+import 'package:connect/src/model/session_store_model.dart';
+import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
+
+// make it easier to read
+typedef DB = DbSignalSessionStore;
+
+class ConnectSessionStore extends SessionStore {
+ ConnectSessionStore();
+
+ @override
+ Future containsSession(SignalProtocolAddress address) async {
+ var list = (await dbProvider.db!.query(DB.tableName,
+ columns: [],
+ where: '${DB.columnDeviceId} = ? AND ${DB.columnName} = ?',
+ whereArgs: [address.getDeviceId(), address.getName()]));
+ return list.isNotEmpty;
+ }
+
+ @override
+ Future deleteAllSessions(String name) async {
+ await dbProvider.db!.delete(DB.tableName,
+ where: '${DB.columnName} = ?', whereArgs: [name]);
+ }
+
+ @override
+ Future deleteSession(SignalProtocolAddress address) async {
+ await dbProvider.db!.delete(DB.tableName,
+ where: '${DB.columnDeviceId} = ? AND ${DB.columnName} = ?',
+ whereArgs: [address.getDeviceId(), address.getName()]);
+ }
+
+ @override
+ Future> getSubDeviceSessions(String name) async {
+ var deviceIds = (await dbProvider.db!.query(DB.tableName,
+ columns: [DB.columnDeviceId],
+ where: '${DB.columnDeviceId} != 1 AND ${DB.columnName} = ?',
+ whereArgs: [name]));
+ return deviceIds.cast();
+ }
+
+ @override
+ Future loadSession(SignalProtocolAddress address) async {
+ var dbSession = (await dbProvider.db!.query(DB.tableName,
+ columns: [DB.columnSessionRecord],
+ where: '${DB.columnDeviceId} = ? AND ${DB.columnName} = ?',
+ whereArgs: [address.getDeviceId(), address.getName()]));
+
+ if (dbSession.isEmpty) {
+ return SessionRecord();
+ }
+ Uint8List session = dbSession.first.cast()[DB.columnSessionRecord];
+ return SessionRecord.fromSerialized(session);
+ }
+
+ @override
+ Future storeSession(
+ SignalProtocolAddress address, SessionRecord record) async {
+ if (await containsSession(address)) {
+ await dbProvider.db!.insert(DB.tableName, {
+ DB.columnDeviceId: address.getDeviceId(),
+ DB.columnName: address.getName(),
+ DB.columnSessionRecord: record.serialize()
+ });
+ } else {
+ await dbProvider.db!.update(
+ DB.tableName, {DB.columnSessionRecord: record.serialize()},
+ where: '${DB.columnDeviceId} = ? AND ${DB.columnName} = ?',
+ whereArgs: [address.getDeviceId(), address.getName()]);
+ }
+ }
+}
diff --git a/lib/src/signal/connect_signal_protocol_store.dart b/lib/src/signal/connect_signal_protocol_store.dart
new file mode 100644
index 0000000..07980a6
--- /dev/null
+++ b/lib/src/signal/connect_signal_protocol_store.dart
@@ -0,0 +1,110 @@
+import 'package:connect/src/signal/connect_identitiy_key_store.dart';
+import 'package:connect/src/signal/connect_pre_key_store.dart';
+import 'package:connect/src/signal/connect_session_store.dart';
+import 'package:connect/src/signal/connect_signed_pre_key_store.dart';
+import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
+
+class ConnectSignalProtocolStore implements SignalProtocolStore {
+ ConnectSignalProtocolStore(
+ IdentityKeyPair identityKeyPair, int registrationId) {
+ _identityKeyStore =
+ ConnectIdentityKeyStore(identityKeyPair, registrationId);
+ }
+
+ final preKeyStore = ConnectPreKeyStore();
+ final sessionStore = ConnectSessionStore();
+ final signedPreKeyStore = ConnectSignedPreKeyStore();
+
+ late IdentityKeyStore _identityKeyStore;
+
+ @override
+ Future getIdentityKeyPair() async =>
+ _identityKeyStore.getIdentityKeyPair();
+
+ @override
+ Future getLocalRegistrationId() async =>
+ _identityKeyStore.getLocalRegistrationId();
+
+ @override
+ Future saveIdentity(
+ SignalProtocolAddress address, IdentityKey? identityKey) async =>
+ _identityKeyStore.saveIdentity(address, identityKey);
+
+ @override
+ Future isTrustedIdentity(SignalProtocolAddress address,
+ IdentityKey? identityKey, Direction direction) async =>
+ _identityKeyStore.isTrustedIdentity(address, identityKey, direction);
+
+ @override
+ Future getIdentity(SignalProtocolAddress address) async =>
+ _identityKeyStore.getIdentity(address);
+
+ @override
+ Future loadPreKey(int preKeyId) async =>
+ preKeyStore.loadPreKey(preKeyId);
+
+ @override
+ Future storePreKey(int preKeyId, PreKeyRecord record) async {
+ await preKeyStore.storePreKey(preKeyId, record);
+ }
+
+ @override
+ Future containsPreKey(int preKeyId) async =>
+ preKeyStore.containsPreKey(preKeyId);
+
+ @override
+ Future removePreKey(int preKeyId) async {
+ await preKeyStore.removePreKey(preKeyId);
+ }
+
+ @override
+ Future loadSession(SignalProtocolAddress address) async =>
+ sessionStore.loadSession(address);
+
+ @override
+ Future> getSubDeviceSessions(String name) async =>
+ sessionStore.getSubDeviceSessions(name);
+
+ @override
+ Future storeSession(
+ SignalProtocolAddress address, SessionRecord record) async {
+ await sessionStore.storeSession(address, record);
+ }
+
+ @override
+ Future containsSession(SignalProtocolAddress address) async =>
+ sessionStore.containsSession(address);
+
+ @override
+ Future deleteSession(SignalProtocolAddress address) async {
+ await sessionStore.deleteSession(address);
+ }
+
+ @override
+ Future deleteAllSessions(String name) async {
+ await sessionStore.deleteAllSessions(name);
+ }
+
+ @override
+ Future loadSignedPreKey(int signedPreKeyId) async =>
+ signedPreKeyStore.loadSignedPreKey(signedPreKeyId);
+
+ @override
+ Future> loadSignedPreKeys() async =>
+ signedPreKeyStore.loadSignedPreKeys();
+
+ @override
+ Future storeSignedPreKey(
+ int signedPreKeyId, SignedPreKeyRecord record) async {
+ await signedPreKeyStore.storeSignedPreKey(signedPreKeyId, record);
+ }
+
+ @override
+ Future containsSignedPreKey(int signedPreKeyId) async =>
+ signedPreKeyStore.containsSignedPreKey(signedPreKeyId);
+
+ @override
+ Future removeSignedPreKey(int signedPreKeyId) async {
+ await signedPreKeyStore.removeSignedPreKey(signedPreKeyId);
+ }
+}
diff --git a/lib/src/signal/connect_signed_pre_key_store.dart b/lib/src/signal/connect_signed_pre_key_store.dart
new file mode 100644
index 0000000..4463612
--- /dev/null
+++ b/lib/src/signal/connect_signed_pre_key_store.dart
@@ -0,0 +1,69 @@
+import 'dart:collection';
+import 'dart:convert';
+import 'dart:typed_data';
+
+import 'package:connect/src/utils.dart';
+import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
+
+class ConnectSignedPreKeyStore extends SignedPreKeyStore {
+ // final store = HashMap();
+
+ Future> getStore() async {
+ final storage = getSecureStorage();
+ final storeSerialized = await storage.read(key: "signed_pre_key_store");
+ var store = HashMap();
+ if (storeSerialized == null) {
+ return store;
+ }
+ final storeHashMap = json.decode(storeSerialized);
+ // for (final item in storeHashMap) {
+ // store[item[0]] = Uint8List.fromList(item[1].codeUnits);
+ // }
+ return storeHashMap;
+ }
+
+ Future safeStore(HashMap store) async {
+ final storage = getSecureStorage();
+ final storeSerialized = json.encode(store);
+ await storage.write(key: "signed_pre_key_store", value: storeSerialized);
+ }
+
+ @override
+ Future loadSignedPreKey(int signedPreKeyId) async {
+ final store = await getStore();
+ if (!store.containsKey(signedPreKeyId)) {
+ throw InvalidKeyIdException(
+ 'No such signedprekeyrecord! $signedPreKeyId');
+ }
+ return SignedPreKeyRecord.fromSerialized(store[signedPreKeyId]!);
+ }
+
+ @override
+ Future> loadSignedPreKeys() async {
+ final store = await getStore();
+ final results = [];
+ for (final serialized in store.values) {
+ results.add(SignedPreKeyRecord.fromSerialized(serialized));
+ }
+ return results;
+ }
+
+ @override
+ Future storeSignedPreKey(
+ int signedPreKeyId, SignedPreKeyRecord record) async {
+ final store = await getStore();
+ store[signedPreKeyId] = record.serialize();
+ await safeStore(store);
+ }
+
+ @override
+ Future containsSignedPreKey(int signedPreKeyId) async =>
+ (await getStore()).containsKey(signedPreKeyId);
+
+ @override
+ Future removeSignedPreKey(int signedPreKeyId) async {
+ final store = await getStore();
+ store.remove(signedPreKeyId);
+ await safeStore(store);
+ }
+}
diff --git a/lib/src/signal/signal_helper.dart b/lib/src/signal/signal_helper.dart
new file mode 100644
index 0000000..9372443
--- /dev/null
+++ b/lib/src/signal/signal_helper.dart
@@ -0,0 +1,186 @@
+import 'dart:convert';
+import 'dart:developer';
+import 'dart:typed_data';
+import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
+
+import 'connect_sender_key_store.dart';
+import 'connect_signal_protocol_store.dart';
+
+class SignalDataModel {
+ String name;
+ ConnectSignalProtocolStore signalStore;
+ ConnectSenderKeyStore senderKeyStore;
+ Map serverKeyBundle;
+ SignalDataModel({
+ required this.name,
+ required this.serverKeyBundle,
+ required this.senderKeyStore,
+ required this.signalStore,
+ });
+ // Session validation
+ Future generateSessionFingerPrint(String target) async {
+ try {
+ IdentityKey? targetIdentity = await signalStore.getIdentity(
+ SignalProtocolAddress(target, SignalHelper.defaultDeviceId));
+ if (targetIdentity != null) {
+ final generator = NumericFingerprintGenerator(5200);
+ final localFingerprint = generator.createFor(
+ 1,
+ Uint8List.fromList(utf8.encode(name)),
+ (await signalStore.getIdentityKeyPair()).getPublicKey(),
+ Uint8List.fromList(utf8.encode(target)),
+ targetIdentity,
+ );
+ return localFingerprint;
+ }
+ return null;
+ } catch (e) {
+ return null;
+ }
+ }
+
+ // one to one implementation
+ Future buildSession(
+ String target,
+ Map remoteBundle,
+ ) async {
+ SignalProtocolAddress targetAddress =
+ SignalProtocolAddress(target, SignalHelper.defaultDeviceId);
+ SessionBuilder sessionBuilder =
+ SessionBuilder.fromSignalStore(signalStore, targetAddress);
+ PreKeyBundle temp = preKeyBundleFromJson(remoteBundle);
+ await sessionBuilder.processPreKeyBundle(temp);
+ }
+
+ PreKeyBundle preKeyBundleFromJson(Map remoteBundle) {
+ // One time pre key calculation
+ List tempPreKeys = remoteBundle["preKeys"];
+ ECPublicKey? tempPrePublicKey;
+ int? tempPreKeyId;
+ if (tempPreKeys.isNotEmpty) {
+ tempPrePublicKey = Curve.decodePoint(
+ DjbECPublicKey(base64Decode(tempPreKeys.first['key'])).serialize(),
+ 1);
+ tempPreKeyId = remoteBundle["preKeys"].first['id'];
+ }
+ // Signed pre key calculation
+ int tempSignedPreKeyId = remoteBundle["signedPreKey"]['id'];
+ Map? tempSignedPreKey = remoteBundle["signedPreKey"];
+ ECPublicKey? tempSignedPreKeyPublic;
+ Uint8List? tempSignedPreKeySignature;
+ if (tempSignedPreKey != null) {
+ tempSignedPreKeyPublic = Curve.decodePoint(
+ DjbECPublicKey(base64Decode(remoteBundle["signedPreKey"]['key']))
+ .serialize(),
+ 1);
+ tempSignedPreKeySignature =
+ base64Decode(remoteBundle["signedPreKey"]['signature']);
+ }
+ // Identity key calculation
+ IdentityKey tempIdentityKey = IdentityKey(Curve.decodePoint(
+ DjbECPublicKey(base64Decode(remoteBundle["identityKey"])).serialize(),
+ 1));
+ return PreKeyBundle(
+ remoteBundle['registrationId'],
+ 1,
+ tempPreKeyId,
+ tempPrePublicKey,
+ tempSignedPreKeyId,
+ tempSignedPreKeyPublic,
+ tempSignedPreKeySignature,
+ tempIdentityKey,
+ );
+ }
+
+ Future getEncryptedText(String text, String target) async {
+ try {
+ SessionCipher session = SessionCipher.fromStore(signalStore,
+ SignalProtocolAddress(target, SignalHelper.defaultDeviceId));
+ final ciphertext =
+ await session.encrypt(Uint8List.fromList(utf8.encode(text)));
+ Map data = {
+ "msg": base64Encode(ciphertext.serialize()),
+ "type": ciphertext.getType(),
+ };
+ return jsonEncode(data);
+ } catch (e) {
+ log(e.toString());
+ return null;
+ }
+ }
+
+ Future getDecryptedText(String source, String msg) async {
+ try {
+ SessionCipher session = SessionCipher.fromStore(signalStore,
+ SignalProtocolAddress(source, SignalHelper.defaultDeviceId));
+ Map data = jsonDecode(msg);
+ if (data["type"] == CiphertextMessage.prekeyType) {
+ PreKeySignalMessage pre =
+ PreKeySignalMessage(base64Decode(data["msg"]));
+ Uint8List plaintext = await session.decrypt(pre);
+ String dectext = utf8.decode(plaintext);
+ return dectext;
+ } else if (data["type"] == CiphertextMessage.whisperType) {
+ SignalMessage signalMsg =
+ SignalMessage.fromSerialized(base64Decode(data["msg"]));
+ Uint8List plaintext = await session.decryptFromSignal(signalMsg);
+ String dectext = utf8.decode(plaintext);
+ return dectext;
+ } else {
+ return null;
+ }
+ } catch (e) {
+ log(e.toString());
+ return null;
+ }
+ }
+}
+
+class SignalHelper {
+ static const int defaultDeviceId = 1;
+
+ static Future createNewSignalIdentity(String name) async {
+ final identityKeyPair = generateIdentityKeyPair();
+ final registrationId = generateRegistrationId(true);
+
+ ConnectSignalProtocolStore signalStore =
+ ConnectSignalProtocolStore(identityKeyPair, registrationId);
+
+ final preKeys = generatePreKeys(0, 100);
+ final signedPreKey =
+ generateSignedPreKey(identityKeyPair, SignalHelper.defaultDeviceId);
+
+ for (final p in preKeys) {
+ await signalStore.preKeyStore.storePreKey(p.id, p);
+ }
+
+ await signalStore.signedPreKeyStore
+ .storeSignedPreKey(signedPreKey.id, signedPreKey);
+
+ Map req = {};
+ req['registrationId'] = registrationId;
+ req['identityKey'] =
+ base64Encode(identityKeyPair.getPublicKey().serialize());
+ req['signedPreKey'] = {
+ 'id': signedPreKey.id,
+ 'signature': base64Encode(signedPreKey.signature),
+ 'key': base64Encode(signedPreKey.getKeyPair().publicKey.serialize()),
+ };
+ List pKeysList = [];
+ for (PreKeyRecord pKey in preKeys) {
+ Map pKeys = {};
+ pKeys['id'] = pKey.id;
+ pKeys['key'] = base64Encode(pKey.getKeyPair().publicKey.serialize());
+ pKeysList.add(pKeys);
+ }
+ req['preKeys'] = pKeysList;
+
+ SignalDataModel sm = SignalDataModel(
+ name: name,
+ serverKeyBundle: req,
+ senderKeyStore: ConnectSenderKeyStore(),
+ signalStore: signalStore,
+ );
+ return sm;
+ }
+}
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
new file mode 100644
index 0000000..0df44a2
--- /dev/null
+++ b/lib/src/utils.dart
@@ -0,0 +1,68 @@
+import 'dart:convert';
+import 'package:connect/main.dart';
+import 'package:connect/src/signal/signal_helper.dart';
+import 'package:flutter_secure_storage/flutter_secure_storage.dart';
+import 'model/user.dart';
+
+// Just a helper function to get the secure storage
+FlutterSecureStorage getSecureStorage() {
+ AndroidOptions _getAndroidOptions() => const AndroidOptions(
+ encryptedSharedPreferences: true,
+ );
+ return FlutterSecureStorage(aOptions: _getAndroidOptions());
+}
+
+Future isUserCreated() async {
+ User? user = await getUser();
+ if (user == null) {
+ return false;
+ }
+ return true;
+}
+
+Future getUser() async {
+ final storage = getSecureStorage();
+ String? userJson = await storage.read(key: "user");
+ if (userJson == null) {
+ return null;
+ }
+ try {
+// Decode the JSON string
+ final userMap = jsonDecode(userJson) as Map;
+ final user = User.fromJson(userMap);
+ return user;
+ } catch (_) {
+ return null;
+ }
+}
+
+Future deleteLocalUserData() async {
+ final storage = getSecureStorage();
+ await storage.delete(key: "user");
+ return true;
+}
+
+Future createNewUser(String username, String inviteCode) async {
+ final storage = getSecureStorage();
+
+ // TODO: API call to server to check username and inviteCode
+ final check = await apiProvider.handshakeCheckRegister(username, inviteCode);
+ if (check != null) {
+ return check;
+ }
+
+ final signalModel = await SignalHelper.createNewSignalIdentity(username);
+
+ final identityKeyPair = await signalModel.signalStore.getIdentityKeyPair();
+
+ final user = User(
+ username: username,
+ identityKeyPairU8List: identityKeyPair.serialize(),
+ registrationId: await signalModel.signalStore.getLocalRegistrationId());
+
+ // TODO: API call to register user
+
+ await storage.write(key: "user", value: jsonEncode(user));
+
+ return null;
+}
diff --git a/lib/src/views/add_new_user_view.dart b/lib/src/views/add_new_user_view.dart
new file mode 100644
index 0000000..916ddc0
--- /dev/null
+++ b/lib/src/views/add_new_user_view.dart
@@ -0,0 +1,148 @@
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:mobile_scanner/mobile_scanner.dart';
+
+class AddNewUserView extends StatefulWidget {
+ const AddNewUserView({super.key});
+
+ @override
+ State createState() => _AddNewUserViewState();
+}
+
+class _AddNewUserViewState extends State
+ with WidgetsBindingObserver {
+ Barcode? _barcode;
+ final MobileScannerController controller = MobileScannerController(
+ // required options for the scanner
+ );
+ void _handleBarcode(BarcodeCapture barcodes) {
+ if (mounted) {
+ setState(() {
+ _barcode = barcodes.barcodes.firstOrNull;
+ });
+ }
+ }
+
+ @override
+ void didChangeAppLifecycleState(AppLifecycleState state) {
+ // If the controller is not ready, do not try to start or stop it.
+ // Permission dialogs can trigger lifecycle changes before the controller is ready.
+ if (!controller.value.hasCameraPermission) {
+ return;
+ }
+
+ switch (state) {
+ case AppLifecycleState.detached:
+ case AppLifecycleState.hidden:
+ case AppLifecycleState.paused:
+ return;
+ case AppLifecycleState.resumed:
+ // Restart the scanner when the app is resumed.
+ // Don't forget to resume listening to the barcode events.
+ _subscription = controller.barcodes.listen(_handleBarcode);
+
+ unawaited(controller.start());
+ case AppLifecycleState.inactive:
+ // Stop the scanner when the app is paused.
+ // Also stop the barcode events subscription.
+ unawaited(_subscription?.cancel());
+ _subscription = null;
+ unawaited(controller.stop());
+ }
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ // Start listening to lifecycle changes.
+ WidgetsBinding.instance.addObserver(this);
+
+ // Start listening to the barcode events.
+ _subscription = controller.barcodes.listen(_handleBarcode);
+
+ // Finally, start the scanner itself.
+ unawaited(controller.start());
+ }
+
+ @override
+ Future dispose() async {
+ // Stop listening to lifecycle changes.
+ WidgetsBinding.instance.removeObserver(this);
+ // Stop listening to the barcode events.
+ unawaited(_subscription?.cancel());
+ _subscription = null;
+ // Dispose the widget itself.
+ super.dispose();
+ // Finally, dispose of the controller.
+ await controller.dispose();
+ }
+
+ Widget _buildBarcode(Barcode? value) {
+ if (value == null) {
+ return Text.rich(
+ TextSpan(
+ children: [
+ TextSpan(
+ text:
+ "One of Connect's most important goals is data confidentiality. The Signal Protocol is therefore used to encrypt the data. The security of the Signal Protocol is based on the fact that the Signal identity is verified in a secure way (you stand next to the person and compare the public key). ",
+ style: TextStyle(color: Colors.white),
+ ),
+ TextSpan(
+ text:
+ "To enforce this, the only way to add a new user is to scan their QR code.",
+ style: TextStyle(
+ color: Colors.white,
+ fontWeight: FontWeight.bold), // Bold style
+ ),
+ ],
+ ),
+ overflow: TextOverflow.fade,
+ textAlign: TextAlign.center,
+ );
+ }
+
+ return Text(
+ value.displayValue ?? 'No display value.',
+ overflow: TextOverflow.fade,
+ style: const TextStyle(color: Colors.white),
+ );
+ }
+
+ StreamSubscription? _subscription;
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text('Add new user'),
+ ),
+ body: Stack(
+ children: [
+ MobileScanner(
+ onDetect: _handleBarcode,
+ ),
+ Align(
+ alignment: Alignment.bottomCenter,
+ child: Padding(
+ padding: EdgeInsets.all(20),
+ child: Container(
+ decoration: BoxDecoration(
+ color: Colors.black.withOpacity(0.4),
+ borderRadius: BorderRadius.circular(9),
+ ),
+ alignment: Alignment.bottomCenter,
+ height: 200,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ Expanded(child: Center(child: _buildBarcode(_barcode))),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/src/views/camera_editor_view.dart b/lib/src/views/camera_editor_view.dart
new file mode 100644
index 0000000..5571da5
--- /dev/null
+++ b/lib/src/views/camera_editor_view.dart
@@ -0,0 +1,288 @@
+// Dart imports:
+import 'dart:io';
+import 'dart:math';
+
+// Flutter imports:
+// import 'package:example/widgets/demo_build_stickers.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+// Package imports:
+import 'package:google_fonts/google_fonts.dart';
+import 'package:pro_image_editor/designs/frosted_glass/frosted_glass.dart';
+import 'package:pro_image_editor/pro_image_editor.dart';
+
+// Project imports:
+// import '../../utils/example_helper.dart';
+
+/// The frosted glass design example
+class CameraEditorView extends StatefulWidget {
+ /// Creates a new [CameraEditorView] widget.
+ const CameraEditorView({
+ super.key,
+ required this.imagePath,
+ });
+
+ /// The URL of the image to display.
+ final String imagePath;
+
+ @override
+ State createState() => _CameraEditorViewState();
+}
+
+class _CameraEditorViewState extends State {
+ final bool _useMaterialDesign =
+ platformDesignMode == ImageEditorDesignModeE.material;
+
+ /// Opens the sticker/emoji editor.
+ void _openStickerEditor(ProImageEditorState editor) async {
+ Layer? layer = await editor.openPage(FrostedGlassStickerPage(
+ configs: editor.configs,
+ callbacks: editor.callbacks,
+ ));
+
+ if (layer == null || !mounted) return;
+
+ if (layer.runtimeType != StickerLayerData) {
+ layer.scale = editor.configs.emojiEditorConfigs.initScale;
+ }
+
+ editor.addLayer(layer);
+ }
+
+ /// Calculates the number of columns for the EmojiPicker.
+ int _calculateEmojiColumns(BoxConstraints constraints) =>
+ max(1, (_useMaterialDesign ? 6 : 10) / 400 * constraints.maxWidth - 1)
+ .floor();
+
+ @override
+ Widget build(BuildContext context) {
+ return LayoutBuilder(builder: (context, constraints) {
+ return ProImageEditor.file(
+ File(widget.imagePath),
+ // key: editorKey,
+ callbacks: ProImageEditorCallbacks(
+ onImageEditingStarted: () {},
+ onImageEditingComplete: (image) async {
+ Navigator.pop(context);
+ },
+ onCloseEditor: () {},
+ stickerEditorCallbacks: StickerEditorCallbacks(
+ onSearchChanged: (value) {
+ /// Filter your stickers
+ debugPrint(value);
+ },
+ )),
+ configs: ProImageEditorConfigs(
+ designMode: platformDesignMode,
+ theme: Theme.of(context).copyWith(
+ iconTheme:
+ Theme.of(context).iconTheme.copyWith(color: Colors.white)),
+ icons: const ImageEditorIcons(
+ paintingEditor: IconsPaintingEditor(
+ bottomNavBar: Icons.edit,
+ ),
+ ),
+ imageEditorTheme: ImageEditorTheme(
+ textEditor: TextEditorTheme(
+ textFieldMargin: const EdgeInsets.only(top: kToolbarHeight),
+ bottomBarBackgroundColor: Colors.transparent,
+ bottomBarMainAxisAlignment: !_useMaterialDesign
+ ? MainAxisAlignment.spaceEvenly
+ : MainAxisAlignment.start),
+ paintingEditor: const PaintingEditorTheme(
+ initialStrokeWidth: 5,
+ ),
+ // filterEditor: const FilterEditorTheme(
+ // filterListSpacing: 7,
+ // filterListMargin: EdgeInsets.fromLTRB(8, 15, 8, 10),
+ // ),
+ emojiEditor: EmojiEditorTheme(
+ backgroundColor: Colors.transparent,
+ textStyle: DefaultEmojiTextStyle.copyWith(
+ fontFamily:
+ !kIsWeb ? null : GoogleFonts.notoColorEmoji().fontFamily,
+ fontSize: _useMaterialDesign ? 48 : 30,
+ ),
+ emojiViewConfig: EmojiViewConfig(
+ gridPadding: EdgeInsets.zero,
+ horizontalSpacing: 0,
+ verticalSpacing: 0,
+ recentsLimit: 40,
+ backgroundColor: Colors.transparent,
+ buttonMode: !_useMaterialDesign
+ ? ButtonMode.CUPERTINO
+ : ButtonMode.MATERIAL,
+ loadingIndicator:
+ const Center(child: CircularProgressIndicator()),
+ columns: _calculateEmojiColumns(constraints),
+ emojiSizeMax: !_useMaterialDesign ? 32 : 64,
+ replaceEmojiOnLimitExceed: false,
+ ),
+ bottomActionBarConfig:
+ const BottomActionBarConfig(enabled: false),
+ ),
+ layerInteraction: const ThemeLayerInteraction(
+ removeAreaBackgroundInactive: Colors.black12,
+ ),
+ ),
+ textEditorConfigs: TextEditorConfigs(
+ customTextStyles: [
+ GoogleFonts.roboto(),
+ GoogleFonts.averiaLibre(),
+ GoogleFonts.lato(),
+ GoogleFonts.comicNeue(),
+ GoogleFonts.actor(),
+ GoogleFonts.odorMeanChey(),
+ GoogleFonts.nabla(),
+ ],
+ ),
+ emojiEditorConfigs: const EmojiEditorConfigs(
+ checkPlatformCompatibility: !kIsWeb,
+ ),
+ customWidgets: ImageEditorCustomWidgets(
+ loadingDialog: (message, configs) => FrostedGlassLoadingDialog(
+ message: message,
+ configs: configs,
+ ),
+ mainEditor: CustomWidgetsMainEditor(
+ closeWarningDialog: (editor) async {
+ if (!context.mounted) return false;
+ return await showDialog(
+ context: context,
+ builder: (BuildContext context) =>
+ FrostedGlassCloseDialog(editor: editor),
+ ) ??
+ false;
+ },
+ appBar: (editor, rebuildStream) => null,
+ bottomBar: (editor, rebuildStream, key) => null,
+ bodyItems: _buildMainBodyWidgets,
+ ),
+ paintEditor: CustomWidgetsPaintEditor(
+ appBar: (paintEditor, rebuildStream) => null,
+ bottomBar: (paintEditor, rebuildStream) => null,
+ colorPicker:
+ (paintEditor, rebuildStream, currentColor, setColor) => null,
+ bodyItems: _buildPaintEditorBody,
+ ),
+ textEditor: CustomWidgetsTextEditor(
+ appBar: (textEditor, rebuildStream) => null,
+ colorPicker:
+ (textEditor, rebuildStream, currentColor, setColor) => null,
+ bottomBar: (textEditor, rebuildStream) => null,
+ bodyItems: _buildTextEditorBody,
+ ),
+ cropRotateEditor: CustomWidgetsCropRotateEditor(),
+ ),
+ ),
+ );
+ });
+ }
+
+ List _buildMainBodyWidgets(
+ ProImageEditorState editor,
+ Stream rebuildStream,
+ ) {
+ return [
+ if (editor.selectedLayerIndex < 0)
+ ReactiveCustomWidget(
+ stream: rebuildStream,
+ builder: (_) => FrostedGlassActionBar(
+ editor: editor,
+ openStickerEditor: () => _openStickerEditor(editor),
+ ),
+ ),
+ ];
+ }
+
+ List _buildPaintEditorBody(
+ PaintingEditorState paintEditor,
+ Stream rebuildStream,
+ ) {
+ return [
+ /// Appbar
+ ReactiveCustomWidget(
+ stream: rebuildStream,
+ builder: (_) {
+ return paintEditor.activePainting
+ ? const SizedBox.shrink()
+ : FrostedGlassPaintingAppbar(paintEditor: paintEditor);
+ },
+ ),
+
+ /// Bottombar
+ ReactiveCustomWidget(
+ stream: rebuildStream,
+ builder: (_) => FrostedGlassPaintBottomBar(paintEditor: paintEditor),
+ ),
+ ];
+ }
+
+ List _buildTuneEditorBody(
+ TuneEditorState tuneEditor,
+ Stream rebuildStream,
+ ) {
+ return [
+ /// Appbar
+ ReactiveCustomWidget(
+ stream: rebuildStream,
+ builder: (_) {
+ return FrostedGlassTuneAppbar(tuneEditor: tuneEditor);
+ },
+ ),
+
+ /// Bottombar
+ ReactiveCustomWidget(
+ stream: rebuildStream,
+ builder: (_) => FrostedGlassTuneBottombar(tuneEditor: tuneEditor),
+ ),
+ ];
+ }
+
+ List _buildTextEditorBody(
+ TextEditorState textEditor,
+ Stream rebuildStream,
+ ) {
+ return [
+ /// Background
+ ReactiveCustomWidget(
+ stream: rebuildStream,
+ builder: (_) => const FrostedGlassEffect(
+ radius: BorderRadius.zero,
+ child: SizedBox.expand(),
+ ),
+ ),
+
+ /// Slider Text size
+ ReactiveCustomWidget(
+ stream: rebuildStream,
+ builder: (_) => Padding(
+ padding: const EdgeInsets.only(top: kToolbarHeight),
+ child: FrostedGlassTextSizeSlider(textEditor: textEditor),
+ ),
+ ),
+
+ /// Appbar
+ ReactiveCustomWidget(
+ stream: rebuildStream,
+ builder: (_) {
+ return FrostedGlassTextAppbar(textEditor: textEditor);
+ },
+ ),
+
+ /// Bottombar
+ ReactiveCustomWidget(
+ stream: rebuildStream,
+ builder: (_) => FrostedGlassTextBottomBar(
+ configs: textEditor.configs,
+ initColor: textEditor.primaryColor,
+ onColorChanged: (color) {
+ textEditor.primaryColor = color;
+ },
+ selectedStyle: textEditor.selectedTextStyle,
+ onFontChange: textEditor.setTextStyle,
+ ),
+ ),
+ ];
+ }
+}
diff --git a/lib/src/views/camera_preview_view.dart b/lib/src/views/camera_preview_view.dart
new file mode 100644
index 0000000..a9a9855
--- /dev/null
+++ b/lib/src/views/camera_preview_view.dart
@@ -0,0 +1,357 @@
+import 'package:camera/camera.dart';
+import 'camera_editor_view.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'dart:math';
+
+class CameraPreviewView extends StatefulWidget {
+ const CameraPreviewView({
+ super.key,
+ required this.cameras,
+ });
+
+ final List cameras;
+ static const routeName = '/camera_preview';
+
+ @override
+ CameraPreviewViewState createState() => CameraPreviewViewState();
+}
+
+class CameraPreviewViewState extends State {
+ late CameraController _controller;
+ late Future _initializeControllerFuture;
+
+ int _selectedCameraIdx = 0;
+
+ double _baseScaleFactor = 1.0;
+ double _basePanY = 0.0;
+ double _scaleFactor = 1;
+ double _minimumScaleFactor = 1;
+
+ @override
+ void initState() {
+ super.initState();
+ // To display the current output from the Camera,
+ // create a CameraController.
+
+ _controller = CameraController(
+ // Get a specific camera from the list of available cameras.
+ widget.cameras.first,
+ // Define the resolution to use.
+ ResolutionPreset.max,
+ );
+
+ final size = Size(50, 300);
+
+ _controller.initialize().then((_) {
+ _controller.value = _controller.value.copyWith(previewSize: size);
+ setState(() {});
+ });
+
+ // Next, initialize the controller. This returns a Future.
+ _initializeControllerFuture = _controller.initialize();
+ }
+
+ Future updateController() async {
+ await _controller.setDescription(widget.cameras[_selectedCameraIdx]);
+ }
+
+ @override
+ void dispose() {
+ // Dispose of the controller when the widget is disposed.
+ _controller.dispose();
+ super.dispose();
+ }
+
+ // function for cropping image
+ Future takePicture() async {
+ try {
+ await _initializeControllerFuture;
+ final image = await _controller.takePicture();
+
+ if (!context.mounted) return;
+ await Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (context) => CameraEditorView(
+ imagePath: image.path,
+ ),
+ ),
+ );
+ } catch (e) {
+ // If an error occurs, log the error to the console.
+ print(e);
+ }
+ }
+
+ List getImageSize(double height, double width) {
+ // https://github.com/flutter/flutter/issues/15953#issuecomment-855182376
+
+ final screenH = max(height, width);
+ final screenW = min(height, width);
+
+ var ratioContainer = screenH / screenW;
+
+ var tmp = _controller.value.previewSize!;
+ final previewH = max(tmp.height, tmp.width);
+ final previewW = min(tmp.height, tmp.width);
+
+ var ratio = previewH / previewW;
+ var missing = ratioContainer - ratio;
+
+ final maxHeight = screenH;
+ final maxWidth = screenH * 0.5625;
+
+ _minimumScaleFactor = 1 + missing;
+
+ return [maxHeight, maxWidth];
+ }
+
+ Future updateScaleFactor(double newScale) async {
+ var minFactor = await _controller.getMinZoomLevel();
+ var maxFactor = await _controller.getMaxZoomLevel();
+ if (newScale < minFactor) {
+ newScale = minFactor;
+ }
+ if (newScale > maxFactor) {
+ newScale = maxFactor;
+ }
+
+ print(newScale);
+ if (newScale <= 1) {
+ await _controller.setZoomLevel(newScale);
+ } else {
+ //await _controller.setZoomLevel(1.0);
+ }
+ setState(() {
+ _scaleFactor = newScale;
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ var isFront = widget.cameras[_selectedCameraIdx].lensDirection ==
+ CameraLensDirection.front;
+ // Fill this out in the next steps.
+ // You must wait until the controller is initialized before displaying the
+ // camera preview. Use a FutureBuilder to display a loading spinner until the
+ // controller has finished initializing.
+ return Scaffold(
+ body: GestureDetector(
+ onPanStart: (details) async {
+ if (_scaleFactor <= 1) {
+ await updateScaleFactor(1);
+ }
+ setState(() {
+ _basePanY = details.localPosition.dy;
+ _baseScaleFactor = _scaleFactor;
+ });
+ },
+ onPanUpdate: (details) async {
+ final diff = details.localPosition.dy - _basePanY;
+ final updateScale = diff / 50;
+ var tmp = _baseScaleFactor - updateScale;
+ if (tmp <= 1) tmp = 1.00001;
+ updateScaleFactor(tmp);
+ },
+ // onScaleStart: (details) {
+ // _baseScaleFactor = _scaleFactor;
+ // },
+ // onScaleUpdate: (details) async {
+ // // print(scale.scale);
+ // var scaleFactor =
+ // ((_baseScaleFactor * details.scale * 10).round() / 10);
+ // var maxFactor = await _controller.getMaxZoomLevel();
+ // if (scaleFactor > maxFactor) scaleFactor = maxFactor;
+ // if (scaleFactor != _scaleFactor) {
+ // await updateScaleFactor(scaleFactor);
+ // }
+ // },
+ onTap: () {},
+ onDoubleTap: () {
+ setState(() {
+ _selectedCameraIdx = (_selectedCameraIdx + 1) % 2;
+ updateController();
+ });
+ },
+ child: Padding(
+ padding: const EdgeInsets.only(top: 60, bottom: 40),
+ child: ClipRRect(
+ borderRadius: BorderRadius.circular(22),
+ child: FutureBuilder(
+ future: _initializeControllerFuture,
+ builder: (context, snapshot) {
+ if (snapshot.connectionState != ConnectionState.done) {
+ return const Center(child: CircularProgressIndicator());
+ }
+ return Stack(
+ alignment: Alignment.bottomCenter,
+ children: [
+ LayoutBuilder(
+ builder:
+ (BuildContext context, BoxConstraints constraints) {
+ var height = constraints.maxHeight;
+ var width = constraints.maxWidth;
+
+ return OverflowBox(
+ maxHeight: getImageSize(height, width)[0],
+ maxWidth: getImageSize(height, width)[1],
+ child: Transform(
+ alignment: Alignment.center,
+ transform: Matrix4.rotationY(isFront ? pi : 0),
+ child: Transform.scale(
+ scale: _minimumScaleFactor +
+ (_scaleFactor > 1 ? _scaleFactor : 1) -
+ 1,
+ child: CameraPreview(_controller)),
+ ),
+ );
+ },
+ ),
+ Positioned(
+ bottom: 50,
+ child: Column(
+ children: [
+ CameraZoomButtons(
+ key: widget.key,
+ isFront: isFront,
+ scaleFactor: _scaleFactor,
+ updateScaleFactor: updateScaleFactor,
+ controller: _controller,
+ ),
+ SizedBox(height: 10),
+ GestureDetector(
+ onTap: () async {
+ await takePicture();
+ },
+ onLongPress: () async {},
+ child: Container(
+ height: 100,
+ width: 100,
+ clipBehavior: Clip.antiAliasWithSaveLayer,
+ padding: const EdgeInsets.all(2),
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ border: Border.all(
+ width: 7,
+ color: Colors.white,
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ );
+ },
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
+
+class CameraZoomButtons extends StatefulWidget {
+ const CameraZoomButtons(
+ {super.key,
+ required this.isFront,
+ required this.controller,
+ required this.updateScaleFactor,
+ required this.scaleFactor});
+
+ final bool isFront;
+ final CameraController controller;
+ final double scaleFactor;
+ final Function updateScaleFactor;
+
+ @override
+ State createState() => _CameraZoomButtonsState();
+}
+
+String beautifulZoomScale(double scale) {
+ var tmp = scale.toStringAsFixed(1);
+ if (tmp[0] == "0") {
+ tmp = tmp.substring(1, tmp.length);
+ }
+ return tmp;
+}
+
+class _CameraZoomButtonsState extends State {
+ @override
+ Widget build(BuildContext context) {
+ final zoomButtonStyle = TextButton.styleFrom(
+ padding: EdgeInsets.zero,
+ foregroundColor: Colors.white,
+ minimumSize: Size(40, 40),
+ alignment: Alignment.center,
+ tapTargetSize: MaterialTapTargetSize.shrinkWrap);
+
+ final zoomTextStyle = TextStyle(fontSize: 13);
+ return ClipRRect(
+ borderRadius: BorderRadius.circular(40.0),
+ child: Container(
+ color: const Color.fromARGB(90, 0, 0, 0),
+ child: Row(
+ children: widget.isFront
+ ? []
+ : [
+ TextButton(
+ style: zoomButtonStyle,
+ onPressed: () async {
+ var level = await widget.controller.getMinZoomLevel();
+ widget.updateScaleFactor(level);
+ },
+ child: FutureBuilder(
+ future: widget.controller.getMinZoomLevel(),
+ builder: (context, snap) {
+ if (snap.hasData) {
+ var minLevel =
+ beautifulZoomScale(snap.data!.toDouble());
+ var currentLevel =
+ beautifulZoomScale(widget.scaleFactor);
+ return Text(
+ widget.scaleFactor < 1
+ ? "${currentLevel}x"
+ : "${minLevel}x",
+ style: zoomTextStyle,
+ );
+ } else {
+ return Text("");
+ }
+ }),
+ ),
+ TextButton(
+ style: zoomButtonStyle,
+ onPressed: () {
+ widget.updateScaleFactor(1.0);
+ },
+ child: Text(
+ widget.scaleFactor >= 1
+ ? "${beautifulZoomScale(widget.scaleFactor)}x"
+ : "1.0x",
+ style: zoomTextStyle,
+ )),
+ TextButton(
+ style: zoomButtonStyle,
+ onPressed: () async {
+ var level = await widget.controller.getMaxZoomLevel();
+ widget.updateScaleFactor(level);
+ },
+ child: FutureBuilder(
+ future: widget.controller.getMaxZoomLevel(),
+ builder: (context, snap) {
+ if (snap.hasData) {
+ var maxLevel = snap.data?.toInt();
+ return Text("${maxLevel}x", style: zoomTextStyle);
+ } else {
+ return Text("");
+ }
+ }),
+ )
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/src/views/chat_item_details_view.dart b/lib/src/views/chat_item_details_view.dart
new file mode 100644
index 0000000..a6782e8
--- /dev/null
+++ b/lib/src/views/chat_item_details_view.dart
@@ -0,0 +1,22 @@
+import 'package:flutter/material.dart';
+
+/// Displays detailed information about a SampleItem.
+class SampleItemDetailsView extends StatelessWidget {
+ const SampleItemDetailsView({super.key, required this.userId});
+
+ final int userId;
+
+ static const routeName = '/sample_item';
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text('Your Chat with $userId'),
+ ),
+ body: const Center(
+ child: Text('Hallo!'),
+ ),
+ );
+ }
+}
diff --git a/lib/src/views/chat_list_view.dart b/lib/src/views/chat_list_view.dart
new file mode 100644
index 0000000..b67ca37
--- /dev/null
+++ b/lib/src/views/chat_list_view.dart
@@ -0,0 +1,277 @@
+import 'add_new_user_view.dart';
+import 'package:flutter/material.dart';
+import 'chat_item_details_view.dart';
+import 'dart:async';
+
+enum MessageSendState { sending, send, opened, received }
+
+class ChatItem {
+ const ChatItem(
+ {required this.username,
+ required this.flames,
+ required this.userId,
+ required this.state,
+ required this.lastMessageInSeconds});
+ final String username;
+ final int lastMessageInSeconds;
+ final int flames;
+ final int userId;
+ final MessageSendState state;
+}
+
+/// Displays a list of SampleItems.
+class ChatListView extends StatefulWidget {
+ const ChatListView({
+ super.key,
+ this.items = const [
+ ChatItem(
+ userId: 0,
+ username: "Alisa",
+ lastMessageInSeconds: 10,
+ flames: 129,
+ state: MessageSendState.sending),
+ ChatItem(
+ userId: 1,
+ username: "Franz",
+ lastMessageInSeconds: 20829,
+ flames: 0,
+ state: MessageSendState.received),
+ ChatItem(
+ userId: 2,
+ username: "Heiner",
+ lastMessageInSeconds: 291829,
+ state: MessageSendState.opened,
+ flames: 38),
+ ],
+ });
+
+ final List items;
+
+ @override
+ State createState() => _ChatListViewState();
+}
+
+class _ChatListViewState extends State {
+ int _secondsSinceOpen = 0;
+ late Timer _timer;
+
+ @override
+ void initState() {
+ super.initState();
+ _startTimer();
+ }
+
+ void _startTimer() {
+ _timer = Timer.periodic(Duration(seconds: 1), (timer) {
+ setState(() {
+ _secondsSinceOpen++;
+ });
+ });
+ }
+
+ @override
+ void dispose() {
+ _timer.cancel(); // Cancel the timer when the widget is disposed
+ super.dispose();
+ }
+
+ String formatDuration(int seconds) {
+ if (seconds < 60) {
+ return '$seconds Sec.';
+ } else if (seconds < 3600) {
+ int minutes = seconds ~/ 60;
+ return '$minutes Min.';
+ } else if (seconds < 86400) {
+ int hours = seconds ~/ 3600;
+ return '$hours Hrs.'; // Assuming "Stu." is for hours
+ } else {
+ int days = seconds ~/ 86400;
+ return '$days Days';
+ }
+ }
+
+ Widget getMessageSateIcon(MessageSendState state) {
+ List children = [];
+ Widget icon = Placeholder();
+ String text = "";
+
+ switch (state) {
+ case MessageSendState.opened:
+ icon = Icon(
+ Icons.crop_square,
+ size: 14,
+ color: Theme.of(context).colorScheme.primary,
+ );
+ text = "Opened";
+ break;
+ case MessageSendState.received:
+ icon = Icon(Icons.square_rounded,
+ size: 14, color: Theme.of(context).colorScheme.primary);
+ text = "Received";
+ break;
+ case MessageSendState.send:
+ icon = Icon(Icons.send, size: 14);
+ text = "Send";
+ break;
+ case MessageSendState.sending:
+ icon = Row(children: [
+ SizedBox(
+ width: 10,
+ height: 10,
+ child: CircularProgressIndicator(
+ strokeWidth: 1,
+ )),
+ SizedBox(width: 2)
+ ]);
+ text = "Sending";
+ break;
+ }
+ children.add(const SizedBox(width: 5));
+ return Row(
+ children: [
+ icon,
+ const SizedBox(width: 3),
+ Text(text, style: TextStyle(fontSize: 12)),
+ const SizedBox(width: 5)
+ ],
+ );
+ }
+
+ Widget getSubtitle(ChatItem item) {
+ return Row(
+ children: [
+ getMessageSateIcon(item.state),
+ Text("•"),
+ const SizedBox(width: 5),
+ Text(formatDuration(item.lastMessageInSeconds + _secondsSinceOpen),
+ style: TextStyle(fontSize: 12)),
+ if (item.flames > 0)
+ Row(
+ children: [
+ const SizedBox(width: 5),
+ Text("•"),
+ const SizedBox(width: 5),
+ Text(item.flames.toString(), style: TextStyle(fontSize: 12)),
+ Icon(
+ Icons.local_fire_department_sharp,
+ color: const Color.fromARGB(255, 215, 73, 58),
+ size: 16,
+ ),
+ ],
+ )
+ ],
+ );
+ }
+
+ Widget createInitialsAvatar(String username) {
+ // Extract initials from the username
+ List nameParts = username.split(' ');
+ String initials = nameParts.map((part) => part[0]).join().toUpperCase();
+
+ // Generate a color based on the initials (you can customize this logic)
+ Color avatarColor = _getColorFromInitials(initials);
+
+ return CircleAvatar(
+ backgroundColor: avatarColor,
+ child: Text(
+ initials,
+ style: TextStyle(
+ color: _getTextColor(Colors.white), fontWeight: FontWeight.bold),
+ ),
+ );
+ }
+
+ Color _getTextColor(Color backgroundColor) {
+ // Calculate the luminance of the background color
+ double luminance = backgroundColor.computeLuminance();
+ // Return white for dark backgrounds and black for light backgrounds
+ return luminance < 0.5 ? Colors.white : Colors.black;
+ }
+
+ Color _getColorFromInitials(String initials) {
+ // Define color lists for light and dark themes
+ List lightColors = [
+ Colors.red,
+ Colors.green,
+ Colors.blue,
+ Colors.orange,
+ Colors.purple,
+ Colors.teal,
+ Colors.amber,
+ Colors.indigo,
+ Colors.cyan,
+ Colors.lime,
+ Colors.pink,
+ Colors.brown,
+ Colors.grey,
+ ];
+
+ List darkColors = [
+ Colors.deepOrange,
+ Colors.deepPurple,
+ Colors.redAccent,
+ Colors.greenAccent,
+ Colors.blueAccent,
+ Colors.orangeAccent,
+ Colors.purpleAccent,
+ Colors.tealAccent,
+ Colors.amberAccent,
+ Colors.indigoAccent,
+ Colors.cyanAccent,
+ Colors.limeAccent,
+ Colors.pinkAccent,
+ ];
+
+ // Simple logic to generate a hash from initials
+ int hash = initials.codeUnits.fold(0, (prev, element) => prev + element);
+
+ // Select the appropriate color list based on the current theme brightness
+ List colors = Theme.of(context).brightness == Brightness.dark
+ ? darkColors
+ : lightColors;
+
+ // Use the hash to select a color from the list
+ return colors[hash % colors.length];
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('Chats'),
+ ),
+ body: ListView.builder(
+ restorationId: 'sampleItemListView',
+ itemCount: widget.items.length,
+ itemBuilder: (BuildContext context, int index) {
+ final item = widget.items[index];
+ return ListTile(
+ title: Text(item.username),
+ subtitle: getSubtitle(item),
+ leading: createInitialsAvatar(item.username),
+ onTap: () {
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => SampleItemDetailsView(
+ userId: item.userId,
+ ),
+ ),
+ );
+ });
+ },
+ ),
+ floatingActionButton: FloatingActionButton(
+ onPressed: () {
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => AddNewUserView(),
+ ),
+ );
+ },
+ child: const Icon(Icons.add),
+ ),
+ );
+ }
+}
diff --git a/lib/src/views/home_view.dart b/lib/src/views/home_view.dart
new file mode 100644
index 0000000..1c1fa69
--- /dev/null
+++ b/lib/src/views/home_view.dart
@@ -0,0 +1,77 @@
+import 'package:camera/camera.dart';
+import 'camera_preview_view.dart';
+import 'chat_list_view.dart';
+import 'permissions_view.dart';
+import 'profile_view.dart';
+import '../settings/settings_controller.dart';
+import 'package:flutter/material.dart';
+
+class HomeView extends StatefulWidget {
+ const HomeView(
+ {super.key, required this.settingsController, required this.cameras});
+ final SettingsController settingsController;
+ final List cameras;
+
+ @override
+ State createState() => HomeViewState();
+}
+
+class HomeViewState extends State {
+ int _activePageIdx = 0;
+ final PageController _pageController = PageController(initialPage: 0);
+ @override
+ Widget build(BuildContext context) {
+ return FutureBuilder(
+ future: checkPermissions(),
+ builder: (context, snap) {
+ if (snap.hasData) {
+ if (snap.data!) {
+ return Scaffold(
+ body: PageView(
+ controller: _pageController,
+ onPageChanged: (index) {
+ setState(() {
+ _activePageIdx = index;
+ });
+ },
+ children: [
+ ChatListView(),
+ CameraPreviewView(cameras: widget.cameras),
+ ProfileView(settingsController: widget.settingsController)
+ ],
+ ),
+ bottomNavigationBar: BottomNavigationBar(
+ showSelectedLabels: false,
+ showUnselectedLabels: false,
+ selectedIconTheme: IconThemeData(
+ color: const Color.fromARGB(255, 255, 255, 255)),
+ items: [
+ BottomNavigationBarItem(icon: Icon(Icons.chat), label: ""),
+ BottomNavigationBarItem(
+ icon: Icon(Icons.camera_alt),
+ label: "",
+ ),
+ BottomNavigationBarItem(
+ icon: Icon(Icons.verified_user), label: ""),
+ ],
+ onTap: (int index) {
+ setState(() {
+ _activePageIdx = index;
+ _pageController.animateToPage(_activePageIdx,
+ duration: const Duration(milliseconds: 100),
+ curve: Curves.bounceIn);
+ });
+ },
+ currentIndex: _activePageIdx,
+ ),
+ );
+ } else {
+ return PermissionHandlerView(onSuccess: () {
+ setState(() {});
+ });
+ }
+ }
+ return const CircularProgressIndicator();
+ });
+ }
+}
diff --git a/lib/src/views/permissions_view.dart b/lib/src/views/permissions_view.dart
new file mode 100644
index 0000000..fec8b7f
--- /dev/null
+++ b/lib/src/views/permissions_view.dart
@@ -0,0 +1,85 @@
+import 'package:flutter/material.dart';
+import 'package:permission_handler/permission_handler.dart';
+
+class PermissionHandlerView extends StatefulWidget {
+ const PermissionHandlerView({super.key, required this.onSuccess});
+
+ final Function onSuccess;
+
+ @override
+ _PermissionHandlerViewState createState() => _PermissionHandlerViewState();
+}
+
+Future checkPermissions() async {
+ if (!await Permission.camera.isGranted) {
+ return false;
+ }
+ if (!await Permission.microphone.isGranted) {
+ return false;
+ }
+ return true;
+}
+
+class _PermissionHandlerViewState extends State {
+ Future> permissionServices() async {
+ // You can request multiple permissions at once.
+ Map statuses = await [
+ Permission.camera,
+ Permission.microphone,
+ //add more permission to request here.
+ ].request();
+
+ if (statuses[Permission.microphone]!.isPermanentlyDenied) {
+ openAppSettings();
+ // setState(() {});
+ } else {
+ // if (statuses[Permission.microphone]!.isDenied) {
+ // }
+ }
+
+ if (statuses[Permission.camera]!.isPermanentlyDenied) {
+ openAppSettings();
+ // setState(() {});
+ } else {
+ // if (statuses[Permission.camera]!.isDenied) {
+ // }
+ }
+ /*{Permission.camera: PermissionStatus.granted, Permission.storage: PermissionStatus.granted}*/
+ return statuses;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return PopScope(
+ onPopInvokedWithResult: (bool didPop, Object? result) async {},
+ child: Scaffold(
+ body: Center(
+ child: Container(
+ padding: EdgeInsets.all(100),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Text(
+ "Connect needs access to the camera and microphone for obvious reasons.",
+ textAlign: TextAlign.center,
+ ),
+ SizedBox(height: 50),
+ FilledButton.icon(
+ label: Text("Request permissions"),
+ icon: const Icon(Icons.perm_camera_mic),
+ onPressed: () async {
+ permissionServices();
+ if (await checkPermissions()) {
+ widget.onSuccess();
+ }
+ },
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/src/views/profile_view.dart b/lib/src/views/profile_view.dart
new file mode 100644
index 0000000..2892f69
--- /dev/null
+++ b/lib/src/views/profile_view.dart
@@ -0,0 +1,59 @@
+import '../settings/settings_controller.dart';
+import '../settings/settings_view.dart';
+import '../utils.dart';
+import 'package:restart_app/restart_app.dart';
+import 'package:flutter/material.dart';
+
+class ProfileView extends StatefulWidget {
+ const ProfileView({super.key, required this.settingsController});
+
+ final SettingsController settingsController;
+
+ @override
+ State createState() => _ProfileViewState();
+}
+
+class _ProfileViewState extends State {
+ @override
+ Widget build(BuildContext context) {
+ // var user = await getUser();
+ return Scaffold(
+ appBar: AppBar(
+ title: FutureBuilder(
+ future: getUser(),
+ builder: (context, snap) {
+ if (snap.hasData) {
+ return Text("Hello ${snap.data!.username}!");
+ } else {
+ return CircularProgressIndicator();
+ }
+ }),
+ actions: [
+ IconButton(
+ icon: const Icon(Icons.settings),
+ onPressed: () {
+ // Navigate to the settings page. If the user leaves and returns
+ // to the app after it has been killed while running in the
+ // background, the navigation stack is restored.
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) =>
+ SettingsView(controller: widget.settingsController)),
+ );
+ },
+ ),
+ ]),
+ body: FilledButton.icon(
+ onPressed: () async {
+ await deleteLocalUserData();
+ Restart.restartApp(
+ notificationTitle: 'Successfully logged out',
+ notificationBody: 'Click here to open the app again',
+ );
+ },
+ label: Text("Logout"),
+ icon: Icon(Icons.no_accounts)),
+ );
+ }
+}
diff --git a/lib/src/views/register_view.dart b/lib/src/views/register_view.dart
new file mode 100644
index 0000000..699384d
--- /dev/null
+++ b/lib/src/views/register_view.dart
@@ -0,0 +1,152 @@
+import '../utils.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+
+class RegisterView extends StatefulWidget {
+ const RegisterView({super.key, required this.callbackOnSuccess});
+
+ final Function callbackOnSuccess;
+ @override
+ State createState() => _RegisterViewState();
+}
+
+class MyButton extends StatelessWidget {
+ final void Function()? onTap;
+ final String text;
+ const MyButton({super.key, required this.onTap, required this.text});
+
+ @override
+ Widget build(BuildContext context) {
+ return GestureDetector(
+ onTap: onTap,
+ child: Container(
+ padding: const EdgeInsets.all(10),
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.primary,
+ borderRadius: BorderRadius.circular(9),
+ ),
+ child: Center(
+ child: Text(
+ text,
+ textAlign: TextAlign.center,
+ ),
+ ),
+ ),
+ );
+ }
+}
+
+class _RegisterViewState extends State {
+ final TextEditingController usernameController = TextEditingController();
+ final TextEditingController inviteCodeController = TextEditingController();
+
+ @override
+ Widget build(BuildContext context) {
+ InputDecoration getInputDecoration(hintText) {
+ return InputDecoration(hintText: hintText, fillColor: Colors.grey[400]);
+ }
+
+ return Scaffold(
+ appBar: AppBar(
+ title: Text("Welcome to Connect!"),
+ ),
+ body: Padding(
+ padding: EdgeInsets.all(10),
+ child: ListView(
+ children: [
+ const SizedBox(height: 20),
+ Center(
+ child: Text(
+ "You made the right decision using Connect which is like SnXpchat but encrypted using the Signal protocol.",
+ textAlign: TextAlign.center,
+ style: TextStyle(fontSize: 15),
+ ),
+ ),
+ const SizedBox(height: 40),
+ Center(
+ child: Text(
+ "Choice wisely, this username can't be changed. Only lowercase and numbers are allowed!",
+ textAlign: TextAlign.center,
+ ),
+ ),
+ const SizedBox(height: 10),
+ TextField(
+ controller: usernameController,
+ inputFormatters: [
+ LengthLimitingTextInputFormatter(
+ 12), // Limit to 12 characters
+ FilteringTextInputFormatter.allow(RegExp(
+ r'[a-z0-9]')), // Allow only lowercase letters and numbers
+ ],
+ decoration: getInputDecoration("Username")),
+ const SizedBox(height: 15),
+ Center(
+ child: Text(
+ "To protect this small experimental project you need an invitation code! To get one just ask the right person!",
+ textAlign: TextAlign.center,
+ ),
+ ),
+ const SizedBox(height: 10),
+ TextField(
+ controller: inviteCodeController,
+ decoration: getInputDecoration("Invitation code")),
+ const SizedBox(height: 25),
+ Center(
+ child: Text(
+ "Where is the password? There is none! So make a backup of your Connect identity in the settings or you will lose your access if you lose your device!",
+ textAlign: TextAlign.center,
+ ),
+ ),
+ const SizedBox(height: 30),
+ FilledButton.icon(
+ icon: Icon(Icons.group),
+ onPressed: () async {
+ final success = await createNewUser(
+ usernameController.text, inviteCodeController.text);
+ if (success == null) {
+ widget.callbackOnSuccess();
+ return;
+ }
+ showAlertDialog(context, "Oh no!", success);
+ },
+ label: Text("Komm in die Gruppe!"),
+ ),
+ OutlinedButton.icon(
+ onPressed: () {
+ showAlertDialog(context, "Coming soon",
+ "This feature is not yet implemented! Just create a new account :/");
+ },
+ label: Text("Restore identity")),
+ // MyButton(onTap: () {}, text: "Komm in die Gruppe!")
+ ],
+ )),
+ );
+ }
+}
+
+showAlertDialog(BuildContext context, String title, String content) {
+ // set up the button
+ Widget okButton = TextButton(
+ child: Text("OK"),
+ onPressed: () {
+ Navigator.pop(context);
+ },
+ );
+
+ // set up the AlertDialog
+ AlertDialog alert = AlertDialog(
+ title: Text(title),
+ content: Text(content),
+ actions: [
+ okButton,
+ ],
+ );
+
+ // show the dialog
+ showDialog(
+ context: context,
+ builder: (BuildContext context) {
+ return alert;
+ },
+ );
+}
diff --git a/linux/.gitignore b/linux/.gitignore
new file mode 100644
index 0000000..d3896c9
--- /dev/null
+++ b/linux/.gitignore
@@ -0,0 +1 @@
+flutter/ephemeral
diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt
new file mode 100644
index 0000000..50fa8d0
--- /dev/null
+++ b/linux/CMakeLists.txt
@@ -0,0 +1,145 @@
+# Project-level configuration.
+cmake_minimum_required(VERSION 3.10)
+project(runner LANGUAGES CXX)
+
+# The name of the executable created for the application. Change this to change
+# the on-disk name of your application.
+set(BINARY_NAME "connect")
+# The unique GTK application identifier for this application. See:
+# https://wiki.gnome.org/HowDoI/ChooseApplicationID
+set(APPLICATION_ID "com.example.connect")
+
+# 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 "$<$>:-O3>")
+ target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>")
+endfunction()
+
+# Flutter library and tool build rules.
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+
+add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
+
+# Define the application target. To change its name, change BINARY_NAME above,
+# not the value here, or `flutter run` will no longer work.
+#
+# Any new source files that you add to the application should be added here.
+add_executable(${BINARY_NAME}
+ "main.cc"
+ "my_application.cc"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+)
+
+# Apply the standard set of build settings. This can be removed for applications
+# that need different build settings.
+apply_standard_settings(${BINARY_NAME})
+
+# Add dependency libraries. Add any application-specific dependencies here.
+target_link_libraries(${BINARY_NAME} PRIVATE flutter)
+target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
+
+# Run the Flutter tool portions of the build. This must not be removed.
+add_dependencies(${BINARY_NAME} flutter_assemble)
+
+# Only the install-generated bundle's copy of the executable will launch
+# correctly, since the resources must in the right relative locations. To avoid
+# people trying to run the unbundled copy, put it in a subdirectory instead of
+# the default top-level location.
+set_target_properties(${BINARY_NAME}
+ PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
+)
+
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# By default, "installing" just makes a relocatable bundle in the build
+# directory.
+set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+# Start with a clean build bundle directory every time.
+install(CODE "
+ file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
+ " COMPONENT Runtime)
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
+ install(FILES "${bundled_library}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endforeach(bundled_library)
+
+# Copy the native assets provided by the build.dart from all packages.
+set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/")
+install(DIRECTORY "${NATIVE_ASSETS_DIR}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
+ install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
diff --git a/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt
new file mode 100644
index 0000000..d5bd016
--- /dev/null
+++ b/linux/flutter/CMakeLists.txt
@@ -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}
+)
diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc
new file mode 100644
index 0000000..dd8425c
--- /dev/null
+++ b/linux/flutter/generated_plugin_registrant.cc
@@ -0,0 +1,19 @@
+//
+// Generated file. Do not edit.
+//
+
+// clang-format off
+
+#include "generated_plugin_registrant.h"
+
+#include
+#include
+
+void fl_register_plugins(FlPluginRegistry* registry) {
+ g_autoptr(FlPluginRegistrar) emoji_picker_flutter_registrar =
+ fl_plugin_registry_get_registrar_for_plugin(registry, "EmojiPickerFlutterPlugin");
+ emoji_picker_flutter_plugin_register_with_registrar(emoji_picker_flutter_registrar);
+ g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
+ fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
+ flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
+}
diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h
new file mode 100644
index 0000000..e0f0a47
--- /dev/null
+++ b/linux/flutter/generated_plugin_registrant.h
@@ -0,0 +1,15 @@
+//
+// Generated file. Do not edit.
+//
+
+// clang-format off
+
+#ifndef GENERATED_PLUGIN_REGISTRANT_
+#define GENERATED_PLUGIN_REGISTRANT_
+
+#include
+
+// Registers Flutter plugins.
+void fl_register_plugins(FlPluginRegistry* registry);
+
+#endif // GENERATED_PLUGIN_REGISTRANT_
diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake
new file mode 100644
index 0000000..a39777e
--- /dev/null
+++ b/linux/flutter/generated_plugins.cmake
@@ -0,0 +1,25 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+ emoji_picker_flutter
+ flutter_secure_storage_linux
+)
+
+list(APPEND FLUTTER_FFI_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
+
+foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
+endforeach(ffi_plugin)
diff --git a/linux/main.cc b/linux/main.cc
new file mode 100644
index 0000000..e7c5c54
--- /dev/null
+++ b/linux/main.cc
@@ -0,0 +1,6 @@
+#include "my_application.h"
+
+int main(int argc, char** argv) {
+ g_autoptr(MyApplication) app = my_application_new();
+ return g_application_run(G_APPLICATION(app), argc, argv);
+}
diff --git a/linux/my_application.cc b/linux/my_application.cc
new file mode 100644
index 0000000..93fd937
--- /dev/null
+++ b/linux/my_application.cc
@@ -0,0 +1,124 @@
+#include "my_application.h"
+
+#include
+#ifdef GDK_WINDOWING_X11
+#include
+#endif
+
+#include "flutter/generated_plugin_registrant.h"
+
+struct _MyApplication {
+ GtkApplication parent_instance;
+ char** dart_entrypoint_arguments;
+};
+
+G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
+
+// Implements GApplication::activate.
+static void my_application_activate(GApplication* application) {
+ MyApplication* self = MY_APPLICATION(application);
+ GtkWindow* window =
+ GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
+
+ // Use a header bar when running in GNOME as this is the common style used
+ // by applications and is the setup most users will be using (e.g. Ubuntu
+ // desktop).
+ // If running on X and not using GNOME then just use a traditional title bar
+ // in case the window manager does more exotic layout, e.g. tiling.
+ // If running on Wayland assume the header bar will work (may need changing
+ // if future cases occur).
+ gboolean use_header_bar = TRUE;
+#ifdef GDK_WINDOWING_X11
+ GdkScreen* screen = gtk_window_get_screen(window);
+ if (GDK_IS_X11_SCREEN(screen)) {
+ const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
+ if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
+ use_header_bar = FALSE;
+ }
+ }
+#endif
+ if (use_header_bar) {
+ GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
+ gtk_widget_show(GTK_WIDGET(header_bar));
+ gtk_header_bar_set_title(header_bar, "connect");
+ gtk_header_bar_set_show_close_button(header_bar, TRUE);
+ gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
+ } else {
+ gtk_window_set_title(window, "connect");
+ }
+
+ gtk_window_set_default_size(window, 1280, 720);
+ gtk_widget_show(GTK_WIDGET(window));
+
+ g_autoptr(FlDartProject) project = fl_dart_project_new();
+ fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
+
+ FlView* view = fl_view_new(project);
+ gtk_widget_show(GTK_WIDGET(view));
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
+
+ fl_register_plugins(FL_PLUGIN_REGISTRY(view));
+
+ gtk_widget_grab_focus(GTK_WIDGET(view));
+}
+
+// Implements GApplication::local_command_line.
+static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
+ MyApplication* self = MY_APPLICATION(application);
+ // Strip out the first argument as it is the binary name.
+ self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
+
+ g_autoptr(GError) error = nullptr;
+ if (!g_application_register(application, nullptr, &error)) {
+ g_warning("Failed to register: %s", error->message);
+ *exit_status = 1;
+ return TRUE;
+ }
+
+ g_application_activate(application);
+ *exit_status = 0;
+
+ return TRUE;
+}
+
+// Implements GApplication::startup.
+static void my_application_startup(GApplication* application) {
+ //MyApplication* self = MY_APPLICATION(object);
+
+ // Perform any actions required at application startup.
+
+ G_APPLICATION_CLASS(my_application_parent_class)->startup(application);
+}
+
+// Implements GApplication::shutdown.
+static void my_application_shutdown(GApplication* application) {
+ //MyApplication* self = MY_APPLICATION(object);
+
+ // Perform any actions required at application shutdown.
+
+ G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application);
+}
+
+// Implements GObject::dispose.
+static void my_application_dispose(GObject* object) {
+ MyApplication* self = MY_APPLICATION(object);
+ g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
+ G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
+}
+
+static void my_application_class_init(MyApplicationClass* klass) {
+ G_APPLICATION_CLASS(klass)->activate = my_application_activate;
+ G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
+ G_APPLICATION_CLASS(klass)->startup = my_application_startup;
+ G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown;
+ G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
+}
+
+static void my_application_init(MyApplication* self) {}
+
+MyApplication* my_application_new() {
+ return MY_APPLICATION(g_object_new(my_application_get_type(),
+ "application-id", APPLICATION_ID,
+ "flags", G_APPLICATION_NON_UNIQUE,
+ nullptr));
+}
diff --git a/linux/my_application.h b/linux/my_application.h
new file mode 100644
index 0000000..72271d5
--- /dev/null
+++ b/linux/my_application.h
@@ -0,0 +1,18 @@
+#ifndef FLUTTER_MY_APPLICATION_H_
+#define FLUTTER_MY_APPLICATION_H_
+
+#include
+
+G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
+ GtkApplication)
+
+/**
+ * my_application_new:
+ *
+ * Creates a new Flutter-based application.
+ *
+ * Returns: a new #MyApplication.
+ */
+MyApplication* my_application_new();
+
+#endif // FLUTTER_MY_APPLICATION_H_
diff --git a/macos/.gitignore b/macos/.gitignore
new file mode 100644
index 0000000..746adbb
--- /dev/null
+++ b/macos/.gitignore
@@ -0,0 +1,7 @@
+# Flutter-related
+**/Flutter/ephemeral/
+**/Pods/
+
+# Xcode-related
+**/dgph
+**/xcuserdata/
diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig
new file mode 100644
index 0000000..4b81f9b
--- /dev/null
+++ b/macos/Flutter/Flutter-Debug.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig
new file mode 100644
index 0000000..5caa9d1
--- /dev/null
+++ b/macos/Flutter/Flutter-Release.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift
new file mode 100644
index 0000000..b14dab6
--- /dev/null
+++ b/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -0,0 +1,24 @@
+//
+// Generated file. Do not edit.
+//
+
+import FlutterMacOS
+import Foundation
+
+import device_info_plus
+import emoji_picker_flutter
+import flutter_secure_storage_macos
+import mobile_scanner
+import path_provider_foundation
+import shared_preferences_foundation
+import sqflite_sqlcipher
+
+func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
+ DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
+ EmojiPickerFlutterPlugin.register(with: registry.registrar(forPlugin: "EmojiPickerFlutterPlugin"))
+ FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
+ MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
+ PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
+ SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
+ SqfliteSqlCipherPlugin.register(with: registry.registrar(forPlugin: "SqfliteSqlCipherPlugin"))
+}
diff --git a/macos/Podfile b/macos/Podfile
new file mode 100644
index 0000000..c795730
--- /dev/null
+++ b/macos/Podfile
@@ -0,0 +1,43 @@
+platform :osx, '10.14'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_macos_podfile_setup
+
+target 'Runner' do
+ use_frameworks!
+ use_modular_headers!
+
+ flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
+ target 'RunnerTests' do
+ inherit! :search_paths
+ end
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_macos_build_settings(target)
+ end
+end
diff --git a/macos/Podfile.lock b/macos/Podfile.lock
new file mode 100644
index 0000000..900f253
--- /dev/null
+++ b/macos/Podfile.lock
@@ -0,0 +1,54 @@
+PODS:
+ - device_info_plus (0.0.1):
+ - FlutterMacOS
+ - emoji_picker_flutter (0.0.1):
+ - FlutterMacOS
+ - flutter_secure_storage_macos (6.1.1):
+ - FlutterMacOS
+ - FlutterMacOS (1.0.0)
+ - mobile_scanner (6.0.2):
+ - FlutterMacOS
+ - path_provider_foundation (0.0.1):
+ - Flutter
+ - FlutterMacOS
+ - shared_preferences_foundation (0.0.1):
+ - Flutter
+ - FlutterMacOS
+
+DEPENDENCIES:
+ - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
+ - emoji_picker_flutter (from `Flutter/ephemeral/.symlinks/plugins/emoji_picker_flutter/macos`)
+ - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
+ - FlutterMacOS (from `Flutter/ephemeral`)
+ - mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos`)
+ - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
+ - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
+
+EXTERNAL SOURCES:
+ device_info_plus:
+ :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos
+ emoji_picker_flutter:
+ :path: Flutter/ephemeral/.symlinks/plugins/emoji_picker_flutter/macos
+ flutter_secure_storage_macos:
+ :path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos
+ FlutterMacOS:
+ :path: Flutter/ephemeral
+ mobile_scanner:
+ :path: Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos
+ path_provider_foundation:
+ :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
+ shared_preferences_foundation:
+ :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
+
+SPEC CHECKSUMS:
+ device_info_plus: 1b14eed9bf95428983aed283a8d51cce3d8c4215
+ emoji_picker_flutter: 533634326b1c5de9a181ba14b9758e6dfe967a20
+ flutter_secure_storage_macos: 59459653abe1adb92abbc8ea747d79f8d19866c9
+ FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
+ mobile_scanner: 07710d6b9b2c220ae899de2d7ecf5d77ffa56333
+ path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
+ shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
+
+PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
+
+COCOAPODS: 1.16.2
diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..a155d51
--- /dev/null
+++ b/macos/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,801 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 54;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
+ buildPhases = (
+ 33CC111E2044C6BF0003C045 /* ShellScript */,
+ );
+ dependencies = (
+ );
+ name = "Flutter Assemble";
+ productName = FLX;
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
+ 93026427A9DF7AAA6CC7B97C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4265710EECAA2F97B8113D01 /* Pods_Runner.framework */; };
+ AB296B701251E3CE239B5DC3 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48B1995FEE403CAECAA47C00 /* Pods_RunnerTests.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 33CC10EC2044A3C60003C045;
+ remoteInfo = Runner;
+ };
+ 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 33CC111A2044C6BA0003C045;
+ remoteInfo = FLX;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 33CC110E2044A8840003C045 /* Bundle Framework */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Bundle Framework";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1789A69B9BC449AC934B2739 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; };
+ 17E8104198C1A0DDA47869A1 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; };
+ 29C6541DF6E6A63F5A5D3C23 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; };
+ 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; };
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; };
+ 33CC10ED2044A3C60003C045 /* connect.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = connect.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; };
+ 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
+ 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; };
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; };
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; };
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; };
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; };
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; };
+ 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; };
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; };
+ 4265710EECAA2F97B8113D01 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 48B1995FEE403CAECAA47C00 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 71E524D25EA1025FA1CAFBF3 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
+ 7A86767FBB6E91E4EB0A83B5 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; };
+ 8999E7436DEE4145BFBCCA69 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 331C80D2294CF70F00263BE5 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ AB296B701251E3CE239B5DC3 /* Pods_RunnerTests.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 33CC10EA2044A3C60003C045 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 93026427A9DF7AAA6CC7B97C /* Pods_Runner.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 331C80D6294CF71000263BE5 /* RunnerTests */ = {
+ isa = PBXGroup;
+ children = (
+ 331C80D7294CF71000263BE5 /* RunnerTests.swift */,
+ );
+ path = RunnerTests;
+ sourceTree = "";
+ };
+ 33BA886A226E78AF003329D5 /* Configs */ = {
+ isa = PBXGroup;
+ children = (
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
+ );
+ path = Configs;
+ sourceTree = "";
+ };
+ 33CC10E42044A3C60003C045 = {
+ isa = PBXGroup;
+ children = (
+ 33FAB671232836740065AC1E /* Runner */,
+ 33CEB47122A05771004F2AC0 /* Flutter */,
+ 331C80D6294CF71000263BE5 /* RunnerTests */,
+ 33CC10EE2044A3C60003C045 /* Products */,
+ D73912EC22F37F3D000D13A0 /* Frameworks */,
+ A3E483E6288DAA9A6CD24475 /* Pods */,
+ );
+ sourceTree = "";
+ };
+ 33CC10EE2044A3C60003C045 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10ED2044A3C60003C045 /* connect.app */,
+ 331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 33CC11242044D66E0003C045 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */,
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */,
+ 33CC10F72044A3C60003C045 /* Info.plist */,
+ );
+ name = Resources;
+ path = ..;
+ sourceTree = "";
+ };
+ 33CEB47122A05771004F2AC0 /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
+ );
+ path = Flutter;
+ sourceTree = "";
+ };
+ 33FAB671232836740065AC1E /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */,
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */,
+ 33E51914231749380026EE4D /* Release.entitlements */,
+ 33CC11242044D66E0003C045 /* Resources */,
+ 33BA886A226E78AF003329D5 /* Configs */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+ A3E483E6288DAA9A6CD24475 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 71E524D25EA1025FA1CAFBF3 /* Pods-Runner.debug.xcconfig */,
+ 8999E7436DEE4145BFBCCA69 /* Pods-Runner.release.xcconfig */,
+ 7A86767FBB6E91E4EB0A83B5 /* Pods-Runner.profile.xcconfig */,
+ 1789A69B9BC449AC934B2739 /* Pods-RunnerTests.debug.xcconfig */,
+ 29C6541DF6E6A63F5A5D3C23 /* Pods-RunnerTests.release.xcconfig */,
+ 17E8104198C1A0DDA47869A1 /* Pods-RunnerTests.profile.xcconfig */,
+ );
+ name = Pods;
+ path = Pods;
+ sourceTree = "";
+ };
+ D73912EC22F37F3D000D13A0 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 4265710EECAA2F97B8113D01 /* Pods_Runner.framework */,
+ 48B1995FEE403CAECAA47C00 /* Pods_RunnerTests.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 331C80D4294CF70F00263BE5 /* RunnerTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
+ buildPhases = (
+ BF4817297D117DAC2F609608 /* [CP] Check Pods Manifest.lock */,
+ 331C80D1294CF70F00263BE5 /* Sources */,
+ 331C80D2294CF70F00263BE5 /* Frameworks */,
+ 331C80D3294CF70F00263BE5 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 331C80DA294CF71000263BE5 /* PBXTargetDependency */,
+ );
+ name = RunnerTests;
+ productName = RunnerTests;
+ productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ 33CC10EC2044A3C60003C045 /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 370419EFA4226E12C806FAA3 /* [CP] Check Pods Manifest.lock */,
+ 33CC10E92044A3C60003C045 /* Sources */,
+ 33CC10EA2044A3C60003C045 /* Frameworks */,
+ 33CC10EB2044A3C60003C045 /* Resources */,
+ 33CC110E2044A8840003C045 /* Bundle Framework */,
+ 3399D490228B24CF009A79C7 /* ShellScript */,
+ 9F6EC26B34D9BD2E254C77E4 /* [CP] Embed Pods Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */,
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 33CC10ED2044A3C60003C045 /* connect.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 33CC10E52044A3C60003C045 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = YES;
+ LastSwiftUpdateCheck = 0920;
+ LastUpgradeCheck = 1510;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 331C80D4294CF70F00263BE5 = {
+ CreatedOnToolsVersion = 14.0;
+ TestTargetID = 33CC10EC2044A3C60003C045;
+ };
+ 33CC10EC2044A3C60003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ LastSwiftMigration = 1100;
+ ProvisioningStyle = Automatic;
+ SystemCapabilities = {
+ com.apple.Sandbox = {
+ enabled = 1;
+ };
+ };
+ };
+ 33CC111A2044C6BA0003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ ProvisioningStyle = Manual;
+ };
+ };
+ };
+ buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 33CC10E42044A3C60003C045;
+ productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 33CC10EC2044A3C60003C045 /* Runner */,
+ 331C80D4294CF70F00263BE5 /* RunnerTests */,
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 331C80D3294CF70F00263BE5 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 33CC10EB2044A3C60003C045 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3399D490228B24CF009A79C7 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
+ };
+ 33CC111E2044C6BF0003C045 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ Flutter/ephemeral/FlutterInputs.xcfilelist,
+ );
+ inputPaths = (
+ Flutter/ephemeral/tripwire,
+ );
+ outputFileListPaths = (
+ Flutter/ephemeral/FlutterOutputs.xcfilelist,
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
+ };
+ 370419EFA4226E12C806FAA3 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 9F6EC26B34D9BD2E254C77E4 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ BF4817297D117DAC2F609608 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 331C80D1294CF70F00263BE5 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 33CC10E92044A3C60003C045 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 33CC10EC2044A3C60003C045 /* Runner */;
+ targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;
+ };
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
+ targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 33CC10F52044A3C60003C045 /* Base */,
+ );
+ name = MainMenu.xib;
+ path = Runner;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 331C80DB294CF71000263BE5 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 1789A69B9BC449AC934B2739 /* Pods-RunnerTests.debug.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.connect.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/connect.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/connect";
+ };
+ name = Debug;
+ };
+ 331C80DC294CF71000263BE5 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 29C6541DF6E6A63F5A5D3C23 /* Pods-RunnerTests.release.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.connect.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/connect.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/connect";
+ };
+ name = Release;
+ };
+ 331C80DD294CF71000263BE5 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 17E8104198C1A0DDA47869A1 /* Pods-RunnerTests.profile.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.connect.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/connect.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/connect";
+ };
+ name = Profile;
+ };
+ 338D0CE9231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.14;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Profile;
+ };
+ 338D0CEA231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Profile;
+ };
+ 338D0CEB231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Profile;
+ };
+ 33CC10F92044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.14;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 33CC10FA2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.14;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Release;
+ };
+ 33CC10FC2044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ 33CC10FD2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
+ 33CC111C2044C6BA0003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 33CC111D2044C6BA0003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Automatic;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 331C80DB294CF71000263BE5 /* Debug */,
+ 331C80DC294CF71000263BE5 /* Release */,
+ 331C80DD294CF71000263BE5 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10F92044A3C60003C045 /* Debug */,
+ 33CC10FA2044A3C60003C045 /* Release */,
+ 338D0CE9231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10FC2044A3C60003C045 /* Debug */,
+ 33CC10FD2044A3C60003C045 /* Release */,
+ 338D0CEA231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC111C2044C6BA0003C045 /* Debug */,
+ 33CC111D2044C6BA0003C045 /* Release */,
+ 338D0CEB231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 33CC10E52044A3C60003C045 /* Project object */;
+}
diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..731b1fb
--- /dev/null
+++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..21a3cc1
--- /dev/null
+++ b/macos/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift
new file mode 100644
index 0000000..8e02df2
--- /dev/null
+++ b/macos/Runner/AppDelegate.swift
@@ -0,0 +1,9 @@
+import Cocoa
+import FlutterMacOS
+
+@main
+class AppDelegate: FlutterAppDelegate {
+ override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
+ return true
+ }
+}
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..a2ec33f
--- /dev/null
+++ b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,68 @@
+{
+ "images" : [
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_16.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_64.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_128.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_1024.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
new file mode 100644
index 0000000..82b6f9d
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
new file mode 100644
index 0000000..13b35eb
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
new file mode 100644
index 0000000..0a3f5fa
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
new file mode 100644
index 0000000..bdb5722
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
new file mode 100644
index 0000000..f083318
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
new file mode 100644
index 0000000..326c0e7
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
new file mode 100644
index 0000000..2f1632c
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ
diff --git a/macos/Runner/Base.lproj/MainMenu.xib b/macos/Runner/Base.lproj/MainMenu.xib
new file mode 100644
index 0000000..80e867a
--- /dev/null
+++ b/macos/Runner/Base.lproj/MainMenu.xib
@@ -0,0 +1,343 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig
new file mode 100644
index 0000000..15307b7
--- /dev/null
+++ b/macos/Runner/Configs/AppInfo.xcconfig
@@ -0,0 +1,14 @@
+// Application-level settings for the Runner target.
+//
+// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
+// future. If not, the values below would default to using the project name when this becomes a
+// 'flutter create' template.
+
+// The application's name. By default this is also the title of the Flutter window.
+PRODUCT_NAME = connect
+
+// The application's bundle identifier
+PRODUCT_BUNDLE_IDENTIFIER = com.example.connect
+
+// The copyright displayed in application information
+PRODUCT_COPYRIGHT = Copyright © 2024 com.example. All rights reserved.
diff --git a/macos/Runner/Configs/Debug.xcconfig b/macos/Runner/Configs/Debug.xcconfig
new file mode 100644
index 0000000..36b0fd9
--- /dev/null
+++ b/macos/Runner/Configs/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Debug.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/macos/Runner/Configs/Release.xcconfig b/macos/Runner/Configs/Release.xcconfig
new file mode 100644
index 0000000..dff4f49
--- /dev/null
+++ b/macos/Runner/Configs/Release.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Release.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/macos/Runner/Configs/Warnings.xcconfig b/macos/Runner/Configs/Warnings.xcconfig
new file mode 100644
index 0000000..42bcbf4
--- /dev/null
+++ b/macos/Runner/Configs/Warnings.xcconfig
@@ -0,0 +1,13 @@
+WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
+GCC_WARN_UNDECLARED_SELECTOR = YES
+CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
+CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
+CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
+CLANG_WARN_PRAGMA_PACK = YES
+CLANG_WARN_STRICT_PROTOTYPES = YES
+CLANG_WARN_COMMA = YES
+GCC_WARN_STRICT_SELECTOR_MATCH = YES
+CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
+CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
+GCC_WARN_SHADOW = YES
+CLANG_WARN_UNREACHABLE_CODE = YES
diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements
new file mode 100644
index 0000000..dddb8a3
--- /dev/null
+++ b/macos/Runner/DebugProfile.entitlements
@@ -0,0 +1,12 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.cs.allow-jit
+
+ com.apple.security.network.server
+
+
+
diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist
new file mode 100644
index 0000000..4789daa
--- /dev/null
+++ b/macos/Runner/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSMinimumSystemVersion
+ $(MACOSX_DEPLOYMENT_TARGET)
+ NSHumanReadableCopyright
+ $(PRODUCT_COPYRIGHT)
+ NSMainNibFile
+ MainMenu
+ NSPrincipalClass
+ NSApplication
+
+
diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift
new file mode 100644
index 0000000..3cc05eb
--- /dev/null
+++ b/macos/Runner/MainFlutterWindow.swift
@@ -0,0 +1,15 @@
+import Cocoa
+import FlutterMacOS
+
+class MainFlutterWindow: NSWindow {
+ override func awakeFromNib() {
+ let flutterViewController = FlutterViewController()
+ let windowFrame = self.frame
+ self.contentViewController = flutterViewController
+ self.setFrame(windowFrame, display: true)
+
+ RegisterGeneratedPlugins(registry: flutterViewController)
+
+ super.awakeFromNib()
+ }
+}
diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements
new file mode 100644
index 0000000..852fa1a
--- /dev/null
+++ b/macos/Runner/Release.entitlements
@@ -0,0 +1,8 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+
+
diff --git a/macos/RunnerTests/RunnerTests.swift b/macos/RunnerTests/RunnerTests.swift
new file mode 100644
index 0000000..61f3bd1
--- /dev/null
+++ b/macos/RunnerTests/RunnerTests.swift
@@ -0,0 +1,12 @@
+import Cocoa
+import FlutterMacOS
+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.
+ }
+
+}
diff --git a/pubspec.lock b/pubspec.lock
new file mode 100644
index 0000000..94e4d81
--- /dev/null
+++ b/pubspec.lock
@@ -0,0 +1,1124 @@
+# Generated by pub
+# See https://dart.dev/tools/pub/glossary#lockfile
+packages:
+ _fe_analyzer_shared:
+ dependency: transitive
+ description:
+ name: _fe_analyzer_shared
+ sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834
+ url: "https://pub.dev"
+ source: hosted
+ version: "72.0.0"
+ _macros:
+ dependency: transitive
+ description: dart
+ source: sdk
+ version: "0.3.2"
+ adaptive_number:
+ dependency: transitive
+ description:
+ name: adaptive_number
+ sha256: "3a567544e9b5c9c803006f51140ad544aedc79604fd4f3f2c1380003f97c1d77"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.0"
+ analyzer:
+ dependency: transitive
+ description:
+ name: analyzer
+ sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.7.0"
+ archive:
+ dependency: transitive
+ description:
+ name: archive
+ sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.6.1"
+ args:
+ dependency: transitive
+ description:
+ name: args
+ sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.6.0"
+ async:
+ dependency: transitive
+ description:
+ name: async
+ sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.11.0"
+ boolean_selector:
+ dependency: transitive
+ description:
+ name: boolean_selector
+ sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.1"
+ build:
+ dependency: transitive
+ description:
+ name: build
+ sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.1"
+ build_config:
+ dependency: transitive
+ description:
+ name: build_config
+ sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.1"
+ build_daemon:
+ dependency: transitive
+ description:
+ name: build_daemon
+ sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.0.2"
+ build_resolvers:
+ dependency: transitive
+ description:
+ name: build_resolvers
+ sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.2"
+ build_runner:
+ dependency: "direct dev"
+ description:
+ name: build_runner
+ sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.13"
+ build_runner_core:
+ dependency: transitive
+ description:
+ name: build_runner_core
+ sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.3.2"
+ built_collection:
+ dependency: transitive
+ description:
+ name: built_collection
+ sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.1.1"
+ built_value:
+ dependency: transitive
+ description:
+ name: built_value
+ sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
+ url: "https://pub.dev"
+ source: hosted
+ version: "8.9.2"
+ camera:
+ dependency: "direct main"
+ description:
+ name: camera
+ sha256: "26ff41045772153f222ffffecba711a206f670f5834d40ebf5eed3811692f167"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.11.0+2"
+ camera_android_camerax:
+ dependency: transitive
+ description:
+ name: camera_android_camerax
+ sha256: e3627fdc2132d89212b8a8676679f5b07008c7e3d8ae00cea775c3397f9e742b
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.6.10"
+ camera_avfoundation:
+ dependency: transitive
+ description:
+ name: camera_avfoundation
+ sha256: "2e4c568f70e406ccb87376bc06b53d2f5bebaab71e2fbcc1a950e31449381bcf"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.9.17+5"
+ camera_platform_interface:
+ dependency: transitive
+ description:
+ name: camera_platform_interface
+ sha256: b3ede1f171532e0d83111fe0980b46d17f1aa9788a07a2fbed07366bbdbb9061
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.8.0"
+ camera_web:
+ dependency: transitive
+ description:
+ name: camera_web
+ sha256: "595f28c89d1fb62d77c73c633193755b781c6d2e0ebcd8dc25b763b514e6ba8f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.3.5"
+ characters:
+ dependency: transitive
+ description:
+ name: characters
+ sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.0"
+ checked_yaml:
+ dependency: transitive
+ description:
+ name: checked_yaml
+ sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.3"
+ cli_util:
+ dependency: transitive
+ description:
+ name: cli_util
+ sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.4.2"
+ clock:
+ dependency: transitive
+ description:
+ name: clock
+ sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.1"
+ code_builder:
+ dependency: transitive
+ description:
+ name: code_builder
+ sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.10.1"
+ collection:
+ dependency: "direct main"
+ description:
+ name: collection
+ sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.18.0"
+ convert:
+ dependency: transitive
+ description:
+ name: convert
+ sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.2"
+ cross_file:
+ dependency: transitive
+ description:
+ name: cross_file
+ sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.3.4+2"
+ crypto:
+ dependency: transitive
+ description:
+ name: crypto
+ sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.6"
+ cv:
+ dependency: "direct main"
+ description:
+ name: cv
+ sha256: b7ad2d39ebd8d0a610c69becbd9b0131c1b1544d42186fa025710f8ebff038a0
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.3"
+ dart_style:
+ dependency: transitive
+ description:
+ name: dart_style
+ sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.3.7"
+ device_info_plus:
+ dependency: transitive
+ description:
+ name: device_info_plus
+ sha256: f545ffbadee826f26f2e1a0f0cbd667ae9a6011cc0f77c0f8f00a969655e6e95
+ url: "https://pub.dev"
+ source: hosted
+ version: "11.1.1"
+ device_info_plus_platform_interface:
+ dependency: transitive
+ description:
+ name: device_info_plus_platform_interface
+ sha256: "282d3cf731045a2feb66abfe61bbc40870ae50a3ed10a4d3d217556c35c8c2ba"
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.1"
+ ed25519_edwards:
+ dependency: transitive
+ description:
+ name: ed25519_edwards
+ sha256: "6ce0112d131327ec6d42beede1e5dfd526069b18ad45dcf654f15074ad9276cd"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.3.1"
+ emoji_picker_flutter:
+ dependency: transitive
+ description:
+ name: emoji_picker_flutter
+ sha256: "08567e6f914d36c32091a96cf2f51d2558c47aa2bd47a590dc4f50e42e0965f6"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.0"
+ fake_async:
+ dependency: transitive
+ description:
+ name: fake_async
+ sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.1"
+ ffi:
+ dependency: transitive
+ description:
+ name: ffi
+ sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.3"
+ file:
+ dependency: transitive
+ description:
+ name: file
+ sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.1"
+ fixnum:
+ dependency: transitive
+ description:
+ name: fixnum
+ sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.1"
+ flutter:
+ dependency: "direct main"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_launcher_icons:
+ dependency: "direct dev"
+ description:
+ name: flutter_launcher_icons
+ sha256: "619817c4b65b322b5104b6bb6dfe6cda62d9729bd7ad4303ecc8b4e690a67a77"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.14.1"
+ flutter_lints:
+ dependency: "direct dev"
+ description:
+ name: flutter_lints
+ sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.0.0"
+ flutter_localizations:
+ dependency: "direct main"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_plugin_android_lifecycle:
+ dependency: transitive
+ description:
+ name: flutter_plugin_android_lifecycle
+ sha256: "9b78450b89f059e96c9ebb355fa6b3df1d6b330436e0b885fb49594c41721398"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.23"
+ flutter_secure_storage:
+ dependency: "direct main"
+ description:
+ name: flutter_secure_storage
+ sha256: "165164745e6afb5c0e3e3fcc72a012fb9e58496fb26ffb92cf22e16a821e85d0"
+ url: "https://pub.dev"
+ source: hosted
+ version: "9.2.2"
+ flutter_secure_storage_linux:
+ dependency: transitive
+ description:
+ name: flutter_secure_storage_linux
+ sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.1"
+ flutter_secure_storage_macos:
+ dependency: transitive
+ description:
+ name: flutter_secure_storage_macos
+ sha256: "1693ab11121a5f925bbea0be725abfcfbbcf36c1e29e571f84a0c0f436147a81"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.2"
+ flutter_secure_storage_platform_interface:
+ dependency: transitive
+ description:
+ name: flutter_secure_storage_platform_interface
+ sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.2"
+ flutter_secure_storage_web:
+ dependency: transitive
+ description:
+ name: flutter_secure_storage_web
+ sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.1"
+ flutter_secure_storage_windows:
+ dependency: transitive
+ description:
+ name: flutter_secure_storage_windows
+ sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.2"
+ flutter_test:
+ dependency: "direct dev"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_web_plugins:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ frontend_server_client:
+ dependency: transitive
+ description:
+ name: frontend_server_client
+ sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.0.0"
+ glob:
+ dependency: transitive
+ description:
+ name: glob
+ sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.2"
+ google_fonts:
+ dependency: "direct main"
+ description:
+ name: google_fonts
+ sha256: b1ac0fe2832c9cc95e5e88b57d627c5e68c223b9657f4b96e1487aa9098c7b82
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.2.1"
+ graphs:
+ dependency: transitive
+ description:
+ name: graphs
+ sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.3.2"
+ http:
+ dependency: transitive
+ description:
+ name: http
+ sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.2"
+ http_multi_server:
+ dependency: transitive
+ description:
+ name: http_multi_server
+ sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.2.1"
+ http_parser:
+ dependency: transitive
+ description:
+ name: http_parser
+ sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.0.2"
+ image:
+ dependency: "direct main"
+ description:
+ name: image
+ sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.3.0"
+ intl:
+ dependency: transitive
+ description:
+ name: intl
+ sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.19.0"
+ io:
+ dependency: transitive
+ description:
+ name: io
+ sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.4"
+ js:
+ dependency: transitive
+ description:
+ name: js
+ sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.6.7"
+ json_annotation:
+ dependency: "direct main"
+ description:
+ name: json_annotation
+ sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.9.0"
+ json_serializable:
+ dependency: "direct dev"
+ description:
+ name: json_serializable
+ sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.8.0"
+ leak_tracker:
+ dependency: transitive
+ description:
+ name: leak_tracker
+ sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
+ url: "https://pub.dev"
+ source: hosted
+ version: "10.0.5"
+ leak_tracker_flutter_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_flutter_testing
+ sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.5"
+ leak_tracker_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_testing
+ sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.1"
+ libsignal_protocol_dart:
+ dependency: "direct main"
+ description:
+ name: libsignal_protocol_dart
+ sha256: "2a8d54cddb89eab0301d333c4346d7edc920da0a0c0daa5e324878de2841dce6"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.7.1"
+ lints:
+ dependency: transitive
+ description:
+ name: lints
+ sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.0.0"
+ logging:
+ dependency: "direct main"
+ description:
+ name: logging
+ sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.0"
+ macros:
+ dependency: transitive
+ description:
+ name: macros
+ sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.1.2-main.4"
+ matcher:
+ dependency: transitive
+ description:
+ name: matcher
+ sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.12.16+1"
+ material_color_utilities:
+ dependency: transitive
+ description:
+ name: material_color_utilities
+ sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.11.1"
+ meta:
+ dependency: transitive
+ description:
+ name: meta
+ sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.15.0"
+ mime:
+ dependency: transitive
+ description:
+ name: mime
+ sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.0"
+ mobile_scanner:
+ dependency: "direct main"
+ description:
+ name: mobile_scanner
+ sha256: "728828a798d1a2ee506beb652ca23d974c542c96ed03dcbd5eaf97bef96cdaad"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.0.2"
+ optional:
+ dependency: transitive
+ description:
+ name: optional
+ sha256: f80327d7a3335a0be68418072668043c7ab291df575c21aa42e0c5633641da39
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.1.0+1"
+ package_config:
+ dependency: transitive
+ description:
+ name: package_config
+ sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.0"
+ path:
+ dependency: "direct main"
+ description:
+ name: path
+ sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.9.0"
+ path_provider:
+ dependency: "direct main"
+ description:
+ name: path_provider
+ sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.5"
+ path_provider_android:
+ dependency: transitive
+ description:
+ name: path_provider_android
+ sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.12"
+ path_provider_foundation:
+ dependency: transitive
+ description:
+ name: path_provider_foundation
+ sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.0"
+ path_provider_linux:
+ dependency: transitive
+ description:
+ name: path_provider_linux
+ sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.1"
+ path_provider_platform_interface:
+ dependency: transitive
+ description:
+ name: path_provider_platform_interface
+ sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.2"
+ path_provider_windows:
+ dependency: transitive
+ description:
+ name: path_provider_windows
+ sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.3.0"
+ permission_handler:
+ dependency: "direct main"
+ description:
+ name: permission_handler
+ sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb"
+ url: "https://pub.dev"
+ source: hosted
+ version: "11.3.1"
+ permission_handler_android:
+ dependency: transitive
+ description:
+ name: permission_handler_android
+ sha256: "71bbecfee799e65aff7c744761a57e817e73b738fedf62ab7afd5593da21f9f1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "12.0.13"
+ permission_handler_apple:
+ dependency: transitive
+ description:
+ name: permission_handler_apple
+ sha256: e6f6d73b12438ef13e648c4ae56bd106ec60d17e90a59c4545db6781229082a0
+ url: "https://pub.dev"
+ source: hosted
+ version: "9.4.5"
+ permission_handler_html:
+ dependency: transitive
+ description:
+ name: permission_handler_html
+ sha256: "6b9cb54b7135073841a35513fba39e598b421702d5f4d92319992fd6eb5532a9"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.1.3+4"
+ permission_handler_platform_interface:
+ dependency: transitive
+ description:
+ name: permission_handler_platform_interface
+ sha256: e9c8eadee926c4532d0305dff94b85bf961f16759c3af791486613152af4b4f9
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.2.3"
+ permission_handler_windows:
+ dependency: transitive
+ description:
+ name: permission_handler_windows
+ sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.2.1"
+ petitparser:
+ dependency: transitive
+ description:
+ name: petitparser
+ sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.0.2"
+ platform:
+ dependency: transitive
+ description:
+ name: platform
+ sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.6"
+ plugin_platform_interface:
+ dependency: transitive
+ description:
+ name: plugin_platform_interface
+ sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.8"
+ pointycastle:
+ dependency: transitive
+ description:
+ name: pointycastle
+ sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.9.1"
+ pool:
+ dependency: transitive
+ description:
+ name: pool
+ sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.5.1"
+ pro_image_editor:
+ dependency: "direct main"
+ description:
+ name: pro_image_editor
+ sha256: "419691ffe116b0282c8c4467aa1098d76e1dc2671002f8269d54715e349ebe24"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.1.3"
+ protobuf:
+ dependency: transitive
+ description:
+ name: protobuf
+ sha256: "01dd9bd0fa02548bf2ceee13545d4a0ec6046459d847b6b061d8a27237108a08"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.0"
+ pub_semver:
+ dependency: transitive
+ description:
+ name: pub_semver
+ sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.4"
+ pubspec_parse:
+ dependency: transitive
+ description:
+ name: pubspec_parse
+ sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.0"
+ restart_app:
+ dependency: "direct main"
+ description:
+ name: restart_app
+ sha256: "00d5ec3e9de871cedbe552fc41e615b042b5ec654385e090e0983f6d02f655ed"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.2"
+ shared_preferences:
+ dependency: transitive
+ description:
+ name: shared_preferences
+ sha256: "95f9997ca1fb9799d494d0cb2a780fd7be075818d59f00c43832ed112b158a82"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.3.3"
+ shared_preferences_android:
+ dependency: transitive
+ description:
+ name: shared_preferences_android
+ sha256: "3b9febd815c9ca29c9e3520d50ec32f49157711e143b7a4ca039eb87e8ade5ab"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.3.3"
+ shared_preferences_foundation:
+ dependency: transitive
+ description:
+ name: shared_preferences_foundation
+ sha256: "07e050c7cd39bad516f8d64c455f04508d09df104be326d8c02551590a0d513d"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.5.3"
+ shared_preferences_linux:
+ dependency: transitive
+ description:
+ name: shared_preferences_linux
+ sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.1"
+ shared_preferences_platform_interface:
+ dependency: transitive
+ description:
+ name: shared_preferences_platform_interface
+ sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.1"
+ shared_preferences_web:
+ dependency: transitive
+ description:
+ name: shared_preferences_web
+ sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.2"
+ shared_preferences_windows:
+ dependency: transitive
+ description:
+ name: shared_preferences_windows
+ sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.1"
+ shelf:
+ dependency: transitive
+ description:
+ name: shelf
+ sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.4.1"
+ shelf_web_socket:
+ dependency: transitive
+ description:
+ name: shelf_web_socket
+ sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.0"
+ sky_engine:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.99"
+ source_gen:
+ dependency: transitive
+ description:
+ name: source_gen
+ sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.5.0"
+ source_helper:
+ dependency: transitive
+ description:
+ name: source_helper
+ sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.4"
+ source_span:
+ dependency: transitive
+ description:
+ name: source_span
+ sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.10.0"
+ sqflite_common:
+ dependency: transitive
+ description:
+ name: sqflite_common
+ sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.5.4+6"
+ sqflite_sqlcipher:
+ dependency: "direct main"
+ description:
+ name: sqflite_sqlcipher
+ sha256: "16033fde6c7d7bd657b71a2bc42332ab02bc8001c3212f502d2e02714e735ec9"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.0+1"
+ stack_trace:
+ dependency: transitive
+ description:
+ name: stack_trace
+ sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.11.1"
+ stream_channel:
+ dependency: transitive
+ description:
+ name: stream_channel
+ sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.2"
+ stream_transform:
+ dependency: transitive
+ description:
+ name: stream_transform
+ sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.0"
+ string_scanner:
+ dependency: transitive
+ description:
+ name: string_scanner
+ sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.0"
+ synchronized:
+ dependency: transitive
+ description:
+ name: synchronized
+ sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.3.0+3"
+ term_glyph:
+ dependency: transitive
+ description:
+ name: term_glyph
+ sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.1"
+ test_api:
+ dependency: transitive
+ description:
+ name: test_api
+ sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.7.2"
+ timing:
+ dependency: transitive
+ description:
+ name: timing
+ sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.1"
+ typed_data:
+ dependency: transitive
+ description:
+ name: typed_data
+ sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.4.0"
+ vector_math:
+ dependency: transitive
+ description:
+ name: vector_math
+ sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.4"
+ very_good_analysis:
+ dependency: transitive
+ description:
+ name: very_good_analysis
+ sha256: "9ae7f3a3bd5764fb021b335ca28a34f040cd0ab6eec00a1b213b445dae58a4b8"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.1.0"
+ vibration:
+ dependency: transitive
+ description:
+ name: vibration
+ sha256: f0af02af2d63132135ae0332a3e54d5de718e214ee94c4f082176ef6ce624a4b
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.1"
+ vibration_platform_interface:
+ dependency: transitive
+ description:
+ name: vibration_platform_interface
+ sha256: f66b39aab2447038978c16f3d6f77228e49ef5717556e3da02313e044e4a7600
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.0.2"
+ vm_service:
+ dependency: transitive
+ description:
+ name: vm_service
+ sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
+ url: "https://pub.dev"
+ source: hosted
+ version: "14.2.5"
+ watcher:
+ dependency: transitive
+ description:
+ name: watcher
+ sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.0"
+ web:
+ dependency: transitive
+ description:
+ name: web
+ sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.0"
+ web_socket:
+ dependency: transitive
+ description:
+ name: web_socket
+ sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.1.6"
+ web_socket_channel:
+ dependency: "direct main"
+ description:
+ name: web_socket_channel
+ sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.1"
+ win32:
+ dependency: transitive
+ description:
+ name: win32
+ sha256: "84ba388638ed7a8cb3445a320c8273136ab2631cd5f2c57888335504ddab1bc2"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.8.0"
+ win32_registry:
+ dependency: transitive
+ description:
+ name: win32_registry
+ sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.5"
+ x25519:
+ dependency: transitive
+ description:
+ name: x25519
+ sha256: cec3c125f0d934dccba6c4cab48f3fbf866dc78895dcc5a1584d35b0a845005b
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.1.1"
+ xdg_directories:
+ dependency: transitive
+ description:
+ name: xdg_directories
+ sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.0"
+ xml:
+ dependency: transitive
+ description:
+ name: xml
+ sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.5.0"
+ yaml:
+ dependency: transitive
+ description:
+ name: yaml
+ sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.2"
+sdks:
+ dart: ">=3.5.4 <4.0.0"
+ flutter: ">=3.24.0"
diff --git a/pubspec.yaml b/pubspec.yaml
new file mode 100644
index 0000000..0ecf289
--- /dev/null
+++ b/pubspec.yaml
@@ -0,0 +1,57 @@
+name: connect
+description: "Connect securely with your friends."
+
+# Prevent accidental publishing to pub.dev.
+publish_to: 'none'
+
+version: 1.0.0+1
+
+environment:
+ sdk: ^3.5.4
+
+dependencies:
+ camera: ^0.11.0+2
+ collection: ^1.18.0
+ cv: ^1.1.3
+ flutter:
+ sdk: flutter
+ flutter_localizations:
+ sdk: flutter
+ flutter_secure_storage: ^9.2.2
+ google_fonts: ^6.2.1
+ image: ^4.3.0
+ json_annotation: ^4.9.0
+ libsignal_protocol_dart: ^0.7.1
+ logging: ^1.3.0
+ mobile_scanner: ^6.0.2
+ path: ^1.9.0
+ path_provider: ^2.1.5
+ permission_handler: ^11.3.1
+ pro_image_editor: ^6.1.3
+ restart_app: ^1.3.2
+ sqflite_sqlcipher: ^3.1.0+1
+ web_socket_channel: ^3.0.1
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+ build_runner: ^2.3.3
+ json_serializable: ^6.8.0
+ flutter_lints: ^5.0.0
+ flutter_launcher_icons: ^0.14.1
+
+flutter_launcher_icons:
+ android: "launcher_icon"
+ ios: true
+ image_path: "assets/images/logo.png"
+ min_sdk_android: 21 # android min sdk min:16, default 21
+
+flutter:
+ uses-material-design: true
+
+ # Enable generation of localized Strings from arb files.
+ generate: true
+
+ assets:
+ # Add assets from the images directory to the application.
+ - assets/images/
diff --git a/test/unit_test.dart b/test/unit_test.dart
new file mode 100644
index 0000000..026f329
--- /dev/null
+++ b/test/unit_test.dart
@@ -0,0 +1,15 @@
+// This is an example unit test.
+//
+// A unit test tests a single function, method, or class. To learn more about
+// writing unit tests, visit
+// https://flutter.dev/to/unit-testing
+
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+ group('Plus Operator', () {
+ test('should add two numbers together', () {
+ expect(1 + 1, 2);
+ });
+ });
+}
diff --git a/test/widget_test.dart b/test/widget_test.dart
new file mode 100644
index 0000000..1d8332f
--- /dev/null
+++ b/test/widget_test.dart
@@ -0,0 +1,31 @@
+// This is an example Flutter widget test.
+//
+// To perform an interaction with a widget in your test, use the WidgetTester
+// utility in the flutter_test package. For example, you can send tap and scroll
+// gestures. You can also use WidgetTester to find child widgets in the widget
+// tree, read text, and verify that the values of widget properties are correct.
+//
+// Visit https://flutter.dev/to/widget-testing for
+// more information about Widget testing.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+ group('MyWidget', () {
+ testWidgets('should display a string of text', (WidgetTester tester) async {
+ // Define a Widget
+ const myWidget = MaterialApp(
+ home: Scaffold(
+ body: Text('Hello'),
+ ),
+ );
+
+ // Build myWidget and trigger a frame.
+ await tester.pumpWidget(myWidget);
+
+ // Verify myWidget shows some text
+ expect(find.byType(Text), findsOneWidget);
+ });
+ });
+}
diff --git a/web/favicon.png b/web/favicon.png
new file mode 100644
index 0000000..8aaa46a
Binary files /dev/null and b/web/favicon.png differ
diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png
new file mode 100644
index 0000000..b749bfe
Binary files /dev/null and b/web/icons/Icon-192.png differ
diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png
new file mode 100644
index 0000000..88cfd48
Binary files /dev/null and b/web/icons/Icon-512.png differ
diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png
new file mode 100644
index 0000000..eb9b4d7
Binary files /dev/null and b/web/icons/Icon-maskable-192.png differ
diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png
new file mode 100644
index 0000000..d69c566
Binary files /dev/null and b/web/icons/Icon-maskable-512.png differ
diff --git a/web/index.html b/web/index.html
new file mode 100644
index 0000000..85c6dab
--- /dev/null
+++ b/web/index.html
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ connect
+
+
+
+
+
+
diff --git a/web/manifest.json b/web/manifest.json
new file mode 100644
index 0000000..57574b7
--- /dev/null
+++ b/web/manifest.json
@@ -0,0 +1,35 @@
+{
+ "name": "connect",
+ "short_name": "connect",
+ "start_url": ".",
+ "display": "standalone",
+ "background_color": "#0175C2",
+ "theme_color": "#0175C2",
+ "description": "A new Flutter project.",
+ "orientation": "portrait-primary",
+ "prefer_related_applications": false,
+ "icons": [
+ {
+ "src": "icons/Icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-maskable-192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "purpose": "maskable"
+ },
+ {
+ "src": "icons/Icon-maskable-512.png",
+ "sizes": "512x512",
+ "type": "image/png",
+ "purpose": "maskable"
+ }
+ ]
+}
diff --git a/windows/.gitignore b/windows/.gitignore
new file mode 100644
index 0000000..d492d0d
--- /dev/null
+++ b/windows/.gitignore
@@ -0,0 +1,17 @@
+flutter/ephemeral/
+
+# Visual Studio user-specific files.
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# Visual Studio build-related files.
+x64/
+x86/
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt
new file mode 100644
index 0000000..4bdc26d
--- /dev/null
+++ b/windows/CMakeLists.txt
@@ -0,0 +1,108 @@
+# Project-level configuration.
+cmake_minimum_required(VERSION 3.14)
+project(connect 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 "connect")
+
+# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
+# versions of CMake.
+cmake_policy(VERSION 3.14...3.25)
+
+# Define build configuration option.
+get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(IS_MULTICONFIG)
+ set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
+ CACHE STRING "" FORCE)
+else()
+ 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()
+endif()
+# Define settings for the Profile build mode.
+set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
+set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
+set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
+set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
+
+# Use Unicode for all projects.
+add_definitions(-DUNICODE -D_UNICODE)
+
+# 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_17)
+ target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
+ target_compile_options(${TARGET} PRIVATE /EHsc)
+ target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
+ target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>")
+endfunction()
+
+# Flutter library and tool build rules.
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# Application build; see runner/CMakeLists.txt.
+add_subdirectory("runner")
+
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# Support files are copied into place next to the executable, so that it can
+# run in place. This is done instead of making a separate bundle (as on Linux)
+# so that building and running from within Visual Studio will work.
+set(BUILD_BUNDLE_DIR "$")
+# Make the "install" step default, as it's required to run.
+set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
+
+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)
+
+if(PLUGIN_BUNDLED_LIBRARIES)
+ install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
+
+# Copy the native assets provided by the build.dart from all packages.
+set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/")
+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.
+install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ CONFIGURATIONS Profile;Release
+ COMPONENT Runtime)
diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt
new file mode 100644
index 0000000..903f489
--- /dev/null
+++ b/windows/flutter/CMakeLists.txt
@@ -0,0 +1,109 @@
+# This file controls Flutter-level build steps. It should not be edited.
+cmake_minimum_required(VERSION 3.14)
+
+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.
+set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
+
+# Set fallback configurations for older versions of the flutter tool.
+if (NOT DEFINED FLUTTER_TARGET_PLATFORM)
+ set(FLUTTER_TARGET_PLATFORM "windows-x64")
+endif()
+
+# === Flutter Library ===
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
+
+# 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/windows/app.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "flutter_export.h"
+ "flutter_windows.h"
+ "flutter_messenger.h"
+ "flutter_plugin_registrar.h"
+ "flutter_texture_registrar.h"
+)
+list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
+add_dependencies(flutter flutter_assemble)
+
+# === Wrapper ===
+list(APPEND CPP_WRAPPER_SOURCES_CORE
+ "core_implementations.cc"
+ "standard_codec.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
+ "plugin_registrar.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_APP
+ "flutter_engine.cc"
+ "flutter_view_controller.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
+
+# Wrapper sources needed for a plugin.
+add_library(flutter_wrapper_plugin STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+)
+apply_standard_settings(flutter_wrapper_plugin)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ POSITION_INDEPENDENT_CODE ON)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ CXX_VISIBILITY_PRESET hidden)
+target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
+target_include_directories(flutter_wrapper_plugin PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_plugin flutter_assemble)
+
+# Wrapper sources needed for the runner.
+add_library(flutter_wrapper_app STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
+apply_standard_settings(flutter_wrapper_app)
+target_link_libraries(flutter_wrapper_app PUBLIC flutter)
+target_include_directories(flutter_wrapper_app PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_app 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.
+set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
+set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+ ${PHONY_OUTPUT}
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
+ ${FLUTTER_TARGET_PLATFORM} $
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc
new file mode 100644
index 0000000..8d54613
--- /dev/null
+++ b/windows/flutter/generated_plugin_registrant.cc
@@ -0,0 +1,20 @@
+//
+// Generated file. Do not edit.
+//
+
+// clang-format off
+
+#include "generated_plugin_registrant.h"
+
+#include
+#include
+#include
+
+void RegisterPlugins(flutter::PluginRegistry* registry) {
+ EmojiPickerFlutterPluginCApiRegisterWithRegistrar(
+ registry->GetRegistrarForPlugin("EmojiPickerFlutterPluginCApi"));
+ FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
+ registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
+ PermissionHandlerWindowsPluginRegisterWithRegistrar(
+ registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
+}
diff --git a/windows/flutter/generated_plugin_registrant.h b/windows/flutter/generated_plugin_registrant.h
new file mode 100644
index 0000000..dc139d8
--- /dev/null
+++ b/windows/flutter/generated_plugin_registrant.h
@@ -0,0 +1,15 @@
+//
+// Generated file. Do not edit.
+//
+
+// clang-format off
+
+#ifndef GENERATED_PLUGIN_REGISTRANT_
+#define GENERATED_PLUGIN_REGISTRANT_
+
+#include
+
+// Registers Flutter plugins.
+void RegisterPlugins(flutter::PluginRegistry* registry);
+
+#endif // GENERATED_PLUGIN_REGISTRANT_
diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake
new file mode 100644
index 0000000..138b578
--- /dev/null
+++ b/windows/flutter/generated_plugins.cmake
@@ -0,0 +1,26 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+ emoji_picker_flutter
+ flutter_secure_storage_windows
+ permission_handler_windows
+)
+
+list(APPEND FLUTTER_FFI_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
+
+foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
+endforeach(ffi_plugin)
diff --git a/windows/runner/CMakeLists.txt b/windows/runner/CMakeLists.txt
new file mode 100644
index 0000000..394917c
--- /dev/null
+++ b/windows/runner/CMakeLists.txt
@@ -0,0 +1,40 @@
+cmake_minimum_required(VERSION 3.14)
+project(runner LANGUAGES CXX)
+
+# Define the application target. To change its name, change BINARY_NAME in the
+# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
+# work.
+#
+# Any new source files that you add to the application should be added here.
+add_executable(${BINARY_NAME} WIN32
+ "flutter_window.cpp"
+ "main.cpp"
+ "utils.cpp"
+ "win32_window.cpp"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+ "Runner.rc"
+ "runner.exe.manifest"
+)
+
+# Apply the standard set of build settings. This can be removed for applications
+# that need different build settings.
+apply_standard_settings(${BINARY_NAME})
+
+# Add preprocessor definitions for the build version.
+target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"")
+target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}")
+target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}")
+target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}")
+target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}")
+
+# Disable Windows macros that collide with C++ standard library functions.
+target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
+
+# Add dependency libraries and include directories. Add any application-specific
+# dependencies here.
+target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
+target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib")
+target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
+
+# Run the Flutter tool portions of the build. This must not be removed.
+add_dependencies(${BINARY_NAME} flutter_assemble)
diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc
new file mode 100644
index 0000000..5cd677d
--- /dev/null
+++ b/windows/runner/Runner.rc
@@ -0,0 +1,121 @@
+// Microsoft Visual C++ generated resource script.
+//
+#pragma code_page(65001)
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_APP_ICON ICON "resources\\app_icon.ico"
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
+#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
+#else
+#define VERSION_AS_NUMBER 1,0,0,0
+#endif
+
+#if defined(FLUTTER_VERSION)
+#define VERSION_AS_STRING FLUTTER_VERSION
+#else
+#define VERSION_AS_STRING "1.0.0"
+#endif
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VERSION_AS_NUMBER
+ PRODUCTVERSION VERSION_AS_NUMBER
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_APP
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "com.example" "\0"
+ VALUE "FileDescription", "connect" "\0"
+ VALUE "FileVersion", VERSION_AS_STRING "\0"
+ VALUE "InternalName", "connect" "\0"
+ VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0"
+ VALUE "OriginalFilename", "connect.exe" "\0"
+ VALUE "ProductName", "connect" "\0"
+ VALUE "ProductVersion", VERSION_AS_STRING "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp
new file mode 100644
index 0000000..955ee30
--- /dev/null
+++ b/windows/runner/flutter_window.cpp
@@ -0,0 +1,71 @@
+#include "flutter_window.h"
+
+#include
+
+#include "flutter/generated_plugin_registrant.h"
+
+FlutterWindow::FlutterWindow(const flutter::DartProject& project)
+ : project_(project) {}
+
+FlutterWindow::~FlutterWindow() {}
+
+bool FlutterWindow::OnCreate() {
+ if (!Win32Window::OnCreate()) {
+ return false;
+ }
+
+ RECT frame = GetClientArea();
+
+ // The size here must match the window dimensions to avoid unnecessary surface
+ // creation / destruction in the startup path.
+ flutter_controller_ = std::make_unique(
+ frame.right - frame.left, frame.bottom - frame.top, project_);
+ // Ensure that basic setup of the controller was successful.
+ if (!flutter_controller_->engine() || !flutter_controller_->view()) {
+ return false;
+ }
+ RegisterPlugins(flutter_controller_->engine());
+ SetChildContent(flutter_controller_->view()->GetNativeWindow());
+
+ flutter_controller_->engine()->SetNextFrameCallback([&]() {
+ this->Show();
+ });
+
+ // Flutter can complete the first frame before the "show window" callback is
+ // registered. The following call ensures a frame is pending to ensure the
+ // window is shown. It is a no-op if the first frame hasn't completed yet.
+ flutter_controller_->ForceRedraw();
+
+ return true;
+}
+
+void FlutterWindow::OnDestroy() {
+ if (flutter_controller_) {
+ flutter_controller_ = nullptr;
+ }
+
+ Win32Window::OnDestroy();
+}
+
+LRESULT
+FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ // Give Flutter, including plugins, an opportunity to handle window messages.
+ if (flutter_controller_) {
+ std::optional result =
+ flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
+ lparam);
+ if (result) {
+ return *result;
+ }
+ }
+
+ switch (message) {
+ case WM_FONTCHANGE:
+ flutter_controller_->engine()->ReloadSystemFonts();
+ break;
+ }
+
+ return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
+}
diff --git a/windows/runner/flutter_window.h b/windows/runner/flutter_window.h
new file mode 100644
index 0000000..6da0652
--- /dev/null
+++ b/windows/runner/flutter_window.h
@@ -0,0 +1,33 @@
+#ifndef RUNNER_FLUTTER_WINDOW_H_
+#define RUNNER_FLUTTER_WINDOW_H_
+
+#include
+#include
+
+#include
+
+#include "win32_window.h"
+
+// A window that does nothing but host a Flutter view.
+class FlutterWindow : public Win32Window {
+ public:
+ // Creates a new FlutterWindow hosting a Flutter view running |project|.
+ explicit FlutterWindow(const flutter::DartProject& project);
+ virtual ~FlutterWindow();
+
+ protected:
+ // Win32Window:
+ bool OnCreate() override;
+ void OnDestroy() override;
+ LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
+ LPARAM const lparam) noexcept override;
+
+ private:
+ // The project to run.
+ flutter::DartProject project_;
+
+ // The Flutter instance hosted by this window.
+ std::unique_ptr flutter_controller_;
+};
+
+#endif // RUNNER_FLUTTER_WINDOW_H_
diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp
new file mode 100644
index 0000000..e5e5d90
--- /dev/null
+++ b/windows/runner/main.cpp
@@ -0,0 +1,43 @@
+#include
+#include
+#include
+
+#include "flutter_window.h"
+#include "utils.h"
+
+int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
+ _In_ wchar_t *command_line, _In_ int show_command) {
+ // Attach to console when present (e.g., 'flutter run') or create a
+ // new console when running with a debugger.
+ if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
+ CreateAndAttachConsole();
+ }
+
+ // Initialize COM, so that it is available for use in the library and/or
+ // plugins.
+ ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+
+ flutter::DartProject project(L"data");
+
+ std::vector command_line_arguments =
+ GetCommandLineArguments();
+
+ project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
+
+ FlutterWindow window(project);
+ Win32Window::Point origin(10, 10);
+ Win32Window::Size size(1280, 720);
+ if (!window.Create(L"connect", origin, size)) {
+ return EXIT_FAILURE;
+ }
+ window.SetQuitOnClose(true);
+
+ ::MSG msg;
+ while (::GetMessage(&msg, nullptr, 0, 0)) {
+ ::TranslateMessage(&msg);
+ ::DispatchMessage(&msg);
+ }
+
+ ::CoUninitialize();
+ return EXIT_SUCCESS;
+}
diff --git a/windows/runner/resource.h b/windows/runner/resource.h
new file mode 100644
index 0000000..66a65d1
--- /dev/null
+++ b/windows/runner/resource.h
@@ -0,0 +1,16 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Runner.rc
+//
+#define IDI_APP_ICON 101
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico
new file mode 100644
index 0000000..c04e20c
Binary files /dev/null and b/windows/runner/resources/app_icon.ico differ
diff --git a/windows/runner/runner.exe.manifest b/windows/runner/runner.exe.manifest
new file mode 100644
index 0000000..153653e
--- /dev/null
+++ b/windows/runner/runner.exe.manifest
@@ -0,0 +1,14 @@
+
+
+