Compare commits

...

64 Commits

Author SHA1 Message Date
Muhammad Hamza
02b4a73de7 web QR dummy screen added 2021-10-19 17:04:51 +05:00
Muhammad Hamza
0f0a647c3c Merge branch 'flutter-app' of https://github.com/simplex-chat/simplex-chat into flutter-app
Some msg here
2021-10-19 16:13:14 +05:00
Muhammad Hamza
86de81adb3 bug fixes for mobile view 2021-10-19 16:13:01 +05:00
Evgeny Poberezkin
7d32be57a9 remove unused import 2021-10-19 08:31:45 +01:00
Evgeny Poberezkin
bc1c8589c3 update format 2021-10-19 08:16:49 +01:00
Muhammad Hamza
6b49e1d5e4 dfdserge branch 'flutter-app' of https://github.com/simplex-chat/simplex-chat into flutter-app 2021-10-19 10:20:20 +05:00
Muhammad Hamza
7790eea069 UI fixes + Scan fix 2021-10-19 10:19:41 +05:00
Evgeny Poberezkin
dd9d990e3f skip flutter test 2021-10-18 19:22:42 +01:00
Muhammad Hamza
504e0af11a Icon fixed 2021-10-18 23:15:36 +05:00
Muhammad Hamza
7248e6096d fixed the responsive issues + other 2021-10-18 22:11:31 +05:00
Evgeny Poberezkin
c183c819fc update code formatting 2021-10-18 07:46:01 +01:00
Muhammad Hamza
28122fa003 comments added for better understanding 2021-10-17 15:35:30 +05:00
Muhammad Hamza
e2e9f78705 bug report icon removed from invitation view 2021-10-16 11:02:20 +05:00
Muhammad Hamza
49444b3056 more options added in user 2021-10-16 11:01:55 +05:00
Muhammad Hamza
4e8ac3fafb Some pull 2021-10-15 11:46:39 +05:00
Muhammad Hamza
ff6cd50cc5 logo fix + bug fixes + updates 2021-10-15 11:43:56 +05:00
Muhammad Hamza
67df1c2b7b logo updates and other changes 2021-10-15 11:43:40 +05:00
Muhammad Hamza
c464724bc8 updates 2021-10-15 11:43:25 +05:00
Muhammad Hamza
6af87aa69c bug fixes 2021-10-15 11:43:13 +05:00
Evgeny Poberezkin
fad1873369 fix module import 2021-10-14 19:45:46 +01:00
Muhammad Hamza
ee533bd76a Merge branch 'flutter-app' of https://github.com/simplex-chat/simplex-chat into flutter-app
Pull before 2.12 dark sdk
2021-10-14 10:00:40 +05:00
Muhammad Hamza
6b5d4c3958 null safe code + latest dart SDK 2021-10-14 10:00:07 +05:00
Evgeny Poberezkin
4c81a280c8 fix imports 2021-10-13 07:35:49 +01:00
Muhammad Hamza
368f9ef617 changes as per asked | app icon | removal of chats/groups separation etc 2021-10-13 01:58:29 +05:00
Muhammad Hamza
deeda09ca3 group conversation + contact enabled 2021-10-11 20:43:08 +05:00
Muhammad Hamza
28a70074c8 edit profile available 2021-10-11 20:42:28 +05:00
Muhammad Hamza
0b7c618f33 fixes 2021-10-11 20:42:18 +05:00
Muhammad Hamza
0118d37232 add members + remove members 2021-10-11 20:42:08 +05:00
Muhammad Hamza
4f5ba12f2d overall bug fixes + UI changes 2021-10-11 14:57:05 +05:00
Muhammad Hamza
2275b582b4 splash added + login persistant 2021-10-10 16:30:28 +05:00
Muhammad Hamza
f635df038a new animation added 2021-10-10 16:30:14 +05:00
Muhammad Hamza
36c3acdf2c local storage + ui fixes 2021-10-10 16:30:01 +05:00
Muhammad Hamza
6704fc7cbb changes 2021-10-10 10:55:12 +05:00
Muhammad Hamza
34f345c237 invites view added 2021-10-10 10:54:47 +05:00
Muhammad Hamza
127b798acd conversation view added 2021-10-10 10:54:36 +05:00
Muhammad Hamza
c2e39982a1 drawer providers 2021-10-10 10:54:22 +05:00
Muhammad Hamza
e1d5e29365 group added 2021-10-10 10:54:02 +05:00
Muhammad Hamza
70dbf3966d changes 2021-10-09 22:37:33 +05:00
Muhammad Hamza
493ea01f85 new screens added 2021-10-09 22:32:22 +05:00
Muhammad Hamza
cf0cc8b305 changes 2021-10-09 22:32:01 +05:00
Muhammad Hamza
46e292193c message bubble in chat view 2021-10-09 22:31:50 +05:00
Muhammad Hamza
19d429d739 contact model for dummy working 2021-10-09 22:31:40 +05:00
Muhammad Hamza
451294ba08 fixes 2021-10-09 22:31:25 +05:00
Muhammad Hamza
f3182675de file name changes 2021-10-09 22:31:08 +05:00
Muhammad Hamza
80a0a4ef63 chat view added 2021-10-09 22:30:48 +05:00
Evgeny Poberezkin
23117fd96f remove deprecated accentColor 2021-10-09 18:13:40 +01:00
Evgeny Poberezkin
d6b6fde480 format code 2021-10-09 17:21:10 +01:00
Evgeny Poberezkin
44892e5b03 Merge branch 'v5' into flutter-app 2021-10-09 13:13:27 +01:00
Evgeny Poberezkin
fd247a4e6b style: prefer single quotes (#117) 2021-10-09 13:12:12 +01:00
Evgeny Poberezkin
19ef1f65db abstract Transport class, SocketTransport class (#115)
* abstract Transport class, SocketTransport class

* build: import simplexmq

* feat: simple io transport unit test for read and write

* more efficient buffer extension

Co-authored-by: alex <alex@tekartik.com>
2021-10-09 12:42:41 +01:00
Evgeny Poberezkin
268e794f5d remove unused assets 2021-10-08 21:16:49 +01:00
Evgeny Poberezkin
7f88684d8c linter warning 2021-10-08 21:12:22 +01:00
Evgeny Poberezkin
17b4b62133 make linter happier 2021-10-08 09:37:26 +01:00
Evgeny Poberezkin
4d3b33d877 remove duplicate simplex.md 2021-10-08 07:37:01 +01:00
Evgeny Poberezkin
1916d4f922 remove haskell folder 2021-10-08 07:34:28 +01:00
Evgeny Poberezkin
3ea1f05460 revert linter changes 2021-10-08 07:30:42 +01:00
Evgeny Poberezkin
1eaa306a70 revert README change 2021-10-08 07:29:12 +01:00
Evgeny Poberezkin
9fa683e7e4 remove extra files 2021-10-08 07:24:48 +01:00
mhmzdev
2292013507 initial push 2021-10-07 17:44:05 +05:00
mhmzdev
13b60b92bf first push flutter simplex chat app 2021-10-07 09:28:59 +05:00
Alexandre Roux
09ace76b82 build: setup dart simple ci using github actions (#114) 2021-10-04 20:27:37 +01:00
Evgeny Poberezkin
e198424da8 move flutter app to packages (#112)
* remove flutter app from root

* add flutter app to packages

* ci: haskell build only on haskell changes

* update app identifiers
2021-10-03 19:59:08 +01:00
Evgeny Poberezkin
a39cd2990f SMP protocol commands encoding/decoding (#111)
* SMP protocol commands encoding/decoding

* change "var" to type

* Parser `word` method now returns null if the word is empty

* refactor Parser `word` method

* move Parser `end` getter

* add linter rules, move linter options to root

* remove omit_local_variable_types linter rule

* ci: only build haskell on changes
2021-10-03 19:10:11 +01:00
Evgeny Poberezkin
fec99d526a Dart / flutter app template (#109) 2021-10-02 11:06:10 +01:00
134 changed files with 6918 additions and 3 deletions

View File

@@ -7,7 +7,13 @@ on:
- v5
tags:
- "v*"
paths:
- haskell/**
- .github/workflows/build_haskell.yml
pull_request:
paths:
- haskell/**
- .github/workflows/build_haskell.yml
jobs:
prepare-release:

32
.github/workflows/ci_dart.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: Run CI
on:
push:
workflow_dispatch:
schedule:
- cron: '0 0 * * 0' # every sunday at midnight
jobs:
test:
name: Test on ${{ matrix.os }} / ${{ matrix.flutter }}
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: packages/repo_support
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
flutter: [stable, beta, dev]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: '12.x'
- uses: subosito/flutter-action@v1
with:
channel: ${{ matrix.flutter }}
- run: flutter --version
- run: flutter upgrade
- run: dart --version
- run: dart pub get
- run: dart run tool/run_ci.dart

51
.gitignore vendored
View File

@@ -43,9 +43,54 @@ cabal.project.local~
*.cabal
stack.yaml.lock
# Idris
*.ibc
# chat database
*.db
*.db.bak
# Misc
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ
*.iml
*.ipr
*.iws
.idea/
# VS Code
.vscode/
# Flutter/Dart/Pub
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
build/
# Default behavior, only keep it for apps
pubspec.lock
# Web
lib/generated_plugin_registrant.dart
# Symbolication
app.*.symbols
# Obfuscation
app.*.map.json
# Android Studio build artifacts
/android/app/debug
/android/app/profile
/android/app/release

View File

@@ -0,0 +1,44 @@
include: package:lints/recommended.yaml
linter:
rules:
prefer_single_quotes: true
constant_identifier_names: false
always_declare_return_types: true
avoid_dynamic_calls: true
avoid_empty_else: true
avoid_relative_lib_imports: true
avoid_shadowing_type_parameters: true
avoid_slow_async_io: true
avoid_types_as_parameter_names: true
await_only_futures: true
camel_case_extensions: true
camel_case_types: true
cancel_subscriptions: true
curly_braces_in_flow_control_structures: true
directives_ordering: true
empty_catches: true
hash_and_equals: true
iterable_contains_unrelated_type: true
list_remove_unrelated_type: true
no_adjacent_strings_in_list: true
no_duplicate_case_values: true
package_api_docs: true
package_prefixed_library_names: true
prefer_generic_function_type_aliases: true
prefer_is_empty: true
prefer_is_not_empty: true
prefer_iterable_whereType: true
prefer_typing_uninitialized_variables: true
sort_child_properties_last: true
test_types_in_equals: true
throw_in_finally: true
unawaited_futures: true
unnecessary_import: true
unnecessary_null_aware_assignments: true
unnecessary_statements: true
unnecessary_type_check: true
unrelated_type_equality_checks: true
unsafe_html: true
use_full_hex_values_for_flutter_colors: true
valid_regexps: true

23
packages/repo_support/.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
# See https://www.dartlang.org/guides/libraries/private-files
# Files and directories created by pub
.dart_tool/
.packages
.pub/
build/
# If you're building an application, you may want to check-in your pubspec.lock
pubspec.lock
# Directory created by dartdoc
# If you don't generate documentation locally you can remove this line.
doc/api/
# Intellij
*.idea/
*.iml
# VS code
.vscode
# Local
.local/

View File

@@ -0,0 +1,8 @@
# repo_support
App flutter utils repo support
```
dart run tool/run_ci.dart
```

View File

@@ -0,0 +1,11 @@
name: simplex_repo_support
description: SimpleX build tools
version: 0.2.0
publish_to: none
environment:
sdk: '>=2.12.0 <3.0.0'
dev_dependencies:
dev_test:

View File

@@ -0,0 +1,14 @@
// ignore_for_file: prefer_double_quotes
import 'package:dev_test/package.dart';
import 'package:path/path.dart';
Future main() async {
for (var dir in [
'simplex_app',
'simplexmq',
'repo_support',
]) {
await packageRunCi(join('..', dir));
}
}

2
packages/simplex_app/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
# Keep for apps
!pubspec.lock

View File

@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 4cc385b4b84ac2f816d939a49ea1f328c4e0b48e
channel: stable
project_type: app

View File

@@ -0,0 +1,16 @@
# simplex_app
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View File

@@ -0,0 +1,45 @@
# https://dart.dev/guides/language/analysis-options
include: package:flutter_lints/flutter.yaml
linter:
# https://dart-lang.github.io/linter/lints/index.html.
rules:
prefer_single_quotes: true
always_declare_return_types: true
avoid_dynamic_calls: true
avoid_empty_else: true
avoid_relative_lib_imports: true
avoid_shadowing_type_parameters: true
avoid_slow_async_io: true
avoid_types_as_parameter_names: true
await_only_futures: true
camel_case_extensions: true
camel_case_types: true
cancel_subscriptions: true
curly_braces_in_flow_control_structures: true
directives_ordering: true
empty_catches: true
hash_and_equals: true
iterable_contains_unrelated_type: true
list_remove_unrelated_type: true
no_adjacent_strings_in_list: true
no_duplicate_case_values: true
package_api_docs: true
package_prefixed_library_names: true
prefer_generic_function_type_aliases: true
prefer_is_empty: true
prefer_is_not_empty: true
prefer_iterable_whereType: true
prefer_typing_uninitialized_variables: true
sort_child_properties_last: true
test_types_in_equals: true
throw_in_finally: true
unawaited_futures: true
unnecessary_import: true
unnecessary_null_aware_assignments: true
unnecessary_statements: true
unnecessary_type_check: true
unrelated_type_equality_checks: true
unsafe_html: true
use_full_hex_values_for_flutter_colors: true
valid_regexps: true

13
packages/simplex_app/android/.gitignore vendored Normal file
View File

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

View File

@@ -0,0 +1,68 @@
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 30
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.simplex_chat"
minSdkVersion 20
targetSdkVersion 30
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
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 '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

View File

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

View File

@@ -0,0 +1,41 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.simplex_chat">
<application
android:label="SimpleX Chat"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<!-- Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame. -->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>

View File

@@ -0,0 +1,6 @@
package com.example.simplex_chat
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,29 @@
buildscript {
ext.kotlin_version = '1.5.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true

View File

@@ -0,0 +1,6 @@
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip

View File

@@ -0,0 +1,11 @@
include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,12 @@
<svg width="815" height="233" viewBox="0 0 815 233" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.5683 66.9141C43.9954 66.9141 47.376 67.2323 50.7104 67.8687C54.0448 68.5051 57.2403 69.3234 60.2968 70.3235C63.3533 71.3236 66.2014 72.4601 68.8411 73.7329C71.4809 75.0058 73.9122 76.2786 76.1351 77.5515L66.132 96.0987L65.9952 95.9474C65.6001 95.5596 64.6732 94.9282 63.2144 94.0531C61.5472 93.053 59.4632 92.0074 56.9624 90.9164C54.4616 89.8254 51.7293 88.8708 48.7654 88.0525C45.8015 87.2342 42.8376 86.8251 39.8737 86.8251C31.723 86.8251 27.6476 89.5072 27.6476 94.8713C27.6476 96.5079 28.0876 97.8716 28.9675 98.9627C29.8474 100.054 31.1441 101.031 32.8576 101.895C34.5711 102.758 36.7245 103.554 39.318 104.281C41.9114 105.009 44.9216 105.827 48.3486 106.736C53.0723 108.009 57.3329 109.395 61.1304 110.896C64.9279 112.396 68.1465 114.26 70.7862 116.487C73.4259 118.715 75.4636 121.419 76.8993 124.602C78.3349 127.784 79.0527 131.602 79.0527 136.057C79.0527 141.512 78.0107 146.126 75.9267 149.899C73.8427 153.673 71.0872 156.718 67.6602 159.037C64.2332 161.355 60.2968 163.037 55.8509 164.083C51.4051 165.128 46.8203 165.651 42.0966 165.651C38.4844 165.651 34.7795 165.378 30.982 164.833C27.1845 164.287 23.4796 163.492 19.8674 162.446C16.2551 161.401 12.7587 160.15 9.37796 158.696C5.99726 157.241 2.87128 155.559 0 153.65L10.0032 134.148L10.1812 134.338C10.6877 134.825 11.8324 135.625 13.6154 136.739C15.6531 138.012 18.177 139.285 21.1872 140.558C24.1974 141.83 27.555 142.967 31.2599 143.967C34.9647 144.967 38.7159 145.467 42.5134 145.467C50.5715 145.467 54.6006 143.058 54.6006 138.239C54.6006 136.421 53.9985 134.921 52.7944 133.739C51.5903 132.557 49.9231 131.489 47.7928 130.534C45.6625 129.579 43.1386 128.693 40.221 127.875C37.3034 127.056 34.1311 126.147 30.7041 125.147C26.1657 123.783 22.2292 122.306 18.8948 120.715C15.5605 119.124 12.805 117.283 10.6284 115.192C8.45174 113.1 6.83086 110.691 5.76571 107.964C4.70056 105.236 4.16798 102.054 4.16798 98.4171C4.16798 93.3257 5.14051 88.8253 7.08557 84.9158C9.03063 81.0063 11.6703 77.7106 15.0047 75.0285C18.3391 72.3464 22.2061 70.3235 26.6056 68.9597C31.0051 67.5959 35.6594 66.9141 40.5683 66.9141ZM131.47 67.7323V164.56H108.685V67.7323H131.47ZM191.39 67.7323L216.954 118.328L242.657 67.7323H267.248V164.56H244.463V106.6L223.067 148.74H210.841L189.445 106.6V164.56H166.66V67.7323H191.39ZM419.925 67.7323V144.922H467.718V164.56H397.14V67.7323H419.925ZM560.072 67.7323V87.3706H514.78V106.191H553.681V124.329H514.78V144.922H561.323V164.56H491.995V67.7323H560.072ZM341.818 67.7323L343.378 67.7677C347.5 67.9563 351.31 68.8991 354.809 70.5962C358.745 72.5055 362.126 75.0058 364.951 78.097C367.776 81.1882 369.999 84.6885 371.62 88.598C373.24 92.5075 374.051 96.4624 374.051 100.463C374.051 104.736 373.287 108.827 371.758 112.737C370.23 116.646 368.1 120.147 365.368 123.238C362.635 126.329 359.324 128.784 355.434 130.602C351.976 132.218 348.189 133.116 344.072 133.296L342.513 133.33H322.507V164.56H299.722V67.7323H341.818ZM340.29 87.3706H322.507V113.828H341.124C343.81 113.828 346.125 112.691 348.07 110.418C350.016 108.145 350.988 104.827 350.988 100.463C350.988 98.1898 350.687 96.2351 350.085 94.5986C349.483 92.9621 348.672 91.5983 347.654 90.5073C346.635 89.4163 345.477 88.6207 344.18 88.1207C342.884 87.6206 341.587 87.3706 340.29 87.3706Z" fill="#062D56"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M642.628 136.08L680.309 173.782L699.513 154.567L699.506 154.561L737.917 116.134L700.236 78.4367L700.243 78.4334L681.404 59.5826L642.993 98.014L642.99 98.0104L681.401 59.5829L643.725 21.881L662.929 2.6652L700.605 40.3673L739.016 1.93511L757.855 20.7859L719.443 59.2176L757.121 96.918L795.533 58.4875L814.373 77.3382L775.959 115.768L813.643 153.471L794.439 172.687L756.756 134.984L718.348 173.415L756.031 211.119L736.827 230.335L699.144 192.63L660.74 231.065L641.901 212.214L680.306 173.78L642.625 136.083L642.628 136.08Z" fill="#07B4B9"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M604.77 59.7651L642.446 97.4664L680.856 59.035L699.696 77.8858L661.285 116.317L698.966 154.019L679.762 173.235L642.081 135.532L603.675 173.965L584.836 155.114L623.243 116.682L585.566 78.9809L604.77 59.7651Z" fill="#062D56"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="815" height="233" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -0,0 +1,4 @@
<svg width="24" height="18" viewBox="0 0 24 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 12H15" stroke="#0D1333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3 6H21" stroke="#0D1333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<clipPath id="clip-path">
<rect id="Rectangle_2" data-name="Rectangle 2" width="2971" height="843" transform="translate(0 0.292)" fill="#fff"/>
</clipPath>
<clipPath id="clip-simpleX">
<rect width="1024" height="1024"/>
</clipPath>
</defs>
<g id="simpleX" clip-path="url(#clip-simpleX)">
<g id="logo" transform="translate(-2047 89.708)" clip-path="url(#clip-path)">
<path id="Path_6" data-name="Path 6" d="M147.9,66.914a198.176,198.176,0,0,1,36.975,3.455,284.4,284.4,0,0,1,34.949,8.885,253.446,253.446,0,0,1,31.15,12.34q14.436,6.911,26.592,13.821L241.1,172.542l-.5-.548q-2.161-2.105-10.138-6.856a202.569,202.569,0,0,0-22.793-11.353,224.542,224.542,0,0,0-29.884-10.365,122.159,122.159,0,0,0-32.417-4.442q-44.573,0-44.573,29.122,0,8.885,4.812,14.808t14.182,10.613a133.233,133.233,0,0,0,23.553,8.636q14.182,3.952,32.923,8.885a437.841,437.841,0,0,1,46.6,15.056q20.767,8.143,35.2,20.236A82.161,82.161,0,0,1,280.355,275.7q7.851,17.275,7.851,41.459,0,29.615-11.4,50.1a94.466,94.466,0,0,1-30.137,33.073A129.137,129.137,0,0,1,203.618,418.6a219.939,219.939,0,0,1-50.145,5.675,287.161,287.161,0,0,1-40.521-2.961,299.792,299.792,0,0,1-40.521-8.639A327.515,327.515,0,0,1,34.19,399.1,215.84,215.84,0,0,1,0,380.839l36.469-70.584.649.688q2.77,2.644,12.52,8.69a199.248,199.248,0,0,0,27.6,13.822,287.382,287.382,0,0,0,36.722,12.338,157.851,157.851,0,0,0,41.027,5.429q44.067,0,44.067-26.16a21.773,21.773,0,0,0-6.585-16.287q-6.585-6.417-18.235-11.6a201.006,201.006,0,0,0-27.6-9.624q-15.955-4.446-34.7-9.874a304.855,304.855,0,0,1-43.054-16.041Q50.651,253,38.748,241.647a72.548,72.548,0,0,1-17.728-26.16Q15.2,200.676,15.2,180.933q0-27.641,10.637-48.865A105.947,105.947,0,0,1,54.7,96.283,126.757,126.757,0,0,1,97,74.318,172.123,172.123,0,0,1,147.9,66.914Zm331.4,2.961v350.45H396.237V69.875Zm218.452,0L790.957,253,884.663,69.875h89.652v350.45H891.247V210.55l-78,152.518H768.671l-78-152.518V420.325H607.6V69.875Zm833.178,0V349.249h174.241v71.076H1447.868V69.875Zm510.939,0v71.077H1876.752V209.07h141.823v65.647H1876.752v74.533h169.683v71.076H1793.684V69.875Zm-795.7,0,5.687.128a106.112,106.112,0,0,1,41.674,10.237,121.073,121.073,0,0,1,36.975,27.148,129.172,129.172,0,0,1,24.313,38.006,110.611,110.611,0,0,1,8.863,42.943,120.122,120.122,0,0,1-8.36,44.423,126.219,126.219,0,0,1-23.3,38.006,109.262,109.262,0,0,1-77.64,36.4l-5.684.123h-72.937V420.325h-83.068V69.875Zm-5.571,71.077h-64.832V236.71h67.873q14.689,0,25.323-12.342t10.638-36.03q0-12.341-3.292-21.225a43.245,43.245,0,0,0-8.863-14.808,33.932,33.932,0,0,0-12.665-8.638A39.4,39.4,0,0,0,1240.607,140.953Z" transform="translate(0 175.261)" fill="#062d56" fill-rule="evenodd"/>
<path id="Path_7" data-name="Path 7" d="M644.551,487.448,781.926,623.9l70.013-69.545-.026-.022L991.95,415.257,854.575,278.818l.025-.012-68.682-68.227-140.036,139.1-.011-.013L785.908,210.581,648.551,74.126,718.563,4.578,855.92,141.033,995.957,1.935l68.682,68.227L924.6,209.258l137.364,136.45L1202,206.616l68.685,68.227L1130.641,413.932l137.386,136.459-70.013,69.549L1060.632,483.481,920.607,622.575l137.382,136.463-70.013,69.549L850.594,692.12,710.583,831.228,641.9,763,781.916,623.9,644.541,487.458Z" transform="translate(1698.3 5.061)" fill="#07b4b9" fill-rule="evenodd"/>
<path id="Path_8" data-name="Path 8" d="M657.51,61.677,794.867,198.13,934.9,59.035l68.686,68.227L863.549,266.357l137.375,136.455-70.013,69.549L793.536,335.9,653.518,475l-68.682-68.228,140.022-139.1L587.5,131.225Z" transform="translate(1547.321 154.623)" fill="#062d56" fill-rule="evenodd"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

33
packages/simplex_app/ios/.gitignore vendored Normal file
View File

@@ -0,0 +1,33 @@
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,91 @@
PODS:
- DKImagePickerController/Core (4.3.2):
- DKImagePickerController/ImageDataManager
- DKImagePickerController/Resource
- DKImagePickerController/ImageDataManager (4.3.2)
- DKImagePickerController/PhotoGallery (4.3.2):
- DKImagePickerController/Core
- DKPhotoGallery
- DKImagePickerController/Resource (4.3.2)
- DKPhotoGallery (0.0.17):
- DKPhotoGallery/Core (= 0.0.17)
- DKPhotoGallery/Model (= 0.0.17)
- DKPhotoGallery/Preview (= 0.0.17)
- DKPhotoGallery/Resource (= 0.0.17)
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Core (0.0.17):
- DKPhotoGallery/Model
- DKPhotoGallery/Preview
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Model (0.0.17):
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Preview (0.0.17):
- DKPhotoGallery/Model
- DKPhotoGallery/Resource
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Resource (0.0.17):
- SDWebImage
- SwiftyGif
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
- Flutter (1.0.0)
- image_picker (0.0.1):
- Flutter
- MTBBarcodeScanner (5.0.11)
- qr_code_scanner (0.2.0):
- Flutter
- MTBBarcodeScanner
- SDWebImage (5.12.0):
- SDWebImage/Core (= 5.12.0)
- SDWebImage/Core (5.12.0)
- share (0.0.1):
- Flutter
- SwiftyGif (5.4.0)
DEPENDENCIES:
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`)
- image_picker (from `.symlinks/plugins/image_picker/ios`)
- qr_code_scanner (from `.symlinks/plugins/qr_code_scanner/ios`)
- share (from `.symlinks/plugins/share/ios`)
SPEC REPOS:
trunk:
- DKImagePickerController
- DKPhotoGallery
- MTBBarcodeScanner
- SDWebImage
- SwiftyGif
EXTERNAL SOURCES:
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
Flutter:
:path: Flutter
image_picker:
:path: ".symlinks/plugins/image_picker/ios"
qr_code_scanner:
:path: ".symlinks/plugins/qr_code_scanner/ios"
share:
:path: ".symlinks/plugins/share/ios"
SPEC CHECKSUMS:
DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
image_picker: 50e7c7ff960e5f58faa4d1f4af84a771c671bc4a
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e
SDWebImage: 4ea20cca2986adc5aacde07aa686742fd4c67a37
share: 0b2c3e82132f5888bccca3351c504d0003b3b410
SwiftyGif: 5d4af95df24caf1c570dbbcb32a3b8a0763bc6d7
PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
COCOAPODS: 1.11.0

View File

@@ -0,0 +1,539 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
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 */; };
AE3860269BF424274DA84C73 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B0B8C697FBAC21A75BCBB5A /* Pods_Runner.framework */; };
/* End PBXBuildFile 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 */
031FDA72D548A2F92732ADA4 /* 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 = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
2FD15BE67425B26EB06BB4AE /* 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 = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
4B0B8C697FBAC21A75BCBB5A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
7E09253D517C54DFA8ECEE7A /* 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 = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
AE3860269BF424274DA84C73 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
415DD71D33C5D1C1BBA9E62B /* Frameworks */ = {
isa = PBXGroup;
children = (
4B0B8C697FBAC21A75BCBB5A /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
5A0F33679D6B606E9775E794 /* Pods */ = {
isa = PBXGroup;
children = (
7E09253D517C54DFA8ECEE7A /* Pods-Runner.debug.xcconfig */,
2FD15BE67425B26EB06BB4AE /* Pods-Runner.release.xcconfig */,
031FDA72D548A2F92732ADA4 /* Pods-Runner.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
5A0F33679D6B606E9775E794 /* Pods */,
415DD71D33C5D1C1BBA9E62B /* Frameworks */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
AA6465993557C0297F2D0450 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
B4CC1E87FA75B139B238C706 /* [CP] Embed Pods Frameworks */,
);
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 = {
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = "";
TargetAttributes = {
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 */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
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;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
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;
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";
};
AA6465993557C0297F2D0450 /* [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;
};
B4CC1E87FA75B139B238C706 /* [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;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
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;
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 = 9.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.simplexChat;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
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;
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 = 9.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;
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;
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 = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
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.simplexChat;
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.simplexChat;
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 */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,122 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 824 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

View File

@@ -0,0 +1,5 @@
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.

View File

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

View File

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

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>io.flutter.embedded_views_preview</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to scan QR codes</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>simplex_chat</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>

View File

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

View File

@@ -0,0 +1,78 @@
import 'dart:async';
import 'package:flutter/material.dart';
class Animator extends StatefulWidget {
final Widget child;
final Duration time;
const Animator(this.child, this.time, {Key? key}) : super(key: key);
@override
_AnimatorState createState() => _AnimatorState();
}
class _AnimatorState extends State<Animator>
with SingleTickerProviderStateMixin {
Timer? timer;
AnimationController? animationController;
Animation? animation;
@override
void initState() {
super.initState();
animationController = AnimationController(
duration: const Duration(milliseconds: 290), vsync: this);
animation =
CurvedAnimation(parent: animationController!, curve: Curves.easeInOut);
timer = Timer(widget.time, animationController!.forward);
}
@override
void dispose() {
animationController!.dispose();
super.dispose();
timer!.cancel();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation!,
child: widget.child,
builder: (BuildContext context, Widget? child) {
return Opacity(
opacity: animation!.value,
child: Transform.translate(
offset: Offset(0.0, (1 - animation!.value) * 20),
child: child,
),
);
},
);
}
}
Timer? timer;
Duration duration = const Duration();
Duration wait() {
if (timer == null || !timer!.isActive) {
timer = Timer(const Duration(microseconds: 120), () {
duration = const Duration();
});
}
duration += const Duration(milliseconds: 100);
return duration;
}
class WidgetAnimator extends StatelessWidget {
final Widget child;
const WidgetAnimator({required this.child, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Animator(child, wait());
}
}

View File

@@ -0,0 +1,70 @@
import 'package:flutter/material.dart';
class EntranceFader extends StatefulWidget {
/// Child to be animated on entrance
final Widget child;
/// Delay after which the animation will start
final Duration delay;
/// Duration of entrance animation
final Duration duration;
/// Starting point from which the widget will fade to its default position
final Offset offset;
const EntranceFader({
Key? key,
required this.child,
this.delay = const Duration(milliseconds: 0),
this.duration = const Duration(milliseconds: 400),
this.offset = const Offset(0.0, 32.0),
}) : super(key: key);
@override
EntranceFaderState createState() {
return EntranceFaderState();
}
}
class EntranceFaderState extends State<EntranceFader>
with SingleTickerProviderStateMixin {
AnimationController? _controller;
Animation? _dxAnimation;
Animation? _dyAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: widget.duration);
_dxAnimation =
Tween(begin: widget.offset.dx, end: 0.0).animate(_controller!);
_dyAnimation =
Tween(begin: widget.offset.dy, end: 0.0).animate(_controller!);
Future.delayed(widget.delay, () {
if (mounted) {
_controller!.forward();
}
});
}
@override
void dispose() {
_controller!.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller!,
builder: (context, child) => Opacity(
opacity: _controller!.value,
child: Transform.translate(
offset: Offset(_dxAnimation!.value, _dyAnimation!.value),
child: widget.child,
),
),
);
}
}

View File

@@ -0,0 +1,12 @@
/// All the named routes are mentioned here
class AppRoutes {
static const splash = '/splash';
static const intro = '/intro';
static const setupProfile = '/setupProfile';
static const home = '/home';
static const addContact = '/addContact';
static const addContactWeb = '/addContactWeb';
static const scanInvitation = '/ScanInvitation';
static const addGroup = '/addGroup';
}

View File

@@ -0,0 +1,24 @@
import 'package:flutter/material.dart';
// colors
const kPrimaryColor = Color(0xff062d56);
const kSecondaryColor = Color(0xff07b4b9);
// text styles
const TextStyle kHeadingStyle = TextStyle(
fontSize: 28.0,
fontWeight: FontWeight.bold,
letterSpacing: 1.3,
color: kPrimaryColor,
);
const TextStyle kMediumHeadingStyle = TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w500,
color: kPrimaryColor,
);
const TextStyle kSmallHeadingStyle = TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.w500,
);

View File

@@ -0,0 +1,18 @@
import 'package:flutter/material.dart';
class ScrollBehaviorModified extends ScrollBehavior {
const ScrollBehaviorModified();
@override
ScrollPhysics getScrollPhysics(BuildContext context) {
switch (getPlatform(context)) {
case TargetPlatform.iOS:
case TargetPlatform.macOS:
case TargetPlatform.android:
return const BouncingScrollPhysics();
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
return const ClampingScrollPhysics();
}
}
}

View File

@@ -0,0 +1,20 @@
//
// Generated file. Do not edit.
//
// ignore_for_file: directives_ordering
// ignore_for_file: lines_longer_than_80_chars
import 'package:file_picker/_internal/file_picker_web.dart';
import 'package:image_picker_for_web/image_picker_for_web.dart';
import 'package:shared_preferences_web/shared_preferences_web.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
// ignore: public_member_api_docs
void registerPlugins(Registrar registrar) {
FilePickerWeb.registerWith(registrar);
ImagePickerPlugin.registerWith(registrar);
SharedPreferencesPlugin.registerWith(registrar);
registrar.registerMessageHandler();
}

View File

@@ -0,0 +1,67 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:simplex_chat/app_routes.dart';
import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/custom_scroll_behavior.dart';
import 'package:simplex_chat/providers/drawer_providers.dart';
import 'package:simplex_chat/views/contacts/add_contact_view.dart';
import 'package:simplex_chat/views/contacts/add_contact_web.dart';
import 'package:simplex_chat/views/group/add_group_view.dart';
import 'package:simplex_chat/views/home/home_view.dart';
import 'package:simplex_chat/views/onboarding/intro_view.dart';
import 'package:simplex_chat/views/scan_invitation/scan_invitation_view.dart';
import 'package:simplex_chat/views/setup_profile_view.dart';
import 'package:simplex_chat/views/splash_screen.dart';
import 'package:url_strategy/url_strategy.dart';
/// Basic Structure is setup on [Providers]
/// Navigations are [namedRoutes]
void main() {
WidgetsFlutterBinding.ensureInitialized();
setPathUrlStrategy();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = ThemeData(
primarySwatch: Colors.teal,
primaryColor: kPrimaryColor,
);
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => DrawerProvider()),
],
child: Consumer<DrawerProvider>(
builder: (context, drawerProvider, chidl) => MaterialApp(
debugShowCheckedModeBanner: false,
title: 'SimpleX Chat',
theme: theme.copyWith(
colorScheme: theme.colorScheme.copyWith(secondary: kPrimaryColor),
primaryColorLight: Colors.white,
),
builder: (context, widget) {
return ScrollConfiguration(
behavior: const ScrollBehaviorModified(), child: widget!);
},
initialRoute: AppRoutes.splash,
routes: <String, WidgetBuilder>{
AppRoutes.splash: (_) => const SplashScreen(),
AppRoutes.intro: (_) => const IntroView(),
AppRoutes.setupProfile: (_) => const SetupProfileView(),
AppRoutes.home: (_) => const HomeView(),
AppRoutes.addContact: (_) => const AddContactView(),
AppRoutes.addContactWeb: (_) => const AddContactWeb(),
AppRoutes.scanInvitation: (_) => const ScanInvitationView(),
AppRoutes.addGroup: (_) => const AddGroupView(),
},
),
),
);
}
}

