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); + }); +}