diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9d532b1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,41 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
diff --git a/.metadata b/.metadata
new file mode 100644
index 0000000..182ccca
--- /dev/null
+++ b/.metadata
@@ -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: 78910062997c3a836feee883712c241a5fd22983
+ channel: stable
+
+project_type: app
diff --git a/SATU.postman_collection.json b/SATU.postman_collection.json
new file mode 100644
index 0000000..456825f
--- /dev/null
+++ b/SATU.postman_collection.json
@@ -0,0 +1,322 @@
+{
+ "info": {
+ "_postman_id": "ee31f1c6-087c-428b-9a9c-a30cee0f8e1f",
+ "name": "SATU",
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
+ },
+ "item": [
+ {
+ "name": "authorization",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"token\":\"eHjTZUIOTieAfcdt3lFkIyjPnP24DAhxVBGQ5oQS2RdrWw6eUxjHXjYRfEWi\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "https://satu.aman.com.kz/api/v1/authorization",
+ "protocol": "https",
+ "host": [
+ "satu",
+ "aman",
+ "com",
+ "kz"
+ ],
+ "path": [
+ "api",
+ "v1",
+ "authorization"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "login",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"username\":\"aidyn@a7.kz\",\n \"password\":\"123456\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "https://satu.aman.com.kz/api/v1/login",
+ "protocol": "https",
+ "host": [
+ "satu",
+ "aman",
+ "com",
+ "kz"
+ ],
+ "path": [
+ "api",
+ "v1",
+ "login"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "auth",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "9KDQAjjXZzH2qhskH9DDcUlvglcmLKLwNuDbePQSq8sDOPCHkM1F9pU1zk0V",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "https://satu.aman.com.kz/api/v1/auth",
+ "protocol": "https",
+ "host": [
+ "satu",
+ "aman",
+ "com",
+ "kz"
+ ],
+ "path": [
+ "api",
+ "v1",
+ "auth"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "logout",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "vuUp5cLovdECsNAGKWLwFGzl8RKa7eFBAUkFv5hmOi8bTEJugxCrdKyuWyG1",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "https://satu.aman.com.kz/api/v1/auth",
+ "protocol": "https",
+ "host": [
+ "satu",
+ "aman",
+ "com",
+ "kz"
+ ],
+ "path": [
+ "api",
+ "v1",
+ "auth"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "info",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "cgsiZ76reQ5DJY1swTihvWg2HvfrKLvgjxuhqpO4LFup2MagPLTNiM2VmPta",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "https://satu.aman.com.kz/api/v1/info",
+ "protocol": "https",
+ "host": [
+ "satu",
+ "aman",
+ "com",
+ "kz"
+ ],
+ "path": [
+ "api",
+ "v1",
+ "info"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "categories",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "9KDQAjjXZzH2qhskH9DDcUlvglcmLKLwNuDbePQSq8sDOPCHkM1F9pU1zk0V",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "https://satu.aman.com.kz/api/v1/categories",
+ "protocol": "https",
+ "host": [
+ "satu",
+ "aman",
+ "com",
+ "kz"
+ ],
+ "path": [
+ "api",
+ "v1",
+ "categories"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "goods",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "9KDQAjjXZzH2qhskH9DDcUlvglcmLKLwNuDbePQSq8sDOPCHkM1F9pU1zk0V",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "https://satu.aman.com.kz/api/v1/goods",
+ "protocol": "https",
+ "host": [
+ "satu",
+ "aman",
+ "com",
+ "kz"
+ ],
+ "path": [
+ "api",
+ "v1",
+ "goods"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "sell",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "UohUd9DwfEA5cJZjsvKSRDAlVNGkm3jFToWi7rtkXJJ93lOU8XtejPQe345T",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"type\": \"g\",\n \"items\": [\n {\n \"name\": \"test\",\n \"articul\": \"100\",\n \"cnt\": 1,\n \"price\": 200,\n \"excise\":\"000000000000_gs1_excise_code\"\n }\n ],\n \"section\":\"new section\",\n \"operator\":{\n \"code\":1,\n \"name\":\"Иван Иванович\"\n },\n \"contragent\":\"Петров Петр\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "https://satu.aman.com.kz/api/v1/sell",
+ "protocol": "https",
+ "host": [
+ "satu",
+ "aman",
+ "com",
+ "kz"
+ ],
+ "path": [
+ "api",
+ "v1",
+ "sell"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "sell_return",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "9KDQAjjXZzH2qhskH9DDcUlvglcmLKLwNuDbePQSq8sDOPCHkM1F9pU1zk0V",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"journal_id\":1167,\n \"invoice_id\":1\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "https://satu.aman.com.kz/api/v1/sell_return",
+ "protocol": "https",
+ "host": [
+ "satu",
+ "aman",
+ "com",
+ "kz"
+ ],
+ "path": [
+ "api",
+ "v1",
+ "sell_return"
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/analysis_options.yaml b/analysis_options.yaml
new file mode 100644
index 0000000..057b980
--- /dev/null
+++ b/analysis_options.yaml
@@ -0,0 +1,3 @@
+analyzer:
+ enable-experiment:
+ - non-nullable
\ No newline at end of file
diff --git a/android/.gitignore b/android/.gitignore
new file mode 100644
index 0000000..0a741cb
--- /dev/null
+++ b/android/.gitignore
@@ -0,0 +1,11 @@
+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
diff --git a/android/app/build.gradle b/android/app/build.gradle
new file mode 100644
index 0000000..013da0a
--- /dev/null
+++ b/android/app/build.gradle
@@ -0,0 +1,77 @@
+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"
+
+def keystoreProperties = new Properties()
+def keystorePropertiesFile = rootProject.file('key.properties')
+if (keystorePropertiesFile.exists()) {
+ keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
+}
+
+android {
+ compileSdkVersion 29
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ lintOptions {
+ disable 'InvalidPackage'
+ }
+ signingConfigs {
+ release {
+ keyAlias keystoreProperties['keyAlias']
+ keyPassword keystoreProperties['keyPassword']
+ storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
+ storePassword keystoreProperties['storePassword']
+ }
+ }
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId "kz.com.aman.satu"
+ minSdkVersion 21
+ targetSdkVersion 29
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ // multiDexEnabled true
+ }
+
+ 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"
+}
diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000..43961b4
--- /dev/null
+++ b/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..2d0901c
--- /dev/null
+++ b/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/kotlin/kz/com/aman/satu/MainActivity.kt b/android/app/src/main/kotlin/kz/com/aman/satu/MainActivity.kt
new file mode 100644
index 0000000..8ab0f26
--- /dev/null
+++ b/android/app/src/main/kotlin/kz/com/aman/satu/MainActivity.kt
@@ -0,0 +1,6 @@
+package kz.com.aman.satu
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity() {
+}
diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 0000000..304732f
--- /dev/null
+++ b/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..db77bb4
Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..17987b7
Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..09d4391
Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d5f1c8d
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4d6372e
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..1f83a33
--- /dev/null
+++ b/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 0000000..43961b4
--- /dev/null
+++ b/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/android/build.gradle b/android/build.gradle
new file mode 100644
index 0000000..3100ad2
--- /dev/null
+++ b/android/build.gradle
@@ -0,0 +1,31 @@
+buildscript {
+ ext.kotlin_version = '1.3.50'
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.5.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+ project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/android/gradle.properties b/android/gradle.properties
new file mode 100644
index 0000000..a673820
--- /dev/null
+++ b/android/gradle.properties
@@ -0,0 +1,4 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
+android.enableR8=true
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..296b146
--- /dev/null
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -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-5.6.2-all.zip
diff --git a/android/settings.gradle b/android/settings.gradle
new file mode 100644
index 0000000..44e62bc
--- /dev/null
+++ b/android/settings.gradle
@@ -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"
diff --git a/assets/google_fonts/Lato-Black.ttf b/assets/google_fonts/Lato-Black.ttf
new file mode 100644
index 0000000..a4a924f
Binary files /dev/null and b/assets/google_fonts/Lato-Black.ttf differ
diff --git a/assets/google_fonts/Lato-BlackItalic.ttf b/assets/google_fonts/Lato-BlackItalic.ttf
new file mode 100644
index 0000000..d6f8945
Binary files /dev/null and b/assets/google_fonts/Lato-BlackItalic.ttf differ
diff --git a/assets/google_fonts/Lato-Bold.ttf b/assets/google_fonts/Lato-Bold.ttf
new file mode 100644
index 0000000..b63a14d
Binary files /dev/null and b/assets/google_fonts/Lato-Bold.ttf differ
diff --git a/assets/google_fonts/Lato-BoldItalic.ttf b/assets/google_fonts/Lato-BoldItalic.ttf
new file mode 100644
index 0000000..8f9a50d
Binary files /dev/null and b/assets/google_fonts/Lato-BoldItalic.ttf differ
diff --git a/assets/google_fonts/Lato-Italic.ttf b/assets/google_fonts/Lato-Italic.ttf
new file mode 100644
index 0000000..49e9f2c
Binary files /dev/null and b/assets/google_fonts/Lato-Italic.ttf differ
diff --git a/assets/google_fonts/Lato-Light.ttf b/assets/google_fonts/Lato-Light.ttf
new file mode 100644
index 0000000..9c0a705
Binary files /dev/null and b/assets/google_fonts/Lato-Light.ttf differ
diff --git a/assets/google_fonts/Lato-LightItalic.ttf b/assets/google_fonts/Lato-LightItalic.ttf
new file mode 100644
index 0000000..53b140b
Binary files /dev/null and b/assets/google_fonts/Lato-LightItalic.ttf differ
diff --git a/assets/google_fonts/Lato-Regular.ttf b/assets/google_fonts/Lato-Regular.ttf
new file mode 100644
index 0000000..33eba8b
Binary files /dev/null and b/assets/google_fonts/Lato-Regular.ttf differ
diff --git a/assets/google_fonts/Lato-Thin.ttf b/assets/google_fonts/Lato-Thin.ttf
new file mode 100644
index 0000000..0c599a0
Binary files /dev/null and b/assets/google_fonts/Lato-Thin.ttf differ
diff --git a/assets/google_fonts/Lato-ThinItalic.ttf b/assets/google_fonts/Lato-ThinItalic.ttf
new file mode 100644
index 0000000..7db3a8c
Binary files /dev/null and b/assets/google_fonts/Lato-ThinItalic.ttf differ
diff --git a/assets/google_fonts/OFL.txt b/assets/google_fonts/OFL.txt
new file mode 100644
index 0000000..98383e3
--- /dev/null
+++ b/assets/google_fonts/OFL.txt
@@ -0,0 +1,93 @@
+Copyright (c) 2010-2014 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name "Lato"
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/assets/images/NBK_Logo.png b/assets/images/NBK_Logo.png
new file mode 100644
index 0000000..bcb79f8
Binary files /dev/null and b/assets/images/NBK_Logo.png differ
diff --git a/assets/images/aman_kassa_check.png b/assets/images/aman_kassa_check.png
new file mode 100644
index 0000000..49b46d4
Binary files /dev/null and b/assets/images/aman_kassa_check.png differ
diff --git a/assets/images/bank_layout.jpg b/assets/images/bank_layout.jpg
new file mode 100644
index 0000000..ee06ee3
Binary files /dev/null and b/assets/images/bank_layout.jpg differ
diff --git a/assets/images/card.png b/assets/images/card.png
new file mode 100644
index 0000000..d4351eb
Binary files /dev/null and b/assets/images/card.png differ
diff --git a/assets/images/halyk-bank.png b/assets/images/halyk-bank.png
new file mode 100644
index 0000000..c2f486f
Binary files /dev/null and b/assets/images/halyk-bank.png differ
diff --git a/assets/images/halykpos.png b/assets/images/halykpos.png
new file mode 100644
index 0000000..40b13b4
Binary files /dev/null and b/assets/images/halykpos.png differ
diff --git a/assets/images/icon_large.png b/assets/images/icon_large.png
new file mode 100644
index 0000000..eabb25a
Binary files /dev/null and b/assets/images/icon_large.png differ
diff --git a/assets/images/icon_large_w.png b/assets/images/icon_large_w.png
new file mode 100644
index 0000000..8091a9f
Binary files /dev/null and b/assets/images/icon_large_w.png differ
diff --git a/assets/images/logo.png b/assets/images/logo.png
new file mode 100644
index 0000000..eec3735
Binary files /dev/null and b/assets/images/logo.png differ
diff --git a/assets/images/phone.png b/assets/images/phone.png
new file mode 100644
index 0000000..89c5fce
Binary files /dev/null and b/assets/images/phone.png differ
diff --git a/assets/images/phone_fit.png b/assets/images/phone_fit.png
new file mode 100644
index 0000000..d2baec9
Binary files /dev/null and b/assets/images/phone_fit.png differ
diff --git a/assets/images/splash.png b/assets/images/splash.png
new file mode 100644
index 0000000..e7f4164
Binary files /dev/null and b/assets/images/splash.png differ
diff --git a/assets/lang/en.json b/assets/lang/en.json
new file mode 100644
index 0000000..af564aa
--- /dev/null
+++ b/assets/lang/en.json
@@ -0,0 +1,4 @@
+{
+ "main.emptyList.title": "List is empty",
+ "main.emptyList.firstPaymentNotAdded": "Need add first payment"
+}
\ No newline at end of file
diff --git a/assets/lang/ru.json b/assets/lang/ru.json
new file mode 100644
index 0000000..8a0e3a6
--- /dev/null
+++ b/assets/lang/ru.json
@@ -0,0 +1,21 @@
+{
+ "main.emptyList.title": "Список пуст",
+ "main.emptyList.firstPaymentNotAdded": "Необходимо добавить первый платеж",
+ "edit.period": "Периодичность платежей",
+ "edit.year": "Ежегодный",
+ "edit.quarter": "Ежеквартальный",
+ "edit.month": "Ежемесячный",
+ "edit.week": "Еженедельный",
+ "edit.periodCount": "Всего периодов",
+ "edit.perPeriodSum": "Сумма платежа за период",
+ "edit.firstPayDate": "Дата первого платежа",
+ "edit.weekendWork": "Учитывать выходные дни недели",
+ "edit.saturdayWork": "Суббота рабочий день",
+ "edit.name": "Наименование кредита",
+ "edit.nameBank": "Наименование банка",
+ "edit.descriptionLabel": "Дополнительная информация",
+ "edit.calendar": "Календарь",
+ "edit.calendarDesc": "Календарь платежей",
+ "edit.description": "Описание",
+ "edit.descriptionSubtitle": "Дополнительная информация"
+}
\ No newline at end of file
diff --git a/ios/.gitignore b/ios/.gitignore
new file mode 100644
index 0000000..e96ef60
--- /dev/null
+++ b/ios/.gitignore
@@ -0,0 +1,32 @@
+*.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/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!default.mode1v3
+!default.mode2v3
+!default.pbxuser
+!default.perspectivev3
diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 0000000..6b4c0f7
--- /dev/null
+++ b/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ App
+ CFBundleIdentifier
+ io.flutter.flutter.app
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ App
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ MinimumOSVersion
+ 8.0
+
+
diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig
new file mode 100644
index 0000000..592ceee
--- /dev/null
+++ b/ios/Flutter/Debug.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig
new file mode 100644
index 0000000..592ceee
--- /dev/null
+++ b/ios/Flutter/Release.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..dae8aac
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,495 @@
+// !$*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 */; };
+/* 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 */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ );
+ sourceTree = "";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ 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";
+ };
+/* 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 = "";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ 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;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = kz.com.aman.satu;
+ 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;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = kz.com.aman.satu;
+ 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;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = kz.com.aman.satu;
+ 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 */;
+}
diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..a28140c
--- /dev/null
+++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift
new file mode 100644
index 0000000..70693e4
--- /dev/null
+++ b/ios/Runner/AppDelegate.swift
@@ -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)
+ }
+}
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..d36b1fa
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -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"
+ }
+}
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 0000000..dc9ada4
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 0000000..28c6bf0
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 0000000..2ccbfd9
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 0000000..f091b6b
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 0000000..4cde121
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 0000000..d0ef06e
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 0000000..dcdc230
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 0000000..2ccbfd9
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 0000000..c8f9ed8
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 0000000..a6d6b86
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 0000000..a6d6b86
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 0000000..75b2d16
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 0000000..c4df70d
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 0000000..6a84f41
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 0000000..d0e1f58
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 0000000..0bedcf2
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 0000000..89c2725
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f2e259c
--- /dev/null
+++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..f3c2851
--- /dev/null
+++ b/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
new file mode 100644
index 0000000..f5484e9
--- /dev/null
+++ b/ios/Runner/Info.plist
@@ -0,0 +1,45 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ satu
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+
+
diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 0000000..308a2a5
--- /dev/null
+++ b/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1 @@
+#import "GeneratedPluginRegistrant.h"
diff --git a/lib/core/base/base_service.dart b/lib/core/base/base_service.dart
new file mode 100644
index 0000000..fc5dfb0
--- /dev/null
+++ b/lib/core/base/base_service.dart
@@ -0,0 +1,13 @@
+import 'package:logger/logger.dart';
+
+import '../utils/logger.dart';
+
+class BaseService {
+ Logger log;
+
+ BaseService({String title}) {
+ this.log = getLogger(
+ title ?? this.runtimeType.toString(),
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/core/entity/Category.dart b/lib/core/entity/Category.dart
new file mode 100644
index 0000000..f816f3a
--- /dev/null
+++ b/lib/core/entity/Category.dart
@@ -0,0 +1,42 @@
+const String Category_tableName = 'goods_category';
+const String Category_columnId = 'id';
+const String Category_columnParentIn = 'parent_id';
+const String Category_columnName = 'name';
+const String Category_columnAppCompanyId = 'app_company_id';
+
+class Category {
+ int id;
+ int parentIn;
+ String name;
+ int appCompanyId;
+
+ Map toMap() {
+ var map = {
+ Category_columnParentIn: parentIn,
+ Category_columnName: name,
+ Category_columnAppCompanyId: appCompanyId
+ };
+ if (id != null) {
+ map[Category_columnId] = id;
+ }
+ return map;
+ }
+
+ Category();
+
+ Category.fromMap(Map map) {
+ id = map[Category_columnId];
+ parentIn = map[Category_columnParentIn];
+ name = map[Category_columnName];
+ appCompanyId = map[Category_columnAppCompanyId];
+ }
+
+ Category.fromJson(Map map) {
+ id = map[Category_columnId];
+ parentIn = map[Category_columnParentIn];
+ name = map[Category_columnName];
+ appCompanyId = map[Category_columnAppCompanyId];
+ }
+
+}
+
diff --git a/lib/core/entity/Goods.dart b/lib/core/entity/Goods.dart
new file mode 100644
index 0000000..6d0f31d
--- /dev/null
+++ b/lib/core/entity/Goods.dart
@@ -0,0 +1,78 @@
+const String Goog_tableName = 'goods';
+const String Goog_columnId = 'id';
+const String Goog_columnArticul = 'articul';
+const String Goog_columnName = 'name';
+const String Goog_columnPrice = 'price';
+const String Goog_columnCategoryId = 'category_id';
+const String Goog_columnEan = 'ean';
+const String Goog_columnAppCompanyId = 'app_company_id';
+
+const String Goog_columnDescription = 'description';
+const String Goog_columnShowPrice = 'show_price';
+const String Goog_columnOkei = 'okei';
+const String Goog_columnDiscount = 'discount';
+
+class Good {
+ int id;
+ int articul;
+ String name;
+ double price;
+ int categoryId;
+ String ean;
+ int appCompanyId;
+ String description;
+ double showPrice;
+ int okei;
+ double discount;
+
+ Map toMap() {
+ var map = {
+ Goog_columnArticul: articul,
+ Goog_columnName: name,
+ Goog_columnPrice: price,
+ Goog_columnCategoryId: categoryId,
+ Goog_columnEan: ean,
+ Goog_columnAppCompanyId: appCompanyId,
+ Goog_columnDescription: description,
+ Goog_columnShowPrice: showPrice,
+ Goog_columnOkei: okei,
+ Goog_columnDiscount: discount
+ };
+ if (id != null) {
+ map[Goog_columnId] = id;
+ }
+ return map;
+ }
+
+ Good();
+
+ Good.fromMap(Map map) {
+ id = map[Goog_columnId];
+ articul = map[Goog_columnArticul];
+ name = map[Goog_columnName];
+ price = map[Goog_columnPrice]?.toDouble();
+ categoryId = map[Goog_columnCategoryId];
+ ean = map[Goog_columnEan];
+ appCompanyId= map[Goog_columnAppCompanyId];
+ description = map[Goog_columnDescription];
+ showPrice = map[Goog_columnShowPrice]?.toDouble();
+ okei = map[Goog_columnOkei];
+ discount = map[Goog_columnDiscount]?.toDouble();
+ }
+
+ Good.fromJson(Map map) {
+ id = map[Goog_columnId];
+ articul = map[Goog_columnArticul];
+ name = map[Goog_columnName];
+ price = double.parse(map[Goog_columnPrice]);
+ categoryId = map[Goog_columnCategoryId];
+ ean = map[Goog_columnEan];
+ appCompanyId = map[Goog_columnAppCompanyId];
+ description = map[Goog_columnDescription];
+ showPrice = map[Goog_columnShowPrice]?.toDouble();
+ okei = map[Goog_columnOkei];
+ discount = map[Goog_columnDiscount]?.toDouble();
+ }
+
+}
+
diff --git a/lib/core/entity/Service.dart b/lib/core/entity/Service.dart
new file mode 100644
index 0000000..1226a93
--- /dev/null
+++ b/lib/core/entity/Service.dart
@@ -0,0 +1,72 @@
+const String Service_tableName = 'services';
+const String Service_columnId = 'id';
+const String Service_columnArticul = 'articul';
+const String Service_columnName = 'name';
+const String Service_columnPrice = 'price';
+const String Service_columnCategoryId = 'category_id';
+const String Service_columnEan = 'ean';
+const String Service_columnAppCompanyId = 'app_company_id';
+
+const String Service_columnDescription = 'description';
+const String Service_columnShowPrice = 'show_price';
+const String Service_columnOkei = 'okei';
+const String Service_columnDiscount = 'discount';
+
+
+class Service {
+ int id;
+ int articul;
+ String name;
+ double price;
+ String ean;
+ int appCompanyId;
+ String description;
+ double showPrice;
+ String okei;
+ double discount;
+
+ Map toMap() {
+ var map = {
+ Service_columnArticul: articul,
+ Service_columnName: name,
+ Service_columnPrice: price,
+ Service_columnAppCompanyId: appCompanyId,
+ Service_columnDescription: description,
+ Service_columnShowPrice: showPrice,
+ Service_columnOkei: okei,
+ Service_columnDiscount: discount
+ };
+ if (id != null) {
+ map[Service_columnId] = id;
+ }
+ return map;
+ }
+
+ Service();
+
+ Service.fromMap(Map map) {
+ id = map[Service_columnId];
+ articul = map[Service_columnArticul];
+ name = map[Service_columnName];
+ price = map[Service_columnPrice]?.toDouble();
+ appCompanyId = map[Service_columnAppCompanyId];
+ description = map[Service_columnDescription];
+ showPrice = map[Service_columnShowPrice]?.toDouble();
+ okei = map[Service_columnOkei];
+ discount = map[Service_columnDiscount]?.toDouble();
+ }
+
+ Service.fromJson(Map map) {
+ id = map[Service_columnId];
+ articul = map[Service_columnArticul];
+ name = map[Service_columnName];
+ price = map[Service_columnPrice]?.toDouble();
+ appCompanyId = map[Service_columnAppCompanyId];
+ description = map[Service_columnDescription];
+ showPrice = map[Service_columnShowPrice]?.toDouble();
+ okei = map[Service_columnOkei];
+ discount = map[Service_columnDiscount]?.toDouble();
+ }
+
+}
+
diff --git a/lib/core/entity/Voucher.dart b/lib/core/entity/Voucher.dart
new file mode 100644
index 0000000..8b6740a
--- /dev/null
+++ b/lib/core/entity/Voucher.dart
@@ -0,0 +1,64 @@
+const String Voucher_tableName = 'vouches';
+const String Voucher_columnId = 'id';
+const String Voucher_columnName = 'name';
+const String Voucher_columnTotal = 'total';
+const String Voucher_columnBase64Data = 'base64Data';
+const String Voucher_columnData = 'data';
+const String Voucher_columnDateTime = 'dateTime';
+const String Voucher_columnAppCompanyId = 'app_company_id';
+const String Voucher_columnKassaId = 'kassaId';
+const String Voucher_columnType = 'type';
+const String Voucher_columnUrl = 'url';
+
+
+const String VoucherTypePayment = 'payment';
+const String VoucherTypeReturnPay = 'returnPay';
+const String VoucherTypeReport = 'report';
+
+class Voucher {
+ int id;
+ String name;
+ double total;
+ String data;
+ String base64Data;
+ DateTime dateTime;
+ int appCompanyId;
+ int kassaId;
+ String type;
+ String url;
+
+ Voucher();
+
+ Map toMap() {
+ var map = {
+ Voucher_columnName: name,
+ Voucher_columnTotal: total,
+ Voucher_columnData: data,
+ Voucher_columnBase64Data: base64Data,
+ Voucher_columnDateTime: dateTime.toIso8601String(),
+ Voucher_columnKassaId: kassaId,
+ Voucher_columnAppCompanyId: appCompanyId,
+ Voucher_columnType: type,
+ Voucher_columnUrl: url,
+ };
+ if (id != null) {
+ map[Voucher_columnId] = id;
+ }
+ return map;
+ }
+
+ Voucher.fromMap(Map map) {
+ id = map[Voucher_columnId];
+ name = map[Voucher_columnName];
+ total = map[Voucher_columnTotal]?.toDouble();
+ data = map[Voucher_columnData];
+ base64Data = map[Voucher_columnBase64Data];
+ dateTime = DateTime.parse(map[Voucher_columnDateTime]);
+ appCompanyId= map[Voucher_columnAppCompanyId];
+ kassaId = map[Voucher_columnKassaId];
+ type = map[Voucher_columnType];
+ url = map[Voucher_columnUrl];
+ }
+
+}
+
diff --git a/lib/core/models/auth/auth_response.dart b/lib/core/models/auth/auth_response.dart
new file mode 100644
index 0000000..b202386
--- /dev/null
+++ b/lib/core/models/auth/auth_response.dart
@@ -0,0 +1,45 @@
+/// user_id : 10
+/// company_id : 281
+/// kassa_id : 3
+/// token : "nFH2bzcwAQ4UMpzT5TVVvZr7QaljNfpmG0aqUzgU6J9gXnaDPo4VvBa3CNUn"
+/// auth_at : "2021-04-30\n11:38:52"
+/// shard : 1
+/// message : "Authorization confirm"
+/// operation : true
+
+class AuthResponse {
+ int userId;
+ int companyId;
+ int kassaId;
+ String token;
+ String authAt;
+ int shard;
+ String message;
+ bool operation;
+
+
+ static AuthResponse fromMap(Map map) {
+ if (map == null) return null;
+ AuthResponse authResponseBean = AuthResponse();
+ authResponseBean.userId = map['user_id'];
+ authResponseBean.companyId = map['company_id'];
+ authResponseBean.kassaId = map['kassa_id'];
+ authResponseBean.token = map['token'];
+ authResponseBean.authAt = map['auth_at'];
+ authResponseBean.shard = map['shard'];
+ authResponseBean.message = map['message'];
+ authResponseBean.operation = map['operation'];
+ return authResponseBean;
+ }
+
+ Map toJson() => {
+ "user_id": userId,
+ "company_id": companyId,
+ "kassa_id": kassaId,
+ "token": token,
+ "auth_at": authAt,
+ "shard": shard,
+ "message": message,
+ "operation": operation,
+ };
+}
\ No newline at end of file
diff --git a/lib/core/models/dialog_models.dart b/lib/core/models/dialog_models.dart
new file mode 100644
index 0000000..bc7a325
--- /dev/null
+++ b/lib/core/models/dialog_models.dart
@@ -0,0 +1,30 @@
+import 'package:flutter/foundation.dart';
+
+class DialogRequest {
+ final String title;
+ final String description;
+ final String buttonTitle;
+ final String cancelTitle;
+ final String formatType;
+
+ DialogRequest(
+ {@required this.title,
+ @required this.description,
+ @required this.buttonTitle,
+ this.cancelTitle,
+ this.formatType});
+}
+
+class DialogResponse {
+ //final String fieldOne;
+ //final String fieldTwo;
+ final String responseText;
+ final bool confirmed;
+
+ DialogResponse({
+ //this.fieldOne,
+ //this.fieldTwo,
+ this.responseText,
+ this.confirmed,
+ });
+}
diff --git a/lib/core/redux/actions/nav_actions.dart b/lib/core/redux/actions/nav_actions.dart
new file mode 100644
index 0000000..4827c24
--- /dev/null
+++ b/lib/core/redux/actions/nav_actions.dart
@@ -0,0 +1,25 @@
+
+import 'package:redux/redux.dart';
+import 'package:meta/meta.dart';
+import 'package:redux_thunk/redux_thunk.dart';
+import 'package:satu/core/redux/state/nav_state.dart';
+
+import '../store.dart';
+
+@immutable
+class SetNavStateAction {
+ final NavState navState;
+ SetNavStateAction(this.navState);
+}
+
+ThunkAction navigateDrawer(Type viewClass) {
+ return (Store store) async {
+ store.dispatch(SetNavStateAction(NavState(drawerViewClass: viewClass)));
+ };
+}
+
+ThunkAction navigateTab(int index) {
+ return (Store store) async {
+ store.dispatch(SetNavStateAction(NavState(selectedTabIndex: index)));
+ };
+}
diff --git a/lib/core/redux/actions/user_actions.dart b/lib/core/redux/actions/user_actions.dart
new file mode 100644
index 0000000..43cfd61
--- /dev/null
+++ b/lib/core/redux/actions/user_actions.dart
@@ -0,0 +1,62 @@
+import 'package:redux/redux.dart';
+import 'package:meta/meta.dart';
+import 'package:redux_thunk/redux_thunk.dart';
+import 'package:satu/core/models/auth/auth_response.dart';
+import 'package:satu/core/redux/constants/auth_type_const.dart';
+import 'package:satu/core/redux/state/user_state.dart';
+import 'package:satu/core/services/api_service.dart';
+import 'package:satu/core/services/dialog_service.dart';
+import 'package:satu/core/services/navigator_service.dart';
+import 'package:satu/core/utils/locator.dart';
+import 'package:satu/routes/route_names.dart';
+import '../store.dart';
+
+@immutable
+class SetUserStateAction {
+ final UserState userState;
+ SetUserStateAction(this.userState);
+}
+
+final ApiService _api = locator();
+final NavigatorService _navigation = locator();
+final DialogService _dialogService = locator();
+
+
+ThunkAction authenticate(String email, String password) {
+ return (Store store) async {
+ store.dispatch(SetUserStateAction(UserState(isLoading: true)));
+ try {
+ AuthResponse result = await _api.login(email, password);
+ if (result.operation) {
+ _api.token = result.token;
+ store.dispatch(SetUserStateAction(UserState(isLoading: false, auth: result)));
+ _navigation.replace(MainViewRoute);
+ } else {
+ _dialogService.showDialog(title: 'Внимание', buttonTitle: 'Ok', description: result.message);
+ }
+ } catch (e) {
+ print(e);
+ } finally {
+ store.dispatch(SetUserStateAction(UserState(isLoading: false)));
+ }
+ };
+}
+
+Future logout(Store store) async {
+ store.dispatch(SetUserStateAction(UserState(isLoading: true)));
+ try {
+ AuthResponse result = await _api.logout();
+ if (result.operation) {
+ _api.token = null;
+ store.dispatch(SetUserStateAction(UserState(isLoading: false, auth: AuthResponse())));
+ _navigation.replace(LoginViewRoute);
+ } else {
+ _dialogService.showDialog(title: 'Внимание', buttonTitle: 'Ok', description: result.message);
+ }
+ } catch (e) {
+ print(e);
+ } finally {
+ store.dispatch(SetUserStateAction(UserState(isLoading: false)));
+ }
+}
+
diff --git a/lib/core/redux/constants/auth_type_const.dart b/lib/core/redux/constants/auth_type_const.dart
new file mode 100644
index 0000000..2227403
--- /dev/null
+++ b/lib/core/redux/constants/auth_type_const.dart
@@ -0,0 +1,2 @@
+const String AuthenticateTypeQr = 'AuthenticateTypeQr';
+const String AuthenticateTypeLogin = 'AuthenticateTypeLogin';
\ No newline at end of file
diff --git a/lib/core/redux/constants/operation_const.dart b/lib/core/redux/constants/operation_const.dart
new file mode 100644
index 0000000..8557ec6
--- /dev/null
+++ b/lib/core/redux/constants/operation_const.dart
@@ -0,0 +1,2 @@
+const String OperationTypePay = 'OperationTypePay';
+const String OperationTypeReturn = 'OperationTypeReturn';
\ No newline at end of file
diff --git a/lib/core/redux/constants/setting_const.dart b/lib/core/redux/constants/setting_const.dart
new file mode 100644
index 0000000..787527a
--- /dev/null
+++ b/lib/core/redux/constants/setting_const.dart
@@ -0,0 +1,6 @@
+const String SettingModeKassa = 'kassaMode';
+const String SettingModeCalc = 'calcMode';
+
+
+const String SettingTradeTypeGood = 'g';
+const String SettingTradeTypeService = 's';
\ No newline at end of file
diff --git a/lib/core/redux/reducers/nav_reducer.dart b/lib/core/redux/reducers/nav_reducer.dart
new file mode 100644
index 0000000..ca3a000
--- /dev/null
+++ b/lib/core/redux/reducers/nav_reducer.dart
@@ -0,0 +1,12 @@
+import 'package:satu/core/redux/actions/nav_actions.dart';
+import 'package:satu/core/redux/actions/user_actions.dart';
+import 'package:satu/core/redux/state/nav_state.dart';
+import 'package:satu/core/redux/state/user_state.dart';
+
+navReducer(NavState prevState, SetNavStateAction action) {
+ final payload = action.navState;
+ return prevState.copyWith(
+ drawerViewClass: payload.drawerViewClass,
+ selectedTabIndex: payload.selectedTabIndex,
+ );
+}
diff --git a/lib/core/redux/reducers/user_reducer.dart b/lib/core/redux/reducers/user_reducer.dart
new file mode 100644
index 0000000..73d4a42
--- /dev/null
+++ b/lib/core/redux/reducers/user_reducer.dart
@@ -0,0 +1,11 @@
+import 'package:satu/core/redux/actions/user_actions.dart';
+import 'package:satu/core/redux/state/user_state.dart';
+
+userReducer(UserState prevState, SetUserStateAction action) {
+ final payload = action.userState;
+ return prevState.copyWith(
+ isError: payload.isError,
+ isLoading: payload.isLoading,
+ auth: payload.auth,
+ );
+}
\ No newline at end of file
diff --git a/lib/core/redux/state/nav_state.dart b/lib/core/redux/state/nav_state.dart
new file mode 100644
index 0000000..ca30dc1
--- /dev/null
+++ b/lib/core/redux/state/nav_state.dart
@@ -0,0 +1,25 @@
+import 'package:meta/meta.dart';
+import 'package:satu/views/work/work_view.dart';
+
+@immutable
+class NavState {
+ final Type drawerViewClass;
+ final int selectedTabIndex;
+
+ NavState({this.drawerViewClass, this.selectedTabIndex});
+
+ factory NavState.initial() => NavState(
+ drawerViewClass: WorkView,
+ selectedTabIndex: 0,
+ );
+
+ NavState copyWith({
+ @required int selectedTabIndex,
+ @required Type drawerViewClass,
+ }) {
+ return NavState(
+ selectedTabIndex: selectedTabIndex ?? this.selectedTabIndex,
+ drawerViewClass: drawerViewClass ?? this.drawerViewClass,
+ );
+ }
+}
diff --git a/lib/core/redux/state/user_state.dart b/lib/core/redux/state/user_state.dart
new file mode 100644
index 0000000..5c9021d
--- /dev/null
+++ b/lib/core/redux/state/user_state.dart
@@ -0,0 +1,51 @@
+
+import 'package:meta/meta.dart';
+import 'package:satu/core/models/auth/auth_response.dart';
+
+
+@immutable
+class UserState {
+ final bool isError;
+ final bool isLoading;
+ final AuthResponse auth;
+
+
+ UserState(
+ {this.isError,
+ this.isLoading,
+ this.auth,
+ });
+
+ factory UserState.initial(UserState payload) => UserState(
+ isLoading: false,
+ isError: false,
+ auth: payload?.auth ?? AuthResponse(),
+ );
+
+ UserState copyWith({
+ @required bool isError,
+ @required bool isLoading,
+ @required AuthResponse auth
+ }) {
+ return UserState(
+ isError: isError ?? this.isError,
+ isLoading: isLoading ?? this.isLoading,
+ auth: auth ?? this.auth,
+ );
+ }
+
+ static UserState fromJson(dynamic json) {
+ return json != null
+ ? UserState(
+ auth: AuthResponse.fromMap(json['auth']),
+ )
+ : null;
+ }
+
+ dynamic toJson() {
+ return {
+ "auth": auth != null ? auth.toJson() : null,
+ };
+ }
+}
+
diff --git a/lib/core/redux/store.dart b/lib/core/redux/store.dart
new file mode 100644
index 0000000..a7a5d17
--- /dev/null
+++ b/lib/core/redux/store.dart
@@ -0,0 +1,101 @@
+
+import 'package:meta/meta.dart';
+import 'package:redux/redux.dart';
+import 'package:redux_persist_flutter/redux_persist_flutter.dart';
+import 'package:redux_thunk/redux_thunk.dart';
+import 'package:redux_persist/redux_persist.dart';
+import 'package:satu/core/redux/actions/nav_actions.dart';
+import 'package:satu/core/redux/reducers/nav_reducer.dart';
+import 'package:satu/core/redux/reducers/user_reducer.dart';
+import 'package:satu/core/redux/state/nav_state.dart';
+import 'package:satu/core/redux/state/user_state.dart';
+
+import 'actions/user_actions.dart';
+
+
+//reducer context
+AppState appReducer(AppState state, dynamic action) {
+ if (action is SetUserStateAction) {
+ /** UserAction **/
+ final nextState = userReducer(state.userState, action);
+ return state.copyWith(userState: nextState);
+ } else if (action is SetNavStateAction) {
+ /** NavAction **/
+ final nextState = navReducer(state.navState, action);
+ return state.copyWith(navState: nextState);
+ }
+ return state;
+}
+
+//Main State
+@immutable
+class AppState {
+ final UserState userState;
+ final NavState navState;
+
+
+ AppState({
+ this.userState,
+ this.navState,
+ });
+
+ //stable work
+ AppState copyWith({
+ UserState userState,
+ NavState navState,
+ }) {
+ return AppState(
+ userState: userState ?? this.userState,
+ navState: navState ?? this.navState,
+ );
+ }
+
+ static AppState fromJson(dynamic json){
+ return json !=null
+ ? AppState(
+ userState: UserState.fromJson(json['userState']),
+ )
+ : null;
+ }
+
+ dynamic toJson() {
+ return {
+ "userState" : userState.toJson(),
+ };
+ }
+}
+
+class Redux {
+ static Store _store;
+
+ static Store get store {
+ if (_store == null) {
+ throw Exception("store is not initialized");
+ } else {
+ return _store;
+ }
+ }
+
+ //initial context
+ static Future init() async {
+ // Create Persistor
+ final persist = Persistor(
+ storage: FlutterStorage(), // Or use other engines
+ serializer: JsonSerializer(AppState.fromJson), // Or use other serializers
+ );
+
+ final initialState = await persist.load();
+
+ final userStateInitial = UserState.initial(initialState?.userState);
+ final navStateInitial = NavState.initial();
+
+ _store = Store(
+ appReducer,
+ middleware: [thunkMiddleware, persist.createMiddleware() ],
+ initialState: AppState(
+ userState: userStateInitial,
+ navState: navStateInitial,
+ )
+ );
+ }
+}
diff --git a/lib/core/services/api_service.dart b/lib/core/services/api_service.dart
new file mode 100644
index 0000000..5e4e76f
--- /dev/null
+++ b/lib/core/services/api_service.dart
@@ -0,0 +1,109 @@
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:satu/core/base/base_service.dart';
+import 'package:http/http.dart' as http;
+import 'package:satu/core/models/auth/auth_response.dart';
+
+/// The service responsible for networking requests
+class ApiService extends BaseService {
+ static const host = 'satu.aman.com.kz';
+ static const endpoint = '/api/v1';
+
+ var client = new http.Client();
+
+ //TOKEN
+ String _token;
+
+ String get token => this._token;
+
+ set token(String value) => this._token = value;
+
+ Future _get(String point, {Map requestBody, Map header}) async {
+ Map headers = {
+ HttpHeaders.contentTypeHeader: "application/json",
+ HttpHeaders.cacheControlHeader: "no-cache"
+ };
+ if (header != null && header.isNotEmpty) {
+ headers.addAll(header);
+ }
+ String url = '$endpoint$point';
+ final response = await http.get(Uri.https(host, url), headers: headers);
+ return response.body;
+ }
+
+ Future _post(String point, {Map requestBody, Map header}) async {
+ Map headers = {
+ HttpHeaders.contentTypeHeader: "application/json",
+ HttpHeaders.cacheControlHeader: "no-cache"
+ };
+ if (header != null && header.isNotEmpty) {
+ headers.addAll(header);
+ }
+ String url = '$endpoint$point';
+ print(jsonEncode(requestBody));
+ final response = await http.post(Uri.https(host, url), body: jsonEncode(requestBody), headers: headers);
+
+ return response.body;
+ }
+
+ Future login(String username, String password) async {
+ Map requestBody = {'username': username, 'password': password};
+ AuthResponse result;
+ try {
+ String response = await _post('/login', requestBody: requestBody);
+ result = AuthResponse.fromMap(json.decode(response));
+ } catch (e, stack) {
+ log.e("login", e, stack);
+ result = new AuthResponse()
+ ..message = 'Ошибка вызова сервера'
+ ..operation = false;
+ }
+ return result;
+ }
+
+ Future authorization(String token) async {
+ Map requestBody = {'token': token};
+ AuthResponse result;
+ try {
+ String response = await _post('/authorization', requestBody: requestBody);
+ result = AuthResponse.fromMap(json.decode(response));
+ } catch (e, stack) {
+ log.e("authorization", e, stack);
+ result = new AuthResponse()
+ ..message = 'Ошибка вызова сервера'
+ ..operation = false;
+ }
+ return result;
+ }
+
+ Future auth(String token) async {
+ Map headers = {HttpHeaders.authorizationHeader: 'Bearer $token'};
+ AuthResponse result;
+ try {
+ String response = await _post('/auth', header: headers);
+ result = AuthResponse.fromMap(json.decode(response));
+ } catch (e, stack) {
+ log.e("auth", e, stack);
+ result = new AuthResponse()
+ ..message = 'Ошибка вызова сервера'
+ ..operation = false;
+ }
+ return result;
+ }
+
+ Future logout() async {
+ Map headers = {HttpHeaders.authorizationHeader: 'Bearer $token'};
+ AuthResponse result;
+ try {
+ String response = await _post('/logout', header: headers);
+ result = AuthResponse.fromMap(json.decode(response));
+ } catch (e, stack) {
+ log.e("auth", e, stack);
+ result = new AuthResponse()
+ ..message = 'Ошибка вызова сервера'
+ ..operation = false;
+ }
+ return result;
+ }
+}
diff --git a/lib/core/services/data_service.dart b/lib/core/services/data_service.dart
new file mode 100644
index 0000000..bab9c77
--- /dev/null
+++ b/lib/core/services/data_service.dart
@@ -0,0 +1,14 @@
+import 'dart:convert';
+
+import 'package:flutter/material.dart';
+import 'package:satu/core/base/base_service.dart';
+import 'package:satu/core/utils/locator.dart';
+
+import 'api_service.dart';
+import 'db_service.dart';
+
+class DataService extends BaseService {
+ final ApiService _api = locator();
+ final DbService _db = locator();
+
+}
diff --git a/lib/core/services/db_service.dart b/lib/core/services/db_service.dart
new file mode 100644
index 0000000..8cd65ef
--- /dev/null
+++ b/lib/core/services/db_service.dart
@@ -0,0 +1,166 @@
+import 'dart:io';
+
+import 'package:satu/core/base/base_service.dart';
+import 'package:satu/core/entity/Category.dart';
+import 'package:satu/core/entity/Goods.dart';
+import 'package:satu/core/entity/Service.dart';
+import 'package:satu/core/entity/Voucher.dart';
+import 'package:sqflite/sqflite.dart';
+import 'package:path/path.dart';
+import 'package:path_provider/path_provider.dart';
+
+class DbService extends BaseService {
+ static final _databaseName = "AmanSatuFlutterDb.db";
+ static final _databaseVersion = 1;
+
+ // make this a singleton class
+ DbService._privateConstructor();
+
+ static final DbService instance = DbService._privateConstructor();
+
+ // only have a single app-wide reference to the database
+ static Database _database;
+
+ Future get database async {
+ if (_database != null) return _database;
+ // lazily instantiate the db the first time it is accessed
+ _database = await _initDatabase();
+ return _database;
+ }
+
+ // this opens the database (and creates it if it doesn't exist)
+ _initDatabase() async {
+ Directory documentsDirectory = await getApplicationDocumentsDirectory();
+ String path = join(documentsDirectory.path, _databaseName);
+ return await openDatabase(path,
+ version: _databaseVersion, onUpgrade: _onUpdate, onCreate: _onCreate);
+ }
+
+ Future _onUpdate(Database db, int oldVersion, int newVersion) async {
+ log.i('update from $oldVersion to $newVersion');
+ //Goods table
+ await db.execute('DROP TABLE IF EXISTS $Goog_tableName;');
+ await db.execute('DROP TABLE IF EXISTS $Category_tableName;');
+ await db.execute('DROP TABLE IF EXISTS $Service_tableName;');
+ //await db.execute('DROP TABLE IF EXISTS $Voucher_tableName;');
+ log.i('dropped tables');
+ if (newVersion > 16) {
+ //Не удалять таблицу с чеками
+ }
+
+ _onCreate(db, newVersion);
+ }
+
+ Future _onCreate(Database db, int version) async {
+ log.i('create tables');
+ //Goods table
+ await db.execute('''
+ CREATE TABLE IF NOT EXISTS $Goog_tableName (
+ $Goog_columnId integer primary key unique,
+ $Goog_columnArticul integer not null,
+ $Goog_columnName text not null,
+ $Goog_columnPrice real not null,
+ $Goog_columnCategoryId integer not null,
+ $Goog_columnEan text,
+ $Goog_columnAppCompanyId integer,
+ $Goog_columnDescription text,
+ $Goog_columnShowPrice real,
+ $Goog_columnOkei integer,
+ $Goog_columnDiscount real
+ );
+ ''');
+ await db.execute('''
+ CREATE TABLE IF NOT EXISTS $Category_tableName (
+ $Category_columnId integer primary key unique,
+ $Category_columnName text not null,
+ $Category_columnParentIn integer,
+ $Category_columnAppCompanyId integer
+ );
+ ''');
+ //Service
+ await db.execute('''
+ CREATE TABLE IF NOT EXISTS $Service_tableName (
+ $Service_columnId integer primary key unique,
+ $Service_columnArticul integer not null,
+ $Service_columnName text not null,
+ $Service_columnPrice real not null,
+ $Service_columnAppCompanyId integer,
+ $Service_columnDescription text,
+ $Service_columnShowPrice real,
+ $Service_columnOkei text,
+ $Service_columnDiscount real
+ );
+ ''');
+ //Voucher
+ await db.execute('''
+ CREATE TABLE IF NOT EXISTS $Voucher_tableName (
+ $Voucher_columnId integer primary key AUTOINCREMENT,
+ $Voucher_columnName text not null,
+ $Voucher_columnTotal real,
+ $Voucher_columnData text,
+ $Voucher_columnBase64Data text,
+ $Voucher_columnDateTime text not null,
+ $Service_columnAppCompanyId integer,
+ $Voucher_columnKassaId integer,
+ $Voucher_columnType text not null,
+ $Voucher_columnUrl text
+ );
+ ''');
+ }
+
+ // Inserts a row in the database where each key in the Map is a column name
+ // and the value is the column value. The return value is the id of the
+ // inserted row.
+ Future insert(String table, Map row) async {
+ Database db = await instance.database;
+ return await db.insert(table, row);
+ }
+
+ // All of the rows are returned as a list of maps, where each map is
+ // a key-value list of columns.
+ Future>> queryAllRows(String table) async {
+ Database db = await instance.database;
+ return await db.query(table);
+ }
+
+ Future>> queryAllRowsOrderBy(String table, String orderBy) async {
+ Database db = await instance.database;
+ return await db.query(table, orderBy: orderBy);
+ }
+
+ Future>> queryRowsWithWhere(
+ String table, String where, List args) async {
+ Database db = await instance.database;
+ return await db.query(table, where: where, whereArgs: args);
+ }
+
+ // All of the methods (insert, query, update, delete) can also be done using
+ // raw SQL commands. This method uses a raw query to give the row count.
+ Future queryRowCount(String table) async {
+ Database db = await instance.database;
+ return Sqflite.firstIntValue(
+ await db.rawQuery('SELECT COUNT(*) FROM $table'));
+ }
+
+ // We are assuming here that the id column in the map is set. The other
+ // column values will be used to update the row.
+ Future update(String table, Map row) async {
+ Database db = await instance.database;
+ int id = row['id'];
+ return await db.update(table, row, where: 'id = ?', whereArgs: [id]);
+ }
+
+ // Deletes the row specified by the id. The number of affected rows is
+ // returned. This should be 1 as long as the row exists.
+ Future delete(String table, int id) async {
+ Database db = await instance.database;
+ return await db.delete(table, where: 'id = ?', whereArgs: [id]);
+ }
+
+ Future deleteAll(String table) async {
+ Database db = await instance.database;
+ return await db.delete(table);
+ }
+
+ Future close() async => instance.close();
+}
diff --git a/lib/core/services/dialog_service.dart b/lib/core/services/dialog_service.dart
new file mode 100644
index 0000000..12c4f5f
--- /dev/null
+++ b/lib/core/services/dialog_service.dart
@@ -0,0 +1,75 @@
+import 'dart:async';
+
+import 'package:flutter/cupertino.dart';
+import 'package:satu/core/models/dialog_models.dart';
+
+class DialogService {
+ GlobalKey _dialogNavigationKey = GlobalKey();
+ Function(DialogRequest) _showDialogListener;
+ Function(DialogRequest) _showDialogInputListener;
+ Completer _dialogCompleter;
+
+ Completer get completer => this._dialogCompleter;
+
+ GlobalKey get dialogNavigationKey => _dialogNavigationKey;
+
+ /// Registers a callback function. Typically to show the dialog
+ void registerDialogListener(Function(DialogRequest) showDialogListener,
+ Function(DialogRequest) showDialogInputListener) {
+ _showDialogListener = showDialogListener;
+ _showDialogInputListener = showDialogInputListener;
+ }
+
+ /// Calls the dialog listener and returns a Future that will wait for dialogComplete.
+ Future showDialog({
+ String title = 'Aman Касса',
+ String description,
+ String buttonTitle = 'Ok',
+ }) {
+ _dialogCompleter = Completer();
+ _showDialogListener(DialogRequest(
+ title: title,
+ description: description,
+ buttonTitle: buttonTitle,
+ ));
+ return _dialogCompleter.future;
+ }
+
+ /// Shows a confirmation dialog
+ Future showConfirmationDialog(
+ {String title,
+ String description,
+ String confirmationTitle = 'Ok',
+ String cancelTitle = 'Cancel'}) {
+ _dialogCompleter = Completer();
+ _showDialogListener(DialogRequest(
+ title: title,
+ description: description,
+ buttonTitle: confirmationTitle,
+ cancelTitle: cancelTitle));
+ return _dialogCompleter.future;
+ }
+
+ Future showConfirmationDialogInput(
+ {String title = ' Aman Касса',
+ String description,
+ String confirmationTitle = 'Ok',
+ String cancelTitle = 'Cancel',
+ String formatType}) {
+ _dialogCompleter = Completer();
+ _showDialogInputListener(DialogRequest(
+ title: title,
+ description: description,
+ buttonTitle: confirmationTitle,
+ cancelTitle: cancelTitle,
+ formatType: formatType));
+ return _dialogCompleter.future;
+ }
+
+ /// Completes the _dialogCompleter to resume the Future's execution call
+ void dialogComplete(DialogResponse response) {
+ _dialogNavigationKey.currentState.pop();
+ _dialogCompleter.complete(response);
+ _dialogCompleter = null;
+ }
+}
diff --git a/lib/core/services/navigator_service.dart b/lib/core/services/navigator_service.dart
new file mode 100644
index 0000000..6b9b31f
--- /dev/null
+++ b/lib/core/services/navigator_service.dart
@@ -0,0 +1,50 @@
+import '../../core/base/base_service.dart';
+import 'package:flutter/material.dart';
+
+class NavigatorService extends BaseService {
+ final GlobalKey navigatorKey = GlobalKey();
+
+ final GlobalKey scaffoldDrawerKey = GlobalKey();
+
+ Future push(String routeName, {dynamic arguments}) {
+ log.i('routeName: $routeName');
+ return navigatorKey.currentState
+ .pushNamed(routeName, arguments: arguments);
+ }
+
+ Future replace(String routeName, {dynamic arguments}) {
+ log.i('routeName: $routeName');
+ return navigatorKey.currentState
+ .pushNamedAndRemoveUntil(routeName, (Route route) => false, arguments: arguments);
+ }
+
+
+ Future navigateToPage(MaterialPageRoute pageRoute) async {
+ log.i('navigateToPage: pageRoute: ${pageRoute.settings.name}');
+ if (navigatorKey.currentState == null) {
+ log.e('navigateToPage: Navigator State is null');
+ return null;
+ }
+ return navigatorKey.currentState.push(pageRoute);
+ }
+
+ Future navigateToPageWithReplacement(
+ MaterialPageRoute pageRoute) async {
+ log.i('navigateToPageWithReplacement: '
+ 'pageRoute: ${pageRoute.settings.name}');
+ if (navigatorKey.currentState == null) {
+ log.e('navigateToPageWithReplacement: Navigator State is null');
+ return null;
+ }
+ return navigatorKey.currentState.pushReplacement(pageRoute);
+ }
+
+ void pop([T result]) {
+ log.i('goBack:');
+ if (navigatorKey.currentState == null) {
+ log.e('goBack: Navigator State is null');
+ return;
+ }
+ navigatorKey.currentState.pop(result);
+ }
+}
\ No newline at end of file
diff --git a/lib/core/utils/locator.dart b/lib/core/utils/locator.dart
new file mode 100644
index 0000000..7d50a5a
--- /dev/null
+++ b/lib/core/utils/locator.dart
@@ -0,0 +1,33 @@
+import 'package:get_it/get_it.dart';
+import 'package:logger/logger.dart';
+import 'package:satu/core/services/api_service.dart';
+import 'package:satu/core/services/data_service.dart';
+import 'package:satu/core/services/db_service.dart';
+import 'package:satu/core/services/dialog_service.dart';
+import 'package:satu/core/services/navigator_service.dart';
+
+import 'logger.dart';
+
+GetIt locator = GetIt.I;
+
+class LocatorInjector {
+ static Logger _log = getLogger('LocatorInjector');
+
+ static Future setupLocator() async {
+ _log.d('Initializing Api Service');
+ locator.registerLazySingleton(() => ApiService());
+ _log.d('Initializing Navigator Service');
+ locator.registerLazySingleton(() => NavigatorService());
+ _log.d('Initializing Dialog Service');
+ locator.registerLazySingleton(() => DialogService());
+ _log.d('Initializing DbService Service');
+ locator.registerLazySingleton(() => DbService.instance);
+
+
+ // depencies
+
+
+ _log.d('Initializing DataService Service');
+ locator.registerLazySingleton(() => DataService());
+ }
+}
\ No newline at end of file
diff --git a/lib/core/utils/logger.dart b/lib/core/utils/logger.dart
new file mode 100644
index 0000000..fc9ffc6
--- /dev/null
+++ b/lib/core/utils/logger.dart
@@ -0,0 +1,79 @@
+import 'dart:convert';
+import 'package:logger/logger.dart';
+
+class SimpleLogPrinter extends LogPrinter {
+ final String className;
+ static final _deviceStackTraceRegex = RegExp(r'#[0-9]+[\s]+(.+) \(([^\s]+)\)');
+ static final _webStackTraceRegex = RegExp(r'^((packages|dart-sdk)\/[^\s]+\/)');
+ SimpleLogPrinter(this.className);
+
+ @override
+ List log(LogEvent event) {
+ var level = event.level;
+ var message = stringifyMessage(event.message);
+ var error = event.error?.toString() ?? '';
+ var color = PrettyPrinter.levelColors[level];
+ var emoji = PrettyPrinter.levelEmojis[level];
+ String stack;
+ if (event.stackTrace == null) {
+ stack = formatStackTrace(StackTrace.current, 2);
+ } else {
+ stack = formatStackTrace(event.stackTrace, 2);
+ }
+ print(color(' $emoji $message $error -> $stack '));
+ return [];
+ }
+
+ String stringifyMessage(dynamic message) {
+ if (message is Map || message is Iterable) {
+ var encoder = JsonEncoder.withIndent(' ');
+ return encoder.convert(message);
+ } else {
+ return message.toString();
+ }
+ }
+
+ String formatStackTrace(StackTrace stackTrace, int methodPosition) {
+ var lines = stackTrace.toString()?.split('\n');
+ var formatted = [];
+ var count = 0;
+ for (var line in lines) {
+ if (_discardDeviceStacktraceLine(line) ||
+ _discardWebStacktraceLine(line)) {
+ continue;
+ }
+ formatted.add('${line.replaceFirst(RegExp(r'#\d+\s+'), '')}');
+ if (++count == methodPosition) {
+ break;
+ }
+ }
+
+ if (formatted.isEmpty) {
+ return null;
+ } else {
+ //return formatted.join('\n');
+ return formatted.last;
+ }
+ }
+
+ bool _discardDeviceStacktraceLine(String line) {
+ var match = _deviceStackTraceRegex.matchAsPrefix(line);
+ if (match == null) {
+ return false;
+ }
+ return match.group(2).startsWith('package:logger');
+ }
+
+ bool _discardWebStacktraceLine(String line) {
+ var match = _webStackTraceRegex.matchAsPrefix(line);
+ if (match == null) {
+ return false;
+ }
+ return match.group(1).startsWith('packages/logger') ||
+ match.group(1).startsWith('dart-sdk/lib');
+ }
+}
+
+Logger getLogger(String className) {
+ return Logger(printer: SimpleLogPrinter(className));
+}
diff --git a/lib/core/utils/utilsParse.dart b/lib/core/utils/utilsParse.dart
new file mode 100644
index 0000000..a5d21ea
--- /dev/null
+++ b/lib/core/utils/utilsParse.dart
@@ -0,0 +1,4 @@
+List parseListString(json){
+ if(json==null) return null;
+ return new List.from(json);
+}
\ No newline at end of file
diff --git a/lib/main.dart b/lib/main.dart
new file mode 100644
index 0000000..6d31a7a
--- /dev/null
+++ b/lib/main.dart
@@ -0,0 +1,64 @@
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_redux/flutter_redux.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:satu/routes/router.dart';
+import 'package:satu/shared/app_colors.dart';
+import 'package:satu/views/start_up/start_up_view.dart';
+import 'package:satu/widgets/dialog/dialog_manager.dart';
+
+import 'core/redux/store.dart';
+import 'core/services/dialog_service.dart';
+import 'core/services/navigator_service.dart';
+import 'core/utils/locator.dart';
+
+void main() async {
+ WidgetsFlutterBinding.ensureInitialized();
+ //initialize locator
+ await LocatorInjector.setupLocator();
+
+ LicenseRegistry.addLicense(() async* {
+ final license = await rootBundle.loadString('assets/google_fonts/OFL.txt');
+ yield LicenseEntryWithLineBreaks(['google_fonts'], license);
+ });
+
+ await Redux.init();
+ runApp(MainApplication());
+}
+
+
+class MainApplication extends StatelessWidget {
+
+ @override
+ Widget build(BuildContext context) {
+
+ return StoreProvider(
+ store: Redux.store,
+ child: ScreenUtilInit(
+ designSize: Size(411.43, 683.43),
+ builder: () => MaterialApp(
+ theme: ThemeData(
+ backgroundColor: backgroundColor,
+ primaryColor: whiteColor,
+ accentColor: yellowColor,
+ scaffoldBackgroundColor: fillColor,
+ // textTheme: GoogleFonts.robotoTextTheme(
+ // Theme.of(context).textTheme,
+ // )
+ ),
+ debugShowCheckedModeBanner: false,
+ builder: (context, child) => Navigator(
+ key: locator().dialogNavigationKey,
+ onGenerateRoute: (settings) => MaterialPageRoute(
+ builder: (context) => DialogManager(child: child)),
+ ),
+ navigatorKey: locator().navigatorKey,
+ home: StartUpView(), // first page
+ onGenerateRoute: generateRoute,
+ ),
+ ),
+ );
+ }
+}
+
diff --git a/lib/routes/route_names.dart b/lib/routes/route_names.dart
new file mode 100644
index 0000000..b3150f4
--- /dev/null
+++ b/lib/routes/route_names.dart
@@ -0,0 +1,8 @@
+const String LoginViewRoute = "LoginView";
+const String MainViewRoute = "MainView";
+const String WorkViewRoute = "WorkView";
+const String AddProductViewRoute = "AddProductView";
+const String AddByBarcodeViewRoute = "AddByBarcodeView";
+
+const String SettingPrinterBluetoothViewRoute = "SettingPrinterBluetoothView";
+// Generate the views here
diff --git a/lib/routes/router.dart b/lib/routes/router.dart
new file mode 100644
index 0000000..825c703
--- /dev/null
+++ b/lib/routes/router.dart
@@ -0,0 +1,90 @@
+
+import 'package:satu/views/add_by_barcode/add_by_barcode_view.dart';
+import 'package:satu/views/add_product/add_product_view.dart';
+import 'package:satu/views/login/login_view.dart';
+import 'package:satu/views/main/main_view.dart';
+import 'package:satu/views/settings/printer_bluetooth/PrinterSelect.dart';
+import 'package:satu/views/work/work_view.dart';
+
+import './route_names.dart';
+import 'package:flutter/material.dart';
+
+Route generateRoute(RouteSettings settings) {
+ switch (settings.name) {
+ case LoginViewRoute:
+ //LoginModel model = settings.arguments as LoginModel;
+ return _getPageRoute(
+ routeName: settings.name,
+ viewToShow: LoginView(),
+ );
+ case WorkViewRoute:
+ return _getPageRoute(
+ routeName: settings.name,
+ viewToShow: WorkView(),
+ );
+ case MainViewRoute:
+ return _getPageRoute(
+ routeName: settings.name,
+ viewToShow: MainView(),
+ );
+ case AddProductViewRoute:
+ return _getPageRoute(
+ routeName: settings.name,
+ viewToShow: AddProductView(),
+ );
+ case AddByBarcodeViewRoute:
+ return _getPageRoute(
+ routeName: settings.name,
+ viewToShow: AddByBarcodeView(title: 'Scanner',),
+ );
+ case SettingPrinterBluetoothViewRoute:
+ return _getPageRoute(
+ routeName: settings.name,
+ viewToShow: PrinterSelectView(title: 'Принтер печати чеков',),
+ );
+ // case ImageShowRoute:
+ // ImageShowModel data = settings.arguments as ImageShowModel;
+ // //return SlideRightRoute(widget: ImageShowContainer(data));
+ // return _getPageRoute(
+ // routeName: settings.name,
+ // viewToShow: ImageShowContainer(data),
+ // );
+ default:
+ return MaterialPageRoute(
+ builder: (_) => Scaffold(
+ body: Center(
+ child: Text('No route defined for ${settings.name}')),
+ ));
+ }
+}
+
+PageRoute _getPageRoute({String routeName, Widget viewToShow}) {
+ return MaterialPageRoute(
+ settings: RouteSettings(
+ name: routeName,
+ ),
+ builder: (_) => viewToShow);
+}
+
+class SlideRightRoute extends PageRouteBuilder {
+ final Widget widget;
+ SlideRightRoute({this.widget})
+ : super(
+ pageBuilder: (BuildContext context, Animation animation,
+ Animation secondaryAnimation) {
+ return widget;
+ },
+ transitionsBuilder: (BuildContext context,
+ Animation animation,
+ Animation secondaryAnimation,
+ Widget child) {
+ return new SlideTransition(
+ position: new Tween(
+ begin: const Offset(1.0, 0.0),
+ end: Offset.zero,
+ ).animate(animation),
+ child: child,
+ );
+ },
+ );
+}
diff --git a/lib/shared/app_colors.dart b/lib/shared/app_colors.dart
new file mode 100644
index 0000000..44d6246
--- /dev/null
+++ b/lib/shared/app_colors.dart
@@ -0,0 +1,45 @@
+import 'package:flutter/material.dart';
+
+const Color backgroundColor = Color.fromRGBO(255, 255, 255, 1);
+// const Color fillColor = Color.fromRGBO(248, 248, 248, 1);
+const Color fillColor = Color.fromRGBO(252, 252, 252, 1);
+const Color primaryColor = Color.fromRGBO(51, 122, 183, 1);
+
+const Color halykColor = Color.fromRGBO(0, 118, 59, 1);
+
+
+const Color menuColor = Color.fromRGBO(0, 75, 120, 1);
+
+const Color redColor = Color.fromRGBO(217, 83, 79, 1);
+const Color greenColor = Color.fromRGBO(92, 184, 92, 1);
+const Color whiteColor = Color.fromRGBO(255, 255, 255, 1);
+const Color blackColor = Color.fromRGBO(0, 0, 0, 1);
+const Color yellowColor = Color.fromRGBO(250, 175, 0, 1);
+
+const Color purpleColor = Color.fromRGBO(118, 122, 230, 1);
+const Color purpleSecondColor = Color.fromRGBO(140, 143, 236, 1);
+
+const Color textColor = Color.fromRGBO(51, 51, 51, 1);
+
+
+
+const Color shadowColor = Color.fromRGBO(80, 137, 196, 0.47);
+const Color cardShadowColor = Color.fromRGBO(228, 229, 231, 0.25);
+const Color blueColor = Color.fromRGBO(96, 205, 255, 1);
+const Color blueColorLigth = Color.fromRGBO(96, 205, 255, 0.536);
+
+
+
+const Color textColorLight = Color.fromRGBO(162, 171, 191, 1);
+const Color dayColor = Color.fromRGBO(52, 72, 94, 0.536);
+const Color dayColorLight = Color.fromRGBO(255, 228, 231, 1);
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/shared/shared_styles.dart b/lib/shared/shared_styles.dart
new file mode 100644
index 0000000..a46dc4f
--- /dev/null
+++ b/lib/shared/shared_styles.dart
@@ -0,0 +1,47 @@
+
+import 'package:flutter/material.dart';
+
+import 'app_colors.dart';
+
+// Box Decorations
+
+BoxDecoration fieldDecoration =
+ BoxDecoration(borderRadius: BorderRadius.circular(5) , color: Colors.white);
+
+BoxDecoration disabledFieldDecoration = BoxDecoration(
+ borderRadius: BorderRadius.circular(5), color: Colors.grey[100]);
+
+// Field Variables
+
+const double fieldHeight = 55;
+const double smallFieldHeight = 40;
+const double inputFieldBottomMargin = 30;
+const double inputFieldSmallBottomMargin = 0;
+const EdgeInsets fieldPadding = const EdgeInsets.symmetric(horizontal: 8.0);
+const EdgeInsets largeFieldPadding =
+ const EdgeInsets.symmetric(horizontal: 15, vertical: 15);
+
+// Text Variables
+const TextStyle productTextStyle = const TextStyle( fontWeight: FontWeight.w600, color: Colors.black, fontSize: 15);
+const TextStyle productSubTextStyle = const TextStyle( fontWeight: FontWeight.w400, color: Colors.black54, fontSize: 12);
+
+const TextStyle buttonTitleTextStyle = const TextStyle(
+ fontWeight: FontWeight.w700, color: whiteColor, fontSize: 14);
+const TextStyle buttonTitleTextBlackStyle = const TextStyle(
+ fontWeight: FontWeight.w700, color: Colors.black, fontSize: 14);
+const TextStyle buttonBigTitleTextStyle = const TextStyle(
+ fontWeight: FontWeight.w700,
+ color: whiteColor,
+ fontSize: 22,
+);
+
+const TextStyle dropDownTradeTypeTextStyle =
+ TextStyle(color: Colors.black54, fontWeight: FontWeight.bold, fontSize: 24);
+
+// Box Shadow
+const BoxShadow mainShadowBox =
+ BoxShadow(blurRadius: 16, color: shadowColor, offset: Offset(0, 5));
+const BoxShadow buttonShadowBox =
+ BoxShadow(blurRadius: 5, color: Colors.grey, offset: Offset(0, 1));
+const BoxShadow cardShadowBox =
+ BoxShadow(blurRadius: 5, color: Colors.black26, offset: Offset(0, 5));
diff --git a/lib/shared/ui_helpers.dart b/lib/shared/ui_helpers.dart
new file mode 100644
index 0000000..eb879f4
--- /dev/null
+++ b/lib/shared/ui_helpers.dart
@@ -0,0 +1,38 @@
+import 'package:flutter/material.dart';
+
+const Widget horizontalSpaceTiny = SizedBox(width: 5.0);
+const Widget horizontalSpaceSmall = SizedBox(width: 10.0);
+const Widget horizontalSpaceMedium = SizedBox(width: 25.0);
+
+const Widget verticalSpaceTiny = SizedBox(height: 5.0);
+const Widget verticalSpaceSmall = SizedBox(height: 10.0);
+const Widget verticalSpaceMedium = SizedBox(height: 25.0);
+const Widget verticalSpaceLarge = SizedBox(height: 50.0);
+const Widget verticalSpaceMassive = SizedBox(height: 120.0);
+
+Widget spacedDivider = Column(
+ children: const [
+ verticalSpaceMedium,
+ const Divider(color: Colors.blueGrey, height: 5.0),
+ verticalSpaceMedium,
+ ],
+);
+
+Widget verticalSpace(double height) => SizedBox(height: height);
+
+double screenWidth(BuildContext context) => MediaQuery.of(context).size.width;
+double screenHeight(BuildContext context) => MediaQuery.of(context).size.height;
+
+double screenHeightFraction(BuildContext context,
+ {int dividedBy = 1, double offsetBy = 0}) =>
+ (screenHeight(context) - offsetBy) / dividedBy;
+
+double screenWidthFraction(BuildContext context,
+ {int dividedBy = 1, double offsetBy = 0}) =>
+ (screenWidth(context) - offsetBy) / dividedBy;
+
+double halfScreenWidth(BuildContext context) =>
+ screenWidthFraction(context, dividedBy: 2);
+
+double thirdScreenWidth(BuildContext context) =>
+ screenWidthFraction(context, dividedBy: 3);
diff --git a/lib/views/add_by_barcode/add_by_barcode_view.dart b/lib/views/add_by_barcode/add_by_barcode_view.dart
new file mode 100644
index 0000000..947279b
--- /dev/null
+++ b/lib/views/add_by_barcode/add_by_barcode_view.dart
@@ -0,0 +1,43 @@
+import 'dart:io';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+
+
+
+class AddByBarcodeView extends StatefulWidget {
+ final String title;
+ const AddByBarcodeView({
+ Key key, this.title,
+ }) : super(key: key);
+
+ @override
+ _AddByBarcodeViewState createState() => _AddByBarcodeViewState();
+}
+
+class _AddByBarcodeViewState extends State {
+
+ @override
+ void initState() {
+ super.initState();
+ }
+
+
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ home: Scaffold(
+ body: Container(
+ height: 2000,
+ child: Column(
+ children: [
+
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+
+}
diff --git a/lib/views/add_product/add_product_view.dart b/lib/views/add_product/add_product_view.dart
new file mode 100644
index 0000000..1a23f11
--- /dev/null
+++ b/lib/views/add_product/add_product_view.dart
@@ -0,0 +1,80 @@
+import 'package:flutter/material.dart';
+import 'package:satu/shared/app_colors.dart';
+import 'package:satu/shared/ui_helpers.dart';
+import 'package:satu/views/add_product/component/add_category_list_item.dart';
+import 'package:satu/views/add_product/component/app_bar.dart';
+import 'package:satu/widgets/fields/input_field.dart';
+
+import 'component/add_product_list_item.dart';
+
+class AddProductView extends StatefulWidget {
+ @override
+ _AddProductViewState createState() => _AddProductViewState();
+}
+
+class _AddProductViewState extends State {
+
+ TextEditingController _searchTextController;
+ final FocusNode _searchFocusNode = new FocusNode();
+
+ @override
+ void initState() {
+ _searchTextController = TextEditingController();
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ _searchTextController.dispose();
+ _searchFocusNode.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AddProductAppBar(title: 'Товар', actions: actions(),),
+ body: Column(
+ children: [
+ InputField(placeholder: 'Поиск по наименованию и коду товара', search: true, controller: _searchTextController, fieldFocusNode: _searchFocusNode,),
+ verticalSpaceTiny,
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 8.0 ),
+ child: ListView.builder(
+ itemCount: 50,
+ itemBuilder: (BuildContext context, int index) {
+ if(index < 5) {
+ return AddCategoryListItem(
+ name: 'Категория',
+ isOdd: index % 2 ==0,
+ key: Key('category_${index}'),
+ );
+ }
+
+ return AddProductListItem(
+ key: Key('product_${index}'),
+ ean: '1234567890123',
+ isOdd: index % 2 ==0,
+ name: 'Хлеб ржаной который необходимо покупать каждый раз когда придет мысль об этом - ${index +1}. ',
+ price: 75,
+ count: 15,
+ categoryName: 'Хлебобулочные изделия',
+ );
+ },
+ ),
+ ),
+ ),
+
+ ],
+ ),
+ );
+ }
+
+ List actions() {
+ return [
+ FlatButton(onPressed: () {}, child: Text('Очистить', style: TextStyle( color: Colors.black ),) ,)
+ ];
+ }
+
+}
diff --git a/lib/views/add_product/component/add_category_list_item.dart b/lib/views/add_product/component/add_category_list_item.dart
new file mode 100644
index 0000000..22733cb
--- /dev/null
+++ b/lib/views/add_product/component/add_category_list_item.dart
@@ -0,0 +1,40 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:satu/shared/app_colors.dart';
+import 'package:satu/shared/shared_styles.dart';
+import 'package:satu/shared/ui_helpers.dart';
+
+class AddCategoryListItem extends StatelessWidget {
+ final String name;
+ final bool isOdd;
+
+ const AddCategoryListItem({Key key, this.name, this.isOdd}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Card(
+ child: ListTile(
+ contentPadding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
+ title: Padding(
+ padding: const EdgeInsets.only(top: 4.0),
+ child: Container(
+ height: 50,
+ child: Center(
+ child: Text(
+ name,
+ style: productTextStyle,
+ overflow: TextOverflow.ellipsis,
+ maxLines: 3,
+ ),
+ )
+ ),
+ ),
+ tileColor: !isOdd ? fillColor : backgroundColor,
+ trailing: Icon(
+ Icons.arrow_right,
+ color: yellowColor,
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/views/add_product/component/add_product_list_item.dart b/lib/views/add_product/component/add_product_list_item.dart
new file mode 100644
index 0000000..68b8f74
--- /dev/null
+++ b/lib/views/add_product/component/add_product_list_item.dart
@@ -0,0 +1,59 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:satu/shared/app_colors.dart';
+import 'package:satu/shared/shared_styles.dart';
+import 'package:satu/shared/ui_helpers.dart';
+
+class AddProductListItem extends StatelessWidget {
+ final String name;
+ final String ean;
+ final String categoryName;
+ final num price;
+ final num count;
+ final bool isOdd;
+
+ const AddProductListItem({Key key, this.name, this.ean, this.categoryName, this.price, this.count, this.isOdd}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Card(
+ child: ListTile(
+ contentPadding: const EdgeInsets.symmetric( horizontal: 8.0 ,vertical: 4.0 ),
+ title: Padding(
+ padding: const EdgeInsets.all(4.0),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Flexible(
+ flex: 3,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(name , style: productTextStyle, overflow: TextOverflow.ellipsis, maxLines: 2,),
+ verticalSpaceTiny,
+ if(ean!=null)
+ Text('Штрих-код: $ean' , style: productSubTextStyle,),
+ if(categoryName!=null)
+ Text(categoryName, style: productSubTextStyle,),
+ ],
+ ),
+ ),
+ Flexible(
+ flex: 1,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text('${price} ₸', style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold ),),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ tileColor: !isOdd ? fillColor : backgroundColor,
+ ),
+ );
+ }
+}
diff --git a/lib/views/add_product/component/app_bar.dart b/lib/views/add_product/component/app_bar.dart
new file mode 100644
index 0000000..a983f02
--- /dev/null
+++ b/lib/views/add_product/component/app_bar.dart
@@ -0,0 +1,26 @@
+import 'package:flutter/material.dart';
+import 'package:satu/core/services/navigator_service.dart';
+import 'package:satu/core/utils/locator.dart';
+import 'package:satu/shared/app_colors.dart';
+
+class AddProductAppBar extends StatelessWidget implements PreferredSizeWidget {
+ final String title;
+ final List actions;
+
+ const AddProductAppBar({Key key, this.title, this.actions}) : super(key: key);
+ @override
+ Widget build(BuildContext context) {
+ return AppBar(
+ title: Text(title, style: const TextStyle(fontWeight: FontWeight.w700, color: Colors.black, fontSize: 25)),
+ backgroundColor: Colors.transparent,
+ elevation: 0.0,
+ actions: actions
+ ,
+ );
+ }
+
+ @override
+ Size get preferredSize {
+ return new Size.fromHeight(60.0);
+ }
+}
diff --git a/lib/views/login/login_view.dart b/lib/views/login/login_view.dart
new file mode 100644
index 0000000..a011c0e
--- /dev/null
+++ b/lib/views/login/login_view.dart
@@ -0,0 +1,181 @@
+import 'dart:ui';
+
+import 'package:barcode_scan/gen/protos/protos.pb.dart';
+import 'package:barcode_scan/gen/protos/protos.pbenum.dart';
+import 'package:barcode_scan/model/scan_options.dart';
+import 'package:barcode_scan/platform_wrapper.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_redux/flutter_redux.dart';
+import 'package:flutter/material.dart';
+import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
+import 'package:satu/core/redux/actions/user_actions.dart';
+import 'package:satu/core/redux/constants/auth_type_const.dart';
+import 'package:satu/core/redux/state/user_state.dart';
+import 'package:satu/core/redux/store.dart';
+import 'package:satu/core/services/api_service.dart';
+import 'package:satu/core/services/dialog_service.dart';
+import 'package:satu/core/utils/locator.dart';
+import 'package:satu/shared/app_colors.dart';
+import 'package:satu/shared/ui_helpers.dart';
+import 'package:satu/widgets/buttons/busy_button.dart';
+import 'package:satu/widgets/fields/input_field.dart';
+
+class LoginView extends StatefulWidget {
+
+
+ @override
+ _LoginViewState createState() => _LoginViewState();
+}
+
+class _LoginViewState extends State {
+ TextEditingController emailController;
+
+ TextEditingController passwordController;
+
+ final FocusNode passwordNode = new FocusNode();
+
+ final GlobalKey _scaffoldKey = new GlobalKey();
+
+ final DialogService _dialogService = locator();
+
+ @override
+ void initState() {
+ super.initState();
+
+ emailController = TextEditingController(text: 'test11@gmail.com');
+ passwordController = TextEditingController(text: 'qwe123');
+
+ }
+
+ @override
+ void dispose() {
+ emailController.dispose();
+ passwordController.dispose();
+ passwordNode.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return StoreConnector(
+ converter: (store) => store.state.userState,
+ builder: (context, vm) {
+ return Scaffold(
+ key: _scaffoldKey,
+ backgroundColor: fillColor,
+ body: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 50),
+ child: Column(
+ mainAxisSize: MainAxisSize.max,
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Stack(
+ alignment: Alignment.bottomLeft,
+ children: [
+ SizedBox(
+ height: 150,
+ child: Image.asset('assets/images/logo.png'),
+ ),
+ Positioned(
+ child: Text(
+ 'онлайн касса',
+ style: TextStyle(fontWeight: FontWeight.bold),
+ ),
+ bottom: 23.0,
+ left: 25.0,
+ ),
+ ],
+ ),
+
+ InputField(
+ placeholder: 'Электронная почта',
+ controller: emailController,
+ textInputType: TextInputType.emailAddress,
+ nextFocusNode: passwordNode,
+ ),
+ verticalSpaceSmall,
+ InputField(
+ placeholder: 'Пароль',
+ password: true,
+ controller: passwordController,
+ fieldFocusNode: passwordNode,
+ enterPressed: _pressBtnEnter,
+ textInputAction: TextInputAction.done,
+ ),
+ verticalSpaceMedium,
+ Row(
+ mainAxisSize: MainAxisSize.max,
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ SizedBox(
+ width: 150,
+ child: BusyButton(
+ title: 'Войти',
+ busy: vm.isLoading,
+ onPressed: _pressBtnEnter,
+ mainColor: yellowColor,
+ ),
+ )
+ ],
+ ),
+ verticalSpaceLarge,
+// TextLink(
+// 'Регистрация',
+// onPressed: () {},
+// ),
+ IconButton(
+ icon: Icon(MdiIcons.qrcodeScan),
+ iconSize: 40,
+ tooltip: "Scan",
+ onPressed: scan,
+ )
+ ],
+ ),
+ ));
+ });
+ }
+
+ _pressBtnEnter() async {
+ Redux.store.dispatch(authenticate(emailController.text, passwordController.text));
+ }
+
+ Future scan() async {
+ try {
+ var options = ScanOptions(strings: {
+ "cancel": 'Отмена',
+ "flash_on": 'Вкл фонарик',
+ "flash_off": 'Выкл фонарик',
+ });
+ var result = await BarcodeScanner.scan(options: options);
+ print(result.type); // The result type (barcode, cancelled, failed)
+ print(result.rawContent); // The barcode content
+ print(result.format); // The barcode format (as enum)
+ print(result.formatNote); // If a unknown format was scanned this field contains a note
+ if (result.type == ResultType.Barcode && result.rawContent?.length == 60) {
+ //Redux.store.dispatch(authenticateToken(result.rawContent));
+ } else if (result.type == ResultType.Error) {
+ _dialogService.showDialog(description: 'Не верный формат QR кода');
+ }
+ } on PlatformException catch (e) {
+ var result = ScanResult.create();
+ result.type = ResultType.Error;
+ result.format = BarcodeFormat.unknown;
+ if (e.code == BarcodeScanner.cameraAccessDenied) {
+ result.rawContent = 'The user did not grant the camera permission!';
+ _dialogService.showDialog(description: 'Нет доступа до камеры устройства');
+ } else {
+ result.rawContent = 'Unknown error: $e';
+ _dialogService.showDialog(description: 'Неизвестная ошибка: $e');
+ }
+ }
+ }
+}
+
+class LoginModel {
+ final String authType;
+ final String login;
+ final String password;
+
+ LoginModel({this.authType, this.login, this.password});
+}
diff --git a/lib/views/main/main_view.dart b/lib/views/main/main_view.dart
new file mode 100644
index 0000000..177bba2
--- /dev/null
+++ b/lib/views/main/main_view.dart
@@ -0,0 +1,46 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_redux/flutter_redux.dart';
+import 'package:satu/core/redux/state/nav_state.dart';
+import 'package:satu/core/redux/store.dart';
+import 'package:satu/core/services/navigator_service.dart';
+import 'package:satu/core/utils/locator.dart';
+import 'package:satu/views/settings/printer_bluetooth/PrinterSelect.dart';
+import 'package:satu/views/settings/setting_view.dart';
+import 'package:satu/views/work/work_view.dart';
+import 'package:satu/widgets/drawer/app_drawer.dart';
+
+class MainView extends StatefulWidget {
+ @override
+ _MainViewState createState() => _MainViewState();
+}
+
+class _MainViewState extends State {
+
+ NavigatorService _navigatorService = locator();
+
+ final _workView = new WorkView(text: '1',);
+ final _settingsView = new SettingsView();
+
+ Widget _body(Type viewClass) {
+ if(viewClass == WorkView) {
+ return _workView;
+ }
+ if(viewClass == SettingsView) {
+ return _settingsView;
+ }
+ return _workView;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ key: _navigatorService.scaffoldDrawerKey,
+ drawer: AppDrawer(),
+ body: StoreConnector(
+ converter: (store) => store.state.navState,
+ builder: (_, vm) {
+ return _body(vm.drawerViewClass);
+ })
+ );
+ }
+}
diff --git a/lib/views/settings/component/setting_item.dart b/lib/views/settings/component/setting_item.dart
new file mode 100644
index 0000000..3445e90
--- /dev/null
+++ b/lib/views/settings/component/setting_item.dart
@@ -0,0 +1,27 @@
+import 'package:flutter/material.dart';
+
+class SettingItem extends StatefulWidget {
+
+ final String name;
+ final String value;
+ final Function onTap;
+
+ SettingItem({Key key, this.name, this.value, this.onTap}) : super(key: key);
+
+ @override
+ _SettingItemState createState() => _SettingItemState();
+}
+
+class _SettingItemState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Card(
+ child: ListTile(
+ title: Text(widget.name),
+ subtitle: widget.value !=null ? Text(widget.value) : null,
+ trailing: Icon(Icons.chevron_right),
+ onTap: widget.onTap,
+ ),
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/views/settings/printer_bluetooth/PrinterSelect.dart b/lib/views/settings/printer_bluetooth/PrinterSelect.dart
new file mode 100644
index 0000000..558ed7a
--- /dev/null
+++ b/lib/views/settings/printer_bluetooth/PrinterSelect.dart
@@ -0,0 +1,366 @@
+import 'dart:convert';
+import 'dart:typed_data';
+
+import 'package:charset_converter/charset_converter.dart';
+import 'package:esc_pos_bluetooth/esc_pos_bluetooth.dart';
+import 'package:esc_pos_utils/esc_pos_utils.dart';
+import 'package:flutter/material.dart' hide Image;
+import 'package:image/image.dart' as Im;
+import 'package:flutter/services.dart';
+import 'package:intl/intl.dart';
+
+class PrinterSelectView extends StatefulWidget {
+ PrinterSelectView({Key key, this.title}) : super(key: key);
+ final String title;
+
+ @override
+ _PrinterSelectViewState createState() => _PrinterSelectViewState();
+}
+
+class _PrinterSelectViewState extends State {
+ PrinterBluetoothManager printerManager = PrinterBluetoothManager();
+ List _devices = [];
+
+ @override
+ void initState() {
+ super.initState();
+
+ printerManager.scanResults.listen((devices) async {
+ // print('UI: Devices found ${devices.length}');
+ setState(() {
+ _devices = devices;
+ });
+ });
+ }
+
+ void _startScanDevices() {
+ setState(() {
+ _devices = [];
+ });
+ printerManager.startScan(Duration(seconds: 4));
+ }
+
+ void _stopScanDevices() {
+ printerManager.stopScan();
+ }
+
+ Future demoReceipt(PaperSize paper) async {
+ final Ticket ticket = Ticket(paper, );
+
+ // Print image
+ final ByteData data = await rootBundle.load('assets/images/aman_kassa_check.png');
+ final Uint8List bytes = data.buffer.asUint8List();
+ final Im.Image image = Im.decodeImage(bytes);
+ Im.Image thumbnail = Im.copyResize(image, width: 270);
+ // ticket.image(thumbnail, align: PosAlign.center);
+
+ //ticket.imageRaster(image, align: PosAlign.center);
+
+ ticket.text('AMAN-SATU',
+ styles: PosStyles(
+ align: PosAlign.center,
+ height: PosTextSize.size2,
+ width: PosTextSize.size2,
+ ),
+ linesAfter: 1);
+
+ ticket.text('889 Watson Lane', styles: PosStyles(align: PosAlign.center));
+ ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, codeTable: PosCodeTable.westEur), containsChinese: true);
+ ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, fontType: PosFontType.fontA), containsChinese: true);
+ ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, fontType: PosFontType.fontB), containsChinese: true);
+ ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, height: PosTextSize.size1), containsChinese: true);
+ ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, width: PosTextSize.size2), containsChinese: true);
+ ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, width: PosTextSize.size3), containsChinese: true);
+ ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, width: PosTextSize.size4), containsChinese: true);
+ ticket.text('Tel: 830-221-1234', styles: PosStyles(align: PosAlign.center));
+ ticket.text('Web: www.example.com',
+ styles: PosStyles(align: PosAlign.center), linesAfter: 1);
+
+ ticket.hr();
+ ticket.row([
+ PosColumn(text: 'Qty', width: 1),
+ PosColumn(text: 'Item', width: 7),
+ PosColumn(
+ text: 'Price', width: 2, styles: PosStyles(align: PosAlign.right)),
+ PosColumn(
+ text: 'Total', width: 2, styles: PosStyles(align: PosAlign.right)),
+ ]);
+
+ ticket.row([
+ PosColumn(text: '2', width: 1),
+ PosColumn(text: 'ONION RINGS', width: 7),
+ PosColumn(
+ text: '0.99', width: 2, styles: PosStyles(align: PosAlign.right)),
+ PosColumn(
+ text: '1.98', width: 2, styles: PosStyles(align: PosAlign.right)),
+ ]);
+ ticket.row([
+ PosColumn(text: '1', width: 1),
+ PosColumn(text: 'PIZZA', width: 7),
+ PosColumn(
+ text: '3.45', width: 2, styles: PosStyles(align: PosAlign.right)),
+ PosColumn(
+ text: '3.45', width: 2, styles: PosStyles(align: PosAlign.right)),
+ ]);
+ ticket.row([
+ PosColumn(text: '1', width: 1),
+ PosColumn(text: 'SPRING ROLLS', width: 7),
+ PosColumn(
+ text: '2.99', width: 2, styles: PosStyles(align: PosAlign.right)),
+ PosColumn(
+ text: '2.99', width: 2, styles: PosStyles(align: PosAlign.right)),
+ ]);
+ ticket.row([
+ PosColumn(text: '3', width: 1),
+ PosColumn(text: 'CRUNCHY STICKS', width: 7),
+ PosColumn(
+ text: '0.85', width: 2, styles: PosStyles(align: PosAlign.right)),
+ PosColumn(
+ text: '2.55', width: 2, styles: PosStyles(align: PosAlign.right)),
+ ]);
+ ticket.hr();
+
+ ticket.row([
+ PosColumn(
+ text: 'TOTAL',
+ width: 6,
+ styles: PosStyles(
+ height: PosTextSize.size2,
+ width: PosTextSize.size2,
+ )),
+ PosColumn(
+ text: '\$10.97',
+ width: 6,
+ styles: PosStyles(
+ align: PosAlign.right,
+ height: PosTextSize.size2,
+ width: PosTextSize.size2,
+ )),
+ ]);
+
+ ticket.hr(ch: '=', linesAfter: 1);
+
+ ticket.row([
+ PosColumn(
+ text: 'Cash',
+ width: 7,
+ styles: PosStyles(align: PosAlign.right, width: PosTextSize.size2)),
+ PosColumn(
+ text: '\$15.00',
+ width: 5,
+ styles: PosStyles(align: PosAlign.right, width: PosTextSize.size2)),
+ ]);
+ ticket.row([
+ PosColumn(
+ text: 'Change',
+ width: 7,
+ styles: PosStyles(align: PosAlign.right, width: PosTextSize.size2)),
+ PosColumn(
+ text: '\$4.03',
+ width: 5,
+ styles: PosStyles(align: PosAlign.right, width: PosTextSize.size2)),
+ ]);
+
+ ticket.feed(2);
+ ticket.text('Thank you!',
+ styles: PosStyles(align: PosAlign.center, bold: true));
+
+ final now = DateTime.now();
+ final formatter = DateFormat('MM/dd/yyyy H:m');
+ final String timestamp = formatter.format(now);
+ ticket.text(timestamp,
+ styles: PosStyles(align: PosAlign.center), linesAfter: 2);
+
+ // Print QR Code from image
+ // try {
+ // const String qrData = 'example.com';
+ // const double qrSize = 200;
+ // final uiImg = await QrPainter(
+ // data: qrData,
+ // version: QrVersions.auto,
+ // gapless: false,
+ // ).toImageData(qrSize);
+ // final dir = await getTemporaryDirectory();
+ // final pathName = '${dir.path}/qr_tmp.png';
+ // final qrFile = File(pathName);
+ // final imgFile = await qrFile.writeAsBytes(uiImg.buffer.asUint8List());
+ // final img = decodeImage(imgFile.readAsBytesSync());
+
+ // ticket.image(img);
+ // } catch (e) {
+ // print(e);
+ // }
+
+ // Print QR Code using native function
+ // ticket.qrcode('example.com');
+
+ ticket.feed(2);
+ ticket.cut();
+ return ticket;
+ }
+
+ Future testTicket(PaperSize paper) async {
+ final Ticket ticket = Ticket(paper);
+ Uint8List encTxt4 =
+ await CharsetConverter.encode("cp866", "Russian: Привет Мир!");
+ ticket.textEncoded(encTxt4,
+ styles: PosStyles(codeTable: PosCodeTable.pc866_2));
+
+ ticket.text('Kazakh: Сәлем Әлем!',
+ styles: PosStyles(codeTable: PosCodeTable.westEur), containsChinese: true);
+
+ // Uint8List encTxt1 =
+ // await CharsetConverter.encode("utf-8", "Kazakh: Сәлем Әлем!");
+ // ticket.textEncoded(encTxt1,
+ // styles: PosStyles(codeTable: PosCodeTable.pc866_2));
+ ticket.text(
+ 'Regular: aA bB cC dD eE fF gG hH iI jJ kK lL mM nN oO pP qQ rR sS tT uU vV wW xX yY zZ');
+ ticket.text('Special 1: àÀ èÈ éÉ ûÛ üÜ çÇ ôÔ',
+ styles: PosStyles(codeTable: PosCodeTable.westEur));
+ ticket.text('Special 2: blåbærgrød',
+ styles: PosStyles(codeTable: PosCodeTable.westEur));
+
+ ticket.text('Bold text', styles: PosStyles(bold: true));
+ ticket.text('Reverse text', styles: PosStyles(reverse: true));
+ ticket.text('Underlined text',
+ styles: PosStyles(underline: true), linesAfter: 1);
+ ticket.text('Align left', styles: PosStyles(align: PosAlign.left));
+ ticket.text('Align center', styles: PosStyles(align: PosAlign.center));
+ ticket.text('Align right',
+ styles: PosStyles(align: PosAlign.right), linesAfter: 1);
+
+ ticket.row([
+ PosColumn(
+ text: 'col3',
+ width: 3,
+ styles: PosStyles(align: PosAlign.center, underline: true),
+ ),
+ PosColumn(
+ text: 'col6',
+ width: 6,
+ styles: PosStyles(align: PosAlign.center, underline: true),
+ ),
+ PosColumn(
+ text: 'col3',
+ width: 3,
+ styles: PosStyles(align: PosAlign.center, underline: true),
+ ),
+ ]);
+
+ ticket.text('Text size 200%',
+ styles: PosStyles(
+ height: PosTextSize.size2,
+ width: PosTextSize.size2,
+ ));
+
+ // Print image
+ //final ByteData data = await rootBundle.load('assets/images/logo.png');
+ //final Uint8List bytes = data.buffer.asUint8List();
+ // Print image using alternative commands
+ // ticket.imageRaster(image);
+ // ticket.imageRaster(image, imageFn: PosImageFn.graphics);
+
+ // Print barcode
+ final List barData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 4];
+ ticket.barcode(Barcode.upcA(barData));
+
+ // Print mixed (chinese + latin) text. Only for printers supporting Kanji mode
+ ticket.text(
+ 'hello ! 中文字 # world @ éphémère &',
+ styles: PosStyles(codeTable: PosCodeTable.westEur),
+ containsChinese: true,
+ );
+
+ ticket.text(
+ 'hello ! Мир # world @ éphémère &',
+ styles: PosStyles(codeTable: PosCodeTable.westEur),
+ containsChinese: true,
+ );
+
+ ticket.feed(2);
+
+ ticket.cut();
+ return ticket;
+ }
+
+ void _testPrint(PrinterBluetooth printer) async {
+ printerManager.selectPrinter(printer);
+
+ // TODO Don't forget to choose printer's paper
+ const PaperSize paper = PaperSize.mm58;
+
+ // TEST PRINT
+ // final PosPrintResult res =
+ // await printerManager.printTicket(await testTicket(paper));
+
+ // DEMO RECEIPT
+ final PosPrintResult res =
+ await printerManager.printTicket(await testTicket(paper) , queueSleepTimeMs: 50);
+
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(widget.title),
+ ),
+ body: ListView.builder(
+ itemCount: _devices.length,
+ itemBuilder: (BuildContext context, int index) {
+ return InkWell(
+ onTap: () => _testPrint(_devices[index]),
+ child: Column(
+ children: [
+ Container(
+ height: 60,
+ padding: EdgeInsets.only(left: 10),
+ alignment: Alignment.centerLeft,
+ child: Row(
+ children: [
+ Icon(Icons.print),
+ SizedBox(width: 10),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(_devices[index].name ?? ''),
+ Text(_devices[index].address),
+ Text(
+ 'Click to print a test receipt',
+ style: TextStyle(color: Colors.grey[700]),
+ ),
+ ],
+ ),
+ )
+ ],
+ ),
+ ),
+ Divider(),
+ ],
+ ),
+ );
+ }),
+ floatingActionButton: StreamBuilder(
+ stream: printerManager.isScanningStream,
+ initialData: false,
+ builder: (c, snapshot) {
+ if (snapshot.data) {
+ return FloatingActionButton(
+ child: Icon(Icons.stop),
+ onPressed: _stopScanDevices,
+ backgroundColor: Colors.red,
+ );
+ } else {
+ return FloatingActionButton(
+ child: Icon(Icons.search),
+ onPressed: _startScanDevices,
+ );
+ }
+ },
+ ),
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/lib/views/settings/setting_view.dart b/lib/views/settings/setting_view.dart
new file mode 100644
index 0000000..b1c8f5a
--- /dev/null
+++ b/lib/views/settings/setting_view.dart
@@ -0,0 +1,33 @@
+import 'package:flutter/material.dart';
+import 'package:satu/core/services/navigator_service.dart';
+import 'package:satu/core/utils/locator.dart';
+import 'package:satu/routes/route_names.dart';
+import 'package:satu/views/work/tabs/component/products_app_bar.dart';
+
+import 'component/setting_item.dart';
+
+class SettingsView extends StatelessWidget {
+ NavigatorService _navigatorService = locator();
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: ProductsAppBar(title: 'Настройки',),
+ body: Padding(
+ padding: const EdgeInsets.symmetric( horizontal: 8.0 ),
+ child: SingleChildScrollView(
+ child: Column(
+ children: [
+ SettingItem(
+ name: 'Принтер',
+ value: 'не выбран',
+ onTap: () {
+ _navigatorService.push(SettingPrinterBluetoothViewRoute);
+ }
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/views/start_up/start_up_view.dart b/lib/views/start_up/start_up_view.dart
new file mode 100644
index 0000000..6fff936
--- /dev/null
+++ b/lib/views/start_up/start_up_view.dart
@@ -0,0 +1,58 @@
+
+import 'package:flutter_redux/flutter_redux.dart';
+import 'package:flutter/material.dart';
+import 'package:satu/core/redux/actions/user_actions.dart';
+import 'package:satu/core/redux/state/user_state.dart';
+import 'package:satu/core/redux/store.dart';
+import 'package:satu/core/services/navigator_service.dart';
+import 'package:satu/core/utils/locator.dart';
+import 'package:satu/routes/route_names.dart';
+
+
+class StartUpView extends StatefulWidget {
+ @override
+ _StartUpViewState createState() => _StartUpViewState();
+}
+
+class _StartUpViewState extends State {
+ final NavigatorService _navigation = locator();
+
+ @override
+ void initState() {
+ super.initState();
+ // Redux.store.dispatch(checkUserAction);
+ redirect();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return StoreConnector(
+ converter: (store) => store.state.userState,
+ builder: (context, userState) {
+ return Scaffold(
+ body: Center(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ SizedBox(
+ width: 500,
+ height: 200,
+ child: Image.asset('assets/images/icon_large.png'),
+ ),
+ CircularProgressIndicator(
+ strokeWidth: 3,
+ valueColor: AlwaysStoppedAnimation(
+ Colors.yellow[300],
+ ),
+ )
+ ],
+ ),
+ ));
+ });
+ }
+
+ void redirect() async {
+ await Future.delayed(Duration(seconds: 3));
+ _navigation.replace(LoginViewRoute);
+ }
+}
diff --git a/lib/views/work/tabs/buy_view.dart b/lib/views/work/tabs/buy_view.dart
new file mode 100644
index 0000000..de5967d
--- /dev/null
+++ b/lib/views/work/tabs/buy_view.dart
@@ -0,0 +1,21 @@
+import 'package:flutter/material.dart';
+import 'package:satu/core/services/navigator_service.dart';
+import 'package:satu/core/utils/locator.dart';
+import 'package:satu/shared/shared_styles.dart';
+
+import 'component/products_app_bar.dart';
+
+class BuyView extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: ProductsAppBar(title: 'Покупка',),
+ body: Center(
+ child: Text(
+ 'Index 2: School',
+ style: productTextStyle,
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/views/work/tabs/component/custom_field.dart b/lib/views/work/tabs/component/custom_field.dart
new file mode 100644
index 0000000..ab3a74b
--- /dev/null
+++ b/lib/views/work/tabs/component/custom_field.dart
@@ -0,0 +1,58 @@
+import 'package:flutter/material.dart';
+
+class CustomField extends StatelessWidget {
+
+ final String hintText;
+ final IconData iconData;
+ final String label;
+
+ CustomField({@required this.hintText, @required this.iconData, this.label});
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ margin: EdgeInsets.only(bottom: 24),
+ child: TextField(
+ style: TextStyle(
+ fontSize: 14,
+ //color: kGreyColor,
+ fontWeight: FontWeight.bold,
+ ),
+ decoration: InputDecoration(
+ hintText: hintText,
+ hintStyle: TextStyle(
+ fontSize: 14,
+ //color: kGreyColor,
+ fontWeight: FontWeight.bold,
+ ),
+ labelText: label,
+ enabledBorder: UnderlineInputBorder(
+ borderSide: BorderSide(
+ //color: kPrimaryColor,
+ width: 2,
+ ),
+ ),
+ focusedBorder: UnderlineInputBorder(
+ borderSide: BorderSide(
+ //color: kPrimaryColor,
+ width: 2,
+ ),
+ ),
+ border: UnderlineInputBorder(
+ borderSide: BorderSide(
+ //color: kPrimaryColor,
+ width: 2,
+ ),
+ ),
+ prefixIcon: Padding(
+ padding: EdgeInsets.only(right: 16),
+ child: Icon(
+ iconData,
+ //color: kPrimaryColor,
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/views/work/tabs/component/option_pill.dart b/lib/views/work/tabs/component/option_pill.dart
new file mode 100644
index 0000000..551dc5b
--- /dev/null
+++ b/lib/views/work/tabs/component/option_pill.dart
@@ -0,0 +1,31 @@
+import 'package:flutter/material.dart';
+
+
+class OptionPill extends StatelessWidget {
+
+ final String text;
+ final bool selected;
+
+ OptionPill({@required this.text, @required this.selected});
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ padding: EdgeInsets.symmetric(vertical: 4, horizontal: 16),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.all(
+ Radius.circular(10),
+ ),
+ color: selected ? Colors.black : Colors.transparent,
+ ),
+ child: Text(
+ text,
+ style: TextStyle(
+ color: selected ? Colors.white : Colors.grey[400],
+ fontWeight: FontWeight.bold,
+ fontSize: 14,
+ ),
+ ),
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/views/work/tabs/component/product_list_item.dart b/lib/views/work/tabs/component/product_list_item.dart
new file mode 100644
index 0000000..c958bc8
--- /dev/null
+++ b/lib/views/work/tabs/component/product_list_item.dart
@@ -0,0 +1,87 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:satu/shared/app_colors.dart';
+import 'package:satu/shared/shared_styles.dart';
+import 'package:satu/shared/ui_helpers.dart';
+
+class ProductListItem extends StatelessWidget {
+ final String name;
+ final String ean;
+ final String categoryName;
+ final num price;
+ final num count;
+ final bool isOdd;
+
+ const ProductListItem({Key key, this.name, this.ean, this.categoryName, this.price, this.count, this.isOdd}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Dismissible(
+ background: Container( alignment: AlignmentDirectional.centerEnd, color: redColor, child: Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Text('Удалить', style: buttonTitleTextStyle,),
+ ),),
+ direction: DismissDirection.endToStart,
+ confirmDismiss: (DismissDirection direction) async {
+ return await showDialog(
+ context: context,
+ builder: (BuildContext context) {
+ return AlertDialog(
+ title: const Text("Confirm"),
+ content: const Text("Are you sure you wish to delete this item?"),
+ actions: [
+ FlatButton(
+ onPressed: () => Navigator.of(context).pop(true),
+ child: const Text("DELETE")
+ ),
+ FlatButton(
+ onPressed: () => Navigator.of(context).pop(false),
+ child: const Text("CANCEL"),
+ ),
+ ],
+ );
+ },
+ );
+ },
+ onDismissed: (direction) {
+ print(direction);
+ },
+ key: Key(name),
+ child: ListTile(
+ contentPadding: const EdgeInsets.symmetric( horizontal: 10.0 ,vertical: 4.0 ),
+ title: Padding(
+ padding: const EdgeInsets.only(top: 4.0),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Flexible(
+ flex: 3,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(name , style: const TextStyle( fontWeight: FontWeight.w500 ), overflow: TextOverflow.ellipsis, maxLines: 2,),
+ verticalSpaceTiny,
+ Text('Штрих-код: $ean' , style: productSubTextStyle,),
+ Text(categoryName, style: productSubTextStyle,)
+ ],
+ ),
+ ),
+ Flexible(
+ flex: 1,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ children: [
+ Text('${price} ₸', style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold ),),
+ Text('${count} шт', style: const TextStyle( fontSize: 12 ),),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ tileColor: !isOdd ? fillColor : backgroundColor,
+ ),
+ );
+ }
+}
diff --git a/lib/views/work/tabs/component/products_app_bar.dart b/lib/views/work/tabs/component/products_app_bar.dart
new file mode 100644
index 0000000..57f551d
--- /dev/null
+++ b/lib/views/work/tabs/component/products_app_bar.dart
@@ -0,0 +1,47 @@
+import 'package:flutter/material.dart';
+import 'package:satu/core/services/navigator_service.dart';
+import 'package:satu/core/utils/locator.dart';
+import 'package:satu/shared/app_colors.dart';
+import 'package:satu/views/work/tabs/component/products_header_bar.dart';
+
+class ProductsAppBar extends StatelessWidget implements PreferredSizeWidget {
+ final String title;
+ final List actions;
+ final Widget child;
+ final int childHeight;
+ final num elevation;
+ final Color backgroundColor;
+
+ const ProductsAppBar({Key key, this.title, this.actions, this.child,this.childHeight = 0, this.elevation = 0.0, this.backgroundColor = Colors.transparent }) : super(key: key);
+ @override
+ Widget build(BuildContext context) {
+ return Material(
+ elevation: elevation,
+ color: backgroundColor,
+ child: Column(
+ children: [
+ AppBar(
+ title: Text(title),
+ backgroundColor: Colors.transparent,
+ elevation: 0.0,
+ leading: IconButton(
+ icon: Icon(Icons.menu, color: yellowColor,),
+ onPressed: () {
+ locator().scaffoldDrawerKey.currentState.openDrawer();
+ },
+ ),
+ actions: actions
+ ,
+ ),
+ if(child !=null && childHeight > 0)
+ child,
+ ],
+ ),
+ );
+ }
+
+ @override
+ Size get preferredSize {
+ return new Size.fromHeight(60.0 + childHeight);
+ }
+}
diff --git a/lib/views/work/tabs/component/products_header_bar.dart b/lib/views/work/tabs/component/products_header_bar.dart
new file mode 100644
index 0000000..c278a5f
--- /dev/null
+++ b/lib/views/work/tabs/component/products_header_bar.dart
@@ -0,0 +1,75 @@
+import 'package:flutter/material.dart';
+import 'package:satu/shared/app_colors.dart';
+import 'package:satu/shared/shared_styles.dart';
+import 'package:satu/widgets/dialog/modal_select_dialog.dart';
+import 'package:searchable_dropdown/searchable_dropdown.dart';
+
+class ProductHeaderBar extends StatelessWidget {
+ final int count;
+ final num sum;
+
+ const ProductHeaderBar({Key key, this.count, this.sum}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.only(
+ bottom: 16.00,
+ left: 8.0,
+ right: 8.0,
+ ),
+ child: Column(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ 'Контрагент: Частное лицо',
+ style: productTextStyle,
+ ),
+ IconButton(
+ icon: Icon(
+ Icons.edit,
+ color: yellowColor,
+ ),
+ onPressed: () {
+ List selected = [1];
+ showDialog(
+ context: context,
+ builder: (BuildContext context) {
+ return DropdownDialog(
+ dialogBox: true,
+ multipleSelection: false,
+ items: ['Частное лицо', 'ИП Иванов', 'ТО "Рога и копыта"', 'Network Energy']
+ .map>((String value) {
+ return DropdownMenuItem(
+ value: value,
+ child: Text(value),
+ );
+ }).toList(),
+ selectedItems: selected,
+ hint: Text('Выберите контрагента'),
+ closeButton: 'Отмена',
+ );
+ }).then((value) => {
+ print(selected)
+ });
+ }),
+
+ ],
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ 'Всего: $count наим.',
+ style: productTextStyle,
+ ),
+ Text('Итого: $sum тнг', style: productTextStyle),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/views/work/tabs/component/transaction_item.dart b/lib/views/work/tabs/component/transaction_item.dart
new file mode 100644
index 0000000..b62f1d3
--- /dev/null
+++ b/lib/views/work/tabs/component/transaction_item.dart
@@ -0,0 +1,68 @@
+import 'package:flutter/material.dart';
+
+class TransactionItem extends StatelessWidget {
+
+ final String fullName;
+ final String status;
+ final String amount;
+ final bool received;
+
+ TransactionItem({@required this.fullName, @required this.status, @required this.amount, @required this.received});
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ margin: EdgeInsets.symmetric(vertical: 8),
+ child: Row(
+ children: [
+
+ Container(
+ width: 16,
+ height: 50,
+ //margin: EdgeInsets.only(right: 16),
+ // child: Icon(
+ // Icons.memory
+ // ),
+ ),
+
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+
+ Text(
+ fullName,
+ style: TextStyle(
+ //color: kPrimaryColor,
+ fontSize: 16,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+
+ Text(
+ status,
+ style: TextStyle(
+ //color: kGreyColor,
+ fontSize: 12,
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+
+ ],
+ ),
+ ),
+
+ Text(
+ (received ? "+" : "-") + r" $ " + amount + " KZT",
+ style: TextStyle(
+ color: received ? Colors.green : Colors.red,
+ fontSize: 16,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+
+ ]
+ ),
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/views/work/tabs/journal_view.dart b/lib/views/work/tabs/journal_view.dart
new file mode 100644
index 0000000..a190ec4
--- /dev/null
+++ b/lib/views/work/tabs/journal_view.dart
@@ -0,0 +1,199 @@
+import 'package:flutter/material.dart';
+import 'package:satu/views/work/tabs/component/products_app_bar.dart';
+
+import 'component/custom_field.dart';
+import 'component/option_pill.dart';
+import 'component/transaction_item.dart';
+
+
+class JournalView extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: ProductsAppBar(title: 'Журнал транзакции',),
+ body: Padding(
+ padding: EdgeInsets.symmetric(horizontal: 24),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+
+ // CustomField(
+ // hintText: "Name, Email or Mobile number",
+ // iconData: Icons.search,
+ // ),
+
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+
+ OptionPill(
+ text: "Все",
+ selected: true,
+ ),
+
+ OptionPill(
+ text: "Приход",
+ selected: false,
+ ),
+
+ OptionPill(
+ text: "Расход",
+ selected: false,
+ ),
+
+ ],
+ ),
+
+ SizedBox(
+ height: 16,
+ ),
+
+ Expanded(
+ child: SingleChildScrollView(
+ physics: BouncingScrollPhysics(),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+
+ SizedBox(
+ height: 16,
+ ),
+
+ Text(
+ "03/06/2020",
+ style: TextStyle(
+ //color: kGreyColor,
+ fontSize: 14,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+
+ SizedBox(
+ height: 8,
+ ),
+
+ TransactionItem(
+ fullName: "Чек № 52021 13:03:05",
+ status: "Частное лицо",
+ amount: "2706.00",
+ received: true,
+ ),
+
+ TransactionItem(
+ fullName: "Чек № 52020 13:01:05",
+ status: "ИП Иванов В.И.",
+ amount: "19000.63",
+ received: false,
+ ),
+
+ SizedBox(
+ height: 16,
+ ),
+
+ Divider(
+ //color: kPrimaryColor,
+ height: 1,
+ thickness: 1,
+ ),
+
+ SizedBox(
+ height: 32,
+ ),
+
+ Text(
+ "02/06/2020",
+ style: TextStyle(
+ //color: kGreyColor,
+ fontSize: 14,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+
+ SizedBox(
+ height: 8,
+ ),
+
+ TransactionItem(
+ fullName: "Чек № 5019 13:03:05",
+ status: "Частное лицо",
+ amount: "114.00",
+ received: true,
+ ),
+
+ TransactionItem(
+ fullName: "Чек № 5019 13:03:05",
+ status: "Частное лицо",
+ amount: "70.16",
+ received: true,
+ ),
+
+ SizedBox(
+ height: 16,
+ ),
+
+ Text(
+ "29/05/2020",
+ style: TextStyle(
+ //color: kGreyColor,
+ fontSize: 14,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+
+ SizedBox(
+ height: 8,
+ ),
+
+ TransactionItem(
+ fullName: "Чек № 5019 13:03:05",
+ status: "Частное лицо",
+ amount: "44.50",
+ received: true,
+ ),
+
+ TransactionItem(
+ fullName: "Чек № 5019 13:03:05",
+ status: "ТОО Рога и копыта",
+ amount: "85.50",
+ received: false,
+ ),
+
+ TransactionItem(
+ fullName: "Чек № 5019 13:03:05",
+ status: "Частное лицо",
+ amount: "155.00",
+ received: true,
+ ),
+
+ TransactionItem(
+ fullName: "Чек № 5019 13:03:05",
+ status: "Частное лицо",
+ amount: "23.50",
+ received: true,
+ ),
+
+ TransactionItem(
+ fullName: "Чек № 5019 13:03:05",
+ status: "Частное лицо",
+ amount: "11.50",
+ received: true,
+ ),
+
+ TransactionItem(
+ fullName: "Чек № 5019 13:03:05",
+ status: "Частное лицо",
+ amount: "36.00",
+ received: true,
+ ),
+
+ ],
+ ),
+ ),
+ ),
+
+ ],
+ ),
+ ),
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/views/work/tabs/sell_view.dart b/lib/views/work/tabs/sell_view.dart
new file mode 100644
index 0000000..f962549
--- /dev/null
+++ b/lib/views/work/tabs/sell_view.dart
@@ -0,0 +1,56 @@
+import 'package:flutter/material.dart';
+import 'package:satu/core/services/navigator_service.dart';
+import 'package:satu/core/utils/locator.dart';
+import 'package:satu/routes/route_names.dart';
+import 'package:satu/shared/app_colors.dart';
+import 'package:satu/views/work/tabs/component/product_list_item.dart';
+import 'package:satu/views/work/tabs/component/products_app_bar.dart';
+import 'package:satu/views/work/tabs/component/products_header_bar.dart';
+
+class SellView extends StatelessWidget {
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: ProductsAppBar(title: 'Продажа', actions: actions(), elevation: 2.0, child: ProductHeaderBar( count: 14, sum: 25000,), backgroundColor: backgroundColor, childHeight: 80,),
+ body: Column(
+ children: [
+ //ProductHeaderBar(count: 14, sum: 25000,),
+ Expanded(
+ child: ListView.builder(
+ physics: BouncingScrollPhysics(),
+ itemCount: 50,
+ itemBuilder: (BuildContext context, int index) {
+ return ProductListItem(
+ ean: '1234567890123',
+ isOdd: index % 2 ==0,
+ name: 'Хлеб ржаной который необходимо покупать каждый раз когда придет мысль об этом - ${index +1}. ',
+ price: 75,
+ count: 15,
+ categoryName: 'Хлебобулочные изделия',
+ );
+ },
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ List actions() {
+ return [
+ Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: IconButton(icon: Icon(Icons.add_box, size: 30.0, color: yellowColor), onPressed: () {
+ locator().push(AddProductViewRoute);
+ }),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: IconButton(icon: Icon(Icons.camera_enhance_rounded, size: 30.0, color: yellowColor), onPressed: () {
+ locator().push(AddByBarcodeViewRoute);
+ }),
+ )
+ ];
+ }
+}
diff --git a/lib/views/work/work_view.dart b/lib/views/work/work_view.dart
new file mode 100644
index 0000000..5ed6c3a
--- /dev/null
+++ b/lib/views/work/work_view.dart
@@ -0,0 +1,62 @@
+import 'package:flutter/material.dart';
+import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
+import 'package:satu/shared/app_colors.dart';
+import 'package:satu/views/work/tabs/buy_view.dart';
+import 'package:satu/views/work/tabs/journal_view.dart';
+import 'package:satu/views/work/tabs/sell_view.dart';
+
+class WorkView extends StatefulWidget {
+ final String text;
+
+ const WorkView({Key key, this.text}) : super(key: key);
+ @override
+ _WorkViewState createState() => _WorkViewState();
+}
+
+class _WorkViewState extends State {
+ int _selectedIndex = 0;
+ static const TextStyle optionStyle =
+ TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
+ final List _widgetOptions = [
+ SellView(),
+ BuyView(),
+ JournalView(),
+ ];
+
+
+ void _onItemTapped(int index) {
+ setState(() {
+ _selectedIndex = index;
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ body: _widgetOptions.elementAt(_selectedIndex),
+ bottomNavigationBar: BottomNavigationBar(
+ items: const [
+ BottomNavigationBarItem(
+ icon: Icon(MdiIcons.cartArrowUp),
+ label: 'Продажа',
+ ),
+ BottomNavigationBarItem(
+ icon: Icon(MdiIcons.cartArrowDown),
+ label: 'Покупка',
+ ),
+ BottomNavigationBarItem(
+ icon: Icon(MdiIcons.cashRegister),
+ label: 'Журнал',
+ ),
+ ],
+ currentIndex: _selectedIndex,
+ unselectedItemColor: Colors.black54,
+ selectedItemColor: yellowColor,
+ selectedLabelStyle: const TextStyle( fontWeight: FontWeight.w600 ),
+ backgroundColor: whiteColor,
+ elevation: 8.0,
+ onTap: _onItemTapped,
+ ),
+ );
+ }
+}
diff --git a/lib/widgets/buttons/aman_icon_button.dart b/lib/widgets/buttons/aman_icon_button.dart
new file mode 100644
index 0000000..2d17a3f
--- /dev/null
+++ b/lib/widgets/buttons/aman_icon_button.dart
@@ -0,0 +1,64 @@
+import 'package:auto_size_text/auto_size_text.dart';
+import 'package:flutter/material.dart';
+import 'package:satu/shared/app_colors.dart';
+import 'package:satu/shared/shared_styles.dart';
+
+/// A button that shows a busy indicator in place of title
+class AmanIconButton extends StatefulWidget {
+ final bool busy;
+ final String title;
+ final Function onPressed;
+ final bool enabled;
+ final Color mainColor;
+ final IconData icon;
+ const AmanIconButton(
+ {
+ @required this.title,
+ this.busy = false,
+ @required this.onPressed,
+ this.enabled = true,
+ this.mainColor,
+ @required this.icon
+ });
+
+ @override
+ _AmanIconButtonState createState() => _AmanIconButtonState();
+}
+
+class _AmanIconButtonState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return GestureDetector(
+ child: InkWell(
+ borderRadius: BorderRadius.circular(6),
+ onTap: widget.busy ? () {} : widget.onPressed,
+ child: Container(
+ //height: 75,
+ width: 120,
+ alignment: Alignment.center,
+ padding: EdgeInsets.symmetric(
+ //horizontal: 25,
+ vertical: 15),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(6),
+ border: Border.all( width: 1.0, color: widget.mainColor )
+ ),
+ child: Column(
+ children: [
+ (!widget.busy
+ ? Icon(
+ widget.icon,
+ color: widget.mainColor,
+ size: 36,
+ )
+ : CircularProgressIndicator(
+ strokeWidth: 3,
+ valueColor: AlwaysStoppedAnimation(widget.mainColor))),
+ AutoSizeText(widget.title, overflow: TextOverflow.fade, maxLines: 2, style: TextStyle(color: widget.mainColor, fontSize: 14, fontWeight: FontWeight.w800, ), textAlign: TextAlign.center,)
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/widgets/buttons/busy_button.dart b/lib/widgets/buttons/busy_button.dart
new file mode 100644
index 0000000..046c7ea
--- /dev/null
+++ b/lib/widgets/buttons/busy_button.dart
@@ -0,0 +1,72 @@
+import 'package:auto_size_text/auto_size_text.dart';
+import 'package:flutter/material.dart';
+import 'package:satu/shared/app_colors.dart';
+import 'package:satu/shared/shared_styles.dart';
+
+/// A button that shows a busy indicator in place of title
+class BusyButton extends StatefulWidget {
+ final bool busy;
+ final String title;
+ final Function onPressed;
+ final bool enabled;
+ final Color mainColor;
+
+ const BusyButton({
+ @required this.title,
+ this.busy = false,
+ @required this.onPressed,
+ this.enabled = true,
+ this.mainColor,
+ });
+
+ @override
+ _BusyButtonState createState() => _BusyButtonState();
+}
+
+class _BusyButtonState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return AnimatedContainer(
+ duration: const Duration(milliseconds: 300),
+ decoration: BoxDecoration(
+ color: widget.enabled
+ ? (widget.mainColor ?? primaryColor)
+ : widget.mainColor?.withOpacity(0.2) ??
+ primaryColor.withOpacity(0.2),
+ borderRadius: BorderRadius.circular(7),
+ boxShadow: [cardShadowBox]),
+ child: Material(
+ type: MaterialType.transparency,
+ child: InkWell(
+ onTap: widget.busy || !widget.enabled ? null : widget.onPressed,
+ child: AnimatedContainer(
+ height: widget.busy ? 45 : 45,
+ //width: widget.busy ? 40 : 40,
+ duration: const Duration(milliseconds: 300),
+ alignment: Alignment.center,
+ margin: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0
+ //horizontal: widget.busy ? 10 : 10,
+ //vertical: widget.busy ? 10 : 10
+ ),
+ child: !widget.busy
+ ? AutoSizeText(
+ widget.title,
+ textAlign: TextAlign.center,
+ style: buttonTitleTextBlackStyle,
+ minFontSize: 2,
+ maxLines: 1,
+ )
+ : SizedBox(
+ width: 30,
+ height: 30,
+ child: CircularProgressIndicator(
+ strokeWidth: 2,
+ valueColor:
+ AlwaysStoppedAnimation(Colors.white)),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/widgets/dialog/dialog_manager.dart b/lib/widgets/dialog/dialog_manager.dart
new file mode 100644
index 0000000..df319da
--- /dev/null
+++ b/lib/widgets/dialog/dialog_manager.dart
@@ -0,0 +1,169 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:mask_text_input_formatter/mask_text_input_formatter.dart';
+import 'package:satu/core/models/dialog_models.dart';
+import 'package:satu/core/services/dialog_service.dart';
+import 'package:satu/core/utils/locator.dart';
+
+class DialogManager extends StatefulWidget {
+ final Widget child;
+
+ DialogManager({Key key, this.child}) : super(key: key);
+
+ _DialogManagerState createState() => _DialogManagerState();
+}
+
+class _DialogManagerState extends State {
+ final DialogService _dialogService = locator();
+ TextEditingController _controller;
+
+ @override
+ void initState() {
+ super.initState();
+ _controller = new TextEditingController();
+ _dialogService.registerDialogListener(_showDialog, _showDialogInput);
+ }
+
+ @override
+ void dispose() {
+ _controller.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return widget.child;
+ }
+
+ void _showDialog(DialogRequest request) {
+ var isConfirmationDialog = request.cancelTitle != null;
+ showDialog(
+ context: context,
+ builder: (context) => AlertDialog(
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(5.0),
+ ),
+ actionsPadding: const EdgeInsets.only(right: 15, bottom: 5),
+ title: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ request.title,
+ style: TextStyle(fontWeight: FontWeight.bold),
+ ),
+ //Divider(),
+ ],
+ ),
+ content: Text(request.description),
+ actions: [
+ if (isConfirmationDialog)
+ FlatButton(
+ child: Text(request.cancelTitle),
+ onPressed: () {
+ _dialogService
+ .dialogComplete(DialogResponse(confirmed: false));
+ },
+ ),
+ FlatButton(
+ child: Text(request.buttonTitle),
+ onPressed: () {
+ _dialogService
+ .dialogComplete(DialogResponse(confirmed: true));
+ },
+ ),
+ ],
+ ));
+ }
+
+ void _showDialogInput(DialogRequest request) {
+ var isConfirmationDialog = request.cancelTitle != null;
+
+ _controller.clear();
+
+ var maskFormatter = new MaskTextInputFormatter(
+ mask: '+% (###) ###-##-##',
+ filter: {"#": RegExp(r'[0-9]'), "%": RegExp(r'[7]')});
+
+ var dialogController = showDialog(
+ context: context,
+ builder: (context) => AlertDialog(
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(5.0),
+ ),
+ actionsPadding: const EdgeInsets.only(right: 15, bottom: 5),
+ title: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ request.title,
+ style: TextStyle(fontWeight: FontWeight.bold),
+ ),
+ //Divider(),
+ ],
+ ),
+ content: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ //Text(request.description),
+ TextField(
+ autofocus: true,
+ decoration: InputDecoration(
+ labelText: request.description,
+ hintText: request.formatType == "phone"
+ ? "+7 (123) 456-78-90"
+ : ""),
+ controller: _controller,
+ onSubmitted: (value) {
+ _dialogService
+ .dialogComplete(DialogResponse(confirmed: false));
+ },
+ keyboardType: TextInputType.phone,
+ inputFormatters: [
+ if (request.formatType == "phone") maskFormatter,
+ if (request.formatType == null)
+ FilteringTextInputFormatter.allow(RegExp("^[0-9.]*")),
+ ],
+ )
+ ],
+ ),
+ actions: [
+ if (isConfirmationDialog)
+ RaisedButton(
+ //color: redColor,
+ child: Text(
+ request.cancelTitle,
+ style: TextStyle(fontSize: 18),
+ ),
+ onPressed: () {
+ _dialogService
+ .dialogComplete(DialogResponse(confirmed: false));
+ },
+ ),
+ SizedBox(
+ width: 5,
+ ),
+ RaisedButton(
+ //color: primaryColor,
+ child: Text(
+ request.buttonTitle,
+ style: TextStyle(fontSize: 18),
+ ),
+ onPressed: () {
+ String _result = _controller.text;
+ if (request.formatType == "phone") {
+ _result = maskFormatter.getUnmaskedText();
+ }
+ _dialogService.dialogComplete(
+ DialogResponse(confirmed: true, responseText: _result));
+ },
+ ),
+ ],
+ ));
+ dialogController.whenComplete(() {
+ //hook when press overlay and response not completed
+ if (_dialogService.completer != null) {
+ _dialogService.completer.complete(DialogResponse(confirmed: false));
+ }
+ });
+ }
+}
diff --git a/lib/widgets/dialog/modal_select_dialog.dart b/lib/widgets/dialog/modal_select_dialog.dart
new file mode 100644
index 0000000..f5d3120
--- /dev/null
+++ b/lib/widgets/dialog/modal_select_dialog.dart
@@ -0,0 +1,95 @@
+import 'package:flutter/material.dart';
+
+class DialogModalSelect extends StatefulWidget {
+ final String title;
+ final String descriptions;
+ final String text;
+
+ const DialogModalSelect({Key key, this.title, this.descriptions, this.text})
+ : super(key: key);
+
+ @override
+ _DialogModalSelectState createState() => _DialogModalSelectState();
+}
+
+class _DialogModalSelectState extends State {
+ double padding = 8.0;
+ double avatarRadius = 16.0;
+
+ @override
+ Widget build(BuildContext context) {
+ return Dialog(
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(padding),
+ ),
+ elevation: 0,
+ backgroundColor: Colors.transparent,
+ child: contentBox(context),
+ );
+ }
+
+ contentBox(context) {
+ return Stack(
+ children: [
+ Container(
+ padding: EdgeInsets.only(
+ left: padding,
+ top: avatarRadius + padding,
+ right: padding,
+ bottom: padding),
+ margin: EdgeInsets.only(top: avatarRadius),
+ decoration: BoxDecoration(
+ shape: BoxShape.rectangle,
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(padding),
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black, offset: Offset(0, 10), blurRadius: 10),
+ ]),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ widget.title,
+ style: TextStyle(fontSize: 22, fontWeight: FontWeight.w600),
+ ),
+ SizedBox(
+ height: 15,
+ ),
+ Text(
+ widget.descriptions,
+ style: TextStyle(fontSize: 14),
+ textAlign: TextAlign.center,
+ ),
+ SizedBox(
+ height: 22,
+ ),
+ Align(
+ alignment: Alignment.bottomRight,
+ child: FlatButton(
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ child: Text(
+ widget.text,
+ style: TextStyle(fontSize: 18),
+ )),
+ ),
+ ],
+ ),
+ ),
+ Positioned(
+ left: padding,
+ right: padding,
+ child: CircleAvatar(
+ backgroundColor: Colors.transparent,
+ radius: avatarRadius,
+ child: ClipRRect(
+ borderRadius: BorderRadius.all(Radius.circular(avatarRadius)),
+ child: Image.asset("assets/model.jpeg")),
+ ),
+ )
+ ],
+ );
+ }
+}
diff --git a/lib/widgets/drawer/app_drawer.dart b/lib/widgets/drawer/app_drawer.dart
new file mode 100644
index 0000000..898138b
--- /dev/null
+++ b/lib/widgets/drawer/app_drawer.dart
@@ -0,0 +1,109 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:satu/core/redux/actions/nav_actions.dart';
+import 'package:satu/core/redux/actions/user_actions.dart';
+import 'package:satu/core/redux/store.dart';
+import 'package:satu/core/services/api_service.dart';
+import 'package:satu/core/utils/locator.dart';
+import 'package:satu/shared/app_colors.dart';
+import 'package:satu/shared/ui_helpers.dart';
+import 'package:satu/views/settings/setting_view.dart';
+import 'package:satu/views/work/work_view.dart';
+
+class AppDrawer extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return Drawer(
+ child: ListView(
+ padding: EdgeInsets.zero,
+ children: [
+ _createHeader(),
+ _createDrawerItem(icon: Icons.contacts, text: 'Касса', onTap: () {
+ Navigator.of(context).pop();
+ Redux.store.dispatch(navigateDrawer(WorkView));
+ }),
+ Divider(),
+ ExpansionTile(
+ title: Text("Справочники"),
+ childrenPadding: EdgeInsets.only(left: 18.0),
+ children: [
+ ListTile(
+ title: Text('Категории'),
+ onTap: () {
+ Navigator.of(context).pop();
+ },
+ ),
+ ListTile(
+ title: Text('Товары'),
+ onTap: () {
+ Navigator.of(context).pop();
+ },
+ ),
+ ListTile(
+ title: Text('Контрагенты'),
+ onTap: () {
+ Navigator.of(context).pop();
+ },
+ ),
+ ],
+ ),
+ _createDrawerItem(icon: Icons.settings, text: 'Настройки', onTap: () {
+ Navigator.of(context).pop();
+ Redux.store.dispatch(navigateDrawer(SettingsView));
+ }),
+ Divider(),
+ _createDrawerItem(icon: Icons.bug_report, text: 'Сообщить об ошибке'),
+ verticalSpaceMedium,
+ _createDrawerItem(icon: Icons.exit_to_app, text: 'Выйти из аккаунта', onTap: () async {
+ Redux.store.dispatch(logout);
+ }),
+ ListTile(
+ title: Text('0.0.1'),
+ ),
+ ],
+ ),
+ );
+ }
+
+ Widget _createHeader() {
+ return DrawerHeader(
+ margin: EdgeInsets.zero,
+ padding: EdgeInsets.zero,
+ decoration: BoxDecoration(
+ color: yellowColor
+ ),
+ // decoration: BoxDecoration(
+ // image: DecorationImage(
+ // fit: BoxFit.fill,
+ // image: AssetImage('assets/images/halyk-bank.png'))),
+ child: Stack(children: [
+ Positioned(
+ bottom: 12.0,
+ left: 16.0,
+ child: Text("Сату - онлайн касса",
+ style: TextStyle(
+ color: Colors.white,
+ fontSize: 20.0,
+ fontWeight: FontWeight.w500)
+ )
+ ),
+ ]));
+ }
+
+ Widget _createDrawerItem(
+ {IconData icon, String text, GestureTapCallback onTap}) {
+ return ListTile(
+ title: Row(
+ children: [
+ Icon(icon),
+ Padding(
+ padding: EdgeInsets.only(left: 8.0),
+ child: Text(text),
+ )
+ ],
+ ),
+ onTap: onTap,
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/lib/widgets/fields/dropdown_field.dart b/lib/widgets/fields/dropdown_field.dart
new file mode 100644
index 0000000..9bd0c40
--- /dev/null
+++ b/lib/widgets/fields/dropdown_field.dart
@@ -0,0 +1,96 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:satu/shared/app_colors.dart';
+import 'package:satu/shared/shared_styles.dart';
+import 'package:satu/shared/ui_helpers.dart';
+import 'package:searchable_dropdown/searchable_dropdown.dart';
+
+import 'note_text.dart';
+
+class DropDownField extends StatefulWidget {
+ final bool isReadOnly;
+ final String placeholder;
+ final String validationMessage;
+ final bool smallVersion;
+ final FocusNode fieldFocusNode;
+ final FocusNode nextFocusNode;
+ final String additionalNote;
+ final Function(String) onChanged;
+ final String initialValue;
+ final String labelText;
+
+ DropDownField(
+ {
+ @required this.placeholder,
+ this.fieldFocusNode,
+ this.nextFocusNode,
+ this.additionalNote,
+ this.onChanged,
+ this.initialValue,
+ this.validationMessage,
+ this.isReadOnly = false,
+ this.smallVersion = false, this.labelText});
+
+ @override
+ _DropDownFieldState createState() => _DropDownFieldState();
+}
+
+class _DropDownFieldState extends State {
+ double fieldHeight = 55;
+
+ @override
+ void initState() {
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ if (widget.labelText != null) NoteText(widget.labelText),
+ Container(
+ //height: widget.smallVersion ? 40 : fieldHeight,
+ constraints: BoxConstraints(minHeight: widget.smallVersion ? 40 : fieldHeight),
+ alignment: Alignment.centerLeft,
+ padding: fieldPadding,
+ decoration: widget.isReadOnly ? disabledFieldDecoration : fieldDecoration,
+ child: Expanded(
+ child:
+ SearchableDropdown.single(
+ items: ['Частное лицо', 'ИП Иванов', 'ТО "Рога и копыта"', 'Network Energy']
+ .map>((String value) {
+ return DropdownMenuItem(
+ value: value,
+ child: Text(value),
+ );
+ }).toList(),
+ value: widget.initialValue,
+ readOnly: widget.isReadOnly,
+ hint: "Контрагент",
+ searchHint: "Укажите контрагента",
+ underline: Container(
+ height: 1.0,
+ decoration: BoxDecoration(
+ border: Border(bottom: BorderSide(color: yellowColor, width: 3.0))
+ ),
+ ),
+ onChanged: (value) {
+ print(value);
+ },
+ isExpanded: true,
+ ),
+ ),
+ ),
+ if (widget.validationMessage != null)
+ NoteText(
+ widget.validationMessage,
+ color: Colors.red,
+ ),
+ if (widget.additionalNote != null) verticalSpace(5),
+ if (widget.additionalNote != null) NoteText(widget.additionalNote),
+ verticalSpaceSmall
+ ],
+ );
+ }
+}
diff --git a/lib/widgets/fields/input_field.dart b/lib/widgets/fields/input_field.dart
new file mode 100644
index 0000000..d784892
--- /dev/null
+++ b/lib/widgets/fields/input_field.dart
@@ -0,0 +1,173 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:satu/shared/app_colors.dart';
+import 'package:satu/shared/shared_styles.dart';
+import 'package:satu/shared/ui_helpers.dart';
+
+import 'note_text.dart';
+
+class InputField extends StatefulWidget {
+ final TextEditingController controller;
+ final TextInputType textInputType;
+ final bool password;
+ final bool search;
+ final bool isReadOnly;
+ final String placeholder;
+ final String validationMessage;
+ final Function enterPressed;
+ final bool smallVersion;
+ final FocusNode fieldFocusNode;
+ final FocusNode nextFocusNode;
+ final TextInputAction textInputAction;
+ final bool multiline;
+ final String additionalNote;
+ final Function(String) onChanged;
+ final TextInputFormatter formatter;
+ final String initialValue;
+ final String labelText;
+
+ InputField(
+ {
+ this.controller,
+ @required this.placeholder,
+ this.enterPressed,
+ this.fieldFocusNode,
+ this.nextFocusNode,
+ this.additionalNote,
+ this.onChanged,
+ this.formatter,
+ this.initialValue,
+ this.validationMessage,
+ this.textInputAction = TextInputAction.next,
+ this.textInputType = TextInputType.text,
+ this.password = false,
+ this.search = false,
+ this.isReadOnly = false,
+ this.multiline = false,
+ this.smallVersion = false, this.labelText});
+
+ @override
+ _InputFieldState createState() => _InputFieldState();
+}
+
+class _InputFieldState extends State {
+ bool isPassword;
+ bool isSearch;
+ double fieldHeight = 55;
+
+ @override
+ void initState() {
+ super.initState();
+ isPassword = widget.password;
+ isSearch = widget.search;
+ if(widget.search == true) {
+ widget.fieldFocusNode.addListener(() {
+ if(widget.fieldFocusNode.hasFocus){
+ setState(() {
+ isSearch = !isSearch;
+ });
+ }
+ });
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ if (widget.labelText != null) NoteText(widget.labelText),
+ Container(
+ //height: widget.smallVersion ? 40 : fieldHeight,
+ constraints: BoxConstraints(minHeight: widget.smallVersion ? 40 : fieldHeight),
+ alignment: Alignment.centerLeft,
+ padding: fieldPadding,
+ decoration:
+ widget.isReadOnly ? disabledFieldDecoration : fieldDecoration,
+ child: Row(
+ children: [
+ Expanded(
+ child: TextFormField(
+ style: TextStyle( color: textColor ),
+ controller: widget.controller,
+ keyboardType: widget.textInputType,
+ focusNode: widget.fieldFocusNode,
+ textInputAction: widget.textInputAction,
+ maxLines: widget.multiline ? null : 1,
+ onChanged: widget.onChanged,
+ initialValue: widget.initialValue,
+ inputFormatters:
+ widget.formatter != null ? [widget.formatter] : null,
+ onEditingComplete: () {
+ if (widget.enterPressed != null) {
+ FocusScope.of(context).requestFocus(FocusNode());
+ widget.enterPressed();
+ }
+ },
+ onFieldSubmitted: (value) {
+ if (widget.nextFocusNode != null) {
+ widget.nextFocusNode.requestFocus();
+ }
+ },
+ obscureText: isPassword,
+ readOnly: widget.isReadOnly,
+ decoration: InputDecoration(
+ hintText: widget.placeholder,
+ filled: true,
+ fillColor: Colors.white,
+ border: InputBorder.none,
+ hintStyle:
+ TextStyle(fontSize: widget.smallVersion ? 12 : 15, color: textColorLight)),
+ ),
+ ),
+ GestureDetector(
+ onTap: () => setState(() {
+ isPassword = !isPassword;
+ }),
+ child: widget.password
+ ? Container(
+ width: fieldHeight,
+ height: fieldHeight,
+ alignment: Alignment.center,
+ child: Icon(isPassword
+ ? Icons.visibility
+ : Icons.visibility_off, color: textColor))
+ : Container(),
+ ),
+ GestureDetector(
+ onTap: () {
+ if(isSearch) {
+ widget.fieldFocusNode.requestFocus();
+ } else {
+ FocusScope.of(context).requestFocus(new FocusNode()); //remove focus
+ WidgetsBinding.instance.addPostFrameCallback((_) => widget.controller.clear()); // clear content
+ }
+ setState(() {
+ isSearch = !isSearch;
+ });
+ },
+ child: widget.search
+ ? Container(
+ width: fieldHeight,
+ height: fieldHeight,
+ alignment: Alignment.center,
+ child: Icon(isSearch
+ ? Icons.search
+ : Icons.search_off, color: textColor))
+ : Container(),
+ ),
+ ],
+ ),
+ ),
+ if (widget.validationMessage != null)
+ NoteText(
+ widget.validationMessage,
+ color: Colors.red,
+ ),
+ if (widget.additionalNote != null) verticalSpace(5),
+ if (widget.additionalNote != null) NoteText(widget.additionalNote),
+ verticalSpaceSmall
+ ],
+ );
+ }
+}
diff --git a/lib/widgets/fields/note_text.dart b/lib/widgets/fields/note_text.dart
new file mode 100644
index 0000000..50e8d3c
--- /dev/null
+++ b/lib/widgets/fields/note_text.dart
@@ -0,0 +1,23 @@
+import 'package:flutter/material.dart';
+import 'package:satu/shared/app_colors.dart';
+
+class NoteText extends StatelessWidget {
+ final String text;
+ final TextAlign textAlign;
+ final Color color;
+ final double fontSize;
+ const NoteText(this.text, {this.textAlign, this.color, this.fontSize});
+
+ @override
+ Widget build(BuildContext context) {
+ return Text(
+ text,
+ textAlign: textAlign,
+ style: TextStyle(
+ fontSize: fontSize ?? 12,
+ fontWeight: FontWeight.normal,
+ color: color ?? dayColor,
+ ),
+ );
+ }
+}
diff --git a/pubspec.lock b/pubspec.lock
new file mode 100644
index 0000000..c42e891
--- /dev/null
+++ b/pubspec.lock
@@ -0,0 +1,677 @@
+# Generated by pub
+# See https://dart.dev/tools/pub/glossary#lockfile
+packages:
+ archive:
+ dependency: transitive
+ description:
+ name: archive
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.13"
+ args:
+ dependency: transitive
+ description:
+ name: args
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.6.0"
+ async:
+ dependency: transitive
+ description:
+ name: async
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.5.0"
+ auto_size_text:
+ dependency: "direct main"
+ description:
+ name: auto_size_text
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.0"
+ barcode_scan:
+ dependency: "direct main"
+ description:
+ name: barcode_scan
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.0.1"
+ boolean_selector:
+ dependency: transitive
+ description:
+ name: boolean_selector
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.0"
+ characters:
+ dependency: transitive
+ description:
+ name: characters
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.0"
+ charcode:
+ dependency: transitive
+ description:
+ name: charcode
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.2.0"
+ charset_converter:
+ dependency: "direct main"
+ description:
+ name: charset_converter
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0"
+ clock:
+ dependency: transitive
+ description:
+ name: clock
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.0"
+ collection:
+ dependency: transitive
+ description:
+ name: collection
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.15.0"
+ convert:
+ dependency: transitive
+ description:
+ name: convert
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.1"
+ crypto:
+ dependency: transitive
+ description:
+ name: crypto
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.5"
+ csslib:
+ dependency: transitive
+ description:
+ name: csslib
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.16.2"
+ cupertino_icons:
+ dependency: "direct main"
+ description:
+ name: cupertino_icons
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.2"
+ device_info:
+ dependency: "direct main"
+ description:
+ name: device_info
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0"
+ device_info_platform_interface:
+ dependency: transitive
+ description:
+ name: device_info_platform_interface
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0"
+ equatable:
+ dependency: "direct main"
+ description:
+ name: equatable
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0"
+ esc_pos_bluetooth:
+ dependency: "direct main"
+ description:
+ name: esc_pos_bluetooth
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.2.8"
+ esc_pos_utils:
+ dependency: "direct main"
+ description:
+ name: esc_pos_utils
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.3.6"
+ fake_async:
+ dependency: transitive
+ description:
+ name: fake_async
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.2.0"
+ ffi:
+ dependency: transitive
+ description:
+ name: ffi
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.0"
+ file:
+ dependency: transitive
+ description:
+ name: file
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "6.1.0"
+ fixnum:
+ dependency: transitive
+ description:
+ name: fixnum
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.10.11"
+ flutter:
+ dependency: "direct main"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_bluetooth_basic:
+ dependency: transitive
+ description:
+ name: flutter_bluetooth_basic
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.1.5"
+ flutter_redux:
+ dependency: "direct main"
+ description:
+ name: flutter_redux
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.8.2"
+ flutter_screenutil:
+ dependency: "direct main"
+ description:
+ name: flutter_screenutil
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "5.0.0"
+ flutter_test:
+ dependency: "direct dev"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_web_plugins:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ gbk_codec:
+ dependency: transitive
+ description:
+ name: gbk_codec
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.3.2"
+ get_it:
+ dependency: "direct main"
+ description:
+ name: get_it
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "6.1.1"
+ hex:
+ dependency: transitive
+ description:
+ name: hex
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.1.2"
+ html:
+ dependency: transitive
+ description:
+ name: html
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.14.0+4"
+ http:
+ dependency: "direct main"
+ description:
+ name: http
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.13.2"
+ http_parser:
+ dependency: transitive
+ description:
+ name: http_parser
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.0.0"
+ image:
+ dependency: transitive
+ description:
+ name: image
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.19"
+ implicitly_animated_reorderable_list:
+ dependency: "direct main"
+ description:
+ name: implicitly_animated_reorderable_list
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.4.0"
+ intl:
+ dependency: "direct main"
+ description:
+ name: intl
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.17.0"
+ js:
+ dependency: transitive
+ description:
+ name: js
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.6.3"
+ json_annotation:
+ dependency: transitive
+ description:
+ name: json_annotation
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.1.1"
+ logger:
+ dependency: "direct main"
+ description:
+ name: logger
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.0"
+ mask_text_input_formatter:
+ dependency: "direct main"
+ description:
+ name: mask_text_input_formatter
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.2.1"
+ matcher:
+ dependency: transitive
+ description:
+ name: matcher
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.12.10"
+ material_design_icons_flutter:
+ dependency: "direct main"
+ description:
+ name: material_design_icons_flutter
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.0.5955"
+ material_floating_search_bar:
+ dependency: "direct main"
+ description:
+ name: material_floating_search_bar
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.3.4"
+ meta:
+ dependency: transitive
+ description:
+ name: meta
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.3.0"
+ nested:
+ dependency: transitive
+ description:
+ name: nested
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.0"
+ path:
+ dependency: transitive
+ description:
+ name: path
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.8.0"
+ path_provider:
+ dependency: "direct main"
+ description:
+ name: path_provider
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.1"
+ path_provider_linux:
+ dependency: transitive
+ description:
+ name: path_provider_linux
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0"
+ path_provider_macos:
+ dependency: transitive
+ description:
+ name: path_provider_macos
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0"
+ path_provider_platform_interface:
+ dependency: transitive
+ description:
+ name: path_provider_platform_interface
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0"
+ path_provider_windows:
+ dependency: transitive
+ description:
+ name: path_provider_windows
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.1"
+ pedantic:
+ dependency: transitive
+ description:
+ name: pedantic
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.11.0"
+ petitparser:
+ dependency: transitive
+ description:
+ name: petitparser
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.1.0"
+ platform:
+ dependency: transitive
+ description:
+ name: platform
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.0.0"
+ plugin_platform_interface:
+ dependency: transitive
+ description:
+ name: plugin_platform_interface
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.3"
+ process:
+ dependency: transitive
+ description:
+ name: process
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.2.1"
+ protobuf:
+ dependency: transitive
+ description:
+ name: protobuf
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.0"
+ provider:
+ dependency: "direct main"
+ description:
+ name: provider
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "5.0.0"
+ qr:
+ dependency: transitive
+ description:
+ name: qr
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0"
+ qr_flutter:
+ dependency: "direct main"
+ description:
+ name: qr_flutter
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.0.0"
+ redux:
+ dependency: "direct main"
+ description:
+ name: redux
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "5.0.0"
+ redux_persist:
+ dependency: "direct main"
+ description:
+ name: redux_persist
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.9.0"
+ redux_persist_flutter:
+ dependency: "direct main"
+ description:
+ name: redux_persist_flutter
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.9.0"
+ redux_thunk:
+ dependency: "direct main"
+ description:
+ name: redux_thunk
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.4.0"
+ responsive_builder:
+ dependency: "direct main"
+ description:
+ name: responsive_builder
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.4.1"
+ rxdart:
+ dependency: transitive
+ description:
+ name: rxdart
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.23.1"
+ searchable_dropdown:
+ dependency: "direct main"
+ description:
+ name: searchable_dropdown
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.3"
+ shared_preferences:
+ dependency: "direct main"
+ description:
+ name: shared_preferences
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.5"
+ shared_preferences_linux:
+ dependency: transitive
+ description:
+ name: shared_preferences_linux
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0"
+ shared_preferences_macos:
+ dependency: transitive
+ description:
+ name: shared_preferences_macos
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0"
+ shared_preferences_platform_interface:
+ dependency: transitive
+ description:
+ name: shared_preferences_platform_interface
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0"
+ shared_preferences_web:
+ dependency: transitive
+ description:
+ name: shared_preferences_web
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0"
+ shared_preferences_windows:
+ dependency: transitive
+ description:
+ name: shared_preferences_windows
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0"
+ sky_engine:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.99"
+ source_span:
+ dependency: transitive
+ description:
+ name: source_span
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.8.0"
+ sqflite:
+ dependency: "direct main"
+ description:
+ name: sqflite
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0+3"
+ sqflite_common:
+ dependency: transitive
+ description:
+ name: sqflite_common
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0+2"
+ stack_trace:
+ dependency: transitive
+ description:
+ name: stack_trace
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.10.0"
+ stream_channel:
+ dependency: transitive
+ description:
+ name: stream_channel
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.0"
+ string_scanner:
+ dependency: transitive
+ description:
+ name: string_scanner
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.0"
+ synchronized:
+ dependency: transitive
+ description:
+ name: synchronized
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.0.0"
+ term_glyph:
+ dependency: transitive
+ description:
+ name: term_glyph
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.2.0"
+ test_api:
+ dependency: transitive
+ description:
+ name: test_api
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.2.19"
+ typed_data:
+ dependency: transitive
+ description:
+ name: typed_data
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.3.0"
+ url_launcher:
+ dependency: "direct main"
+ description:
+ name: url_launcher
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "5.7.10"
+ url_launcher_linux:
+ dependency: transitive
+ description:
+ name: url_launcher_linux
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.0.1+4"
+ url_launcher_macos:
+ dependency: transitive
+ description:
+ name: url_launcher_macos
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.0.1+9"
+ url_launcher_platform_interface:
+ dependency: transitive
+ description:
+ name: url_launcher_platform_interface
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.9"
+ url_launcher_web:
+ dependency: transitive
+ description:
+ name: url_launcher_web
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.1.5+1"
+ url_launcher_windows:
+ dependency: transitive
+ description:
+ name: url_launcher_windows
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.0.1+3"
+ vector_math:
+ dependency: transitive
+ description:
+ name: vector_math
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.0"
+ win32:
+ dependency: transitive
+ description:
+ name: win32
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.5"
+ xdg_directories:
+ dependency: transitive
+ description:
+ name: xdg_directories
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.2.0"
+ xml:
+ dependency: transitive
+ description:
+ name: xml
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.5.1"
+sdks:
+ dart: ">=2.12.0 <3.0.0"
+ flutter: ">=1.24.0-10"
diff --git a/pubspec.yaml b/pubspec.yaml
new file mode 100644
index 0000000..9094888
--- /dev/null
+++ b/pubspec.yaml
@@ -0,0 +1,107 @@
+name: satu
+description: Aman Satu App
+
+# The following line prevents the package from being accidentally published to
+# pub.dev using `pub publish`. This is preferred for private packages.
+publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+
+# The following defines the version and build number for your application.
+# A version number is three numbers separated by dots, like 1.2.43
+# followed by an optional build number separated by a +.
+# Both the version and the builder number may be overridden in flutter
+# build by specifying --build-name and --build-number, respectively.
+# In Android, build-name is used as versionName while build-number used as versionCode.
+# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
+# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
+# Read more about iOS versioning at
+# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
+version: 1.0.0+1
+
+environment:
+ sdk: ">=2.7.0 <3.0.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+
+
+ # The following adds the Cupertino Icons font to your application.
+ # Use with the CupertinoIcons class for iOS style icons.
+ cupertino_icons: ^1.0.2
+ redux: ^5.0.0
+ flutter_redux: ^0.8.2
+ redux_thunk: ^0.4.0
+ redux_persist: ^0.9.0
+ redux_persist_flutter: ^0.9.0
+ responsive_builder: ^0.4.1
+ provider: ^5.0.0
+ logger: ^1.0.0
+ get_it: ^6.1.1
+ equatable: ^2.0.0
+ http: ^0.13.2
+ sqflite: ^2.0.0+3
+ path_provider: ^2.0.1
+ material_design_icons_flutter: ^4.0.5955
+ intl: ^0.17.0
+ barcode_scan: ^3.0.1
+ device_info: ^2.0.0
+ #esys_flutter_share: ^1.0.2
+ auto_size_text: ^2.1.0
+ url_launcher: ^5.7.10
+ qr_flutter: ^4.0.0
+ mask_text_input_formatter: ^1.2.1
+ flutter_screenutil: ^5.0.0
+ shared_preferences: ^2.0.5
+ searchable_dropdown: ^1.1.3
+ material_floating_search_bar: ^0.3.4
+ implicitly_animated_reorderable_list: ^0.4.0
+ esc_pos_bluetooth: ^0.2.8
+ esc_pos_utils: ^0.3.6 # for esc_pos_bluetooth: ^0.2.8
+ charset_converter: ^2.0.0
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+
+# For information on the generic Dart part of this file, see the
+# following page: https://dart.dev/tools/pub/pubspec
+
+# The following section is specific to Flutter.
+flutter:
+
+ # The following line ensures that the Material Icons font is
+ # included with your application, so that you can use the icons in
+ # the material Icons class.
+ uses-material-design: true
+
+ # To add assets to your application, add an assets section, like this:
+ assets:
+ - assets/images/
+ - assets/lang/en.json
+ - assets/lang/ru.json
+ - assets/google_fonts/
+
+ # An image asset can refer to one or more resolution-specific "variants", see
+ # https://flutter.dev/assets-and-images/#resolution-aware.
+
+ # For details regarding adding assets from package dependencies, see
+ # https://flutter.dev/assets-and-images/#from-packages
+
+ # To add custom fonts to your application, add a fonts section here,
+ # in this "flutter" section. Each entry in this list should have a
+ # "family" key with the font family name, and a "fonts" key with a
+ # list giving the asset and other descriptors for the font. For
+ # example:
+ # fonts:
+ # - family: Schyler
+ # fonts:
+ # - asset: fonts/Schyler-Regular.ttf
+ # - asset: fonts/Schyler-Italic.ttf
+ # style: italic
+ # - family: Trajan Pro
+ # fonts:
+ # - asset: fonts/TrajanPro.ttf
+ # - asset: fonts/TrajanPro_Bold.ttf
+ # weight: 700
+ #
+ # For details regarding fonts from package dependencies,
+ # see https://flutter.dev/custom-fonts/#from-packages
diff --git a/test/widget_test.dart b/test/widget_test.dart
new file mode 100644
index 0000000..3d1b9ca
--- /dev/null
+++ b/test/widget_test.dart
@@ -0,0 +1,30 @@
+// This is a basic Flutter widget test.
+//
+// To perform an interaction with a widget in your test, use the WidgetTester
+// utility that Flutter provides. For example, you can send tap and scroll
+// gestures. You can also use WidgetTester to find child widgets in the widget
+// tree, read text, and verify that the values of widget properties are correct.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'package:satu/main.dart';
+
+void main() {
+ testWidgets('Counter increments smoke test', (WidgetTester tester) async {
+ // Build our app and trigger a frame.
+ await tester.pumpWidget(MainApplication());
+
+ // Verify that our counter starts at 0.
+ expect(find.text('0'), findsOneWidget);
+ expect(find.text('1'), findsNothing);
+
+ // Tap the '+' icon and trigger a frame.
+ await tester.tap(find.byIcon(Icons.add));
+ await tester.pump();
+
+ // Verify that our counter has incremented.
+ expect(find.text('0'), findsNothing);
+ expect(find.text('1'), findsOneWidget);
+ });
+}