View File

@@ -0,0 +1,44 @@
import 'dart:convert';
class Contact {
final String? name;
final String? subtitle;
final String? photo;
final bool? isGroup;
Contact({
required this.name,
required this.subtitle,
this.photo = '',
this.isGroup = false,
});
factory Contact.fromJson(Map<String, dynamic> json) {
return Contact(
name: json['name'],
subtitle: json['subtitle'],
photo: json['photo'],
isGroup: json['isGroup'],
);
}
static Map<String, dynamic> toJson(Contact contact) {
return {
'name': contact.name,
'subtitle': contact.subtitle,
'photo': contact.photo,
'isGroup': false,
};
}
static String encode(List<Contact> contacts) => json.encode(
contacts
.map<Map<String, dynamic>>((contact) => Contact.toJson(contact))
.toList(),
);
static List<Contact> decode(String contacts) =>
(json.decode(contacts) as List<dynamic>)
.map<Contact>((item) => Contact.fromJson(item))
.toList();
}

View File

@@ -0,0 +1,48 @@
import 'dart:convert';
class Group {
final String? name;
final String? subtitle;
final String? photo;
final List<dynamic>? members;
final bool? isGroup;
Group({
required this.name,
required this.subtitle,
this.photo = '',
this.isGroup = true,
this.members = const [],
});
factory Group.fromJson(Map<String, dynamic> json) {
return Group(
name: json['name'],
subtitle: json['subtitle'],
photo: json['photo'],
isGroup: json['isGroup'],
members: json['members'],
);
}
static Map<String, dynamic> toJson(Group group) {
return {
'name': group.name,
'subtitle': group.subtitle,
'photo': group.photo,
'isGroup': true,
'members': group.members,
};
}
static String encode(List<Group> groups) => json.encode(
groups
.map<Map<String, dynamic>>((group) => Group.toJson(group))
.toList(),
);
static List<Group> decode(String groups) =>
(json.decode(groups) as List<dynamic>)
.map<Group>((item) => Group.fromJson(item))
.toList();
}

View File

@@ -0,0 +1,12 @@
import 'package:flutter/cupertino.dart';
class DrawerProvider extends ChangeNotifier {
int _currentIndex = 1;
int get currentIndex => _currentIndex;
set currentIndex(int value) {
_currentIndex = value;
notifyListeners();
}
}

View File

@@ -0,0 +1,180 @@
import 'package:flutter/material.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
class AddContactView extends StatefulWidget {
const AddContactView({Key? key}) : super(key: key);
@override
_AddContactViewState createState() => _AddContactViewState();
}
class _AddContactViewState extends State<AddContactView> {
bool? _dontShow = false;
final qrKey = GlobalKey(debugLabel: 'qr');
QRViewController? _qrViewController;
Barcode? result;
// alert dialgoue
void _initialWarning() {
showDialog(
context: context,
builder: (context) => StatefulBuilder(
builder: (context, setState) => AlertDialog(
title: const Text('Are you Sure?'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Your profile will be sent to your contact!'),
const SizedBox(height: 15.0),
Row(
children: [
Checkbox(
value: _dontShow,
onChanged: (value) {
setState(() {
_dontShow = value;
});
}),
const Text("Don't ask again")
],
),
],
),
actions: [
InkWell(
onTap: () => Navigator.pop(context),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.check, color: Colors.green),
),
),
InkWell(
onTap: () => Navigator.pop(context),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.cancel_outlined, color: Colors.red),
),
)
],
),
),
);
}
void _intiBox() {
Future.delayed(const Duration(seconds: 1), _initialWarning);
}
@override
void initState() {
_intiBox();
super.initState();
}
@override
void dispose() {
_qrViewController?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Add Contact'),
actions: [
IconButton(
icon: FutureBuilder(
future: _qrViewController?.getFlashStatus(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Icon(snapshot.data == false
? Icons.flash_on
: Icons.flash_off);
}
return const Icon(Icons.flash_off);
},
),
onPressed: () async {
await _qrViewController?.toggleFlash();
setState(() {});
},
),
IconButton(
icon: FutureBuilder(
future: _qrViewController?.getCameraInfo(),
builder: (context, snapshot) {
return const Icon(Icons.camera_alt);
},
),
onPressed: () async {
await _qrViewController?.flipCamera();
setState(() {});
},
),
],
),
body: SizedBox(
height: MediaQuery.of(context).size.height,
child: Stack(
children: [
Center(
child: GestureDetector(
onTap: () => Navigator.of(context).pop(true),
child: _qrViewBuild()),
),
const Center(
child: Text(
'Tap Here!',
style:
TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
),
Positioned(
top: MediaQuery.of(context).size.height * 0.15,
left: MediaQuery.of(context).size.width * 0.125,
child: const Text(
'Position QR Code within the frame.',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
fontSize: 18.0,
),
),
),
],
),
),
);
}
QRView _qrViewBuild() {
var scanArea = (MediaQuery.of(context).size.width < 400 ||
MediaQuery.of(context).size.height < 400)
? 220.0
: 370.0;
return QRView(
key: qrKey,
onQRViewCreated: (QRViewController controller) {
_qrViewController = controller;
controller.scannedDataStream.listen((scanData) {
setState(() async {
result = scanData;
await controller.stopCamera();
if (result != null) {
Navigator.of(context).pop(true);
}
});
});
},
overlay: QrScannerOverlayShape(
borderColor: Colors.red,
borderRadius: 0,
borderLength: 50,
borderWidth: 10,
cutOutSize: scanArea,
),
);
}
}

View File

@@ -0,0 +1,113 @@
import 'package:flutter/material.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
class AddContactWeb extends StatefulWidget {
const AddContactWeb({Key? key}) : super(key: key);
@override
_AddContactWebState createState() => _AddContactWebState();
}
class _AddContactWebState extends State<AddContactWeb> {
bool? _dontShow = false;
// alert dialgoue
void _initialWarning() {
showDialog(
context: context,
builder: (context) => StatefulBuilder(
builder: (context, setState) => AlertDialog(
title: const Text('Are you Sure?'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Your profile will be sent to your contact!'),
const SizedBox(height: 15.0),
Row(
children: [
Checkbox(
value: _dontShow,
onChanged: (value) {
setState(() {
_dontShow = value;
});
}),
const Text("Don't ask again")
],
),
],
),
actions: [
InkWell(
onTap: () => Navigator.pop(context),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.check, color: Colors.green),
),
),
InkWell(
onTap: () => Navigator.pop(context),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.cancel_outlined, color: Colors.red),
),
)
],
),
),
);
}
void _intiBox() {
Future.delayed(const Duration(seconds: 1), _initialWarning);
}
@override
void initState() {
_intiBox();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: const Text('Add Contact'),
),
body: SizedBox(
height: MediaQuery.of(context).size.height,
child: Stack(
children: [
Center(
child: GestureDetector(
onTap: () => Navigator.of(context).pop(true),
child: Container(
padding: const EdgeInsets.all(5.0),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.red,
width: 5.0,
),
),
child: Image.asset('assets/code.png')),
),
),
Positioned(
top: MediaQuery.of(context).size.height * 0.15,
left: MediaQuery.of(context).size.width * 0.41,
child: const Text(
'Position QR Code within the frame.',
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 18.0,
),
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,482 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:simplex_chat/animations/bottom_animation.dart';
import 'package:simplex_chat/app_routes.dart';
import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/model/contact.dart';
import 'package:simplex_chat/model/group.dart';
import 'package:simplex_chat/views/conversation/conversation_view.dart';
class Conversations extends StatefulWidget {
const Conversations({Key? key}) : super(key: key);
@override
_ConversationsState createState() => _ConversationsState();
}
class _ConversationsState extends State<Conversations> {
bool? _eraseMedia = false;
List<dynamic> _conversations = [];
List<Contact> _contactsList = []; // for storing contacts
List<Group> _groupList = []; // for storing groups
final List<String> _options = [
'Add contact',
'Scan invitation',
'Add group',
];
// getting data from local storage FOR NOW!!
void _getContacts() async {
debugPrint('Getting contacts!');
SharedPreferences prefs = await SharedPreferences.getInstance();
final String? _contacts = prefs.getString('contacts');
if (_contacts != null) {
setState(() {
_contactsList = List.from(Contact.decode(_contacts));
});
}
}
// getting data from local storage FOR NOW!!
void _getGroups() async {
debugPrint('Getting groups!');
SharedPreferences prefs = await SharedPreferences.getInstance();
final String? _groups = prefs.getString('groups');
if (_groups != null) {
setState(() {
_groupList = List.from(Group.decode(_groups));
});
}
_gettingGroupContactsChats();
}
void _gettingGroupContactsChats() {
debugPrint('Merging!');
setState(() {
_conversations = List.from(_contactsList);
_conversations = _conversations + _groupList;
});
}
@override
void initState() {
_getContacts();
_getGroups();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0.0,
backgroundColor: Colors.transparent,
leadingWidth: 30,
title: InkWell(
onTap: _addNewContacts,
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(
Icons.bug_report,
color: Colors.grey,
size: 22.0,
),
),
),
),
backgroundColor: Colors.white,
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0),
child: Center(
child: Column(
children: [
const SizedBox(height: 10.0),
_contactsList.isEmpty
? SizedBox(
height: MediaQuery.of(context).size.height * 0.7,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text(
"You don't have any conversation yet!",
style: kMediumHeadingStyle,
textAlign: TextAlign.center,
),
SizedBox(height: 8.0),
Text(
'Click the icon below to add a contact',
textAlign: TextAlign.center,
),
],
),
),
)
: ListView(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
children: List.generate(
_conversations.length,
(index) => WidgetAnimator(
child: ListTile(
leading: CircleAvatar(
backgroundImage:
// ignore: avoid_dynamic_calls
_conversations[index].photo == ''
? const AssetImage('assets/dp.png')
as ImageProvider
: FileImage(
// ignore: avoid_dynamic_calls
File(_conversations[index].photo ??
'')),
),
// ignore: avoid_dynamic_calls
title: Text(_conversations[index].name ?? ''),
subtitle:
// ignore: avoid_dynamic_calls
Text(_conversations[index].subtitle ?? ''),
// ignore: avoid_dynamic_calls
trailing: Icon(
// ignore: avoid_dynamic_calls
_conversations[index].isGroup
? Icons.group
: Icons.person,
size: 18,
color: Colors.grey[400],
),
onTap: () async {
var value = await Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ConversationView(
data: _conversations[index],
),
),
);
value ??= false;
if (value) {
_getContacts();
_getGroups();
}
},
onLongPress: () =>
_conversationOptions(_conversations[index]),
),
),
),
),
],
),
),
),
),
floatingActionButton: PopupMenuButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
),
offset: const Offset(-10, -155),
onSelected: (value) async {
if (value == _options[0]) {
var newMember =
await Navigator.pushNamed(context, AppRoutes.scanInvitation);
newMember ??= false;
if (newMember == true) {
_addNewMember();
}
} else if (value == _options[1]) {
var newMember = await Navigator.pushNamed(context,
kIsWeb ? AppRoutes.addContactWeb : AppRoutes.addContact);
newMember ??= false;
if (newMember == true) {
_addNewMember();
}
} else {
var newGroup =
await Navigator.pushNamed(context, AppRoutes.addGroup);
if (newGroup == true) {
_getGroups();
}
}
},
itemBuilder: (context) => _options
.map(
(opt) => PopupMenuItem(
value: opt,
child: Text(opt),
),
)
.toList(),
child: const FloatingActionButton(
heroTag: 'connect',
onPressed: null,
child: Icon(
Icons.person_add,
),
),
),
);
}
// delete a contact
void _deleteContact(Contact contact) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_contactsList.remove(contact);
});
await prefs.setString('contacts', Contact.encode(_contactsList));
var snackBar = SnackBar(
backgroundColor: Colors.red,
content: Text('${contact.name} deleted!'),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(snackBar);
}
// delete a group
void _deleteGroup(Group group) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_groupList.remove(group);
});
await prefs.setString('groups', Group.encode(_groupList));
var snackBar = SnackBar(
backgroundColor: Colors.red,
content: Text('${group.name} deleted!'),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(snackBar);
}
void _conversationOptions(var chat) {
showDialog(
context: context,
builder: (context) => AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
onPressed: () {
Navigator.pop(context);
_deleteConversation(chat);
},
child: const Text(
'Delete Conversation',
style: TextStyle(color: Colors.red),
)),
TextButton(
onPressed: () {
Navigator.pop(context);
_disconnect();
},
child: const Text('Disconnect')),
],
),
),
);
}
void _deleteConversation(var chat) {
showDialog(
context: context,
builder: (context) => StatefulBuilder(
builder: (context, setState) => AlertDialog(
title: const Text('Are you Sure?'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'All conversation history will be deleted from your device!'),
const SizedBox(height: 15.0),
Row(
children: [
Checkbox(
value: _eraseMedia,
onChanged: (value) {
setState(() {
_eraseMedia = value;
});
}),
const Text('Erase files & Media')
],
),
],
),
actions: [
InkWell(
// ignore: avoid_dynamic_calls
onTap: chat.isGroup
? () {
_deleteGroup(chat);
_conversations.remove(chat);
Navigator.pop(context);
}
: () {
_deleteContact(chat);
_conversations.remove(chat);
Navigator.pop(context);
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.check, color: Colors.green),
),
),
InkWell(
onTap: () => Navigator.pop(context),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.cancel_outlined, color: Colors.red),
),
)
],
),
),
);
}
void _disconnect() {
showDialog(
context: context,
builder: (context) => StatefulBuilder(
builder: (context, setState) => AlertDialog(
title: const Text('Are you Sure?'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'Disconnecting will erase all the data from your device and you will no longer be able to contact again!'),
const SizedBox(height: 15.0),
Row(
children: [
Checkbox(
value: _eraseMedia,
onChanged: (value) {
setState(() {
_eraseMedia = value;
});
}),
const Text('Erase files & Media')
],
),
],
),
actions: [
InkWell(
onTap: () => Navigator.pop(context),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.check, color: Colors.green),
),
),
InkWell(
onTap: () => Navigator.pop(context),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.cancel_outlined, color: Colors.red),
),
)
],
),
),
);
}
// dummy ftn for loading new contacts
void _addNewContacts() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
// adding dummy contact
List<Contact> _localList = [];
final String? _local = prefs.getString('contacts');
if (_local != null) {
_localList = List.from(Contact.decode(_local));
}
List<Contact> _newList = [
Contact(
name: 'Harry',
subtitle: 'Hello!',
),
];
_newList = _localList + _newList;
// dummy ftn for filling the list
final String _newContacts = Contact.encode(_newList);
await prefs.setString('contacts', _newContacts);
// adding dummy contact
List<Group> _localListGroup = [];
final String? _localGroups = prefs.getString('groups');
if (_local != null) {
_localListGroup = List.from(Group.decode(_localGroups!));
}
List<Group> _newGroups = [
Group(
name: 'College Friends',
subtitle: 'Lovely people',
members: ['Alice', 'James', 'Rio']),
];
_newGroups = _localListGroup + _newGroups;
// dummy ftn for filling the list
final String _newGroup = Group.encode(_newGroups);
await prefs.setString('groups', _newGroup);
_getContacts();
_getGroups();
const snackBar = SnackBar(
backgroundColor: Colors.green,
content: Text('New contacts loaded!'),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(snackBar);
}
void _addNewMember() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
// adding dummy contact
List<Contact> _localList = [];
final String? _local = prefs.getString('contacts');
if (_local != null) {
_localList = List.from(Contact.decode(_local));
}
List<Contact> _newList = [
Contact(
name: 'Bob',
subtitle: 'You and Bob are connected now!',
),
];
_newList = _localList + _newList;
// dummy ftn for filling the list
final String _newContacts = Contact.encode(_newList);
await prefs.setString('contacts', _newContacts);
_getContacts();
_getGroups();
const snackBar = SnackBar(
backgroundColor: Colors.green,
content: Text('New connection added!'),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(snackBar);
}
}

View File

@@ -0,0 +1,36 @@
import 'package:flutter/material.dart';
import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/model/contact.dart';
class ContactDetailsConversation extends StatelessWidget {
final Contact contact;
const ContactDetailsConversation({Key? key, required this.contact})
: super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text(contact.name!),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 12.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircleAvatar(
radius: 65,
backgroundImage: AssetImage('assets/dp.png'),
),
const SizedBox(height: 15.0),
const Text('Display Name', style: kMediumHeadingStyle),
Text(contact.name!),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,211 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:simplex_chat/views/conversation/contact_detail_conversation.dart';
import 'package:simplex_chat/views/conversation/group_detail_conversation.dart';
import 'package:simplex_chat/widgets/message_bubble.dart';
/// View the details of group by tapping the [appBar]
/// Viwe the details of chat by tapping the [appBar]
/// Generate [Fake Messages] by tapping the [Message Icon]
class ConversationView extends StatefulWidget {
// ignore: prefer_typing_uninitialized_variables
final data;
const ConversationView({Key? key, this.data}) : super(key: key);
@override
_ConversationViewState createState() => _ConversationViewState();
}
class _ConversationViewState extends State<ConversationView> {
final ScrollController _scrollController = ScrollController();
final TextEditingController _messageFieldController = TextEditingController();
FocusNode? _focus;
bool _fieldEnabled = false;
final List<Widget> _chatMessages = [];
@override
void initState() {
_focus = FocusNode();
_focus!.addListener(() {
debugPrint('FOCUS ${_focus!.hasFocus}');
_fieldEnabled = _focus!.hasFocus;
debugPrint('MESSAGE ENABLED $_fieldEnabled');
});
super.initState();
}
@override
void dispose() {
_messageFieldController.dispose();
_scrollController.dispose();
_focus!.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Scaffold(
appBar: AppBar(
leading: BackButton(
onPressed: () => Navigator.of(context).pop(true),
),
leadingWidth: MediaQuery.of(context).size.width * 0.085,
title: InkWell(
// ignore: avoid_dynamic_calls
onTap: widget.data.isGroup
? () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => GroupDetailsConversation(
group: widget.data,
),
),
);
}
: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ContactDetailsConversation(
contact: widget.data,
),
),
);
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
children: [
CircleAvatar(
radius: 15,
// ignore: avoid_dynamic_calls
backgroundImage: widget.data.photo == ''
? const AssetImage('assets/dp.png') as ImageProvider
: FileImage(
// ignore: avoid_dynamic_calls
File(widget.data.photo),
),
),
const SizedBox(width: 10.0),
Text(
// ignore: avoid_dynamic_calls
widget.data.name,
),
],
),
),
),
actions: [
IconButton(
onPressed: () {
setState(() {
_chatMessages.add(MessageBubble(
isUser: false,
// ignore: avoid_dynamic_calls
sender: widget.data.isGroup
// ignore: avoid_dynamic_calls
? widget.data.members.length == 0
? 'some person'
// ignore: avoid_dynamic_calls
: widget.data.members[0]
// ignore: avoid_dynamic_calls
: widget.data.name,
text: 'Hey there! How is it going?',
));
});
},
icon: const Icon(
Icons.bug_report,
),
)
],
),
body: Column(
children: [
Expanded(
child: Container(
child: _chatMessages.isEmpty
? const Center(
child: Text('Send a message to get started'),
)
: SingleChildScrollView(
controller: _scrollController,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: _chatMessages,
),
),
),
),
Container(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(width: 10.0),
Expanded(
child: SizedBox(
height: 45.0,
child: TextFormField(
maxLines: null,
focusNode: _focus,
textCapitalization: TextCapitalization.sentences,
keyboardType: TextInputType.multiline,
textInputAction: TextInputAction.newline,
controller: _messageFieldController,
decoration: InputDecoration(
contentPadding: const EdgeInsets.only(
left: 10.0,
),
hintText: 'Message...',
hintStyle: Theme.of(context).textTheme.caption,
fillColor: Colors.grey[200],
filled: true,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(360),
borderSide:
const BorderSide(color: Colors.transparent),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(360),
borderSide:
const BorderSide(color: Colors.transparent),
),
),
),
)),
IconButton(
onPressed: () {
if (_messageFieldController.text.isNotEmpty) {
setState(() {
_chatMessages.add(MessageBubble(
isUser: true,
sender: 'You',
text: _messageFieldController.text.trim(),
));
});
_messageFieldController.clear();
_focus!.unfocus();
}
},
icon: const Icon(Icons.send_rounded,
size: 25.0, color: Colors.teal),
),
],
),
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.02,
)
],
),
),
);
}
}

View File

@@ -0,0 +1,330 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/model/contact.dart';
import 'package:simplex_chat/model/group.dart';
enum MemberSetting { owner, admin, member }
class GroupDetailsConversation extends StatefulWidget {
final Group group;
const GroupDetailsConversation({Key? key, required this.group})
: super(key: key);
@override
_GroupDetailsConversationState createState() =>
_GroupDetailsConversationState();
}
class _GroupDetailsConversationState extends State<GroupDetailsConversation> {
MemberSetting _memberSetting = MemberSetting.member;
bool _addMember = false;
List<Contact> _contactsList = []; // for storing contacts
List _newMembers = [];
List<Group> _groupList = [];
List _members = [];
// getting all members of group
void _getMembers() {
setState(() {
_members = List.from(widget.group.members!);
});
}
// getting groups
void _getGroups() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final String? _groups = prefs.getString('groups');
if (_groups != null) {
setState(() {
_groupList = List.from(Group.decode(_groups));
});
}
}
// getting data from local storage FOR NOW
void _getContacts() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final String? _contacts = prefs.getString('contacts');
setState(() {
_contactsList = List.from(Contact.decode(_contacts!));
});
}
@override
void initState() {
_getContacts();
_getGroups();
_getMembers();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text(widget.group.name!),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Center(
child: CircleAvatar(
radius: 70,
backgroundImage: widget.group.photo == ''
? const AssetImage('assets/dp.png') as ImageProvider
: FileImage(File(widget.group.photo!)),
),
),
const SizedBox(height: 25.0),
const Text('Group Name', style: kMediumHeadingStyle),
Text(widget.group.name!),
const Divider(),
ListTile(
leading: const Icon(Icons.person_add),
title: const Text('Add a member'),
onTap: () {
setState(() {
_addMember = !_addMember;
});
},
),
const Divider(),
_newMembers.isNotEmpty
? const Text('New Members Added')
: Container(),
SizedBox(height: _newMembers.isNotEmpty ? 10.0 : 0.0),
_newMembers.isNotEmpty
? SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: List.generate(
_newMembers.length,
(index) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 5.0),
child: InkWell(
onTap: () {
setState(() {
_newMembers.remove(_newMembers[index]);
});
},
child: Container(
padding: const EdgeInsets.all(7.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
color: Colors.grey.withAlpha(100)),
child: Text(_newMembers[index]),
),
),
),
)),
)
: Container(),
SizedBox(height: _addMember ? 10.0 : 0.0),
_addMember ? const Text('Contacts Available') : Container(),
SizedBox(height: _addMember ? 10.0 : 0.0),
_addMember
? ListView(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
children: List.generate(
_contactsList.length,
(index) => ListTile(
leading: const CircleAvatar(
backgroundImage: AssetImage('assets/dp.png'),
),
title: Text(_contactsList[index].name!),
onTap: () {
setState(() {
_newMembers.add(_contactsList[index].name);
});
},
),
),
)
: Container(),
SizedBox(height: _addMember ? 10.0 : 0.0),
const Text('Members', style: kMediumHeadingStyle),
const SizedBox(height: 5.0),
for (int i = 0; i < _members.length; i++)
ListTile(
leading: const CircleAvatar(
backgroundImage: AssetImage('assets/dp.png'),
),
title: Text(_members[i]),
trailing: InkWell(
onTap: () => _memberSettings(_members[i]),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.settings),
),
),
)
],
),
),
),
floatingActionButton: _newMembers.isEmpty
? Container()
: FloatingActionButton(
onPressed: _addNewMembers,
child: const Icon(Icons.check),
),
);
}
void _memberSettings(String contact) {
Size _size = MediaQuery.of(context).size;
showDialog(
context: context,
builder: (context) => StatefulBuilder(
builder: (context, setState) => AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(width: _size.width * 0.15),
const Expanded(child: Text('Owner')),
Radio(
value: MemberSetting.owner,
groupValue: _memberSetting,
onChanged: (MemberSetting? value) {
setState(() {
_memberSetting = value!;
});
}),
SizedBox(width: _size.width * 0.15),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(width: _size.width * 0.15),
const Expanded(child: Text('Admin')),
Radio(
groupValue: _memberSetting,
value: MemberSetting.admin,
onChanged: (MemberSetting? value) {
setState(() {
_memberSetting = value!;
});
}),
SizedBox(width: _size.width * 0.15),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(width: _size.width * 0.15),
const Expanded(child: Text('Member')),
Radio(
groupValue: _memberSetting,
value: MemberSetting.member,
onChanged: (MemberSetting? value) {
setState(() {
_memberSetting = value!;
});
}),
SizedBox(width: _size.width * 0.15),
],
),
const Divider(),
TextButton(
onPressed: () => _removeMember(contact),
child: const Text('Remove from group',
style: TextStyle(color: Colors.red)),
),
],
),
),
),
);
}
void _addNewMembers() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
// add new members to the _members list
setState(() {
_members = _members + _newMembers;
_newMembers = [];
_addMember = false;
});
const snackBar = SnackBar(
backgroundColor: Colors.green, content: Text('New members added!'));
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(snackBar);
// get index of group in local
int index = 0;
index = _groupList.indexWhere((group) => group.name == widget.group.name);
// add the full _members list to the group
Group _updatedGroup = Group(
name: widget.group.name,
subtitle: widget.group.subtitle,
members: _members,
photo: widget.group.photo,
);
// put it in updated group local list
_groupList[index] = _updatedGroup;
// convert to string
final String _updatedGroupString = Group.encode(_groupList);
// update the group in local storage
await prefs.setString('groups', _updatedGroupString);
}
void _removeMember(String contact) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
Navigator.pop(context);
// remove the current member
setState(() {
_members.remove(contact);
});
var snackBar = SnackBar(
backgroundColor: Colors.red, content: Text('$contact removed!'));
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(snackBar);
// get index of group in local
int index = 0;
index = _groupList.indexWhere((group) => group.name == widget.group.name);
// new instance of group (updated)
Group _updatedGroup = Group(
name: widget.group.name,
subtitle: widget.group.subtitle,
photo: widget.group.photo,
members: _members,
);
// put it in group local list
_groupList[index] = _updatedGroup;
// convert to string
final String _updatedGroupString = Group.encode(_groupList);
// update the group in local storage
await prefs.setString('groups', _updatedGroupString);
}
}

View File

@@ -0,0 +1,432 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/model/contact.dart';
import 'package:simplex_chat/model/group.dart';
import 'package:simplex_chat/widgets/custom_text_field.dart';
class AddGroupView extends StatefulWidget {
const AddGroupView({Key? key}) : super(key: key);
@override
_AddGroupViewState createState() => _AddGroupViewState();
}
class _AddGroupViewState extends State<AddGroupView> {
// Image Picker --> DP properties
final imgPicker = ImagePicker();
File? image;
String _groupPhotoPath = '';
bool _uploading = false;
bool _imageUploaded = false;
final List _members = [];
final _formKey = GlobalKey<FormState>();
final _displayNameController = TextEditingController();
final _descController = TextEditingController();
bool _addMember = false;
List<Contact> _contactsList = []; // for storing contacts
// image buttons options
final _dpBtnText = ['Remove', 'Gallery', 'Camera'];
final _dpBtnColors = [Colors.red, Colors.purple, Colors.green];
final _dpBtnIcons = [
Icons.delete,
Icons.photo_rounded,
Icons.camera_alt_rounded
];
// getting data from local storage FOR NOW
void _getContacts() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final String? _contacts = prefs.getString('contacts');
setState(() {
_contactsList = List.from(Contact.decode(_contacts!));
});
}
String _userPhotoPath = '';
void _getUserPhoto() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? _photo = prefs.getString('photo${prefs.getString('displayName')}');
if (_photo != null) {
setState(() {
_userPhotoPath = _photo;
});
}
debugPrint(_userPhotoPath);
}
@override
void initState() {
_getUserPhoto();
_getContacts();
super.initState();
}
@override
void dispose() {
_displayNameController.dispose();
_descController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
Size _size = MediaQuery.of(context).size;
return GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Scaffold(
appBar: AppBar(
leading: BackButton(
onPressed: () => Navigator.of(context).pop(false),
),
title: const Text('New Group'),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(height: _size.height * 0.012),
Center(
child: SizedBox(
height: 180.0,
width: 180.0,
child: Stack(
children: [
_imageUploaded
? CircleAvatar(
radius: _size.height * 0.12,
backgroundImage:
FileImage(File(_groupPhotoPath)),
)
: CircleAvatar(
radius: _size.height * 0.12,
backgroundImage:
const AssetImage('assets/dp.png'),
),
Positioned(
right: 0,
bottom: 0,
child: FloatingActionButton(
backgroundColor: kSecondaryColor,
elevation: 2.0,
mini: true,
onPressed: _updateProfilePic,
child: _uploading
? const SizedBox(
height: 18.0,
width: 18.0,
child: CircularProgressIndicator(
strokeWidth: 2.0,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.white),
),
)
: const Icon(
Icons.add_a_photo,
size: 20,
),
),
)
],
),
),
),
SizedBox(height: _size.height * 0.03),
const Text('Group Name', style: kSmallHeadingStyle),
SizedBox(height: _size.height * 0.012),
CustomTextField(
textEditingController: _displayNameController,
textInputType: TextInputType.name,
hintText: 'e.g College friends',
validatorFtn: (String? value) {
if (value!.isEmpty) {
return 'Group name cannot be empty';
}
return null;
},
),
SizedBox(height: _size.height * 0.012),
const Text('Group Description', style: kSmallHeadingStyle),
SizedBox(height: _size.height * 0.012),
CustomTextField(
textEditingController: _descController,
textInputType: TextInputType.text,
hintText: 'e.g Friends from UK',
),
SizedBox(height: _size.height * 0.012),
_members.isNotEmpty
? const Text('Members Added')
: Container(),
SizedBox(height: _members.isNotEmpty ? 10.0 : 0.0),
_members.isNotEmpty
? SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: List.generate(
_members.length,
(index) => Padding(
padding:
const EdgeInsets.symmetric(horizontal: 5.0),
child: InkWell(
onTap: () {
setState(() {
_members.remove(_members[index]);
});
},
child: Container(
padding: const EdgeInsets.all(7.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
color: Colors.grey.withAlpha(100)),
child: Text(_members[index]),
),
),
),
)),
)
: Container(),
SizedBox(
height: _members.isNotEmpty ? _size.height * 0.012 : 0.0),
ListTile(
leading: const Icon(Icons.person_add),
title: const Text('Add a member'),
onTap: () {
setState(() {
_addMember = !_addMember;
});
},
),
SizedBox(height: _addMember ? _size.height * 0.012 : 0.0),
_addMember ? const Text('Contacts Available') : Container(),
SizedBox(height: _addMember ? _size.height * 0.012 : 0.0),
_addMember
? ListView(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
children: List.generate(
_contactsList.length,
(index) => ListTile(
leading: const CircleAvatar(
backgroundImage: AssetImage('assets/dp.png'),
),
title: Text(_contactsList[index].name!),
onTap: () {
setState(() {
_members.add(_contactsList[index].name);
});
},
),
),
)
: Container(),
Divider(height: _size.height * 0.035),
ListTile(
leading: CircleAvatar(
backgroundImage: _userPhotoPath == ''
? const AssetImage('assets/dp.png') as ImageProvider
: FileImage(File(_userPhotoPath)),
),
title: const Text('You'),
subtitle: const Text(
'Owner',
style: TextStyle(color: Colors.grey, fontSize: 12.0),
)),
],
),
),
),
),
floatingActionButton: Visibility(
visible: MediaQuery.of(context).viewInsets.bottom == 0,
child: FloatingActionButton(
heroTag: 'setup',
onPressed: () async {
if (_formKey.currentState!.validate()) {
FocusScope.of(context).unfocus();
_addNewGroup(_displayNameController.text.trim(),
_descController.text.trim());
_descController.clear();
_displayNameController.clear();
Navigator.of(context).pop(true);
}
},
child: const Icon(Icons.check),
),
),
),
);
}
void _updateProfilePic() {
showModalBottomSheet(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.0),
topRight: Radius.circular(10.0),
),
),
context: context,
builder: (context) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(360.0)),
height: 7.0,
width: 50.0,
),
const SizedBox(height: 20.0),
const Align(
alignment: Alignment.centerLeft,
child: Text(
' Profile photo',
style: kHeadingStyle,
),
),
const SizedBox(height: 15.0),
Row(
children: List.generate(
3,
(index) => Column(
mainAxisSize: MainAxisSize.min,
children: [
MaterialButton(
color: _dpBtnColors.map((e) => e).elementAt(index),
shape: const CircleBorder(),
onPressed: index == 0
? () => _removePic()
: index == 1
? () => _galleryPic()
: () => _cameraPic(),
child: Icon(
_dpBtnIcons.map((e) => e).elementAt(index),
color: Colors.white,
),
),
Text(
_dpBtnText.map((e) => e).elementAt(index),
textAlign: TextAlign.center,
)
],
),
))
],
),
),
);
}
void _removePic() {
setState(() {
_imageUploaded = false;
image = null;
_groupPhotoPath = '';
});
Navigator.pop(context);
}
void _cameraPic() async {
try {
setState(() {
_uploading = true;
});
// picking Image from Camera
final file = await imgPicker.getImage(
source: ImageSource.camera,
);
if (file != null) {
image = File(file.path);
setState(() {
_uploading = false;
_imageUploaded = true;
_groupPhotoPath = file.path;
});
} else {
setState(() {
_uploading = false;
});
}
Navigator.pop(context);
} catch (e) {
rethrow;
}
}
void _galleryPic() async {
try {
setState(() {
_uploading = true;
});
// picking Image from local storage
final file = await imgPicker.getImage(
source: ImageSource.gallery,
);
if (file != null) {
image = File(file.path);
setState(() {
_uploading = false;
_imageUploaded = true;
_groupPhotoPath = file.path;
});
} else {
setState(() {
_uploading = false;
});
}
Navigator.pop(context);
} catch (e) {
rethrow;
}
}
void _addNewGroup(String name, String desc) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<Group> _localList = [];
final String? _local = prefs.getString('groups');
if (_local != null) {
_localList = List.from(Group.decode(_local));
}
List<Group> _groups = [
Group(
name: name,
photo: _groupPhotoPath,
subtitle: desc,
members: _members,
),
];
debugPrint(_groups[0].isGroup.toString());
_groups = _localList + _groups;
final String _newGroups = Group.encode(_groups);
await prefs.setString('groups', _newGroups);
var snackBar = SnackBar(
backgroundColor: Colors.green,
content: Text('$name added'),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(snackBar);
}
}

View File

@@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:simplex_chat/providers/drawer_providers.dart';
class MyDrawer extends StatelessWidget {
const MyDrawer({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final _drawerProviders = Provider.of<DrawerProvider>(context);
return Padding(
padding: const EdgeInsets.all(10.0),
child: Builder(builder: (context) {
return Column(
children: [
const SizedBox(height: 30.0),
SvgPicture.asset(
'assets/logo.svg',
height: 55.0,
),
const Divider(height: 30.0),
ListTile(
leading: const Icon(Icons.insert_invitation),
title: const Text('Invitations'),
onTap: () {
_drawerProviders.currentIndex = 2;
Navigator.pop(context);
},
),
const Spacer(),
ListTile(
leading: const Icon(Icons.refresh),
title: const Text('Switch Profile'),
subtitle: const Text(
'Not supported yet!',
style: TextStyle(fontStyle: FontStyle.italic),
),
onTap: () => _switchProfile(context),
),
],
);
}),
);
}
// remove the locally stored user data
void _switchProfile(BuildContext context) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
int _count = 0;
Navigator.of(context).popUntil((route) => _count++ >= 2);
String? _name = prefs.getString('displayName');
await prefs.remove('displayName');
await prefs.remove('fullName');
await prefs.remove('photo$_name');
}
}

View File

@@ -0,0 +1,189 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/providers/drawer_providers.dart';
import 'package:simplex_chat/views/contacts/conversations.dart';
import 'package:simplex_chat/views/home/drawer.dart';
import 'package:simplex_chat/views/invitations/invitation_view.dart';
import 'package:simplex_chat/views/profile/profile_view.dart';
/// Generate [Fake Contacts] by tapping the [Bug Icon]
class HomeView extends StatefulWidget {
const HomeView({Key? key}) : super(key: key);
@override
_HomeViewState createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> {
String? _photo = '';
String? _displayName = '';
// views
final List<Widget> _views = [
const ProfileView(),
const Conversations(),
const Invitations(),
];
void _getUserData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_displayName = prefs.getString('displayName');
_photo = prefs.getString('photo$_displayName');
});
}
@override
void initState() {
_getUserData();
super.initState();
}
@override
Widget build(BuildContext context) {
final _drawerProviders = Provider.of<DrawerProvider>(context);
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
drawer: const Drawer(child: MyDrawer()),
body: SafeArea(
child: Builder(builder: (context) {
return Stack(
children: [
_views[_drawerProviders.currentIndex],
_drawerProviders.currentIndex == 0
? Positioned(
top: MediaQuery.of(context).size.height * 0.017,
left: MediaQuery.of(context).size.width * 0.02,
child: InkWell(
onTap: () {
_drawerProviders.currentIndex = 1;
_getUserData();
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.arrow_back, color: kPrimaryColor),
),
),
)
: _drawerProviders.currentIndex == 2
? Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
InkWell(
onTap: () {
_drawerProviders.currentIndex = 1;
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.arrow_back,
color: kPrimaryColor)),
),
const Spacer(),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('Hi! $_displayName',
style: kSmallHeadingStyle),
const Text('Good day!'),
],
),
const SizedBox(width: 10.0),
GestureDetector(
onTap: () {
_drawerProviders.currentIndex = 0;
},
child: CircleAvatar(
backgroundColor: Colors.white,
backgroundImage: _photo!.isEmpty
? const AssetImage('assets/dp.png')
as ImageProvider
: FileImage(File(_photo!)),
),
)
],
),
)
: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
InkWell(
onTap: () {
Scaffold.of(context).openDrawer();
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SvgPicture.asset('assets/menu.svg'),
),
),
const Spacer(),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('Hi! $_displayName',
style: kSmallHeadingStyle),
const Text('Good day!'),
],
),
const SizedBox(width: 10.0),
GestureDetector(
onTap: () {
_drawerProviders.currentIndex = 0;
},
child: CircleAvatar(
backgroundImage: _photo!.isEmpty
? const AssetImage('assets/dp.png')
as ImageProvider
: FileImage(File(_photo!)),
),
)
],
),
),
],
);
}),
),
),
);
}
Future<bool> _onWillPop() async {
return (await showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Exit Application', style: kMediumHeadingStyle),
content: const Text('Are You Sure?'),
actions: <Widget>[
TextButton(
child: const Text(
'Yes',
style: TextStyle(
color: Colors.red,
),
),
onPressed: () => SystemNavigator.pop(),
),
TextButton(
child: const Text('No'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
)) ??
false;
}
}

View File

@@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
import 'package:simplex_chat/constants.dart';
class Invitations extends StatefulWidget {
const Invitations({Key? key}) : super(key: key);
@override
State<Invitations> createState() => _InvitationsState();
}
class _InvitationsState extends State<Invitations> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0),
child: Column(
children: [
const SizedBox(height: 40.0),
Row(
children: const [
Icon(Icons.inventory_outlined, color: kPrimaryColor),
SizedBox(width: 8.0),
Text(
'Invitations',
style: kHeadingStyle,
)
],
),
const SizedBox(height: 5.0),
SizedBox(
height: MediaQuery.of(context).size.height * 0.7,
child: const Center(
child: Text(
"You don't have any invitations yet!",
style: kSmallHeadingStyle,
textAlign: TextAlign.center,
),
),
)
],
),
),
);
}
}

View File

@@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:simplex_chat/app_routes.dart';
import 'package:simplex_chat/constants.dart';
class IntroView extends StatelessWidget {
const IntroView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
'assets/logo.svg',
height: 80.0,
),
SizedBox(height: MediaQuery.of(context).size.height * 0.05),
const Text(
'Complete your profile to begin using SimpleX. Your profile is local to your device and will help identify you to your connections.',
style: kMediumHeadingStyle,
)
],
),
),
),
floatingActionButton: FloatingActionButton(
heroTag: 'welcome',
onPressed: () => Navigator.pushNamed(context, AppRoutes.setupProfile),
child: const Icon(
Icons.arrow_forward,
),
),
);
}
}

View File

@@ -0,0 +1,320 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/widgets/custom_text_field.dart';
class ProfileView extends StatefulWidget {
const ProfileView({Key? key}) : super(key: key);
@override
_ProfileViewState createState() => _ProfileViewState();
}
class _ProfileViewState extends State<ProfileView> {
final _formKey = GlobalKey<FormState>();
// controllers
final TextEditingController _displayNameController = TextEditingController();
final TextEditingController _fullNameController = TextEditingController();
// Image Picker --> DP properties
final imgPicker = ImagePicker();
File? image;
String? _photo = '';
bool _uploading = false;
// image buttons options
final _dpBtnText = ['Remove', 'Gallery', 'Camera'];
final _dpBtnColors = [Colors.red, Colors.purple, Colors.green];
final _dpBtnIcons = [
Icons.delete,
Icons.photo_rounded,
Icons.camera_alt_rounded
];
String? _displayName = '';
String? _fullName = '';
void _getUserData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_fullName = prefs.getString('fullName');
_displayName = prefs.getString('displayName');
_photo = prefs.getString('photo$_displayName');
});
_displayNameController.text = _displayName!;
if (_fullName != null) _fullNameController.text = _fullName!;
}
@override
void initState() {
_getUserData();
super.initState();
}
@override
void dispose() {
_displayNameController.dispose();
_fullNameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
Size _size = MediaQuery.of(context).size;
return Scaffold(
body: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(height: _size.height * 0.05),
Center(
child: SizedBox(
height: 180.0,
width: 180.0,
child: Stack(
children: [
_photo != ''
? CircleAvatar(
backgroundColor: Colors.white,
radius: 100.0,
backgroundImage: FileImage(File(_photo!)),
)
: const CircleAvatar(
backgroundColor: Colors.white,
radius: 100.0,
backgroundImage: AssetImage('assets/dp.png'),
),
Positioned(
right: 0,
bottom: 0,
child: FloatingActionButton(
backgroundColor: kSecondaryColor,
elevation: 2.0,
mini: true,
onPressed: _updateProfilePic,
child: _uploading
? const SizedBox(
height: 18.0,
width: 18.0,
child: CircularProgressIndicator(
strokeWidth: 2.0,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.white),
),
)
: const Icon(
Icons.add_a_photo,
size: 20,
),
),
)
],
),
)),
SizedBox(height: _size.height * 0.035),
const Text('Display Name', style: kSmallHeadingStyle),
const SizedBox(height: 10.0),
CustomTextField(
textEditingController: _displayNameController,
textInputType: TextInputType.name,
hintText: 'e.g John',
validatorFtn: (value) {
if (value!.isEmpty) {
return 'Display name cannot be empty';
}
return null;
},
),
SizedBox(height: _size.height * 0.035),
const Text('Full Name', style: kSmallHeadingStyle),
SizedBox(height: _size.height * 0.012),
CustomTextField(
textEditingController: _fullNameController,
textInputType: TextInputType.name,
hintText: 'e.g John Doe',
),
SizedBox(height: _size.height * 0.035),
const Text(
'Your display name is what your contact will know you :)',
style: TextStyle(letterSpacing: 1.2),
)
],
),
),
),
),
),
),
floatingActionButton: FloatingActionButton(
heroTag: 'save',
onPressed: () async {
if (_formKey.currentState!.validate()) {
FocusManager.instance.primaryFocus!.unfocus();
await _createProfile();
const snackBar = SnackBar(
backgroundColor: Colors.green,
content: Text('Profile updated!'),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(snackBar);
}
},
child: const Icon(Icons.check),
),
);
}
// create profile and store in local
Future<void> _createProfile() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('displayName', _displayNameController.text.trim());
await prefs.setString('fullName', _fullNameController.text.trim());
await prefs.setString(
'photo${_displayNameController.text.trim()}', _photo!);
debugPrint(prefs.getString('photo'));
}
void _updateProfilePic() {
Size _size = MediaQuery.of(context).size;
showModalBottomSheet(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.0),
topRight: Radius.circular(10.0),
),
),
context: context,
builder: (context) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(360.0)),
height: 7.0,
width: 50.0,
),
SizedBox(height: _size.height * 0.025),
const Align(
alignment: Alignment.centerLeft,
child: Text(
' Profile photo',
style: kHeadingStyle,
),
),
SizedBox(height: _size.height * 0.015),
Row(
children: List.generate(
3,
(index) => Column(
mainAxisSize: MainAxisSize.min,
children: [
MaterialButton(
color: _dpBtnColors.map((e) => e).elementAt(index),
shape: const CircleBorder(),
onPressed: index == 0
? () => _removePic()
: index == 1
? () => _galleryPic()
: () => _cameraPic(),
child: Icon(
_dpBtnIcons.map((e) => e).elementAt(index),
color: Colors.white,
),
),
Text(
_dpBtnText.map((e) => e).elementAt(index),
textAlign: TextAlign.center,
)
],
),
))
],
),
),
);
}
void _removePic() {
setState(() {
image = null;
_photo = '';
});
Navigator.pop(context);
}
void _cameraPic() async {
try {
setState(() {
_uploading = true;
});
// picking Image from Camera
final file = await imgPicker.getImage(
source: ImageSource.camera,
);
if (file != null) {
image = File(file.path);
setState(() {
_uploading = false;
_photo = file.path;
});
} else {
setState(() {
_uploading = false;
});
}
Navigator.pop(context);
} catch (e) {
rethrow;
}
}
void _galleryPic() async {
try {
debugPrint('gallery pic');
setState(() {
_uploading = true;
});
// picking Image from local storage
final file = await imgPicker.getImage(
source: ImageSource.gallery,
);
if (file != null) {
image = File(file.path);
setState(() {
_uploading = false;
_photo = file.path;
});
} else {
setState(() {
_uploading = false;
});
}
Navigator.pop(context);
} catch (e) {
rethrow;
}
}
}

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