commit 171bf7f45d3c0ac53c6a7dcbbd0976f8b59e8b92 Author: jefry Date: Mon Apr 25 14:56:20 2022 +0700 Bloc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0fa6b67 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# 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 + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..a5584fc --- /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: 18116933e77adc82f80866c928266a5b4f1ed645 + channel: stable + +project_type: app diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..61b6c4d --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..2975cd6 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,86 @@ +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 31 + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.thamringroup.unitstocks" + minSdkVersion 18 + targetSdkVersion 31 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + multiDexEnabled true + } + signingConfigs { + release { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile keystoreProperties['storeFile'] ? rootProject.file(keystoreProperties['storeFile']) : null + storePassword keystoreProperties['storePassword'] + } + } + buildTypes { + release { + signingConfig signingConfigs.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 +// debuggable false +// minifyEnabled false +// shrinkResources false +// proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'androidx.multidex:multidex:2.0.1' +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..42ba5a7 --- /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..aba82fd --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/thamringroup/unitstocks/MainActivity.kt b/android/app/src/main/kotlin/com/thamringroup/unitstocks/MainActivity.kt new file mode 100644 index 0000000..86624ab --- /dev/null +++ b/android/app/src/main/kotlin/com/thamringroup/unitstocks/MainActivity.kt @@ -0,0 +1,6 @@ +package com.thamringroup.unitstocks + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/launcher_icon.png b/android/app/src/main/res/mipmap-hdpi/launcher_icon.png new file mode 100644 index 0000000..4ab6980 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..17987b7 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/launcher_icon.png b/android/app/src/main/res/mipmap-mdpi/launcher_icon.png new file mode 100644 index 0000000..3bc14c8 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..09d4391 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png new file mode 100644 index 0000000..002774c Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png new file mode 100644 index 0000000..b30c3b6 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d6372e Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png new file mode 100644 index 0000000..1c96606 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..449a9f9 --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..d74aa35 --- /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..42ba5a7 --- /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..ba6fe4a --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + ext.kotlin_version = '1.6.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.1.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..94adc3a --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..595fb86 --- /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-7.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/asset/icon.png b/asset/icon.png new file mode 100644 index 0000000..f13d7f7 Binary files /dev/null and b/asset/icon.png differ diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..8d4492f --- /dev/null +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 9.0 + + diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..ec97fc6 --- /dev/null +++ b/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..c4855bf --- /dev/null +++ b/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 0000000..1e8c3c9 --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 0000000..19ce863 --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,87 @@ +PODS: + - barcode_scan2 (0.0.1): + - Flutter + - MTBBarcodeScanner + - SwiftProtobuf + - Flutter (1.0.0) + - FMDB (2.7.5): + - FMDB/standard (= 2.7.5) + - FMDB/standard (2.7.5) + - image_picker (0.0.1): + - Flutter + - location (0.0.1): + - Flutter + - MTBBarcodeScanner (5.0.11) + - package_info (0.0.1): + - Flutter + - path_provider_ios (0.0.1): + - Flutter + - "permission_handler (5.1.0+2)": + - Flutter + - shared_preferences_ios (0.0.1): + - Flutter + - sqflite (0.0.2): + - Flutter + - FMDB (>= 2.7.5) + - SwiftProtobuf (1.18.0) + - url_launcher_ios (0.0.1): + - Flutter + +DEPENDENCIES: + - barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`) + - Flutter (from `Flutter`) + - image_picker (from `.symlinks/plugins/image_picker/ios`) + - location (from `.symlinks/plugins/location/ios`) + - package_info (from `.symlinks/plugins/package_info/ios`) + - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) + - permission_handler (from `.symlinks/plugins/permission_handler/ios`) + - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`) + - sqflite (from `.symlinks/plugins/sqflite/ios`) + - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + +SPEC REPOS: + trunk: + - FMDB + - MTBBarcodeScanner + - SwiftProtobuf + +EXTERNAL SOURCES: + barcode_scan2: + :path: ".symlinks/plugins/barcode_scan2/ios" + Flutter: + :path: Flutter + image_picker: + :path: ".symlinks/plugins/image_picker/ios" + location: + :path: ".symlinks/plugins/location/ios" + package_info: + :path: ".symlinks/plugins/package_info/ios" + path_provider_ios: + :path: ".symlinks/plugins/path_provider_ios/ios" + permission_handler: + :path: ".symlinks/plugins/permission_handler/ios" + shared_preferences_ios: + :path: ".symlinks/plugins/shared_preferences_ios/ios" + sqflite: + :path: ".symlinks/plugins/sqflite/ios" + url_launcher_ios: + :path: ".symlinks/plugins/url_launcher_ios/ios" + +SPEC CHECKSUMS: + barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0 + Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a + FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + image_picker: 9aa50e1d8cdacdbed739e925b7eea16d014367e6 + location: 3a2eed4dd2fab25e7b7baf2a9efefe82b512d740 + MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb + package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 + path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5 + permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0 + shared_preferences_ios: aef470a42dc4675a1cdd50e3158b42e3d1232b32 + sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 + SwiftProtobuf: c3c12645230d9b09c72267e0de89468c5543bd86 + url_launcher_ios: 02f1989d4e14e998335b02b67a7590fa34f971af + +PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c + +COCOAPODS: 1.11.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..3a83ff7 --- /dev/null +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,539 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 69397765FFFFA1BA6362EA4B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E08104BAAD3056C48BDF5EE3 /* Pods_Runner.framework */; }; + 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 */ + 0501EB5CBC7531739408FD72 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 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 = ""; }; + 57F20BC6CF6E6D6B67DC1552 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 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 = ""; }; + B7FFDF13C26510059A9B9116 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + E08104BAAD3056C48BDF5EE3 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 69397765FFFFA1BA6362EA4B /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1E2EBAB5ED4DD3D3A2A11AC8 /* Pods */ = { + isa = PBXGroup; + children = ( + 0501EB5CBC7531739408FD72 /* Pods-Runner.debug.xcconfig */, + B7FFDF13C26510059A9B9116 /* Pods-Runner.release.xcconfig */, + 57F20BC6CF6E6D6B67DC1552 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 1E2EBAB5ED4DD3D3A2A11AC8 /* Pods */, + B22B7682CA04E9426C3AC3CF /* Frameworks */, + ); + 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 = ""; + }; + B22B7682CA04E9426C3AC3CF /* Frameworks */ = { + isa = PBXGroup; + children = ( + E08104BAAD3056C48BDF5EE3 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + EC10BB94B8F198D49E183BDD /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + EFB4DCC5315D54E9F1DC5BEF /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + EC10BB94B8F198D49E183BDD /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + EFB4DCC5315D54E9F1DC5BEF /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 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; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.thamringroup.unitstocks; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.thamringroup.unitstocks; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.thamringroup.unitstocks; + 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 */; +} \ No newline at end of file diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..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..21a3cc1 --- /dev/null +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + 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..90293cf 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..c490c69 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..7988d16 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..a88f86a 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..5d27e95 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..51f6118 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..bcd56ce 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..7988d16 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..f55fe0f 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..d512004 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..d512004 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..d06e92a 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..f6cccde 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..8472580 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..aceb25b 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..d503459 --- /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 + unitstocks + 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/Blocs/Cabang/cabang_bloc.dart b/lib/Blocs/Cabang/cabang_bloc.dart new file mode 100644 index 0000000..eac868c --- /dev/null +++ b/lib/Blocs/Cabang/cabang_bloc.dart @@ -0,0 +1,67 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:bloc/bloc.dart'; +import 'package:meta/meta.dart'; +import 'package:unitstocks/Utils/keys.dart'; + +import '../../main.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:path/path.dart'; + +part 'cabang_event.dart'; +part 'cabang_state.dart'; + +class CabangBloc extends Bloc { + CabangBloc() : super(CabangInitial()) { + on((event, emit) async { + if(event is CabangInit){ + emit(CabangLoading()); + List cabangs = event.cabangList??[]; + if(cabangs.isEmpty){ + var cabangList = await util.JsonDataPostRaw({"company":event.company,"User":event.userId}, '${prefs.getString(Keys.hostAddress)}/user/cabangs/',timeout: true); + if(cabangList['STATUS']==1){ + cabangs = cabangList['DATA']; + emit(CabangDisplay(cabangList: cabangs)); + } + else { + emit(CabangError(err: "Gagal mengambil akses cabang. Pastikan anda memiliki akses!!")); + } + } + else emit(CabangDisplay(cabangList: cabangs)); + } + else if (event is CabangPicked){ + String lastCabang = prefs.getString(Keys.cabangId)??''; + if(lastCabang!=event.cabangId){ + prefs.setString(Keys.cabangId, event.cabangId); + await Future.sync(()async{ + try{ + Directory? documentsDirectory = await getApplicationDocumentsDirectory(); + String path = join(documentsDirectory.path, "UnitStocking.db"); + File db = File(path); + if(db.existsSync()){ + db.deleteSync(); + } + await prefs.remove(Keys.lastDownload); + await prefs.remove(Keys.lastUpload); + await prefs.remove(Keys.targetProccess); + await prefs.remove(Keys.submitProccess); + emit(CabangFinished(cabangChanged: lastCabang!=event.cabangId)); + } + catch(e){ + // print(e); + prefs.setString(Keys.cabangId, lastCabang); + emit(const CabangFinished(cabangChanged: false)); + } + }); + } + else { + emit(const CabangFinished(cabangChanged: false)); + } + } + else if (event is CabangCanceled){ + emit(CabangInitial()); + } + }); + } +} diff --git a/lib/Blocs/Cabang/cabang_event.dart b/lib/Blocs/Cabang/cabang_event.dart new file mode 100644 index 0000000..fb0b1c5 --- /dev/null +++ b/lib/Blocs/Cabang/cabang_event.dart @@ -0,0 +1,18 @@ +part of 'cabang_bloc.dart'; + +@immutable +abstract class CabangEvent { + const CabangEvent(); + @override + List get props => []; +} +class CabangInit extends CabangEvent{ + const CabangInit({this.cabangList,this.company,this.userId}); + final List? cabangList; + final String? company,userId; +} +class CabangPicked extends CabangEvent{ + const CabangPicked({required this.cabangId}); + final String cabangId; +} +class CabangCanceled extends CabangEvent{} diff --git a/lib/Blocs/Cabang/cabang_state.dart b/lib/Blocs/Cabang/cabang_state.dart new file mode 100644 index 0000000..a8c7262 --- /dev/null +++ b/lib/Blocs/Cabang/cabang_state.dart @@ -0,0 +1,24 @@ +part of 'cabang_bloc.dart'; + +@immutable +abstract class CabangState { + const CabangState(); + @override + List get props => []; +} + +class CabangInitial extends CabangState { +} +class CabangLoading extends CabangState{} +class CabangDisplay extends CabangState{ + const CabangDisplay({required this.cabangList}); + final List cabangList; +} +class CabangFinished extends CabangState{ + const CabangFinished({required this.cabangChanged}); + final bool cabangChanged; +} +class CabangError extends CabangState{ + const CabangError({required this.err}); + final String err; +} diff --git a/lib/Blocs/Login/login_bloc.dart b/lib/Blocs/Login/login_bloc.dart new file mode 100644 index 0000000..b54b85f --- /dev/null +++ b/lib/Blocs/Login/login_bloc.dart @@ -0,0 +1,57 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; + +import '../../main.dart'; +import '../../Utils/keys.dart'; +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; + +part 'login_event.dart'; +part 'login_state.dart'; + + +class LoginBloc extends Bloc { + LoginBloc() : super(LoginInit()) { + on((event, emit) async { + if(event is LoginInitiated){ + emit(LoginLoading()); + var result = await util.JsonDataPostRaw({"User":event.email,"Pass":event.password}, '${prefs.getString(Keys.hostAddress)}/user//login/'); + Map obj = {}; + obj["SUCCESS"] = (result["STATUS"]==1&&result["DATA"]['status'] =='OK'); + if(result["STATUS"]==1&&obj["SUCCESS"]){ + prefs.setString(Keys.loginId, result['DATA'][Keys.loginId.toLowerCase()]); + prefs.setString(Keys.company, result['DATA'][Keys.company.toLowerCase()]); + obj[Keys.company] = result["DATA"][Keys.company.toLowerCase()]; + obj[Keys.loginId] = result["DATA"][Keys.loginId.toLowerCase()]; + } + else{ + obj["MESSAGE"] = (result["DATA"] is String)?result["DATA"]:result["DATA"]['info']; + } + emit(LoginFinish(data: obj)); + } + else if(event is LoginError){ + prefs.setBool(Keys.loggedIn, false); + prefs.remove(Keys.cabangId); + prefs.remove(Keys.company); + prefs.remove(Keys.loginId); + if(event.err != ''){ + util.showFlushbar(event.context, event.err); + } + emit(LoginInit()); + } + else if (event is LoginSuccess){ + prefs.setBool(Keys.loggedIn, true); + var cabangList = await util.JsonDataPostRaw({"company":event.company,"User":event.userId}, '${prefs.getString(Keys.hostAddress)}/user/cabangs/'); + if(cabangList['STATUS']==1){ + emit(LoginFinalize(cabangList: cabangList['DATA'])); + } + else{ + Map obj = {}; + obj["SUCCESS"] = false; + obj["MESSAGE"] = cabangList['DATA']; + emit(LoginFinish(data: obj)); + } + } + }); + } +} diff --git a/lib/Blocs/Login/login_event.dart b/lib/Blocs/Login/login_event.dart new file mode 100644 index 0000000..3386d1c --- /dev/null +++ b/lib/Blocs/Login/login_event.dart @@ -0,0 +1,46 @@ +part of 'login_bloc.dart'; + +abstract class LoginEvent extends Equatable { + const LoginEvent(); + @override + List get props => []; +} + +class LoginInitiated extends LoginEvent { + const LoginInitiated({required this.email,required this.password}); + + final String email; + final String password; + + @override + List get props => [email,password]; +} + +// class LoginDone extends LoginEvent{ +// const LoginDone({required this.data}); +// final Map data; +// @override +// List get props => [data]; +// } +// class LoginSuccess extends LoginState{ +// const LoginSuccess({required this.data}); +// final Map data; +// } +// class LoginError extends LoginState{ +// const LoginError({required this.err}); +// final String err; +// } + +class LoginSuccess extends LoginEvent{ + const LoginSuccess({required this.userId,required this.company}); + final String userId,company; +} +class LoginError extends LoginEvent{ + const LoginError({required this.context,required this.err}); + final BuildContext context; + final String err; + @override + List get props => [context,err]; +} + + diff --git a/lib/Blocs/Login/login_state.dart b/lib/Blocs/Login/login_state.dart new file mode 100644 index 0000000..a5ea082 --- /dev/null +++ b/lib/Blocs/Login/login_state.dart @@ -0,0 +1,24 @@ +part of 'login_bloc.dart'; + +abstract class LoginState extends Equatable { + // final String email; + // final String password; + // final String err; + // final Map data; + + const LoginState(); + @override + List get props => []; +} + +class LoginInit extends LoginState {} +class LoginLoading extends LoginState {} +class LoginFinish extends LoginState{ + const LoginFinish({required this.data}); + final Map data; +} +class LoginFinalize extends LoginState{ + const LoginFinalize({required this.cabangList}); + final List cabangList; +} + diff --git a/lib/Blocs/Logout/logout_bloc.dart b/lib/Blocs/Logout/logout_bloc.dart new file mode 100644 index 0000000..1201248 --- /dev/null +++ b/lib/Blocs/Logout/logout_bloc.dart @@ -0,0 +1,27 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:meta/meta.dart'; +import 'package:unitstocks/Utils/keys.dart'; + +import '../../main.dart'; + +part 'logout_event.dart'; +part 'logout_state.dart'; + +class LogoutBloc extends Bloc { + LogoutBloc() : super(LogoutInitial()) { + on((event, emit) { + if(event is LogoutInitiate){ + emit(LogoutLoading()); + // prefs.remove(Keys.cabangId); + prefs.remove(Keys.company); + prefs.remove(Keys.loginId); + prefs.setBool(Keys.loggedIn, false); + emit(LogoutFinish()); + Navigator.pushReplacementNamed(event.context, '/login'); + } + }); + } +} diff --git a/lib/Blocs/Logout/logout_event.dart b/lib/Blocs/Logout/logout_event.dart new file mode 100644 index 0000000..c412c16 --- /dev/null +++ b/lib/Blocs/Logout/logout_event.dart @@ -0,0 +1,17 @@ +part of 'logout_bloc.dart'; + +@immutable +abstract class LogoutEvent { + const LogoutEvent(); + + @override + List get props => []; +} +class LogoutInitiate extends LogoutEvent{ + const LogoutInitiate({required this.context}); + final BuildContext context; +} +// class LogoutInit extends LogoutEvent{} +// class LogoutLoading extends LogoutEvent{} +// class LogoutFinish extends LogoutEvent{} + diff --git a/lib/Blocs/Logout/logout_state.dart b/lib/Blocs/Logout/logout_state.dart new file mode 100644 index 0000000..22911cb --- /dev/null +++ b/lib/Blocs/Logout/logout_state.dart @@ -0,0 +1,13 @@ +part of 'logout_bloc.dart'; + +@immutable +abstract class LogoutState { + const LogoutState(); + @override + List get props => []; +} + +class LogoutInitial extends LogoutState {} +class LogoutLoading extends LogoutState {} +class LogoutFinish extends LogoutState {} + diff --git a/lib/Blocs/Stock_Taking/Backup/backup_bloc.dart b/lib/Blocs/Stock_Taking/Backup/backup_bloc.dart new file mode 100644 index 0000000..0216521 --- /dev/null +++ b/lib/Blocs/Stock_Taking/Backup/backup_bloc.dart @@ -0,0 +1,22 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:meta/meta.dart'; +import '../../../Utils/db_helper.dart'; + +part 'backup_event.dart'; +part 'backup_state.dart'; + +class BackupBloc extends Bloc { + BackupBloc() : super(BackupInitial()) { + on((event, emit)async { + if (event is BackupInit){ + emit(BackupLoading()); + var result = await DBHelper.database.backupDb(context:event.context); + emit(BackupFinish(success: result["STATUS"]==1,msg: result['MSG'])); + } + }); + } +} diff --git a/lib/Blocs/Stock_Taking/Backup/backup_event.dart b/lib/Blocs/Stock_Taking/Backup/backup_event.dart new file mode 100644 index 0000000..c1ad693 --- /dev/null +++ b/lib/Blocs/Stock_Taking/Backup/backup_event.dart @@ -0,0 +1,13 @@ +part of 'backup_bloc.dart'; + +abstract class BackupEvent extends Equatable { + const BackupEvent(); + @override + List get props => []; +} +class BackupInit extends BackupEvent{ + const BackupInit({required this.context}); + final BuildContext context; +} + + diff --git a/lib/Blocs/Stock_Taking/Backup/backup_state.dart b/lib/Blocs/Stock_Taking/Backup/backup_state.dart new file mode 100644 index 0000000..018ae5a --- /dev/null +++ b/lib/Blocs/Stock_Taking/Backup/backup_state.dart @@ -0,0 +1,17 @@ +part of 'backup_bloc.dart'; + +abstract class BackupState extends Equatable { + const BackupState(); + @override + List get props => []; +} + +class BackupInitial extends BackupState {} + +class BackupLoading extends BackupState {} + +class BackupFinish extends BackupState{ + const BackupFinish({this.success=false,required this.msg}); + final bool success; + final String msg; +} diff --git a/lib/Blocs/Stock_Taking/Clear/clear_data_bloc.dart b/lib/Blocs/Stock_Taking/Clear/clear_data_bloc.dart new file mode 100644 index 0000000..751557d --- /dev/null +++ b/lib/Blocs/Stock_Taking/Clear/clear_data_bloc.dart @@ -0,0 +1,39 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'dart:io'; +import 'package:path_provider/path_provider.dart'; +import 'package:path/path.dart'; +import 'package:unitstocks/main.dart'; +import '../../../Utils/keys.dart'; + +part 'clear_data_event.dart'; +part 'clear_data_state.dart'; + +class ClearDataBloc extends Bloc { + ClearDataBloc() : super(ClearDataInitial()) { + on((event, emit) async{ + if(event is ClearDataInit){ + emit(ClearDataLoading()); + try{ + Directory? documentsDirectory = await getExternalStorageDirectory(); + String path = join(documentsDirectory!.path, "UnitStocking.db"); + File db = File(path); + if(db.existsSync()){ + db.deleteSync(); + } + await prefs.remove(Keys.lastDownload); + await prefs.remove(Keys.lastUpload); + await prefs.remove(Keys.targetProccess); + await prefs.remove(Keys.submitProccess); + await prefs.remove(Keys.stockId); + emit(ClearDataFinish(msg: 'Data Cleared')); + } + catch(e){ + emit(ClearDataFinish(cleared: false, msg: 'Failed to delete database file')); + } + } + }); + } +} diff --git a/lib/Blocs/Stock_Taking/Clear/clear_data_event.dart b/lib/Blocs/Stock_Taking/Clear/clear_data_event.dart new file mode 100644 index 0000000..78dd5db --- /dev/null +++ b/lib/Blocs/Stock_Taking/Clear/clear_data_event.dart @@ -0,0 +1,13 @@ +part of 'clear_data_bloc.dart'; + +abstract class ClearDataEvent extends Equatable { + const ClearDataEvent(); + @override + List get props => []; +} + + +class ClearDataInit extends ClearDataEvent{ + +} + diff --git a/lib/Blocs/Stock_Taking/Clear/clear_data_state.dart b/lib/Blocs/Stock_Taking/Clear/clear_data_state.dart new file mode 100644 index 0000000..011e0ff --- /dev/null +++ b/lib/Blocs/Stock_Taking/Clear/clear_data_state.dart @@ -0,0 +1,17 @@ +part of 'clear_data_bloc.dart'; + +abstract class ClearDataState extends Equatable { + const ClearDataState(); + @override + List get props => []; +} + +class ClearDataInitial extends ClearDataState {} + +class ClearDataLoading extends ClearDataState {} + +class ClearDataFinish extends ClearDataState { + const ClearDataFinish({this.cleared=true,required this.msg}); + final bool cleared; + final String msg; +} \ No newline at end of file diff --git a/lib/Blocs/Stock_Taking/Process/process_units_bloc.dart b/lib/Blocs/Stock_Taking/Process/process_units_bloc.dart new file mode 100644 index 0000000..126c024 --- /dev/null +++ b/lib/Blocs/Stock_Taking/Process/process_units_bloc.dart @@ -0,0 +1,31 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import '../../../Utils/keys.dart'; +import '../../../main.dart'; + +part 'process_units_event.dart'; +part 'process_units_state.dart'; + +class ProcessUnitsBloc extends Bloc { + ProcessUnitsBloc() : super(ProcessUnitsInitial()) { + on((event, emit)async { + if(event is ProcessUnit){ + emit(ProcessLoading()); + var unpack = await util.JsonDataPutRaw({ + "userId": prefs.getString(Keys.loginId), + "cabangId": prefs.getString(Keys.cabangId), + "company": prefs.getString(Keys.company), + "dbPath": + prefs.getString(Keys.targetProccess) + }, '${prefs.getString(Keys.hostAddress)}/stock_taking/add_collection/'); + emit(ProcessFinish(success: unpack['STATUS'] == 1,msg:unpack['DATA'] )); + if (unpack['STATUS'] == 1) { + prefs.remove(Keys.targetProccess); + prefs.setBool(Keys.submitProccess, true); + } + } + }); + } +} diff --git a/lib/Blocs/Stock_Taking/Process/process_units_event.dart b/lib/Blocs/Stock_Taking/Process/process_units_event.dart new file mode 100644 index 0000000..977b16f --- /dev/null +++ b/lib/Blocs/Stock_Taking/Process/process_units_event.dart @@ -0,0 +1,9 @@ +part of 'process_units_bloc.dart'; + +abstract class ProcessUnitsEvent extends Equatable { + const ProcessUnitsEvent(); + @override + List get props => []; +} + +class ProcessUnit extends ProcessUnitsEvent{} \ No newline at end of file diff --git a/lib/Blocs/Stock_Taking/Process/process_units_state.dart b/lib/Blocs/Stock_Taking/Process/process_units_state.dart new file mode 100644 index 0000000..ea71a95 --- /dev/null +++ b/lib/Blocs/Stock_Taking/Process/process_units_state.dart @@ -0,0 +1,17 @@ +part of 'process_units_bloc.dart'; + +abstract class ProcessUnitsState extends Equatable { + const ProcessUnitsState(); + @override + List get props => []; +} + +class ProcessUnitsInitial extends ProcessUnitsState {} + +class ProcessLoading extends ProcessUnitsState {} + +class ProcessFinish extends ProcessUnitsState{ + const ProcessFinish({required this.msg, this.success=true}); + final bool success; + final String msg; +} diff --git a/lib/Blocs/Stock_Taking/Restore/restore_bloc.dart b/lib/Blocs/Stock_Taking/Restore/restore_bloc.dart new file mode 100644 index 0000000..f26b060 --- /dev/null +++ b/lib/Blocs/Stock_Taking/Restore/restore_bloc.dart @@ -0,0 +1,52 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:meta/meta.dart'; +import 'package:unitstocks/Model/unit.dart'; +import 'package:unitstocks/Utils/db_helper.dart'; +import '../../../main.dart'; +import '../../../Utils/keys.dart'; +import '../../../Model/unit.dart'; +import 'package:intl/intl.dart'; + +part 'restore_event.dart'; +part 'restore_state.dart'; + +class RestoreBloc extends Bloc { + RestoreBloc() : super(RestoreInitial()) { + on((event, emit) async { + if(event is RestoreInit){ + if(defaultTargetPlatform == TargetPlatform.android){ + emit(RestoreLoading()); + var result = await DBHelper.database.restoreDb(event.context); + if(result["STATUS"]==1){ + await prefs.remove(Keys.lastDownload); + await prefs.remove(Keys.lastUpload); + await prefs.remove(Keys.targetProccess); + await prefs.remove(Keys.submitProccess); + var value = await DBHelper.database.getValue(Keys.startDate); + if(value != null) await prefs.setString(Keys.lastDownload, DateFormat('dd-MM-yyyy HH:mm:ss').parse(value.value).toIso8601String()); + else { + await prefs.setString(Keys.lastDownload, DateTime.now().toIso8601String()); + } + value = await DBHelper.database.getValue(Keys.stockId); + if(value != null) await prefs.setString(Keys.stockId,value.value); + else { + await prefs.setString(Keys.stockId,prefs.getString(Keys.backupStockId)); + } + emit(RestoreFinish(msg: result["MSG"])); + } + else{ + emit(RestoreFinish(success:false,msg: result["MSG"])); + } + } + else { + emit(RestoreFinish(success:false,msg: "OS ini tidak mendukung fitur backup")); + } + } + }); + } +} diff --git a/lib/Blocs/Stock_Taking/Restore/restore_event.dart b/lib/Blocs/Stock_Taking/Restore/restore_event.dart new file mode 100644 index 0000000..9fb72de --- /dev/null +++ b/lib/Blocs/Stock_Taking/Restore/restore_event.dart @@ -0,0 +1,12 @@ +part of 'restore_bloc.dart'; + +abstract class RestoreEvent extends Equatable { + const RestoreEvent(); + @override + List get props => []; +} + +class RestoreInit extends RestoreEvent{ + const RestoreInit({required this.context}); + final BuildContext context; +} diff --git a/lib/Blocs/Stock_Taking/Restore/restore_state.dart b/lib/Blocs/Stock_Taking/Restore/restore_state.dart new file mode 100644 index 0000000..eb99cfe --- /dev/null +++ b/lib/Blocs/Stock_Taking/Restore/restore_state.dart @@ -0,0 +1,15 @@ +part of 'restore_bloc.dart'; + +abstract class RestoreState extends Equatable { + const RestoreState(); + @override + List get props => []; +} + +class RestoreInitial extends RestoreState {} +class RestoreLoading extends RestoreState{} +class RestoreFinish extends RestoreState{ + const RestoreFinish({this.success=true,required this.msg}); + final bool success; + final String msg; +} diff --git a/lib/Blocs/Stock_Taking/Send/upload_bloc.dart b/lib/Blocs/Stock_Taking/Send/upload_bloc.dart new file mode 100644 index 0000000..a0b09ea --- /dev/null +++ b/lib/Blocs/Stock_Taking/Send/upload_bloc.dart @@ -0,0 +1,59 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import '../../../Utils/db_helper.dart'; +import '../../../Model/unit.dart'; +import '../../../Utils/download_upload_handler.dart'; +import '../../../Utils/keys.dart'; +import '../../../main.dart'; +import 'package:intl/intl.dart'; +import 'package:flutter/cupertino.dart'; + + + +part 'upload_event.dart'; +part 'upload_state.dart'; + +class UploadBloc extends Bloc { + UploadBloc() : super(UploadInitial()) { + on((event, emit) async{ + if(event is Upload){ + emit(UploadLoading()); + if(await prefs.getString(Keys.lastDownload)!=null){ + var unitsInserted = await DBHelper.database.getAllUnits(inserted: true); + if(unitsInserted.isNotEmpty){ + await DBHelper.database.insertUpdateValue(Value(name: 'TGL_SELESAI',value: DateFormat('dd-MM-yyyy HH:mm:ss').format(new DateTime.now()))); + await DBHelper.database.closeDb(); + file_Trans_Handler trans = new file_Trans_Handler(); + bool popped = false; + // util.showLoading(event.context,onwillpop:()async{ + // await trans.cancel(); + // popped = true; + // return true; + // },dissmissable: true); + util.showLoading(event.context); + var upload = await trans.uploadFile('UnitStocking.db',"${prefs.getString(Keys.hostAddress)}/stock_taking/upload/",prefs.getString(Keys.company),prefs.getString(Keys.cabangId)); + if(!popped){ + Navigator.pop(event.context); + if(upload['STATUS']==1){ + prefs.setString(Keys.lastUpload, DateTime.now().toIso8601String()); + prefs.setString(Keys.targetProccess, upload[Keys.targetProccess]); + emit(const UploadFinish(msg: "Upload selesai!")); + } + else{ + emit(UploadFinish(success: false,msg: upload['DATA'])); + } + } + } + else{ + emit(UploadFinish(success: false,msg: "Belum ada data yang disimpan")); + } + } + else{ + emit(UploadFinish(success: false,msg: "Data unit tidak ditemukan. Tarik data terlebih dahulu!")); + } + } + }); + } +} diff --git a/lib/Blocs/Stock_Taking/Send/upload_event.dart b/lib/Blocs/Stock_Taking/Send/upload_event.dart new file mode 100644 index 0000000..efb4614 --- /dev/null +++ b/lib/Blocs/Stock_Taking/Send/upload_event.dart @@ -0,0 +1,11 @@ +part of 'upload_bloc.dart'; + +abstract class UploadEvent extends Equatable { + const UploadEvent(); + @override + List get props => []; +} +class Upload extends UploadEvent{ + const Upload({required this.context}); + final BuildContext context; +} diff --git a/lib/Blocs/Stock_Taking/Send/upload_state.dart b/lib/Blocs/Stock_Taking/Send/upload_state.dart new file mode 100644 index 0000000..48233e7 --- /dev/null +++ b/lib/Blocs/Stock_Taking/Send/upload_state.dart @@ -0,0 +1,17 @@ +part of 'upload_bloc.dart'; + +abstract class UploadState extends Equatable { + const UploadState(); + @override + List get props => []; +} + +class UploadInitial extends UploadState {} + +class UploadFinish extends UploadState{ + const UploadFinish({this.success=true,required this.msg}); + final String msg; + final bool success; +} +class UploadLoading extends UploadState{} + diff --git a/lib/Blocs/Stock_Taking/State/load_state_bloc.dart b/lib/Blocs/Stock_Taking/State/load_state_bloc.dart new file mode 100644 index 0000000..e604de3 --- /dev/null +++ b/lib/Blocs/Stock_Taking/State/load_state_bloc.dart @@ -0,0 +1,42 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import '../../../Utils/keys.dart'; +import 'package:unitstocks/main.dart'; +import '../../../Utils/db_helper.dart'; +import '../../../Model/unit.dart'; +part 'load_state_event.dart'; +part 'load_state_state.dart'; + +class LoadStateBloc extends Bloc { + LoadStateBloc() : super(LoadStateInitial()) { + on( (event, emit)async { + if(event is LoadState){ + emit(StateLoading()); + if(prefs.getString(Keys.lastDownload)== null){ + emit(LoadFailed(err: "Tarik data terlebih dahulu!")); + } + else{ + String company = prefs.getString(Keys.company); + if(prefs.getString(Keys.stockId) == null) + { + var value = await DBHelper.database.getValue(Keys.stockId); + if(value != null)await prefs.setString(Keys.stockId, value.value); + } + String stock_taking_id = prefs.getString(Keys.stockId); + if(prefs.getString(Keys.backupStockId) == null) prefs.setString(Keys.backupStockId,stock_taking_id); + if(company!=null&&stock_taking_id!=null){ + var result = await util.JsonDataPostRaw({"company":company,"stockTakingId":stock_taking_id}, '${prefs.getString(Keys.hostAddress)}/stock_taking/state/'); + if(result['STATUS']==1){ + emit(LoadSuccess(state: result['DATA'])); + } + } + else{ + emit(LoadFailed(err: "Error mengecek state Stoking Unit.")); + } + } + } + }); + } +} diff --git a/lib/Blocs/Stock_Taking/State/load_state_event.dart b/lib/Blocs/Stock_Taking/State/load_state_event.dart new file mode 100644 index 0000000..937781d --- /dev/null +++ b/lib/Blocs/Stock_Taking/State/load_state_event.dart @@ -0,0 +1,11 @@ +part of 'load_state_bloc.dart'; + +abstract class LoadStateEvent extends Equatable { + const LoadStateEvent(); + @override + List get props => []; +} + +class LoadState extends LoadStateEvent{ + const LoadState(); +} diff --git a/lib/Blocs/Stock_Taking/State/load_state_state.dart b/lib/Blocs/Stock_Taking/State/load_state_state.dart new file mode 100644 index 0000000..85370c5 --- /dev/null +++ b/lib/Blocs/Stock_Taking/State/load_state_state.dart @@ -0,0 +1,21 @@ +part of 'load_state_bloc.dart'; + +abstract class LoadStateState extends Equatable { + const LoadStateState(); + @override + List get props => []; +} + +class LoadStateInitial extends LoadStateState { + +} +class StateLoading extends LoadStateState{} +class LoadFailed extends LoadStateState{ + const LoadFailed({required this.err}); + final String err; +} +class LoadSuccess extends LoadStateState{ + const LoadSuccess({required this.state}); + final String state; +} + diff --git a/lib/Blocs/Stock_Taking/Submit/submit_unit_bloc.dart b/lib/Blocs/Stock_Taking/Submit/submit_unit_bloc.dart new file mode 100644 index 0000000..344e338 --- /dev/null +++ b/lib/Blocs/Stock_Taking/Submit/submit_unit_bloc.dart @@ -0,0 +1,31 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import '../../../main.dart'; +import '../../../Utils/db_helper.dart'; +import '../../../Utils/keys.dart'; +import '../../../Model/unit.dart'; + +part 'submit_unit_event.dart'; +part 'submit_unit_state.dart'; + +class SubmitUnitBloc extends Bloc { + SubmitUnitBloc() : super(SubmitUnitInitial()) { + on((event, emit) async{ + if(event is Submit){ + emit(SubmitLoading()); + if(prefs.getString(Keys.stockId) == null) + { + Value value = await DBHelper.database.getValue(Keys.stockId); + await prefs.setString(Keys.stockId, value.value); + } + var submits = await util.JsonDataPostRaw({"stockTakingId":prefs.getString(Keys.stockId),"company":prefs.getString(Keys.company),"user_id":prefs.getString(Keys.loginId)}, '${prefs.getString(Keys.hostAddress)}/stock_taking/submit/'); + emit(SubmitFinish(msg: submits['DATA'],success: submits['STATUS']==1)); + if(submits['STATUS']==1){ + prefs.remove(Keys.submitProccess); + } + } + }); + } +} diff --git a/lib/Blocs/Stock_Taking/Submit/submit_unit_event.dart b/lib/Blocs/Stock_Taking/Submit/submit_unit_event.dart new file mode 100644 index 0000000..1608b0f --- /dev/null +++ b/lib/Blocs/Stock_Taking/Submit/submit_unit_event.dart @@ -0,0 +1,9 @@ +part of 'submit_unit_bloc.dart'; + +abstract class SubmitUnitEvent extends Equatable { + const SubmitUnitEvent(); + @override + List get props => []; +} + +class Submit extends SubmitUnitEvent{} diff --git a/lib/Blocs/Stock_Taking/Submit/submit_unit_state.dart b/lib/Blocs/Stock_Taking/Submit/submit_unit_state.dart new file mode 100644 index 0000000..c2c7f52 --- /dev/null +++ b/lib/Blocs/Stock_Taking/Submit/submit_unit_state.dart @@ -0,0 +1,18 @@ +part of 'submit_unit_bloc.dart'; + +abstract class SubmitUnitState extends Equatable { + const SubmitUnitState(); + @override + List get props => []; +} + +class SubmitUnitInitial extends SubmitUnitState {} + +class SubmitLoading extends SubmitUnitState{} + +class SubmitFinish extends SubmitUnitState{ + const SubmitFinish({required this.msg,this.success=true}); + final bool success; + final String msg; +} + diff --git a/lib/Blocs/Stock_Taking/Unit/get_unit_bloc.dart b/lib/Blocs/Stock_Taking/Unit/get_unit_bloc.dart new file mode 100644 index 0000000..40ca913 --- /dev/null +++ b/lib/Blocs/Stock_Taking/Unit/get_unit_bloc.dart @@ -0,0 +1,44 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import '../../../Utils/keys.dart'; +import '../../../Model/unit.dart'; +import '../../../Utils/db_helper.dart'; +import 'package:unitstocks/main.dart'; +import 'package:intl/intl.dart'; +import '../../../Utils/download_upload_handler.dart'; +part 'get_unit_event.dart'; +part 'get_unit_state.dart'; + +class GetUnitBloc extends Bloc { + GetUnitBloc() : super(GetUnitInitial()) { + on((event, emit) async { + if(event is GetUnitInit){ + if(prefs.getString(Keys.cabangId) != null && prefs.getString(Keys.company) != null){ + emit(GetUnitLoading(percent: 0.0)); + file_Trans_Handler trans = new file_Trans_Handler(); + trans.downloadFile('UnitStocking.db',"${prefs.getString(Keys.hostAddress)}/stock_taking/get_units/${prefs.getString(Keys.company)}/${prefs.getString(Keys.cabangId)}"); + await for (double? value in await trans.progress){ + if(value != null){ + emit(GetUnitLoading(percent:value)); + if(value >= 1.0) { + await prefs.setString(Keys.lastDownload, DateTime.now().toIso8601String()); + await DBHelper.database.insertUpdateValue(Value(name: 'TGL_START',value: DateFormat('dd-MM-yyyy HH:mm:ss').format(DateTime.parse(DateTime.now().toIso8601String())))); + await DBHelper.database.insertUpdateValue(Value(name: 'TGL_STOCK_TAKING',value: DateFormat('dd-MM-yyyy').format(DateTime.parse(DateTime.now().toIso8601String())))); + await DBHelper.database.closeDb(); + emit(GetUnitFinish(respond: 'Data terdownload')); + } + if(value==-1.0){ + emit(GetUnitFinish(respond: 'Data download gagal',success: false)); + } + } + } + } + else{ + emit(GetUnitFinish(respond: 'Belum ada cabang yang dipilih!',success: false)); + } + } + }); + } +} diff --git a/lib/Blocs/Stock_Taking/Unit/get_unit_event.dart b/lib/Blocs/Stock_Taking/Unit/get_unit_event.dart new file mode 100644 index 0000000..22eaa0c --- /dev/null +++ b/lib/Blocs/Stock_Taking/Unit/get_unit_event.dart @@ -0,0 +1,9 @@ +part of 'get_unit_bloc.dart'; + +abstract class GetUnitEvent extends Equatable { + const GetUnitEvent(); + @override + List get props => []; +} + +class GetUnitInit extends GetUnitEvent{} diff --git a/lib/Blocs/Stock_Taking/Unit/get_unit_state.dart b/lib/Blocs/Stock_Taking/Unit/get_unit_state.dart new file mode 100644 index 0000000..203bddb --- /dev/null +++ b/lib/Blocs/Stock_Taking/Unit/get_unit_state.dart @@ -0,0 +1,22 @@ +part of 'get_unit_bloc.dart'; + +abstract class GetUnitState extends Equatable { + const GetUnitState(); + @override + List get props => []; +} + +class GetUnitInitial extends GetUnitState{} + +class GetUnitLoading extends GetUnitState{ + const GetUnitLoading({required this.percent}); + final double percent; + @override + List get props => [percent]; +} + +class GetUnitFinish extends GetUnitState{ + const GetUnitFinish({required this.respond, this.success=true}); + final String respond; + final bool success; +} \ No newline at end of file diff --git a/lib/Blocs/Units/Images/get_images_bloc.dart b/lib/Blocs/Units/Images/get_images_bloc.dart new file mode 100644 index 0000000..c732023 --- /dev/null +++ b/lib/Blocs/Units/Images/get_images_bloc.dart @@ -0,0 +1,26 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import '../../../Model/unit.dart'; +import '../../../Utils/db_helper.dart'; + +part 'get_images_event.dart'; +part 'get_images_state.dart'; + +class GetImagesBloc extends Bloc { + GetImagesBloc() : super(GetImagesInitial()) { + on((event, emit) async{ + if(event is GetImages){ + emit(GetImagesLoading()); + var result = await DBHelper.database.getBlobUnits(event.mesin); + if(result!=null){ + emit(GetImagesFinish(blobs: result)); + } + else{ + emit(GetImagesError(err: "Image Data not Found")); + } + } + }); + } +} diff --git a/lib/Blocs/Units/Images/get_images_event.dart b/lib/Blocs/Units/Images/get_images_event.dart new file mode 100644 index 0000000..6a4c1d6 --- /dev/null +++ b/lib/Blocs/Units/Images/get_images_event.dart @@ -0,0 +1,12 @@ +part of 'get_images_bloc.dart'; + +abstract class GetImagesEvent extends Equatable { + const GetImagesEvent(); + @override + List get props => []; +} + +class GetImages extends GetImagesEvent{ + const GetImages({required this.mesin}); + final String mesin; +} diff --git a/lib/Blocs/Units/Images/get_images_state.dart b/lib/Blocs/Units/Images/get_images_state.dart new file mode 100644 index 0000000..d828a41 --- /dev/null +++ b/lib/Blocs/Units/Images/get_images_state.dart @@ -0,0 +1,21 @@ +part of 'get_images_bloc.dart'; + +abstract class GetImagesState extends Equatable { + const GetImagesState(); + @override + List get props => []; +} + +class GetImagesInitial extends GetImagesState {} + +class GetImagesLoading extends GetImagesState {} + +class GetImagesFinish extends GetImagesState { + final List blobs; + const GetImagesFinish({required this.blobs}); +} + +class GetImagesError extends GetImagesState{ + const GetImagesError({required this.err}); + final String err; +} diff --git a/lib/Blocs/Units/List/list_unit_bloc.dart b/lib/Blocs/Units/List/list_unit_bloc.dart new file mode 100644 index 0000000..f169bb2 --- /dev/null +++ b/lib/Blocs/Units/List/list_unit_bloc.dart @@ -0,0 +1,38 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import '../../../Model/unit.dart'; +import '../../../Utils/db_helper.dart'; + + +part 'list_unit_event.dart'; +part 'list_unit_state.dart'; + +class ListUnitBloc extends Bloc { + ListUnitBloc() : super(ListUnitInitial()) { + on((event, emit) async { + if(event is ListUnit){ + emit(ListUnitLoading()); + List units = []; + List blobs = []; + try{ + await DBHelper.database.closeDb(); + if(event.search != null && event.search != ""){ + units = await DBHelper.database.searchAllUnits(event.search!.toUpperCase(),inserted: event.completed); + } + else{ + units = await DBHelper.database.getAllUnits(inserted: event.completed); + } + for (int i = 0;i get props => []; +} + +class ListUnit extends ListUnitEvent{ + const ListUnit({this.search, this.completed=false}); + final bool completed; + final String? search; +} diff --git a/lib/Blocs/Units/List/list_unit_state.dart b/lib/Blocs/Units/List/list_unit_state.dart new file mode 100644 index 0000000..bfb014d --- /dev/null +++ b/lib/Blocs/Units/List/list_unit_state.dart @@ -0,0 +1,24 @@ +part of 'list_unit_bloc.dart'; + +abstract class ListUnitState extends Equatable { + const ListUnitState(); + @override + List get props => []; +} + +class ListUnitInitial extends ListUnitState {} + +class ListUnitLoading extends ListUnitState {} + +class ListUnitFinish extends ListUnitState { + const ListUnitFinish ({required this.units,required this.blobs}); + final List units; + final List blobs; +} + +class ListUnitError extends ListUnitState { + const ListUnitError ({required this.err}); + final String err; +} + + diff --git a/lib/Blocs/Units/Save/save_unit_bloc.dart b/lib/Blocs/Units/Save/save_unit_bloc.dart new file mode 100644 index 0000000..00fe7b2 --- /dev/null +++ b/lib/Blocs/Units/Save/save_unit_bloc.dart @@ -0,0 +1,45 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import '../../../main.dart'; +import '../../../Model/unit.dart'; +import '../../../Utils/db_helper.dart'; +import 'package:intl/intl.dart'; + +part 'save_unit_event.dart'; +part 'save_unit_state.dart'; + +class SaveUnitBloc extends Bloc { + SaveUnitBloc() : super(SaveUnitInitial()) { + on((event, emit) async{ + if(event is Save){ + emit(SaveUnitLoading()); + String? errMsg; + bool succeed = false; + Unit _currentUnit = event.unit; + List blobList = event.blobs; + for(int i = 0 ; i get props => []; +} + +class Save extends SaveUnitEvent { + final List blobs; + final Unit unit; + const Save({required this.blobs, required this.unit}); +} diff --git a/lib/Blocs/Units/Save/save_unit_state.dart b/lib/Blocs/Units/Save/save_unit_state.dart new file mode 100644 index 0000000..03abef8 --- /dev/null +++ b/lib/Blocs/Units/Save/save_unit_state.dart @@ -0,0 +1,17 @@ +part of 'save_unit_bloc.dart'; + +abstract class SaveUnitState extends Equatable { + const SaveUnitState(); + @override + List get props => []; +} + +class SaveUnitInitial extends SaveUnitState {} + +class SaveUnitLoading extends SaveUnitState{} + +class SaveFinish extends SaveUnitState{ + const SaveFinish({this.success=true,required this.msg}); + final bool success; + final String msg; +} diff --git a/lib/Model/unit.dart b/lib/Model/unit.dart new file mode 100644 index 0000000..e217b57 --- /dev/null +++ b/lib/Model/unit.dart @@ -0,0 +1,159 @@ +import 'dart:typed_data'; + +import '../Utils/keys.dart'; + + +class Unit { + int id; + String rangka; + String mesin; + String kode; + String tahun; + String tipe; + String warna; + String state; + String flag; + String timestamp; + String channel; + + + Unit({ + required this.id, + required this.rangka, + required this.mesin, + this.kode = '', + this.tahun = '', + this.tipe = '', + this.warna = '', + this.state = '', + this.flag = '', + this.timestamp = '', + this.channel = '', + }); + + factory Unit.fromJson(Map json) { + return Unit( + id: json[Columns.id], + rangka: json[Columns.rangka], + mesin: json[Columns.mesin], + kode: json[Columns.kode], + tahun: json[Columns.tahun], + tipe: json[Columns.tipe], + warna : json[Columns.warna], + state: json[Columns.state], + flag: json[Columns.flag], + timestamp: json[Columns.timestamp]??'', + channel: json[Columns.channel], + );} + + Map toJson() => { + Columns.id: id, + Columns.rangka: rangka.toUpperCase(), + Columns.mesin : mesin.toUpperCase(), + Columns.kode : kode.toUpperCase(), + Columns.tahun : tahun, + Columns.tipe: tipe, + Columns.warna: warna, + Columns.state :state, + Columns.flag :flag, + Columns.timestamp :timestamp, + Columns.channel:channel, + }; +} + +class Blob { + int id; + Uint8List? bytes; + String mesin; + String? lat; + String? long; + String jenis; + String noUrut; + + + Blob({ + required this.id, + required this.bytes, + required this.mesin, + required this.lat, + required this.long, + required this.jenis, + required this.noUrut + }); + + Map toJson() => { + Columns.blobId: id, + Columns.bytes : bytes, + Columns.mesin : mesin, + Columns.lat : lat, + Columns.long : long, + Columns.jenis : jenis, + Columns.noUrut : noUrut + }; + + factory Blob.fromJson(Map json) { + return Blob( + id: json[Columns.blobId], + bytes: json[Columns.bytes], + mesin: json[Columns.mesin], + lat: json[Columns.lat], + long: json[Columns.long], + jenis: json[Columns.jenis], + noUrut: json[Columns.noUrut] + ); + } + + +} + +class Value { + String name; + String value; + + + Value({ + required this.name, + required this.value + }); + + Map toJson() => { + Columns.name : name, + Columns.value : value, + }; + + factory Value.fromJson(Map json) => Value( + name: json[Columns.name], + value: json[Columns.value], + ); +} + +class ImageType { + String value; + String noUrut; + + ImageType({ + required this.value, + required this.noUrut + }); + + Map toJson() => { + Columns.value : value, + Columns.noUrut : noUrut + }; + + factory ImageType.fromJson(Map json) => ImageType( + value: json[Columns.value], + noUrut: json[Columns.noUrut] + ); +} + +class Count { + int count; + Count({ + required this.count, + }); + + factory Count.fromJson(Map json) => Count( + count: json["count"], + ); +} diff --git a/lib/Pages/home_page.dart b/lib/Pages/home_page.dart new file mode 100644 index 0000000..efeff8c --- /dev/null +++ b/lib/Pages/home_page.dart @@ -0,0 +1,1771 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:in_app_update/in_app_update.dart'; +import 'package:package_info/package_info.dart'; +import 'package:path/path.dart' as paths; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:path_provider/path_provider.dart'; + +// import 'Util/download_Upload_Handler.dart'; +import 'package:intl/intl.dart'; +import 'package:unitstocks/Blocs/Cabang/cabang_bloc.dart'; +import 'package:unitstocks/Blocs/Logout/logout_bloc.dart'; +import 'package:unitstocks/Blocs/Stock_Taking/Restore/restore_bloc.dart'; +import '../main.dart'; +import '../Utils/keys.dart'; +// import 'Util/UnitModel.dart'; +import 'package:permission_handler/permission_handler.dart' as pHandler; +import '../Blocs/Stock_Taking/State/load_state_bloc.dart'; +import '../Blocs/Stock_Taking/Unit/get_unit_bloc.dart'; +import '../Blocs/Stock_Taking/Clear/clear_data_bloc.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; +import '../Blocs/Stock_Taking/Backup/backup_bloc.dart'; +import '../Blocs/Stock_Taking/Send/upload_bloc.dart'; +import '../Blocs/Stock_Taking/Process/process_units_bloc.dart'; +import '../Blocs/Stock_Taking/Submit/submit_unit_bloc.dart'; + + + + +class HomePage extends StatefulWidget { + const HomePage({Key? key}) : super(key: key); + + @override + _HomePageState createState() => _HomePageState(); +} + +class _HomePageState extends State with RouteAware { + StreamSubscription? progressDLStream, progressULStream; + String lastDownload = ''; + String lastUpload = ''; + double? progressDL, progressUL; + String timeString = ''; + bool isLoading = false; + String? state = ''; + late LoadStateBloc _stateBloc; + late GetUnitBloc _getDataBloc; + late BackupBloc _backupBloc; + late RestoreBloc _restoreBloc; + late UploadBloc _uploadBloc; + late ProcessUnitsBloc _processBloc; + late SubmitUnitBloc _submitBloc; + final hostAddress = new TextEditingController(); + final _refreshController = new RefreshController(initialRefresh: false); + String currentVersion = ''; + + clearData(contextParent)async{ + ClearDataBloc _clearBloc = ClearDataBloc(); + setState(() { + progressDL = null; + isLoading = false; + }); + bool? result; + await showDialog( + context: contextParent,builder: (context)=>WillPopScope( + onWillPop: ()async{ + Navigator.pop(context,false); + return false; + }, + child: BlocProvider( + create: (context) => _clearBloc, + child: BlocListener( + listener: (context, state) { + setState(() { + isLoading =(state is ClearDataLoading); + }); + if(state is ClearDataFinish){ + result = state.cleared; + if(result??false) { + Navigator.pop(context); + setState(() { + lastDownload = ""; + }); + } + util.showFlushbar(contextParent, state.msg,color: (result??false)?Colors.grey:Colors.red); + } + }, + child: AlertDialog( + title: const Text('Clear Data ?'), + content: const Text('Proceed to clear any remaining units data on this device?'), + actions: [ + TextButton( + child: const Text('Proceed'), + onPressed: ()async{ + _clearBloc.add(ClearDataInit()); + }, + ), + TextButton( + child: const Text('Cancel'), + onPressed: (){Navigator.pop(context);}, + ) + ], + ), + ), + ), + )); + return result??false; + } + + @override + void initState() { + // TODO: implement initState + super.initState(); + _stateBloc = LoadStateBloc(); + _getDataBloc = GetUnitBloc(); + _backupBloc = BackupBloc(); + _restoreBloc = RestoreBloc(); + _processBloc = ProcessUnitsBloc(); + _submitBloc = SubmitUnitBloc(); + _uploadBloc = UploadBloc(); + lastDownload = prefs.getString(Keys.lastDownload) ?? ''; + lastUpload = prefs.getString(Keys.lastUpload) ?? ''; + WidgetsBinding.instance!.addPostFrameCallback((_) async { + await _check_Update(); + await util.permissionCheck(context,pHandler.Permission.storage,()async{print("storage permit granted!");},customMessage: " untuk menyimpan data backup"); + if(prefs.getString(Keys.stockId)!= null){ + _stateBloc.add(const LoadState()); + } + }); + } + + + _check_Update() async { + if (defaultTargetPlatform == TargetPlatform.android) { + try { + final PackageInfo info = await PackageInfo.fromPlatform(); + AppUpdateInfo _updateInfo = await InAppUpdate.checkForUpdate(); + + currentVersion = info.version.trim(); + String latestversion = '0.0.0'; + var result = await util.getMinAppVer(); + if (result['STATUS'] == 1) { + latestversion = result['DATA']; + } + var current = currentVersion.split('.'); + var latest = latestversion.split('.'); + if (int.parse(current[0]) == int.parse(latest[0])) { + if (int.parse(current[1]) == int.parse(latest[1])) { + if (int.parse(current[2]) < int.parse(latest[2])) { + await util.updateDialog(context); + } else { + if (_updateInfo.updateAvailability == 2) { + await InAppUpdate.startFlexibleUpdate(); + await InAppUpdate.completeFlexibleUpdate(); + } + } + } else if (int.parse(current[1]) < int.parse(latest[1])) { + await util.updateDialog(context); +// await InAppUpdate.performImmediateUpdate(); + } else { + if (_updateInfo.updateAvailability == 2) { + await InAppUpdate.startFlexibleUpdate(); + await InAppUpdate.completeFlexibleUpdate(); + } + } + } else if (int.parse(current[0]) < int.parse(latest[0])) { + await util.updateDialog(context); +// await InAppUpdate.performImmediateUpdate(); + } else { + if (_updateInfo.updateAvailability == 2) { + await InAppUpdate.startFlexibleUpdate(); + await InAppUpdate.completeFlexibleUpdate(); + } + } + } catch (e) { + print("error Update $e"); + } + } + } + + changeCabang(context) async { + String selected = prefs.getString(Keys.cabangId) ?? ''; + return await showDialog( + context: context, + builder: (context) => StatefulBuilder( + builder: (context, setState) => BlocProvider( + create: (_) => CabangBloc(), + child: BlocListener( + listener: (context, cabangState) async { + if (cabangState is CabangFinished) { + if (cabangState.cabangChanged) { + setState(() { + lastUpload = ''; + lastDownload = ''; + timeString = ''; + }); + } + Navigator.pop(context, cabangState.cabangChanged); + } + }, + child: Material( + color: Colors.white.withOpacity(0.9), + child: BlocBuilder( + builder: (context, cabangState) { + if (cabangState is CabangLoading) { + return const Center( + child: SizedBox( + height: 10, + width: 10, + child: CircularProgressIndicator(), + ), + ); + } else if (cabangState is CabangInitial) { + BlocProvider.of(context).add(CabangInit( + userId: prefs.getString(Keys.loginId), + company: prefs.getString(Keys.company))); + } else if (cabangState is CabangDisplay) { + selected = (selected == '') + ? cabangState.cabangList[0]["RETURN_VALUE"] + : selected; + return Center( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 2, + offset: const Offset( + 0, 0), // changes position of shadow + ), + ], + borderRadius: BorderRadius.circular(5)), + // height: MediaQuery.of(context).size.height/3.2, + height: 220, + width: MediaQuery.of(context).size.width * 0.75, + child: Column( + children: [ + Flexible( + flex: 3, + child: Container( + padding: const EdgeInsets.only( + top: 10, left: 10, right: 10), + alignment: Alignment.center, + decoration: const BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(5), + topRight: Radius.circular(5))), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: + CrossAxisAlignment.center, + children: const [ + Icon( + Icons.business, + size: 70, + color: Colors.indigo, + ), + SizedBox( + height: 5, + ), + Text( + 'Set Cabang', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + color: Colors.indigo), + ) + ], + ), + ), + ), + Flexible( + flex: 1, + child: Container( + padding: const EdgeInsets.only( + left: 20, right: 20), + alignment: Alignment.centerLeft, + child: Theme( + data: ThemeData( + canvasColor: Colors.white, + primaryColor: Colors.indigo, + accentColor: Colors.indigo, + hintColor: Colors.indigo), + child: DropdownButtonFormField( + style: TextStyle( + color: Colors.black.withOpacity(0.6), + fontWeight: FontWeight.w500, + fontSize: 14, + ), + decoration: const InputDecoration( + contentPadding: EdgeInsets.all(8.0), + ), + value: selected, + onChanged: (value) { + setState(() { + selected = value.toString(); + }); + }, + items: + cabangState.cabangList.map((item) { + return DropdownMenuItem( + value: item['RETURN_VALUE'], + child: Text( + (item["DISPLAY_VALUE"].length > + 20) + ? item["DISPLAY_VALUE"] + .substring(0, 20) + + "..." + : item["DISPLAY_VALUE"]), + ); + }).toList(), + ), + ), + ), + ), + Flexible( + flex: 1, + child: Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + child: const Text( + 'OK', + style: + TextStyle(color: Colors.indigo), + ), + onPressed: () async { + BlocProvider.of(context) + .add(CabangPicked( + cabangId: selected)); + }, + ), + ], + ), + ), + ) + ], + ), + ), + ); + } else if (cabangState is CabangError) { + return Center( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 2, + offset: const Offset( + 0, 0), // changes position of shadow + ), + ], + borderRadius: BorderRadius.circular(5)), + // height: MediaQuery.of(context).size.height/3.2, + height: 220, + width: MediaQuery.of(context).size.width * 0.75, + child: Column( + children: [ + Flexible( + flex: 3, + child: Container( + padding: const EdgeInsets.only( + top: 10, left: 10, right: 10), + alignment: Alignment.center, + decoration: const BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(5), + topRight: Radius.circular(5))), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: + CrossAxisAlignment.center, + children: const [ + Icon( + Icons.business, + size: 70, + color: Colors.indigo, + ), + SizedBox( + height: 5, + ), + Text( + 'Error', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + color: Colors.indigo), + ) + ], + ), + ), + ), + Flexible( + flex: 1, + child: Container( + padding: const EdgeInsets.only( + left: 20, right: 20), + alignment: Alignment.centerLeft, + child: Theme( + data: ThemeData( + canvasColor: Colors.white, + primaryColor: Colors.indigo, + accentColor: Colors.indigo, + hintColor: Colors.indigo), + child: const Text( + "Error menarik data list cabang!"), + ), + ), + ), + Flexible( + flex: 1, + child: Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + child: const Text( + 'Retry', + style: + TextStyle(color: Colors.indigo), + ), + onPressed: () async { + BlocProvider.of(context) + .add(CabangInit( + userId: prefs.getString( + Keys.loginId), + company: prefs.getString( + Keys.company))); + }, + ), + ], + ), + ), + ) + ], + ), + ), + ); + } + return Container(); + }), + ), + ), + ), + )); + } + + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: () async { + await showDialog( + context: context, + builder: (context) => Material( + color: Colors.white.withOpacity(0.9), + child: Center( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 2, + offset: const Offset(0, 0), // changes position of shadow + ), + ], + borderRadius: BorderRadius.circular(5)), + // height: MediaQuery.of(context).size.height/4.8, + height: 220, + width: MediaQuery.of(context).size.width * 0.75, + child: Column( + children: [ + Flexible( + flex: 3, + child: Container( + padding: const EdgeInsets.only(top: 10, left: 10, right: 10), + alignment: Alignment.center, + decoration: const BoxDecoration( +// color: Colors.indigo, + borderRadius: BorderRadius.only( + topLeft: const Radius.circular(5), + topRight: Radius.circular(5))), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Icon( + Icons.exit_to_app, + size: 70, + color: Colors.indigo, + ), + ], + ), + ), + ), + Flexible( + flex: 1, + child: Container( + padding: const EdgeInsets.only(left: 20, right: 20), + alignment: Alignment.centerLeft, + child: Text( + 'Keluar dari aplikasi?', + style: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 14, + color: Colors.black.withOpacity(0.6)), + )), + ), + Flexible( + flex: 1, + child: Container( + padding: const EdgeInsets.only(bottom: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + child: const Text( + 'Exit', + style: TextStyle(color: Colors.indigo), + ), + onPressed: () async { + Navigator.pop(context); + await locationStream?.cancel(); + exit(0); + }, + ), + TextButton( + child: const Text( + 'Cancel', + style: TextStyle(color: Colors.indigo), + ), + onPressed: () { + Navigator.pop(context); + }, + ) + ], + ), + ), + ) + ], + ), + ), + ), + ), + ); + return false; + }, + child: MultiBlocProvider( + providers: [ + BlocProvider( + create: (BuildContext context) => _stateBloc, + ), + BlocProvider( + create: (BuildContext context) => _getDataBloc, + ), + BlocProvider( + create: (BuildContext context) => _backupBloc, + ), + BlocProvider( + create: (BuildContext context) => _restoreBloc, + ), + BlocProvider( + create: (BuildContext context) => _uploadBloc, + ), + BlocProvider( + create: (BuildContext context) => _processBloc, + ), + BlocProvider( + create: (BuildContext context) => _submitBloc, + ), + ], + child: MultiBlocListener( + listeners: [ + BlocListener( + listener: (context,state){ + if(state is GetUnitLoading){ + setState(() { + isLoading = true; + progressDL= state.percent; + }); + } + else if(state is GetUnitFinish){ + setState(() { + isLoading = false; + progressDL= null; + }); + if(state.success){ + setState(() { + lastDownload = prefs.getString(Keys.lastDownload); + }); + util.showFlushbar(context, state.respond); + _stateBloc.add(const LoadState()); + } + else util.showFlushbar(context, state.respond,color: Colors.red); + } + }, + ), + BlocListener( + listener: (context,states){ + setState(() { + isLoading = (states is StateLoading); + }); + if(states is LoadFailed){ + util.showFlushbar(context, states.err,color: Colors.red); + } + else if(states is LoadSuccess){ + setState(() { + state = states.state; + }); + } + }, + ), + BlocListener( + listener: (context,state){ + setState(() { + isLoading = (state is BackupLoading); + }); + if(state is BackupFinish){ + util.showFlushbar(context, state.msg,color: state.success?Colors.grey:Colors.red); + } + }, + ), + BlocListener( + listener: (context,state){ + setState(() { + isLoading = (state is RestoreLoading); + }); + if(state is RestoreFinish){ + util.showFlushbar(context, state.msg,color: state.success?Colors.grey:Colors.red); + setState(() { + lastDownload = prefs.getString(Keys.lastDownload); + }); + _stateBloc.add(const LoadState()); + } + }, + ), + BlocListener( + listener: (context,state){ + setState(() { + isLoading = (state is UploadLoading); + }); + if(state is UploadFinish){ + util.showFlushbar(context, state.msg,color: state.success?Colors.grey:Colors.red); + if(state.success){ + setState(() { + lastUpload = prefs.getString(Keys.lastUpload); + }); + } + _stateBloc.add(const LoadState()); + } + }, + ), + BlocListener( + listener: (context,state){ + setState(() { + isLoading = (state is ProcessLoading); + }); + if(state is ProcessLoading){ + util.showLoading(context); + } + else if(state is ProcessFinish){ + Navigator.pop(context); + util.showFlushbar(context, state.msg,color: state.success?Colors.grey:Colors.red); + _stateBloc.add(const LoadState()); + } + }, + ), + BlocListener( + listener: (context,state){ + setState(() { + isLoading = (state is SubmitLoading); + }); + if(state is SubmitLoading){ + util.showLoading(context); + } + else if(state is SubmitFinish){ + Navigator.pop(context); + util.showFlushbar(context, state.msg,color: state.success?Colors.grey:Colors.red); + _stateBloc.add(const LoadState()); + } + }, + ) + ], + child: Scaffold( + body: Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Container( + padding: const EdgeInsets.only(bottom: 0, left: 15, right: 10, top: 10), + height: MediaQuery.of(context).size.height / 13, + width: MediaQuery.of(context).size.width, + alignment: Alignment.bottomLeft, + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Home', + style: TextStyle( + color: Colors.indigo.withOpacity(0.8), + fontSize: 18, + fontWeight: FontWeight.bold), + ), + PopupMenuButton( + enabled: !isLoading, + child: Padding( + padding: const EdgeInsets.only(right: 2.0), + child: Icon( + Icons.menu, + color: Colors.indigo.withOpacity(0.8), + ), + ), + itemBuilder: (context) { + return [ + // PopupMenuItem( + // value:'hostChange', + // child: Row( + // children: [ + // Icon(Icons.network_wifi,color: Colors.black,), + // SizedBox(width: 10,), + // Text('Change Ip Address') + // ], + // ), + // ), + PopupMenuItem( + value: 'cabangChange', + child: Row( + children: const [ + Icon( + Icons.business, + color: Colors.black, + ), + SizedBox( + width: 10, + ), + Text('Change Cabang') + ], + ), + ), + PopupMenuItem( + value:'backup', + child: Row( + children: [ + const Icon(Icons.save,color: Colors.black,), + const SizedBox(width: 10,), + const Text('Backup Data') + ], + ), + ), + PopupMenuItem( + value: 'restore', + child: Row( + children: const [ + Icon( + Icons.sync, + color: Colors.black, + ), + SizedBox( + width: 10, + ), + Text('Restore Backup') + ], + ), + ), + PopupMenuItem( + value: 'logout', + child: Row( + children: const [ + Icon( + Icons.exit_to_app, + color: Colors.black, + ), + SizedBox( + width: 10, + ), + Text('Logout') + ], + ), + ), + ]; + }, + onSelected: (value) async { +// if(value == 'hostChange'){ +// hostAddress.text = prefs.getString(Keys.hostAddress); +// await showDialog(context: context,builder: (context)=> +// Material( +// color: Colors.white.withOpacity(0.9), +// child: Center( +// child: Container( +// decoration: BoxDecoration( +// color: Colors.white, +// boxShadow: [ +// BoxShadow( +// color: Colors.grey.withOpacity(0.5), +// spreadRadius: 2, +// blurRadius: 2, +// offset: Offset(0, 0), // changes position of shadow +// ), +// ], +// borderRadius: BorderRadius.circular(5) +// ), +// height:220, +// // height: MediaQuery.of(context).size.height/3.2, +// width: MediaQuery.of(context).size.width*0.75, +// child: Column( +// children: [ +// Flexible( +// flex:3, +// child: Container( +// padding: EdgeInsets.only(top:10,left: 10,right: 10), +// alignment: Alignment.center, +// decoration: BoxDecoration( +// // color: Colors.indigo, +// borderRadius: BorderRadius.only(topLeft: Radius.circular(5),topRight: Radius.circular(5)) +// ), +// child: Column( +// mainAxisSize: MainAxisSize.min, +// crossAxisAlignment: CrossAxisAlignment.center, +// children: [ +// Icon(Icons.network_wifi,size: 70,color: Colors.indigo,), +// SizedBox(height: 5,), +// Text('Set Ip Address',style: TextStyle(fontWeight: FontWeight.bold,fontSize: 16,color: Colors.indigo),) +// ], +// ), +// ), +// ), +// Flexible( +// flex: 1, +// child: Container( +// padding: EdgeInsets.only(left: 20,right: 20), +// alignment: Alignment.centerLeft, +// child: Theme( +// data: ThemeData( +// canvasColor: Colors.white, +// primaryColor: Colors.indigo, +// accentColor: Colors.indigo, +// hintColor: Colors.indigo +// ), +// child: TextField( +// controller: hostAddress, +// decoration: const InputDecoration( +// contentPadding: EdgeInsets.all(8), +// ), +// onSubmitted: (value){ +// prefs.setString(Keys.hostAddress,(value=='')?'https://tbg.thamringroup.web.id/ords/tbs/unit/v1':value); +// Navigator.pop(context); +// }, +// ), +// ), +// ), +// ), +// Flexible( +// flex: 1, +// child: Container( +// child: Row( +// mainAxisAlignment: MainAxisAlignment.end, +// children: [ +// TextButton( +// child: Text('OK',style: TextStyle(color: Colors.indigo),), +// onPressed: (){ +// prefs.setString(Keys.hostAddress,(hostAddress.text=='')?'https://tbg.thamringroup.web.id/ords/tbs/unit/v1':hostAddress.text); +// Navigator.pop(context); +// }, +// ) +// ], +// ), +// ), +// ) +// ], +// ), +// ), +// ), +// ) +// ); +// } + if (value == "cabangChange") { + var res = await changeCabang(context); + if (res ?? false) { + setState(() {}); + } + } + if (value == 'logout') { + await showDialog( + context: context, + builder: (context) => Material( + color: Colors.white.withOpacity(0.9), + child: BlocProvider( + create: (_) => LogoutBloc(), + child: Center( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey + .withOpacity(0.5), + spreadRadius: 2, + blurRadius: 2, + offset: const Offset(0, + 0), // changes position of shadow + ), + ], + borderRadius: + BorderRadius.circular(5)), + height: 220, + // height: MediaQuery.of(context).size.height/3.2, + width: + MediaQuery.of(context).size.width * + 0.75, + child: BlocBuilder( + bloc: LogoutBloc(), + builder: (context, state) { + final blocLogout = + BlocProvider.of( + context); + if (state is LogoutLoading) { + return const Center( + child: SizedBox( + height: 10, + width: 10, + child: + CircularProgressIndicator(), + ), + ); + } + return Column( + children: [ + Flexible( + flex: 3, + child: Container( + padding: const EdgeInsets.only( + top: 10, + left: 10, + right: 10), + alignment: Alignment.center, + decoration: const BoxDecoration( + borderRadius: + BorderRadius.only( + topLeft: Radius + .circular( + 5), + topRight: Radius + .circular( + 5))), + child: Column( + mainAxisSize: + MainAxisSize.min, + crossAxisAlignment: + CrossAxisAlignment + .center, + children: const [ + Icon( + Icons.logout, + size: 70, + color: Colors.indigo, + ), + SizedBox( + height: 5, + ), + Text( + 'Logout', + style: TextStyle( + fontWeight: + FontWeight + .bold, + fontSize: 16, + color: Colors + .indigo), + ) + ], + ), + ), + ), + Flexible( + flex: 1, + child: Container( + padding: const EdgeInsets.only( + left: 20, right: 20), + alignment: + Alignment.centerLeft, + child: const Text( + "Kembali ke halaman login?"), + ), + ), + Flexible( + flex: 1, + child: Container( + child: Row( + mainAxisAlignment: + MainAxisAlignment.end, + children: [ + TextButton( + child: const Text( + 'Logout', + style: TextStyle( + color: Colors + .indigo), + ), + onPressed: () { + blocLogout.add( + LogoutInitiate( + context: + context)); + }, + ), + TextButton( + child: const Text( + 'Cancel', + style: TextStyle( + color: Colors + .indigo), + ), + onPressed: () { + Navigator.pop(context); + }, + ) + ], + ), + ), + ) + ], + ); + }, + ), + ), + ), + ), + )); + } + if(value == 'backup'){ + if(lastDownload != null && lastDownload != '' ){ + bool? isBackup = await showDialog(context: context,builder: (context)=>AlertDialog( + title: const Text('Backup Data ?'), + content: const Text('Backup data akan menghapus dan mengganti data backup unit dan cabang sebelumnya.'), + actions: [ + TextButton( + child: const Text('OK'), + onPressed: ()async{ + Navigator.pop(context,true); + }, + ), + TextButton( + child: const Text('Cancel'), + onPressed: ()=>Navigator.pop(context,false), + ) + ], + )); + if(isBackup??false){ + _backupBloc.add(BackupInit(context: context)); + } + } + else{ + util.showFlushbar(context, "Data Unit cabang tidak ditemukan. Silakan get data terlbih dahulu."); + } + } + if(value == 'restore'){ + if(prefs.getString(Keys.stockId) == null){ + util.showFlushbar(context, "Data Unit cabang tidak ditemukan. Silakan get data terlbih dahulu."); + } + else{ + if(prefs.getString(Keys.stockId) == prefs.getString(Keys.backupStockId)){ + // _stateBloc.add(LoadState()); + // state di load waktu get data + if(['Open','Submitted'].contains(state)){ + bool? isRestore = await showDialog(context: context,builder: (context)=>AlertDialog( + title: const Text('Restore Data ?'), + content: const Text('Restore data akan menghapus dan mengganti data unit dan cabang yang ada dengan data backup.'), + actions: [ + TextButton( + child: const Text('OK'), + onPressed: ()async{ + Navigator.pop(context,true); + }, + ), + TextButton( + child: const Text('Cancel'), + onPressed: ()=>Navigator.pop(context,false), + ) + ], + )); + if(isRestore??false){ + bool? isclear; + // check exist + Directory? documentsDirectory = await getExternalStorageDirectory(); + String path = paths.join(documentsDirectory!.path, "UnitStocking.db"); + File db = File(path); + if(db.existsSync()){ + isclear = await clearData(context); + } + else{ + isclear = true; + } + if(isclear??false){ + _restoreBloc.add(RestoreInit(context: context)); + } + } + } + else{ + util.showFlushbar(context, "Stocking Unit sudah selesai dan tidak perlu di restore"); + } + } + else { + util.showFlushbar(context, "File Backup tidak ditemukan atau sudah selesai"); + } + } + } + // if (value == 'restore') { + // await showDialog(context: context,builder: (context)=>BlocProvider( + // create: (_)=>RestoreBloc(), + // child: BlocBuilder( + // builder: (context,restoreState) { + // if(restoreState is RestoreLoading){ + // return const Center( + // child: SizedBox( + // height: 10, + // width: 10, + // child: + // CircularProgressIndicator(), + // ), + // ); + // } + // else if(restoreState is RestoreSuccess){ + // Navigator.pop(context); + // setState(() { + // lastUpload = ''; + // lastDownload = ''; + // timeString = ''; + // }); + // } + // return AlertDialog( + // title: Text((restoreState is RestoreFailed)?"Error":'Restore Data ?'), + // content: Text((restoreState is RestoreFailed)?"${restoreState.err}. Ulangi?":'Restore data akan menghapus dan mengganti data unit yang ada dengan data unit backup.'), + // actions: [ + // TextButton( + // child: const Text('OK'), + // onPressed: ()async{ + // BlocProvider.of(context).add(const RestoreInit()); + // }, + // ), + // TextButton( + // child: const Text('Batal'), + // onPressed: ()=>Navigator.pop(context), + // ) + // ], + // ); + // } + // ), + // )); + // } + }, + ), + ], + ), + ), + (prefs.getString(Keys.stockId) == null) + ? Container() + : Padding( + padding: const EdgeInsets.only(top: 8, right: 8), + child: Container( + decoration: BoxDecoration( + color: Colors.indigo.withOpacity(0.9), + borderRadius: BorderRadius.circular(8) +// border: Border.all(), + ), + padding: const EdgeInsets.all(8), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Text( + 'Status : ', + style: TextStyle(color: Colors.white), + ), + Text( + state ?? '-', + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.white), + ), + ], + ), + ), + ), + Expanded( + child: Container( + child: + SmartRefresher( + controller: _refreshController, + enablePullDown: !(isLoading), + onRefresh: (){ + if(prefs.getString(Keys.stockId)!= null){ + _stateBloc.add(const LoadState()); + } + else { + util.showFlushbar(context, "Silakan tarik data unit terlebih dulu"); + } + _refreshController.refreshCompleted(); + }, + child: GridView.count( + padding: const EdgeInsets.all(10), + crossAxisSpacing: 10, + mainAxisSpacing: 10, + crossAxisCount: 2, + children: [ + AbsorbPointer( + absorbing: isLoading, + child: InkWell( + splashColor: Colors.black, + onTap: () async { + bool? getData = await showDialog( + context: context, + builder: (context)=>AlertDialog( + title: const Text("Get Data Units?"), + content: const Text('Fetch data unit for stocking'), + actions: [ + TextButton( + child: const Text('Proceed'), + onPressed: (){ + Navigator.pop(context,true); + }, + ), + TextButton( + child: const Text('Cancel'), + onPressed: (){ + Navigator.pop(context,false); + }, + ) + ], + ) + ); + if(getData??false){ + setState(() { + isLoading = true; + }); + // var a = Stopwatch(); + // a.start(); + await Future.delayed(const Duration(milliseconds: 200)); + bool? isclear; + Directory? documentsDirectory = await getExternalStorageDirectory(); + String path = paths.join(documentsDirectory!.path, "UnitStocking.db"); + File db = File(path); + if(db.existsSync()){ + isclear = await clearData(context); + } + else{ + isclear = true; + } + if(isclear??false){ + _getDataBloc.add(GetUnitInit()); + } + } + }, + child: Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 2, + offset: + const Offset(0, 0), // changes position of shadow + ), + ], + borderRadius: const BorderRadius.all(const Radius.circular(5.0)), + color: Colors.green.withAlpha(220).withOpacity(0.7), + ), + child: Stack( + children: [ + Container( + alignment: (progressDL != null) + ? Alignment.bottomCenter + : Alignment.bottomLeft, + padding: const EdgeInsets.only( + left: 10, right: 10, top: 10, bottom: 15), + child: (progressDL != null) + ? Column( + mainAxisSize: MainAxisSize.min, + children: [ + LinearProgressIndicator( + value: progressDL, + ), + Text( + '${(progressDL! * 100).floor()}%', + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold), + ) + ], + ) + : (lastDownload == '') + ? null + : Text( + 'Last download : ${DateFormat('dd MMM yyyy HH:mm').format(DateTime.parse(lastDownload))}', + style: const TextStyle( + color: Colors.white, + fontSize: 11, + fontWeight: FontWeight.bold)), + ), + Positioned.fill( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.file_upload, + size: MediaQuery.of(context).size.width / 6, + color: Colors.white, + ), + const SizedBox( + height: 5, + ), + const Text( + 'Get Data Master', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16), + ), + ], + ), + ), + ], + ), + ), + ), + ), + InkWell( + splashColor: Colors.black, + onTap: () async { + await Future.delayed(const Duration(milliseconds: 200)); + if (lastDownload != '') { + Navigator.pushNamed(context, '/units'); + } else { + util.showFlushbar(context, + 'Data Master tidak ditemukan. Get data master dulu.'); + } + }, + child: Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 2, + offset: const Offset(0, 0), // changes position of shadow + ), + ], + borderRadius: const BorderRadius.all(const Radius.circular(5.0)), + color: Colors.blueGrey.withAlpha(230).withOpacity(0.8), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.receipt, + size: MediaQuery.of(context).size.width / 6, + color: Colors.white, + ), + const SizedBox( + height: 5, + ), + const Text( + 'Stocking', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16), + ) + ], + ), + ), + ), + InkWell( + splashColor: Colors.black, + onTap: () async { + _uploadBloc.add(Upload(context: context)); + }, + child: Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 2, + offset: const Offset(0, 0), // changes position of shadow + ), + ], + borderRadius: const BorderRadius.all(const Radius.circular(5.0)), + color: Colors.indigo.withAlpha(220).withOpacity(0.7), + ), + child: Stack( + children: [ + BlocBuilder( + bloc: _uploadBloc, + builder: (context, state){ + if(state is UploadLoading){ + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + LinearProgressIndicator( + value: progressUL, + ), + Text( + '${(progressUL ?? 0 * 100).floor()}%', + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold), + ) + ], + ); + } + if(lastUpload != ''){ + return Positioned.fill( + child: Container( + alignment: Alignment.bottomCenter, + child: Text( + 'Last upload : ${DateFormat('dd MMM yyyy HH:mm').format(DateTime.parse(lastUpload))}\n${(timeString != '') ? 'Duration: $timeString' : ''}', + style: const TextStyle( + color: Colors.white, + fontSize: 11, + fontWeight: FontWeight.bold)), + ), + ); + } + return Container(); + }), + Positioned.fill( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.file_upload, + size: MediaQuery.of(context).size.width / 6, + color: Colors.white, + ), + const SizedBox( + height: 5, + ), + const Text( + 'Send Data Stokan', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16), + ), + ], + ), + ), + ], + ), + ), + ), + (prefs.getString(Keys.targetProccess) == null && + prefs.getBool(Keys.submitProccess) == null) + ? InkWell( + splashColor: Colors.black, + onTap: () async { + await Future.delayed(const Duration(milliseconds: 200)); + await clearData(context); + }, + child: Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 2, + offset: const Offset( + 0, 0), // changes position of shadow + ), + ], + borderRadius: + const BorderRadius.all(Radius.circular(5.0)), + color: Colors.red.withAlpha(200).withOpacity(0.7), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.restore_from_trash, + size: MediaQuery.of(context).size.width / 6, + color: Colors.white, + ), + const SizedBox( + height: 5, + ), + const Text( + 'Clear Data', + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16), + ) + ], + ), + ), + ) + : (prefs.getString(Keys.targetProccess) != null) + ? InkWell( + onTap: () async { + bool result = await showDialog( + context: context, + builder: (context) => WillPopScope( + onWillPop: () async { + Navigator.pop(context, false); + return false; + }, + child: AlertDialog( + title: const Text('Process Data ?'), + content: const Text( + 'Proceed to unpack the uploaded data.'), + actions: [ + TextButton( + child: const Text('Proceed'), + onPressed: () async { + Navigator.pop(context, true); + }, + ), + TextButton( + child: const Text('Cancel'), + onPressed: () { + Navigator.pop(context, false); + }, + ) + ], + ), + )); + if (result) { + _processBloc.add(ProcessUnit()); + } + }, + child: Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 2, + offset: const Offset( + 0, 0), // changes position of shadow + ), + ], + borderRadius: + const BorderRadius.all(const Radius.circular(5.0)), + color: Colors.orangeAccent + .withAlpha(230) + .withOpacity(0.7), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.arrow_forward, + size: + MediaQuery.of(context).size.width / 6, + color: Colors.white, + ), + const SizedBox( + height: 5, + ), + const Text( + 'Process Units', + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16), + ) + ], + ), + ), + ) + : InkWell( + onTap: () async { + bool result = await showDialog(context: context,builder: (context)=>WillPopScope( + onWillPop: ()async{ + Navigator.pop(context,false); + return false; + }, + child: AlertDialog( + title: const Text('Submit Data ?'), + content: const Text('Submit the uploaded data.'), + actions: [ + TextButton( + child: const Text('Submit'), + onPressed: ()async{ + Navigator.pop(context,true); + }, + ), + TextButton( + child: const Text('Cancel'), + onPressed: (){Navigator.pop(context,false);}, + ) + ], + ), + )); + if(result){ + _submitBloc.add(Submit()); + } + }, + child: Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 2, + offset: const Offset( + 0, 0), // changes position of shadow + ), + ], + borderRadius: + const BorderRadius.all(const Radius.circular(5.0)), + color: Colors.lightGreen.withAlpha(230), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.arrow_forward, + size: + MediaQuery.of(context).size.width / 6, + color: Colors.white, + ), + const SizedBox( + height: 5, + ), + const Text( + 'Submit Process', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16), + ) + ], + ), + ), + ), + (prefs.getString(Keys.targetProccess) != null || + (prefs.getBool(Keys.submitProccess) != null && + prefs.getBool(Keys.submitProccess))) + ? InkWell( + splashColor: Colors.black, + onTap: () async { + await Future.delayed(const Duration(milliseconds: 200)); + await clearData(context); + }, + child: Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 2, + offset: const Offset( + 0, 0), // changes position of shadow + ), + ], + borderRadius: + const BorderRadius.all(const Radius.circular(5.0)), + color: Colors.red.withAlpha(200), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.restore_from_trash, + size: MediaQuery.of(context).size.width / 6, + color: Colors.white, + ), + const SizedBox( + height: 5, + ), + const Text( + 'Clear Data', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16), + ) + ], + ), + ), + ) + : Container(), + ], + ), + ), + ), + ), + ], + ), + bottomSheet: Container( + padding: const EdgeInsets.only(bottom: 15, right: 10), + height: 58, + alignment: Alignment.bottomRight, + child: Opacity( + opacity: 0.5, + child: Padding( + padding: const EdgeInsets.all(5), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + (lastDownload != '') + ? 'Master Data Downloaded' + : 'No Master Data Found', + style: const TextStyle(fontSize: 12), + ), + (currentVersion!="")?Text("ver. $currentVersion",style: const TextStyle(fontWeight: FontWeight.bold,fontSize: 12),):Container() + ] + )))), + ), + ), + ), + ); + } + + @override + void didPushNext() async { + //pushed from home + if(prefs.getString(Keys.stockId)!= null){ + _stateBloc.add(const LoadState()); + } + } + + @override + void didPopNext() async { + //popped to home + if(prefs.getString(Keys.stockId)!= null){ + _stateBloc.add(const LoadState()); + } + } + @override + void didChangeDependencies() { + super.didChangeDependencies(); + routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute); + } + + @override + void dispose() { + _stateBloc.close(); + _getDataBloc.close(); + _backupBloc.close(); + _restoreBloc.close(); + _uploadBloc.close(); + _processBloc.close(); + _submitBloc.close(); + routeObserver.unsubscribe(this); + super.dispose(); + } +} diff --git a/lib/Pages/login_page.dart b/lib/Pages/login_page.dart new file mode 100644 index 0000000..de732c1 --- /dev/null +++ b/lib/Pages/login_page.dart @@ -0,0 +1,451 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:unitstocks/Blocs/Cabang/cabang_bloc.dart'; +import 'package:unitstocks/Utils/keys.dart'; +import '../Blocs/Login/login_bloc.dart'; +import '../main.dart'; +import 'package:permission_handler/permission_handler.dart' as pHandler; +import 'dart:io'; + + + +class LoginPage extends StatefulWidget { + const LoginPage({Key? key}) : super(key: key); + + @override + _LoginPageState createState() => _LoginPageState(); +} + +class _LoginPageState extends State { + final hostAddress = TextEditingController(); + final userCon = TextEditingController(); + final passCon = TextEditingController(); + final focusUser = FocusNode(); + final focusPass = FocusNode(); + final _formKey = GlobalKey(); + late LoginBloc _loginBloc; + late CabangBloc _cabangBloc; + bool _obscure = true; + + @override + void initState() { + _loginBloc = LoginBloc(); + _cabangBloc = CabangBloc(); + WidgetsBinding.instance!.addPostFrameCallback((_) async { + await util.permissionCheck(context,pHandler.Permission.storage,()async{print("storage permit granted!");},customMessage: " untuk menyimpan data backup"); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + var selected; + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (_)=>_loginBloc, + ), + BlocProvider( + create: (_)=>_cabangBloc, + ) + ], + child: MultiBlocListener( + listeners: [ + BlocListener( + bloc: _cabangBloc, + listener: (context,state){ + if (state is CabangFinished){ + Navigator.pushReplacementNamed(context, "/home"); + } + else if (state is CabangError){ + Navigator.pushReplacementNamed(context, "/home"); + util.showFlushbar(context, state.err); + } + }, + ), + BlocListener( + bloc: _loginBloc, + listener: (context,state){ + print(state.toString()); + if(state is LoginFinish){ + if(state.data["SUCCESS"]){ + _loginBloc.add(LoginSuccess(userId: state.data[Keys.loginId],company:state.data[Keys.company])); + } + else { + _loginBloc.add(LoginError(context: context,err: state.data["MESSAGE"])); + } + } + else if (state is LoginFinalize && (prefs.getBool(Keys.loggedIn)??false)){ + if(prefs.getString(Keys.cabangId) !=null && prefs.getString(Keys.cabangId) != ''){ + Navigator.pushReplacementNamed(context, '/home'); + } + else _cabangBloc.add(CabangInit(cabangList: state.cabangList)); + } + }, + ), + ], + child: BlocBuilder( + bloc: _cabangBloc, + builder: (context,cabangState)=>BlocBuilder( + bloc: _loginBloc, + builder: (context,state){ + final bloc = _loginBloc; + final blocCabang = _cabangBloc; + if(state is LoginLoading || (state is LoginFinish && state.data["SUCCESS"] && !(cabangState is CabangDisplay || cabangState is CabangError))) { + return Container( + color: Colors.white, + child: const Center( + child: SizedBox( + height: 10, + width: 10, + child: CircularProgressIndicator(), + ), + ),); + } + else{ + return Scaffold( + resizeToAvoidBottomInset: true, + body: Container( + decoration: const BoxDecoration( + color: Colors.white + ), + child: Stack( + children: [ + WillPopScope( + onWillPop: ()async{ + await showDialog( + context: context, + builder: (context) => Material( + color: Colors.white.withOpacity(0.9), + child: Center( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 2, + offset: Offset(0, 0), // changes position of shadow + ), + ], + borderRadius: BorderRadius.circular(5)), + // height: MediaQuery.of(context).size.height/4.8, + height: 220, + width: MediaQuery.of(context).size.width * 0.75, + child: Column( + children: [ + Flexible( + flex: 3, + child: Container( + padding: EdgeInsets.only(top: 10, left: 10, right: 10), + alignment: Alignment.center, + decoration: BoxDecoration( +// color: Colors.indigo, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(5), + topRight: Radius.circular(5))), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon( + Icons.exit_to_app, + size: 70, + color: Colors.indigo, + ), + ], + ), + ), + ), + Flexible( + flex: 1, + child: Container( + padding: EdgeInsets.only(left: 20, right: 20), + alignment: Alignment.centerLeft, + child: Text( + 'Keluar dari aplikasi?', + style: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 14, + color: Colors.black.withOpacity(0.6)), + )), + ), + Flexible( + flex: 1, + child: Container( + padding: EdgeInsets.only(bottom: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + child: Text( + 'Exit', + style: TextStyle(color: Colors.indigo), + ), + onPressed: () async { + Navigator.pop(context); + await locationStream?.cancel(); + exit(0); + }, + ), + TextButton( + child: Text( + 'Cancel', + style: TextStyle(color: Colors.indigo), + ), + onPressed: () { + Navigator.pop(context); + }, + ) + ], + ), + ), + ) + ], + ), + ), + ), + ), + ); + return false; + }, + child: Positioned.fill( + child: Column( + children: [ + Expanded( + flex: 1, + child: Container(), + ), + Expanded( + flex: 3, + child: Column( + children: const [ + Icon(Icons.input,size: 150,color: Colors.indigo,), + Text('Login',style: TextStyle(fontSize: 25,color: Colors.indigo,fontWeight: FontWeight.bold),), + ], + ), + ), + Expanded( + flex: 3, + child: Form( + key: _formKey, + child: SingleChildScrollView( + child: Column( + children: [ + SizedBox( + width: MediaQuery.of(context).size.width*0.8, + child: TextFormField( + validator: (val)=>val!.isEmpty?"User tidak boleh kosong!":null, + focusNode: focusUser, + controller: userCon, + textInputAction: TextInputAction.next, + style: const TextStyle(color: Colors.indigo,fontSize: 16), + decoration: InputDecoration( + hintText: 'User ID', + contentPadding:const EdgeInsets.only(left: 30,right: 30,top: 20,bottom: 20), + hintStyle: TextStyle(fontSize: 16,color: Colors.indigo.withOpacity(0.7)), + focusedBorder: OutlineInputBorder( + borderSide: const BorderSide( + color: Colors.indigo, + width: 2), + borderRadius: BorderRadius.circular(100) + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.indigo.withOpacity(0.5), + width: 0.5), + borderRadius: BorderRadius.circular(100) + ) + ), + ), + ), + const SizedBox(height: 15,), + SizedBox( + width: MediaQuery.of(context).size.width*0.8, + child: TextFormField( + validator: (val)=>val!.isEmpty?"Password tidak boleh kosong!":null, + focusNode: focusPass, + obscureText: _obscure, + controller: passCon, + textInputAction: TextInputAction.done, + onFieldSubmitted: (val){ + if(_formKey.currentState!.validate()){ + bloc.add(LoginInitiated(email: userCon.text.trim(), password: passCon.text.trim())); + } + }, + style: const TextStyle(color: Colors.indigo,fontSize: 16), + decoration: InputDecoration( + suffixIcon: InkWell( + onTap: (){ + setState(() { + _obscure = !_obscure; + }); + }, + child: Padding(padding: EdgeInsets.only(right: 10),child:Icon((_obscure)?Icons.visibility_off:Icons.visibility,color: Colors.indigo,)), + ), + contentPadding:const EdgeInsets.only(left: 30,right: 30,top: 20,bottom: 20), + hintText: 'Password', + hintStyle: TextStyle(fontSize: 16,color: Colors.indigo.withOpacity(0.7)), + focusedBorder: OutlineInputBorder( + borderSide: const BorderSide( + color: Colors.indigo, + width: 2), + borderRadius: BorderRadius.circular(100) + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.indigo.withOpacity(0.5), + width: 0.5), + borderRadius: BorderRadius.circular(100) + ) + ), + ), + ), + const SizedBox(height: 30,), + TextButton( + onPressed: (){ + if(_formKey.currentState!.validate()){ + bloc.add(LoginInitiated(email: userCon.text.trim(), password: passCon.text.trim())); + } + }, + style: TextButton.styleFrom( + backgroundColor: Colors.indigo, + padding: EdgeInsets.all(15), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(100.0), + side: BorderSide(color: Colors.indigo) + ), + ), + child: SizedBox(width: MediaQuery.of(context).size.width*0.7,child: Text('Login',textAlign: TextAlign.center,style: TextStyle(color: Colors.white,fontSize: 21),)), + ) + ], + ), + ), + ), + ), + ], + ), + ), + ), + (cabangState is CabangDisplay) + ? Positioned.fill( + child: Container( + color: Colors.white.withOpacity(0.9), + child: Center( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 5, + blurRadius: 7, + offset: Offset(0, 3), // changes position of shadow + ), + ], + borderRadius: BorderRadius.circular(5) + ), + height: MediaQuery.of(context).size.height/3.2, + width: MediaQuery.of(context).size.width*0.75, + child: Column( + children: [ + Flexible( + flex:3, + child: Container( + padding: EdgeInsets.only(top:10,left: 10,right: 10), + alignment: Alignment.center, + decoration: const BoxDecoration( + borderRadius: BorderRadius.only(topLeft: Radius.circular(5),topRight: Radius.circular(5)) + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: const [ + Icon(Icons.business,size: 70,color: Colors.indigo,), + SizedBox(height: 5,), + Text('Pilih Cabang',style: TextStyle(fontWeight: FontWeight.bold,fontSize: 16,color: Colors.indigo),) + ], + ), + ), + ), + Flexible( + flex: 1, + child: Container( + padding: EdgeInsets.only(left: 20,right: 20), + alignment: Alignment.centerLeft, + child: Theme( + data: ThemeData( + canvasColor: Colors.white, + primaryColor: Colors.indigo, + hintColor: Colors.indigo + ), + child: DropdownButtonFormField( + style: TextStyle(color: Colors.black.withOpacity(0.6),fontWeight: FontWeight.w500,fontSize: 14,), + decoration: const InputDecoration( + contentPadding: EdgeInsets.all(8.0), + ), + value: selected??cabangState.cabangList[0]["RETURN_VALUE"], + onChanged: (value){ + selected = value; + }, + items: cabangState.cabangList.map((item)=>DropdownMenuItem( + value: item['RETURN_VALUE'], + child: Text((item["DISPLAY_VALUE"].length>20)?item["DISPLAY_VALUE"].substring(0,20)+"...":item["DISPLAY_VALUE"]), + )).toList(), + ), + ), + ), + ), + Flexible( + flex: 1, + child: Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ButtonTheme( + minWidth: 10, + child: TextButton( + onPressed: (){ + blocCabang.add(CabangPicked(cabangId: selected??cabangState.cabangList[0]["RETURN_VALUE"])); + }, + child: const Text('Set',style: TextStyle(color: Colors.indigo),), + ), + ), + TextButton( + onPressed: (){ + blocCabang.add(CabangCanceled()); + bloc.add(LoginError(context: context,err: '')); + }, + child: const Text('Cancel',style: TextStyle(color: Colors.indigo)), + ) + ], + ), + ), + ) + ], + ), + ), + ), + ) + ) + : Container(), + ], + ), + ), + ); + } + }, + ), + ), + ), + ); + } + @override + void dispose() { + _cabangBloc.close(); + _loginBloc.close(); + super.dispose(); + } +} diff --git a/lib/Pages/unit_details.dart b/lib/Pages/unit_details.dart new file mode 100644 index 0000000..ac68976 --- /dev/null +++ b/lib/Pages/unit_details.dart @@ -0,0 +1,833 @@ +import 'dart:typed_data'; +import '../Model/unit.dart'; +import '../Widgets/photo_viewer.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:image_picker/image_picker.dart'; +import '../Utils/db_helper.dart'; +import '../main.dart'; +import 'package:intl/intl.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import '../Blocs/Units/Images/get_images_bloc.dart'; +import '../Blocs/Units/Save/save_unit_bloc.dart'; + + +class UnitDetails extends StatefulWidget { + Unit unit; + UnitDetails(this.unit); + @override + _UnitDetailsState createState() => _UnitDetailsState(); +} + +class _UnitDetailsState extends State { + final tipeController = TextEditingController(); + final rangkaController = TextEditingController(); + final colorController = TextEditingController(); + final kodeController = TextEditingController(); + final mesinController = TextEditingController(); + final tahunController = TextEditingController(); + final stateController = TextEditingController(); + final channelController = TextEditingController(); + + bool isChanged = false; + List images = []; + List blobIds = []; + List deleteBlobIds = []; + List dropMenu = []; + List jenisImages = []; + int jenisLength=0; + late Unit _currentUnit; + late GetImagesBloc _unitImagesBloc; + late SaveUnitBloc _saveUnitBloc; + List blobList = []; + + loadUnitDetails(){ + _currentUnit = widget.unit; + tipeController.text= _currentUnit.tipe; + rangkaController.text = _currentUnit.rangka; + colorController.text = _currentUnit.warna; + kodeController.text = _currentUnit.kode; + mesinController.text = _currentUnit.mesin; + tahunController.text = _currentUnit.tahun; + stateController.text = _currentUnit.state; + channelController.text = (_currentUnit.channel==null||_currentUnit.channel==""||_currentUnit.channel=="null")?'-':_currentUnit.channel; + _unitImagesBloc.add(GetImages(mesin:mesinController.text)); + } + + @override + void dispose() { + super.dispose(); + _unitImagesBloc.close(); + _saveUnitBloc.close(); + } + + @override + void initState() { + super.initState(); + _unitImagesBloc = GetImagesBloc(); + _saveUnitBloc = SaveUnitBloc(); + } + @override + Widget build(BuildContext context) { + return Scaffold( + resizeToAvoidBottomInset: true, + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0.0, + iconTheme: IconThemeData( + color: Colors.indigo, + ), + title: Text((widget.unit.flag=='FALSE')?'Inserting New Unit':'Editing Unit',style: TextStyle(color: Colors.indigo),), + ), + body: WillPopScope( + onWillPop: ()async{ + if(widget.unit.flag=='TRUE'){ + if(isChanged){ + await showDialog(context: context,builder: (context)=> + Material( + color: Colors.white.withOpacity(0.9), + child: Center( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 2, + offset: Offset(0, 0), // changes position of shadow + ), + ], + borderRadius: BorderRadius.circular(5) + ), + height:220, + // height: MediaQuery.of(context).size.height/4.8, + width: MediaQuery.of(context).size.width*0.75, + child: Column( + children: [ + Flexible( + flex:3, + child: Container( + padding: EdgeInsets.only(top:10,left: 10,right: 10), + alignment: Alignment.center, + decoration: BoxDecoration( +// color: Colors.indigo, + borderRadius: BorderRadius.only(topLeft: Radius.circular(5),topRight: Radius.circular(5)) + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon(Icons.warning,size: 70,color: Colors.indigo,), + ], + ), + ), + ), + Flexible( + flex: 1, + child: Container( + padding: EdgeInsets.only(left: 20,right: 20), + alignment: Alignment.centerLeft, + child: Text('Data belum disimpan. Lanjutkan ?',style: TextStyle(fontWeight: FontWeight.w500,fontSize: 14,color: Colors.black.withOpacity(0.6)),) + ), + ), + Flexible( + flex: 1, + child: Container( + padding: EdgeInsets.only(bottom:10), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + child: Text('Proceed'), +// color: Colors.red, + onPressed: (){ + Navigator.pop(context); + Navigator.pop(context); + }, + ), + TextButton( + child: Text('Cancel'), +// color: Colors.grey, + onPressed: (){ + Navigator.pop(context); + }, + ), + ], + ), + ), + ) + ], + ), + ), + ), + ), + ); + } + else Navigator.pop(context); + } + else{ + bool not_empty = (stateController.text!='-'&&mesinController.text!='-'&&tahunController.text!='-'&&kodeController.text!='-'&&colorController.text!='-'&&rangkaController.text!='-'&&tipeController.text!='-'); + if(blobList.where((element) => element.bytes!=null).length!=0){ + await showDialog(context: context,builder: (context)=> + Material( + color: Colors.white.withOpacity(0.9), + child: Center( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 2, + offset: Offset(0, 0), // changes position of shadow + ), + ], + borderRadius: BorderRadius.circular(5) + ), + height:220, + // height: MediaQuery.of(context).size.height/4.8, + width: MediaQuery.of(context).size.width*0.75, + child: Column( + children: [ + Flexible( + flex:3, + child: Container( + padding: EdgeInsets.only(top:10,left: 10,right: 10), + alignment: Alignment.center, + decoration: BoxDecoration( +// color: Colors.indigo, + borderRadius: BorderRadius.only(topLeft: Radius.circular(5),topRight: Radius.circular(5)) + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon(Icons.warning,size: 70,color: Colors.indigo,), + ], + ), + ), + ), + Flexible( + flex: 1, + child: Container( + padding: EdgeInsets.only(left: 20,right: 20), + alignment: Alignment.centerLeft, + child: Text('Data belum disimpan. Lanjutkan ?',style: TextStyle(fontWeight: FontWeight.w500,fontSize: 14,color: Colors.black.withOpacity(0.6)),) + ), + ), + Flexible( + flex: 1, + child: Container( + padding: EdgeInsets.only(bottom:10), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + child: Text('Back',style: TextStyle(color: Colors.indigo),), + onPressed: (){ + Navigator.pop(context); + Navigator.pop(context); + }, + ), + TextButton( + child: Text('Cancel',style: TextStyle(color: Colors.indigo)), + onPressed: (){ + Navigator.pop(context); + }, + ), + ], + ), + ), + ) + ], + ), + ), + ), + ), + ); + } + else Navigator.pop(context); + } + return false; + }, + child: MultiBlocProvider( + providers: [ + BlocProvider( + create: (_)=>_unitImagesBloc, + ), + BlocProvider( + create: (_)=>_saveUnitBloc, + ), + ], + child:MultiBlocListener( + listeners: [ + BlocListener( + listener: (context,state){ + if(state is GetImagesError){ + util.showFlushbar(context, state.err,color: Colors.red); + } + }, + ), + BlocListener( + listener: (context,state){ + if(state is SaveFinish){ + if(state.success){ + Navigator.pop(context,true); + } + util.showFlushbar(context, state.msg,color: state.success?Colors.grey:Colors.red); + } + }, + ), + ], + + child: BlocBuilder( + bloc: _unitImagesBloc, + builder: (context,state){ + if(state is GetImagesLoading){ + return Center( + child: Container( + height: 15, + width: 15, + child: CircularProgressIndicator(), + ), + ); + } + else if (state is GetImagesFinish){ + blobList = state.blobs; + } + else if (state is GetImagesInitial){ + loadUnitDetails(); + } + return Container( + padding: EdgeInsets.only(left:10,right: 10,bottom: 10), + child: Column( + children: [ + Expanded( + child: Row( + children: [ + Flexible( + flex: 4, + child: Container( + height: MediaQuery.of(context).size.height, + child: + ListView.builder( +// itemCount: ((images.length+1)>jenisLength)?jenisLength:images.length+1, + itemCount: blobList.length, + itemBuilder: (context, index){ +// Uint8List image = ((images.length==0)||(images.length<(index+1)))?null:images[index]; + Uint8List? image = blobList[index].bytes; + return Padding( + padding: const EdgeInsets.only(top: 10,right: 10), + child: Material( + child: InkWell( + onTap: ()async{ + if(image==null){ +// var selected = await showDialog( +// context: context, +// builder: (context){ +// var selected = dropMenu[0].value; +// return WillPopScope( +// onWillPop: ()async{ +// Navigator.pop(context,false); +// return false; +// }, +// child: AlertDialog( +// content: StatefulBuilder( +// builder: (context,setState){ +// return DropdownButtonFormField( +// decoration: InputDecoration.collapsed(), +// value: selected, +// onChanged: (value){ +// setState(() { +// selected = value; +// }); +// }, +// items: dropMenu.map((item) { +// return new DropdownMenuItem( +// value: item.value, +// child: Row( +// children: [ +// Text(item.value), +//// (item.optional=='TRUE')?Container():Text(' *',style: TextStyle(color: Colors.red),) +// ], +// ), +// ); +// }).toList(), +// ); +// }, +// ), +// actions: [ +// TextButton( +// child: Text('Done'), +// onPressed: ()async{ +// jenisImages.add(selected); +// +// Navigator.pop(context,true); +// }, +// ), +// TextButton( +// child: Text('Cancel'), +// onPressed: (){ +// Navigator.pop(context,false); +// }, +// ), +// ], +// ), +// ); +// } +// ); +// if (selected){ + XFile? temp = await ImagePicker().pickImage(source: ImageSource.camera,maxWidth: 800,imageQuality: 80); + if(temp!=null){ + isChanged = true; + blobList[index].bytes = await temp.readAsBytes(); +// if(widget.idInsert!=null) { +// blobIds.add(null); +// isChanged = true; +// } + setState(() { + }); + } +// else { +// jenisImages.removeLast(); +// } +// await loadDropMenu(); +// } + } + else { +// List dropNewMenu = new List.from(dropMenu); +// dropNewMenu.add(new ImageType(value: jenisImages[index])); + var result = await Navigator.push(context, MaterialPageRoute(builder: (context) => PhotoViewer(image,id: widget.unit.id.toString()+'.$index',jenis: blobList[index].jenis,))); + if(result!=null){ + blobList[index] = result['byte']; + isChanged = true; +// images[index] = result['byte']; +// jenisImages[index] = result['jenis']; +// await loadDropMenu(); +// if(widget.idInsert!=null) { +// isChanged = true; +// } + setState(() { + }); + } + } + }, + splashColor: Colors.grey, + child: Column( + children: [ + Container(padding: EdgeInsets.all(5),child: Text(blobList[index].jenis)), + Stack( + children: [ + Container( + decoration: BoxDecoration( + color: Colors.grey.withOpacity(0.7), + image: (image!=null)?DecorationImage( + image: MemoryImage(image), + fit: BoxFit.cover + ):null, + ), + height: MediaQuery.of(context).size.width*0.35, + width: MediaQuery.of(context).size.width*0.35, + child: (image==null)?Icon(Icons.add):null, + ), + (image!=null)?Container( + height: MediaQuery.of(context).size.width*0.35, + width: MediaQuery.of(context).size.width*0.35, + alignment: Alignment.topRight, + child: InkWell( + onTap: ()async{ +// if(widget.idInsert!=null){ +// isChanged = true; +// deleteBlobIds.add(blobIds[index]); +// blobIds.removeAt(index); +// } + setState(() { + blobList[index].bytes = null; +// images.removeAt(index); +// jenisImages.removeAt(index); + }); +// await loadDropMenu(); + }, + child: Container( + padding: EdgeInsets.all(5), + child: Icon(Icons.cancel,color: Colors.red,)), + ), + ):Container(), + ], + ), + ], + ), + ), + ), + ); + }) + ), + ), + Flexible( + flex: 6, + child: Container( + alignment: Alignment.topLeft, + padding: EdgeInsets.only(top: 10), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ +// Divider(), +// Row( +// mainAxisAlignment: MainAxisAlignment.spaceBetween, +// children: [ +// Expanded( +// child: +// textfield??Container(), +// ), +// ], +// ), + Padding( + padding: const EdgeInsets.all(5.0), + child: Text('Units Details',style: TextStyle(fontSize: 18,fontWeight: FontWeight.bold),), + ), + Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Tipe : '), + Expanded( + child: + TextFormField( + enabled: false, + style: TextStyle(color: Colors.grey), + controller: tipeController, + decoration: InputDecoration.collapsed(hintText: null), + ), + ), + ], + ), + Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Mesin : '), + Expanded( + child: TextFormField( + enabled: false, + style: TextStyle(color: Colors.grey), + controller: mesinController, + decoration: InputDecoration.collapsed(hintText: null), + ), + ), + ], + ), + Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Rangka : '), + Expanded( + child: TextFormField( + enabled: false, + style: TextStyle(color: Colors.grey), + controller: rangkaController, + decoration: InputDecoration.collapsed(hintText: null), + ), + ), + ], + ), + Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Kode : '), + Expanded( + child: TextFormField( + enabled: false, + style: TextStyle(color: Colors.grey), + controller: kodeController, + decoration: InputDecoration.collapsed(hintText: null), + ), + ), + ], + ), + Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Tahun : '), + Expanded( + child: TextFormField( + enabled: false, + style: TextStyle(color: Colors.grey), + controller: tahunController, + decoration: InputDecoration.collapsed(hintText: null), + ), + ), + ], + ), + Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Warna : '), + Expanded( + child: TextFormField( + enabled: false, + style: TextStyle(color: Colors.grey), + controller: colorController, + decoration: InputDecoration.collapsed(hintText: null), + ), + ), + ], + ), + Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('State : '), + Expanded( + child: TextFormField( + enabled: false, + style: TextStyle(color: Colors.grey), + controller: stateController, + decoration: InputDecoration.collapsed(hintText: null), + ), + ), + ], + ), + Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Channel : '), + Expanded( + child: TextFormField( + enabled: false, + style: TextStyle(color: Colors.grey), + controller: channelController, + decoration: InputDecoration.collapsed(hintText: null), + ), + ), + ], + ), + Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TextButton( + style: TextButton.styleFrom( + backgroundColor: Colors.green, + ), + child: Text((widget.unit.flag=='FALSE')?'Add':'Save'), + onPressed: ()async{ + if(currentPosisiton!=null) { + if(blobList.where((element) => element.bytes!=null).length==blobList.length){ + _saveUnitBloc.add(Save(blobs: blobList, unit: _currentUnit)); + } + else{ + util.showFlushbar(context, 'Tolong lengkapi data foto'); + } + } + else{ + util.showFlushbar(context, 'Gagal mengamil koordinat lokasi. Tolong cek gps.'); + } + }, + + ), + TextButton( + child: Text('Cancel'), + onPressed: ()async { + if(widget.unit.flag=='TRUE'){ + if(isChanged){ + await showDialog(context: context,builder: (context)=> + Material( + color: Colors.white.withOpacity(0.9), + child: Center( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 2, + offset: Offset(0, 0), // changes position of shadow + ), + ], + borderRadius: BorderRadius.circular(5) + ), + height:220, + // height: MediaQuery.of(context).size.height/4.8, + width: MediaQuery.of(context).size.width*0.75, + child: Column( + children: [ + Flexible( + flex:3, + child: Container( + padding: EdgeInsets.only(top:10,left: 10,right: 10), + alignment: Alignment.center, + decoration: BoxDecoration( +// color: Colors.indigo, + borderRadius: BorderRadius.only(topLeft: Radius.circular(5),topRight: Radius.circular(5)) + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon(Icons.warning,size: 70,color: Colors.indigo,), + ], + ), + ), + ), + Flexible( + flex: 1, + child: Container( + padding: EdgeInsets.only(left: 20,right: 20), + alignment: Alignment.centerLeft, + child: Text('Data belum disimpan. Lanjutkan ?',style: TextStyle(fontWeight: FontWeight.w500,fontSize: 14,color: Colors.black.withOpacity(0.6)),) + ), + ), + Flexible( + flex: 1, + child: Container( + padding: EdgeInsets.only(bottom:10), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + child: Text('Proceed'), + onPressed: (){ + Navigator.pop(context); + Navigator.popUntil(context,ModalRoute.withName('/stocking')); + }, + ), + TextButton( + child: Text('Cancel'), +// color: Colors.grey, + onPressed: (){ + Navigator.pop(context); + }, + ), + ], + ), + ), + ) + ], + ), + ), + ), + ), + ); + } + else Navigator.pop(context); + } + else { +// bool not_empty = (stateController.text!='-'&&mesinController.text!='-'&&tahunController.text!='-'&&kodeController.text!='-'&&colorController.text!='-'&&rangkaController.text!='-'&&tipeController.text!='-'); + if(blobList.where((element) => element.bytes!=null).length!=0){ + await showDialog(context: context,builder: (context)=> + Material( + color: Colors.white.withOpacity(0.9), + child: Center( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 2, + offset: Offset(0, 0), // changes position of shadow + ), + ], + borderRadius: BorderRadius.circular(5) + ), + height:220, + // height: MediaQuery.of(context).size.height/4.8, + width: MediaQuery.of(context).size.width*0.75, + child: Column( + children: [ + Flexible( + flex:3, + child: Container( + padding: EdgeInsets.only(top:10,left: 10,right: 10), + alignment: Alignment.center, + decoration: BoxDecoration( +// color: Colors.indigo, + borderRadius: BorderRadius.only(topLeft: Radius.circular(5),topRight: Radius.circular(5)) + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon(Icons.warning,size: 70,color: Colors.indigo,), + ], + ), + ), + ), + Flexible( + flex: 1, + child: Container( + padding: EdgeInsets.only(left: 20,right: 20), + alignment: Alignment.centerLeft, + child: Text('Data belum disimpan. Lanjutkan ?',style: TextStyle(fontWeight: FontWeight.w500,fontSize: 14,color: Colors.black.withOpacity(0.6)),) + ), + ), + Flexible( + flex: 1, + child: Container( + padding: EdgeInsets.only(bottom:10), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + child: Text('Back'), + onPressed: (){ + Navigator.pop(context); + Navigator.popUntil(context,ModalRoute.withName('/stocking')); + }, + ), + TextButton( + child: Text('Cancel'), + onPressed: (){ + Navigator.pop(context); + }, + ), + ], + ), + ), + ) + ], + ), + ), + ), + ), + ); + } + else Navigator.pop(context); + } + }, + style: TextButton.styleFrom( + backgroundColor: Colors.redAccent, + ), + ) + ], + ) + ], + ), + ), + ), + ) + ], + ), + ), + ], + ), + ); + }, + ), + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/Pages/unit_list.dart b/lib/Pages/unit_list.dart new file mode 100644 index 0000000..1365cd6 --- /dev/null +++ b/lib/Pages/unit_list.dart @@ -0,0 +1,393 @@ +import 'dart:async'; +import 'dart:typed_data'; +import 'package:flutter/services.dart'; +import '../main.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'unit_details.dart'; +import '../Widgets/photo_viewer.dart'; +import 'package:barcode_scan2/barcode_scan2.dart'; +import '../Model/unit.dart'; +import '../Utils/db_helper.dart'; +import '../Blocs/Units/List/list_unit_bloc.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class UnitsPage extends StatefulWidget { + UnitsPage({Key? key}) : super(key: key); + + @override + _UnitsState createState() => _UnitsState(); +} + +class _UnitsState extends State with SingleTickerProviderStateMixin,RouteAware { + List unitsRef = []; + List BlobsRef = []; + bool completed = false; + int totalUnit = 0; + final search_controller = new TextEditingController(); + bool hideAppbar = false; + String search = ''; + late ListUnitBloc _unitBloc; + + + Map _timeouts = {}; + void debounce(Duration timeout, Function target, [List arguments = const []]) { + if (_timeouts.containsKey(target)) { + _timeouts[target]?.cancel(); + } + + Timer timer = Timer(timeout, () { + Function.apply(target, arguments); + }); + + _timeouts[target] = timer; + } + + void _onChanged(String val)async { + search = search_controller.text; + _unitBloc.add(ListUnit(search: search,completed: completed)); + } + + Future scan() async { + try { + search = ''; + setState(() => search_controller.text = search); + ScanResult result = await BarcodeScanner.scan(); + String barcode = result.rawContent; + setState(() => this.search = barcode); + } on PlatformException catch (e) { + if (e.code == BarcodeScanner.cameraAccessDenied) { + util.showFlushbar(context, 'The user did not grant the camera permission!',color: Colors.red); + setState(() { + this.search = ''; + }); + } else { + util.showFlushbar(context, 'Unknown error: $e',color: Colors.red); + setState(() => this.search = ''); + } + } on FormatException { + setState(() => this.search = + ''); + } catch (e) { + util.showFlushbar(context, 'Unknown error: $e',color: Colors.red); + setState(() => this.search = ''); + } + setState(() => search_controller.text = search); + } + + @override + void initState() { + super.initState(); + _unitBloc = ListUnitBloc(); + WidgetsBinding.instance!.addPostFrameCallback((_) async { + if(locationStream==null)await util.streamLocation(context); + else { + if(locationStream!.isPaused) locationStream!.resume(); + } + }); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute); + } + + + + @override + void dispose() { + print('LocationStream paused'); + if(locationStream!.isPaused)locationStream!.pause(); + _unitBloc.close(); + routeObserver.unsubscribe(this); + super.dispose(); + + } + + // @override + // void didPushNext() async { + // //pushed from home + // _unitBloc.add(ListUnit(search: search,completed: completed)); + // } + + @override + void didPopNext() async { + //popped to home + _unitBloc.add(ListUnit(search: search,completed: completed)); + } + + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: ()async{ + if(hideAppbar) { + setState(() { + hideAppbar=false; + search = ''; + search_controller.text = ''; + }); + _unitBloc.add(ListUnit(search: search,completed: completed)); + return false; + } + else { + return true; + } + }, + child: Scaffold( + appBar: (hideAppbar)? + AppBar( + backgroundColor: Colors.white, + leading: GestureDetector( + onTap:()async{ + setState(() { + hideAppbar=false; + search = ''; + search_controller.text = ''; + }); + _unitBloc.add(ListUnit(search: search,completed: completed)); + },child: Container(width: 20,child: Icon(Icons.arrow_back,color:Colors.grey))), + title: Container( + color: Colors.white, + child: TextFormField( + maxLines: 1, + controller: search_controller, + onChanged: (val) => debounce(const Duration(milliseconds: 300), _onChanged, [val]), + decoration: InputDecoration.collapsed( + hintText: 'Search..', + ), + ), + ), + actions: [ + TextButton( + onPressed: ()async{ + await scan(); + _unitBloc.add(ListUnit(search: search,completed: completed)); + }, + child: Icon(Icons.select_all,color:Colors.grey), + ) + ], + ) + :null, + body: BlocProvider( + create: (BuildContext context) => _unitBloc, + child: BlocListener( + bloc: _unitBloc, + listener: (context,state){ + if(state is ListUnitError){ + util.showFlushbar(context, state.err,color: Colors.red); + } + }, + child: Column( + children: [ + (hideAppbar)?Container():SizedBox(height: MediaQuery.of(context).size.height/25,), + Expanded( + child:Stack( + children: [ + Container( + alignment: Alignment.topRight, + child: BlocBuilder( + bloc: _unitBloc, + builder: (context, state) { + if (state is ListUnitFinish){ + unitsRef = state.units; + BlobsRef = state.blobs; + } + else if(state is ListUnitInitial){ + _unitBloc.add(ListUnit()); + } + if(state is ListUnitLoading){ + return Center( + child: Container( + height: 15, + width: 15, + child: CircularProgressIndicator(), + ), + ); + } + else{ + return (unitsRef.isEmpty) + ? Container(padding: EdgeInsets.only(top: 10,left: 10,right: 10), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Checkbox( + value: completed, + onChanged: (value)async{ + setState(() { + completed = value??false; + }); + _unitBloc.add(ListUnit(search: search,completed: completed)); + }, + ), + Text('Completed') + ], + ), + (completed||hideAppbar)?Container():Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text('Unit : ',style: TextStyle(color: Colors.grey,fontSize: 15),), + Text('${(unitsRef.where((element) => element.flag=='TRUE').length==unitsRef.length)?'Done':'${unitsRef.length-unitsRef.where((element) => element.flag=='TRUE').length}'}',style: TextStyle(color: (unitsRef.length==unitsRef.where((element) => element.flag=='TRUE').length)?Colors.green:Colors.black,fontWeight: FontWeight.bold,fontSize: 16),), + Text(!(unitsRef.length==unitsRef.where((element) => element.flag=='TRUE').length)?' of':'',style: TextStyle(color: Colors.grey,fontSize: 15),), + Text(!(unitsRef.length==unitsRef.where((element) => element.flag=='TRUE').length)?' ${unitsRef.length}':'',style: TextStyle(color: Colors.black,fontSize: 16,fontWeight: FontWeight.bold,),), + ], + ), + ], + ),) + :ListView.builder( + padding: EdgeInsets.only(bottom: 10), + itemCount: unitsRef.length, + shrinkWrap: false, + itemBuilder: (context,index){ + Uint8List? display; + display = unitsRef[index].flag=='FALSE'?null:(BlobsRef.length==unitsRef.length)?BlobsRef[index]!.bytes:null; + return Column( + children: [ + (index==0)?Container(padding: EdgeInsets.only(top: 10,left: 10,right: 10),alignment: Alignment.centerRight, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Checkbox( + value: completed, + onChanged: (value)async{ + setState(() { + completed = value??false; + }); + _unitBloc.add(ListUnit(search: search,completed: completed)); + }, + ), + Text('Completed') + ], + ), + (completed||hideAppbar)?Container():Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text('Unit : ',style: TextStyle(color: Colors.grey,fontSize: 15),), + Text('${(unitsRef.where((element) => element.flag=='TRUE').length==unitsRef.length)?'Done':'${unitsRef.length-unitsRef.where((element) => element.flag=='TRUE').length}'}',style: TextStyle(color: (unitsRef.length==unitsRef.where((element) => element.flag=='TRUE').length)?Colors.green:Colors.black,fontWeight: FontWeight.bold,fontSize: 16),), + Text(!(unitsRef.length==unitsRef.where((element) => element.flag=='TRUE').length)?' of':'',style: TextStyle(color: Colors.grey,fontSize: 15),), + Text(!(unitsRef.length==unitsRef.where((element) => element.flag=='TRUE').length)?' ${unitsRef.length}':'',style: TextStyle(color: Colors.black,fontSize: 16,fontWeight: FontWeight.bold,),), + ], + ), + ], + ),):Container(), + Padding( + padding: const EdgeInsets.only(left:10,right: 10,top: 10), + child: Container( + padding: EdgeInsets.all(5.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey, + blurRadius: 5.0, // has the effect of softening the shadow + spreadRadius: 2.0, // has the effect of extending the shadow + offset: Offset( + 00.0, // horizontal, move right 10 + 2.0, // vertical, move down 10 + ), + ) + ], + border: Border.all(color: Colors.grey.withOpacity(0.5)), + ), + child:Row( + children: [ + Expanded( + flex: 3, + child: InkWell( + onTap: ()async{ + if(display==null){ + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => UnitDetails(unitsRef[index],), + ), + ); + } + else{ + await Navigator.push(context, MaterialPageRoute(builder: (context) => PhotoViewer(display!,id: unitsRef[index].id.toString(),jenis: '',edit: false,))); + } + }, + child: Hero(tag: unitsRef[index].id,child: Padding( + padding: EdgeInsets.only(right: 10), + child: Container(width: 100,height: 100,alignment: Alignment.center,child:(display==null)?Icon(Icons.crop_original,color: Colors.white,size: 80,):null, + decoration: BoxDecoration( + color: Colors.grey.withOpacity(0.5), + image: (display!=null)?DecorationImage( + image: MemoryImage(display),fit: BoxFit.cover,):null,)), + )), + )), + Expanded( + flex: 7, + child: InkWell( + onTap: ()async{ + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => UnitDetails(unitsRef[index],), + ), + ); + }, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Rangka : ${unitsRef[index].rangka}'), + Divider(height: 10,), + Text('Tipe : ${unitsRef[index].tipe}'), + Divider(height: 10,), + Row( + children: [ + // Container(width: MediaQuery.of(context).size.width*0.35,child: Text('State : ${unitsRef[index].state}')), + // Container(width: MediaQuery.of(context).size.width*0.5,child: Text('Tipe : ${unitsRef[index].tipe}',style: TextStyle(fontSize: 12),)), + Text('Timestamp : ${unitsRef[index].timestamp}'), + ], + ), + Divider(height: 10,), + Row( + children: [ + Container(width: MediaQuery.of(context).size.width*0.35,child: Text('Warna : ${unitsRef[index].warna}')), + Text('State : ${unitsRef[index].state}'), + ], + ) + ], + ), + ), + )), + ], + )), + ), + ], + ); + }, + ); + } + }), + ), + ], + ), + ), + ], + ), + ) + ), + floatingActionButton: FloatingActionButton( + backgroundColor: Colors.indigo.withOpacity(0.8), + onPressed: ()async{ + setState(() { + hideAppbar = !(hideAppbar); + }); + }, + child: Icon(Icons.search,color:Colors.white), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/Utils/db_helper.dart b/lib/Utils/db_helper.dart new file mode 100644 index 0000000..2b06099 --- /dev/null +++ b/lib/Utils/db_helper.dart @@ -0,0 +1,371 @@ +import 'dart:async'; +import 'dart:io' as io; +import '../Model/unit.dart'; +import 'package:path/path.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:sqflite/sqflite.dart'; +import 'keys.dart'; +import '../main.dart'; +import 'package:permission_handler/permission_handler.dart' as pHandler; +import 'dart:typed_data'; + + +class DBHelper { + DBHelper._(); + + static final DBHelper database = DBHelper._(); + static Database? _db; + + Future get db async { + _db ??= await initDb(); + Database database = _db!; + return database; + } + backupDb({context}) async{ + var result = {"STATUS":0,"MSG":"Backup gagal!"}; + var stock_id = prefs.getString(Keys.stockId)??''; + if(stock_id != ''){ + if(context!= null){ + await util.permissionCheck(context,pHandler.Permission.storage,()async{ + try{ + // if(dbPath == null || dbPath == ''){ + final database = await db; + var dbPath = database.path; + await DBHelper.database.closeDb(); + // } + await prefs.remove(Keys.backupStockId); + if(stock_id !='' && stock_id != null ) await prefs.setString(Keys.backupStockId,stock_id); + //backupDatabase + String backupPath = join(dbPath.substring(0,dbPath.indexOf("/Android"))+"/Download", "${prefs.getString(Keys.backupStockId)}_UnitStocking.db"); + io.File backupFile = new io.File(backupPath); + io.File dbFile = io.File(dbPath); + if(dbFile.existsSync()){ + // dbFile.copySync(backupPath); + Uint8List byte = dbFile.readAsBytesSync(); + backupFile.writeAsBytesSync(byte); + result = {"STATUS":1,"MSG":"Backup Berhasil!"}; + } + else{ + result = {"STATUS":0,"MSG":"Backup gagal data unit tidak ditemukan!"}; + print('file backup dont exist tho'); + } + } + catch(e){ + print(e); + result = {"STATUS":0,"MSG":"Backup gagal!"}; + } + },customMessage: " untuk menyimpan data backup"); + } + else{ + try{ + // if(dbPath == null || dbPath == ''){ + final database = await db; + var dbPath = database.path; + await DBHelper.database.closeDb(); + // } + await prefs.remove(Keys.backupStockId); + if(stock_id !='' && stock_id != null ) await prefs.setString(Keys.backupStockId,stock_id); + //backupDatabase + String backupPath = join(dbPath.substring(0,dbPath.indexOf("/Android"))+"/Download", "${prefs.getString(Keys.backupStockId)}_UnitStocking.db"); + io.File backupFile = new io.File(backupPath); + io.File dbFile = io.File(dbPath); + if(dbFile.existsSync()){ + // dbFile.copySync(backupPath); + Uint8List byte = dbFile.readAsBytesSync(); + backupFile.writeAsBytesSync(byte); + result = {"STATUS":1,"MSG":"Backup Berhasil!"}; + } + else{ + result = {"STATUS":0,"MSG":"Backup gagal data unit tidak ditemukan!"}; + print('file backup dont exist tho'); + } + } + catch(e){ + print(e); + result = {"STATUS":0,"MSG":"Backup gagal!"}; + } + } + + } + else{ + result = {"STATUS":0,"MSG":"Backup gagal data unit tidak ditemukan!"}; + } + + return result; + } + + restoreDb(context)async{ + var result = {"STATUS":0,"MSG":"File Backup tidak ditemukan!"}; + if(prefs.getString(Keys.backupStockId)!=null){ + await util.permissionCheck(context,pHandler.Permission.storage,()async{ + io.Directory? documentsDirectory = await getExternalStorageDirectory(); + String backupPath = join(documentsDirectory!.path.substring(0,documentsDirectory.path.indexOf("/Android"))+"/Download", "${prefs.getString(Keys.backupStockId)}_UnitStocking.db"); + io.File backupdbFile = io.File(backupPath); + if(backupdbFile.existsSync()){ + io.File dbFile = new io.File(join(documentsDirectory.path, "UnitStocking.db")); + Uint8List byte = backupdbFile.readAsBytesSync(); + dbFile.writeAsBytesSync(byte); + result = {"STATUS":1,"MSG":"Restore data successfull"}; + await DBHelper.database.closeDb(); + } + else result = {"STATUS":0,"MSG":"File Backup tidak ditemukan!"}; + },customMessage: " untuk menyimpan data backup"); + } + return result; + } + + initDb() async { + io.Directory? documentsDirectory = await getExternalStorageDirectory(); + String path = join(documentsDirectory!.path, "UnitStocking.db"); + var theDb = await openDatabase(path, version: 1,onCreate: _onCreate); + return theDb; + } + + // restoreDb()async{ + // await closeDb(); + // io.Directory documentsDirectory = await getExternalStorageDirectory(); + // String backupPath = join(documentsDirectory.path.substring(0,documentsDirectory.path.indexOf("/Android"))+"/Download/UnitDBBackup/", "${prefs.getString(Keys.stockId)}_UnitStocking.db"); + // io.File backupdbFile = io.File(backupPath); + // if(backupdbFile.existsSync()){ + // String path = join(documentsDirectory.path, "UnitStocking.db"); + // backupdbFile.copySync(path); + // return {"STATUS":1,"MSG":"File Back up successfull"}; + // } + // return {"STATUS":0,"MSG":"File Backup tidak ditemukan!"}; + // } + + closeDb() async { + final database = await db; + if(database.isOpen){ + await database.close(); + _db = null; + } + } + + _onCreate(Database db, int version) async { + io.Directory? documentsDirectory = await getExternalStorageDirectory(); + String path = join(documentsDirectory!.path, "UnitStocking.db"); + io.File file = io.File(path); + print('Database created, ${file.lengthSync()}'); + var res = await db.query(Tables.values,where: "${Columns.name} = ?",whereArgs: [Keys.stockId]); + Value value = Value.fromJson(res.first); + await prefs.setString(Keys.stockId, value.value); + } + + getCount(mesin) async { + final database = await db; + var res = + await database.rawQuery("SELECT COUNT(*) as count FROM ${Tables.blob} where ${Columns.mesin} = '$mesin'"); + return res.isNotEmpty ? Count.fromJson(res.first).count : 0; + } + + getUnitdetailMaster(id) async{ + try{ + final database = await db; + var res = await database.rawQuery("SELECT * FROM '${Tables.master}' WHERE ${Columns.id} = $id"); + return res.isNotEmpty ? Unit.fromJson(res.first) : null; + } + catch(e){ + print(e); + return null; + } + } + + + Future> getAllUnits({inserted=false}) async { + final database = await db; + var res = await database.query("${Tables.master}",where: (inserted)?"${Columns.flag} = 'TRUE'":null,orderBy: "CASE WHEN ${Columns.flag} = 'FALSE' THEN 0 ELSE 1 END, ${Columns.state} desc"); +// var res = await database.rawQuery("SELECT ${Columns.id}, ${Columns.mesin}, ${Columns.tipe}, ${Columns.state}, ${Columns.warna}, ${Columns.rangka},${Columns.kode},${Columns.flag}, ${Columns.timestamp} from ${Tables.master} ORDER BY CASE WHEN ${Columns.flag} = 'FALSE' THEN 0 ELSE 1 END, ${Columns.state} desc"); + List list = + res.isNotEmpty ? res.map((c) => Unit.fromJson(c)).toList() : []; + return list; + } + + Future> searchAllUnits(String query,{inserted:false}) async { + // print("${(inserted)?"${Columns.flag} = 'TRUE' AND ":''} REPLACE(${Columns.mesin},'-','') like '%$query%' or ${Columns.rangka} like '%$query%' or ${Columns.mesin} like '%$query%' or ${Columns.timestamp} like '%$query%' or ${Columns.tipe} like '%$query%' or ${Columns.warna} like '%$query%' or UPPER(${Columns.state}) like '%$query%'"); + final database = await db; + try{ + var res = await database.query(Tables.master,where: "${(inserted)?"${Columns.flag} = 'TRUE' AND ":''} (REPLACE(${Columns.mesin},'-','') like '%$query%' or ${Columns.rangka} like '%$query%' or ${Columns.mesin} like '%$query%' or ${Columns.timestamp} like '%$query%' or ${Columns.tipe} like '%$query%' or ${Columns.warna} like '%$query%' or UPPER(${Columns.state}) like '%$query%')",orderBy: "CASE WHEN ${Columns.flag} = 'FALSE' THEN 0 ELSE 1 END, ${Columns.state} desc"); +// var res = await database.rawQuery( +// "select * from (SELECT ${Columns.id}, ${Columns.state}, ${Columns.rangka}, ${Columns.mesin}, ${Columns.tipe}, ${Columns.warna}, ${Columns.timestamp}, ${Columns.mesin} from ${Tables.master} ) where REPLACE(${Columns.mesin},'-','') like '%$query%' or ${Columns.rangka} like '%$query%' or ${Columns.mesin} like '%$query%' or ${Columns.timestamp} like '%$query%' or ${Columns.tipe} like '%$query%' or ${Columns.warna} like '%$query%' or UPPER(${Columns.state}) like '%$query%' ORDER BY CASE WHEN ${Columns.flag} = 'FALSE' THEN 0 ELSE 1 END, ${Columns.state} desc"); + List list = + res.isNotEmpty ? res.map((c) => Unit.fromJson(c)).toList() : []; + return list; + } + catch(e){ + print(e); + return []; + } + + } + + + getBlobUnits(mesin) async{ + try{ + final database = await db; + var res = await database.query("${Tables.blob}" ,where: "${Columns.mesin} = ?", whereArgs: [mesin]); + List list = + res!=null ? res.map((c) => Blob.fromJson(c)).toList() : []; + return list; + } + catch(e){ + // print(e); + return null; + } + } + + insertUpdateBlob(Blob blob) async{ + return (blob.id==null)?newInsertBlob(blob):updateBlob(blob); + } + + deleteBlob(id) async { + final database = await db; + try{ + await database.delete("${Tables.blob}", where: "${Columns.id} = ?", whereArgs: [id]); + return true; + } + catch(e){ + print(e); + return null; + } + } + + updateUnit(Unit unitUpdate) async{ + try{ + final database = await db; + var res = await database.update("${Tables.master}", unitUpdate.toJson(), + where: "${Columns.id} = ?", whereArgs: [unitUpdate.id]); + var checkCount = await database.query(Tables.master,where: "${Columns.flag} = ?", whereArgs: ['TRUE']); + if(checkCount.isNotEmpty&& checkCount.length%10==0){ + backupDb(); + } + return res; + } + catch(e){ + print(e); + return null; + } + } + + updateBlob(Blob blobUpdate) async{ + try{ + final database = await db; + var res = await database.update("${Tables.blob}", blobUpdate.toJson(), + where: "${Columns.blobId} = ?", whereArgs: [blobUpdate.id]); + return res; + } + catch(e){ + print(e); + return null; + } + } + + newInsertBlob(Blob newBlob) async { + final database = await db; + try{ + var res = await database.rawInsert("Insert into ${Tables.blob}(${Columns.bytes},${Columns.mesin},${Columns.lat},${Columns.long},${Columns.jenis})VALUES(?,?,?,?,?)", [ + newBlob.bytes, + newBlob.mesin, + newBlob.lat, + newBlob.long, + newBlob.jenis + ]); + print('Image Inserted'); + return res; + } + catch(e){ + print(e); + return null; + } + } + + getThumbnail(mesin) async{ + final database = await db; + var res = await database.query("${Tables.blob}",where: "${Columns.mesin} = ? AND ${Columns.noUrut} = ?",whereArgs: [mesin,'1']); + return res.isNotEmpty ? Blob.fromJson(res.first) : null; + } + + + + Future?> getAllJenis() async { + try{ + final database = await db; + var res = await database.query("${Tables.jenis}"); + List list = + res.isNotEmpty ? res.map((c) => ImageType.fromJson(c)).toList() : []; + return list; + } + catch(e){ + print(e); + return null; + } + } + + getValue(value)async{ + try{ + final database = await db; + var res = await database.query(Tables.values,where: "${Columns.name} = ?",whereArgs: [value]); + return res.isEmpty?null:Value.fromJson(res.first); + } + catch(e){ + return null; + } + } + + insertUpdateValue(Value newValue)async{ + try{ + final database = await db; + var res = await database.rawQuery("Select Count(*) as count from ${Tables.values} where ${Columns.name} = '${newValue.name}'"); + int count = res.isNotEmpty ? Count.fromJson(res.first).count : 0; + return (count==0)?addNewValue(newValue):updateValue(newValue); + } + catch(e){ + print(e); + return null; + } + } + + updateValue(Value newValue) async { + try{ + final database = await db; + var res = await database.update("${Tables.values}", newValue.toJson(), + where: "${Columns.name} = ?", whereArgs: [newValue.name]); + print('Row Updated'); + return res; + } + catch(e){ + print(e); + return null; + } + } + + addNewValue(Value newValue) async { + final database = await db; + try{ + var res = await database.rawInsert("Insert into ${Tables.values}(${Columns.name},${Columns.value})VALUES(?,?)", [ + newValue.name, + newValue.value + ]); + print('Row Inserted'); + return res; + } + catch(e){ + print(e); + return null; + } + } + + deleteAll() async { + final database = await db; + try{ + await database.rawQuery("DROP TABLE IF EXISTS ${Tables.blob}"); + await database.rawQuery("DROP TABLE IF EXISTS ${Tables.master}"); + return true; + } + catch(e){ + print(e); + return null; + } + } + +} diff --git a/lib/Utils/download_upload_handler.dart b/lib/Utils/download_upload_handler.dart new file mode 100644 index 0000000..c4c5ea8 --- /dev/null +++ b/lib/Utils/download_upload_handler.dart @@ -0,0 +1,267 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; +import 'package:http/io_client.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:http/http.dart'; +import 'package:unitstocks/Utils/keys.dart'; +import '../main.dart'; +import 'package:oauth2/oauth2.dart' as oauth2; + +class file_Trans_Handler { + +// double _progress = 0; + String _path = ''; + late StreamSubscription dlulStream; + String _error = ''; + get path => _path; + get error => _error; + StreamController _progress = new StreamController(); + Stream get progress =>_progress.stream; + bool useLocal = false; + late Client httpClient; + + downloadFile(String fileName,String link) async { + StreamedResponse _response; + List _bytes = []; + int _total = 0; + httpClient = await util.getOauth2Client(); + print('Start Download $link'); + _progress.add(null); + try { + Directory? dir = await getExternalStorageDirectory(); + void mainThrow(e){ + cancel(); + _progress.add(-1.0); + if(util.htmlErrorTitle(e.message??e)!=""){ + _error = util.htmlErrorTitle(e.message??e); + } + else _error = e.message??e; + } + + Request req = new Request('GET', Uri.parse(link)); +// req.headers = ''; + _response = await httpClient.send(req).timeout( + Duration(seconds: 20)); +// _response = await client.get('$link',headers: 'application/json') + if(_response.statusCode==200){ + _total = _response.contentLength?? int.parse(_response.headers['file-size']??'0'); + } + + // print('${_total / 1024} KB'); + _error = ''; + dlulStream = _response.stream.listen((value) { + if(_response.statusCode==200){ + _bytes.addAll(value); + _progress.add(((_bytes.length / _total))); + } + else{ + // print("masuk error"); + // this.cancel(); + // cancel(); //stop stream + String resString = utf8.decode(value); + // if(util.htmlErrorTitle(resString)!=""){ + // _error = util.htmlErrorTitle(resString); + // } + // else + // _error = resString; + mainThrow(Exception(resString)); + } + },onDone:() async { + if(_error==''){ + // _progress.add(0.0); + print('Finish Download'); + final file = File( + "${dir!.path}/$fileName"); + await file.writeAsBytes(_bytes); + _path = file.path; + } + else{ + mainThrow(_error); + } + } ,onError: (e) async { + mainThrow(e); + },cancelOnError: true); + } + on HandshakeException catch(e){ + cancel(); + if(useLocal){ + // print('Error Download 3, $e'); + _progress.add(-1.0); + if(util.htmlErrorTitle(e.message)!=""){ + _error = util.htmlErrorTitle(e.message); + } + else _error = e.toString(); + } + else{ + useLocal = true; + http = IOClient(HttpClient(context: clientContext)); + await downloadFile(fileName,link); + } + } + catch(e){ + cancel(); + _progress.add(-1.0); + if(util.htmlErrorTitle(e.toString())!=""){ + _error = util.htmlErrorTitle(e.toString()); + } + else _error = e.toString(); + } + } + + uploadFile(String fileName,String link,String company,String cabang_id) async{ + httpClient = await util.getOauth2Client(); + Directory? dir = await getExternalStorageDirectory(); + oauth2.Credentials tokenRestData = oauth2.Credentials.fromJson(prefs.getString(Keys.restData)); + final file = File( + "${dir!.path}/$fileName"); + String mimeType = 'application/vnd.sqlite3'; + if(file.existsSync()){ + Uint8List byte = file.readAsBytesSync(); + try{ + var _response = await httpClient.put( + Uri.parse(link), headers: {"cabangId":cabang_id,"company":company,'Content-type': mimeType,'Authorization':'Bearer ${tokenRestData.accessToken}'}, + body: byte).onError((error, stackTrace){ + return new Response("Internal Server Error", 400); + }); + print('File send ${file.lengthSync()/1024} KB'); + if(_response.statusCode!=200){ + if(util.htmlErrorTitle(_response.body.toString())!=""){ + return {"STATUS":0,"DATA":util.htmlErrorTitle(_response.body.toString())}; + } + throw _response.body.toString(); + } + final Map data = JsonDecoder().convert(_response.body); +// print(_response.body); +// if(data['STATUS']==1){ +// return {"STATUS":1,"DATA":'File send ${file.lengthSync()/1024} KB'}; +// } +// else { + return data; +// } + } + on HandshakeException catch(e){ + if(useLocal){ + return {"STATUS":0,"DATA":'Request timeout. Make sure server is up and running'}; + } + else{ + useLocal = true; + http = IOClient(HttpClient(context: clientContext)); + return await uploadFile( fileName, link, company, cabang_id); + } + } + catch(e){ + print(e); + return {"STATUS":0,"DATA":'Request timeout. Make sure server is up and running'}; + } + } + else{ + return {"STATUS":0,"DATA":'No such file'}; + } + } + + unPackDb(String link,String company,String cabang_id,String dbPath) async{ + + try{ + var _response = await http.post( + Uri.parse(link), headers: {'Content-type': 'application/json'}, + body: json.encode({"cabangId":cabang_id,"company":company,"dbPath":dbPath})); + final Map data = JsonDecoder().convert(_response.body); + return data; + } + on HandshakeException catch(e){ + if(useLocal){ + return {"STATUS":0,"DATA":'Upload timeout. Make sure server is up and running'}; + } + else{ + useLocal = true; + http = IOClient(HttpClient(context: clientContext)); + return await unPackDb( link, company, cabang_id, dbPath); + } + } + catch(e){ + print(e); + return {"STATUS":0,"DATA":'Upload timeout. Make sure server is up and running'}; + } + } + + submitDb(String link,String company,String stock_id) async{ + try{ + var _response = await http.post( + Uri.parse(link), headers: {'Content-type': 'application/json'}, + body: json.encode({"stockTakingId":stock_id,"company":company})); + final Map data = JsonDecoder().convert(_response.body); + return data; + } + on HandshakeException catch(e){ + if(useLocal){ + return {"STATUS":0,"DATA":'Request timeout. Make sure server is up and running'}; + } + else{ + useLocal = true; + http = IOClient(HttpClient(context: clientContext)); + return await submitDb(link,company,stock_id); + } + } + catch(e){ + print(e); + return {"STATUS":0,"DATA":'Request timeout. Make sure server is up and running'}; + } + } +// apex rest blm support multipart + uploadMultipart(String fileName,String link)async{ + httpClient = await util.getOauth2Client(); + StreamedResponse _response; + List _bytes = []; + int _total = 0; + try{ + Directory? dir = await getExternalStorageDirectory(); + var request = MultipartRequest('PUT', Uri.parse(link)); + request.files.add( + await MultipartFile.fromPath( + 'picture', + "${dir!.path}/$fileName" + ) + ); + _response = await httpClient.send(request); + _total = File("${dir.path}/$fileName").lengthSync(); + dlulStream = _response.stream.listen((value) { + _bytes.addAll(value); + print('upload ${_bytes.length/_total}'); + _progress.add(((_bytes.length / _total))); + }) + ..onDone(() async { + _progress.add(0.0); + print('Finish Download'); + final file = File( + "${dir.path}/$fileName"); + await file.writeAsBytes(_bytes); + _path = file.path; + }) + ..onError((e) async { + print('Error Download, $e'); + _progress.add(-1.0); + _error = e.toString(); + }); + } + on HandshakeException catch(e){ + if(useLocal){ + print('Error Download, $e'); + _progress.add(-1.0); + _error = e.toString(); + } + else{ + useLocal = true; + http = IOClient(HttpClient(context: clientContext)); + await uploadMultipart(fileName,link); + } + } + } + + cancel()async{ + // http?.close(); + await dlulStream.cancel(); + _progress.close(); + } +} \ No newline at end of file diff --git a/lib/Utils/keys.dart b/lib/Utils/keys.dart new file mode 100644 index 0000000..2ae62ee --- /dev/null +++ b/lib/Utils/keys.dart @@ -0,0 +1,47 @@ +class Keys{ + static String lastDownload = 'LAST_DOWNLOAD'; + static String lastUpload = 'LAST_UPLOAD'; + static String duration = 'DURATION'; + static String loginId = 'LOGIN_ID'; + static String hostAddress = 'HOST'; + static String restTokenAddress = 'REST'; + static String company = 'COMPANY'; + static String userId = 'USER'; + static String cabangId = 'cabangId'; + static String loggedIn = 'loggedIn'; + static String targetProccess = 'dbPath'; + static String submitProccess = 'SUBMIT'; + static String stockId = 'STOCK_TAKING_UNIT_ID'; + static String clientCredential = 'clientCredential'; + static String restData = 'restData'; + static String startDate = 'startDate'; + static String backupStockId = 'backup_stock_takingId'; +} +class Columns{ + static String id = 'ID'; + static String mesin = 'MESIN'; + static String warna = 'WARNA'; + static String tipe = 'TIPE'; + static String rangka = 'RANGKA'; + static String kode = 'KODE'; + static String tahun = 'TAHUN'; + static String lat = 'LATITUDE'; + static String long = 'LONGITUDE'; + static String timestamp = 'TIMESTAMP'; + static String blobId = 'BLOB_ID'; + static String bytes = 'BLOB_FILE'; + static String name = 'NAME'; + static String value = 'VALUE'; + static String optional = 'OPTIONAL'; + static String jenis = 'JENIS'; + static String state = 'STATE'; + static String noUrut = 'NO_URUT'; + static String flag = 'FLAG_INSERT'; + static String channel = "CHANNEL"; +} +class Tables{ + static String master = 'refTable'; + static String blob = 'blobTable'; + static String jenis = 'jenisBlobTable'; + static String values = 'valueTable'; +} \ No newline at end of file diff --git a/lib/Utils/utils.dart b/lib/Utils/utils.dart new file mode 100644 index 0000000..d25b409 --- /dev/null +++ b/lib/Utils/utils.dart @@ -0,0 +1,382 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'package:flutter/services.dart'; +import 'package:flutter/material.dart'; +import 'package:another_flushbar/flushbar.dart'; +import 'package:http/io_client.dart'; +import 'package:in_app_update/in_app_update.dart'; +import 'package:url_launcher/url_launcher.dart'; +import 'package:location/location.dart'; +import 'package:permission_handler/permission_handler.dart' as pHandler; +import 'keys.dart'; +import 'package:oauth2/oauth2.dart' as oauth2; +import '../main.dart'; + +class Util{ + bool tokenValidity = true; + bool useLocal = false; + + showLoading(context,{dissmissable=false,onwillpop}){ + showDialog( + context: context, + builder: (BuildContext context) { + return WillPopScope( + onWillPop: onwillpop??()async{return true;}, + child: const Center( + child: CircularProgressIndicator(), + ), + ); + }, + barrierDismissible: dissmissable, + ); + } + + checkinternet()async{ + try { + var result = await InternetAddress.lookup('google.com').timeout(const Duration(seconds: 3)); + if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) { + return true; + } + return false; + } + on TimeoutException catch(_){ + return false; + } + on SocketException catch (_) { + return false; + } + } + + getMinAppVer()async{ + var req = await JsonDataGetRaw({},'${prefs.getString(Keys.hostAddress)}/app/min_ver/'); + return req; + } + + launchURL(String url) async { + if (await canLaunch(url)) { + await launch(url); + } else { + throw 'Could not launch $url'; + } + } + + updateDialog(context)async{ + WidgetsBinding.instance!.addPostFrameCallback((_) async { + await showDialog( + context: context, + builder: (BuildContext context) { + return WillPopScope( + onWillPop: ()async{ + return false; + }, + child: AlertDialog( + title: const Text('App version is too old'), + content: const Text('Please update the application'), + actions: [ + TextButton( + child: const Text('Update',style: TextStyle(color: Colors.blue),), + onPressed: ()async{ + try{ + AppUpdateInfo _updateInfo; + + _updateInfo = await InAppUpdate.checkForUpdate(); + if(_updateInfo.updateAvailability == 2){ + await InAppUpdate.performImmediateUpdate(); + } + else throw {"message":"manual Update"}; + } + catch(e){ + await Future.delayed(const Duration(milliseconds: 500)); + await launchURL('https://play.google.com/store/apps/details?id=com.thamringroup.unitstocks'); + } + }, + ), + ], + ), + ); + }, + barrierDismissible: false, + ); + }); + } + + getOauth2Client() async { + bool resetClient = false; + oauth2.Credentials? restData = (prefs.getString(Keys.restData)!=null&&prefs.getString(Keys.restData).contains("accessToken"))?oauth2.Credentials.fromJson(prefs.getString(Keys.restData)):null; + if(restData == null)resetClient = true; + else{ + if(DateTime.now().isAfter(restData.expiration??DateTime.now().subtract(const Duration(hours:1))))resetClient = true; + } + oauth2.Client client; + if(resetClient||!tokenValidity){ + final tokenEndpoint = Uri.parse(prefs.getString(Keys.restTokenAddress)); + if(prefs.getString(Keys.clientCredential)==null){ + client = await initRest(); + } + else{ + var clients = const JsonDecoder().convert(prefs.getString(Keys.clientCredential)); + client = await oauth2.clientCredentialsGrant(tokenEndpoint, clients["id"], clients["secret"],httpClient: http); + prefs.setString(Keys.restData, client.credentials.toJson()); + } + } + else { + if (prefs.getString(Keys.clientCredential) == null) { + client = await initRest(); + } + else { + var clients = const JsonDecoder().convert(prefs.getString(Keys.clientCredential)); + client = oauth2.Client(restData!, identifier: clients["id"], + secret: clients["secret"], + httpClient: http); + + } + } + return client; + } + + JsonDataPostRaw(Map jsonData, String url,{secure:true,timeout:false}) async{ + var httpClient; + const JsonDecoder decoder = const JsonDecoder(); + var headers = {'Content-type': 'application/json'}; + try { + var response; + if(secure) { + httpClient = await getOauth2Client(); + oauth2.Credentials tokenRestData = oauth2.Credentials.fromJson(prefs.getString(Keys.restData)); + headers["Authorization"] = + "bearer ${tokenRestData.accessToken}"; + } + else { + httpClient = http; + } + if (timeout) + response = await httpClient.post( + Uri.parse(url), headers: headers, + body: json.encode(jsonData)).timeout( + const Duration(seconds: 8)); + else + response = await httpClient.post( + Uri.parse(url), headers: {'Content-type': 'application/json'}, + body: json.encode(jsonData)); + if(response.statusCode == 403) tokenValidity = false; + if(htmlErrorTitle(response.body.toString())!=""){ + return {"STATUS":(response.statusCode != 200)?0:1,"DATA":htmlErrorTitle(response.body.toString())}; + } + final Map data = decoder.convert(response.body); + try{data["DATA"] = decoder.convert(data["DATA"]);}catch(e){} + return data; + } on TimeoutException catch(e){ + return {"STATUS":0,"DATA":"Request Timeout"}; + } + on HandshakeException catch(e){ + if(useLocal){ + return {"STATUS":0,"DATA":"Not Connected to Server. $e"}; + } + else{ + useLocal = true; + http = IOClient(HttpClient(context: clientContext)); + return await JsonDataPostRaw(jsonData, url,timeout:timeout,secure: secure); + } + + } + on Exception catch(exception){ + print(url); +// Toast("Not Connected to Server", Colors.red); + return {"STATUS":0,"DATA":"Not Connected to Server. $exception"}; + } + } + + JsonDataGetRaw(Map jsonData, String url,{secure:true,timeout:false}) async{ + var httpClient; + const JsonDecoder decoder = const JsonDecoder(); + var headers = {'Content-type': 'application/json'}; + try { + var response; + if(secure) { + httpClient = await getOauth2Client(); + oauth2.Credentials tokenRestData = oauth2.Credentials.fromJson(prefs.getString(Keys.restData)); + headers["Authorization"] = + "bearer ${tokenRestData.accessToken}"; + } + else { + httpClient = http; + } + if(jsonData!= null&&jsonData.length!=0) { + url = url + '?'; + for (var i in jsonData.keys) { + url = url + i + '=' + jsonData[i] + '&'; + } + url = url.substring(0, url.length - 1); + } + if (timeout) + response = await httpClient.get( + Uri.parse(url), headers: headers, + ).timeout(const Duration(seconds: 10)); + else + response = await httpClient.get( + Uri.parse(url), headers: {'Content-type': 'application/json'}, + ); + if(response.statusCode == 403) tokenValidity = false; + if(htmlErrorTitle(response.body.toString())!=""){ + return {"STATUS":(response.statusCode != 200)?0:1,"DATA":htmlErrorTitle(response.body.toString())}; + } + final Map data = decoder.convert(response.body); + try{data["DATA"] = decoder.convert(data["DATA"]);}catch(e){} + return data; + } on TimeoutException catch(e){ + return {"STATUS":0,"DATA":"Request Timeout"}; + } + on HandshakeException catch(e){ + if(useLocal){ + return {"STATUS":0,"DATA":"Not Connected to Server. $e"}; + } + else{ + useLocal = true; + http = IOClient(HttpClient(context: clientContext)); + return await JsonDataPostRaw(jsonData, url,timeout:timeout,secure: secure); + } + + } + on Exception catch(exception){ + print(url); +// Toast("Not Connected to Server", Colors.red); + return {"STATUS":0,"DATA":"Not Connected to Server. $exception"}; + } + } + + JsonDataPutRaw(Map jsonData, String url,{secure:true,timeout:false}) async{ + var httpClient; + const JsonDecoder decoder = const JsonDecoder(); + var headers = {'Content-type': 'application/json'}; + try { + var response; + if(secure) { + httpClient = await getOauth2Client(); + oauth2.Credentials tokenRestData = oauth2.Credentials.fromJson(prefs.getString(Keys.restData)); + headers["Authorization"] = + "bearer ${tokenRestData.accessToken}"; + } + else { + httpClient = http; + } + if (timeout) + response = await httpClient.put( + Uri.parse(url), headers: headers, + body: json.encode(jsonData)).timeout( + const Duration(seconds: 10)); + else + response = await httpClient.put( + Uri.parse(url), headers: {'Content-type': 'application/json'}, + body: json.encode(jsonData)); + if(response.statusCode == 403) tokenValidity = false; + if(htmlErrorTitle(response.body.toString())!=""){ + return {"STATUS":(response.statusCode != 200)?0:1,"DATA":htmlErrorTitle(response.body.toString())}; + } + final Map data = decoder.convert(response.body); + try{data["DATA"] = decoder.convert(data["DATA"]);}catch(e){} + return data; + } on TimeoutException catch(e){ + return {"STATUS":0,"DATA":"Request Timeout"}; + } + on HandshakeException catch(e){ + if(useLocal){ + return {"STATUS":0,"DATA":"Not Connected to Server. $e"}; + } + else{ + useLocal = true; + http = IOClient(HttpClient(context: clientContext)); + return await JsonDataPostRaw(jsonData, url,timeout:timeout,secure: secure); + } + + } + on Exception catch(exception){ + print(url); +// Toast("Not Connected to Server", Colors.red); + return {"STATUS":0,"DATA":"Not Connected to Server. $exception"}; + } + } + + htmlErrorTitle(String html){ + // FlutterLogs.logInfo("TAG", "subTag", html); + try{ + if(html.contains('')) { + String titleElement = html.substring(html.indexOf("<title>")+7,html.indexOf("<\/title>")); + return titleElement; + } + return ''; + } + catch(e){ + return ''; + } + } + initRest()async{ + try{ + if(prefs.getString(Keys.clientCredential)==null){ + prefs.setString(Keys.clientCredential, json.encode({ + "id": "IZ4W8u8YZmLtUV0p1zd-_A..", + "secret" : "AQ16v4bzGWm9AsWHvUcu2Q.." + }).toString()); + } + return await getOauth2Client(); + } + catch(e){ + print(e); + print("error fetching Rest token"); + } + } + + permissionCheck(context,pHandler.Permission permissionType,ifGranted,{customMessage=''})async{ + pHandler.PermissionStatus permission = await permissionType.status; + if(permission!= pHandler.PermissionStatus.granted){ + if(permission== pHandler.PermissionStatus.denied || permission== pHandler.PermissionStatus.restricted){ + showFlushbar(context,'${permissionType.toString().substring(permissionType.toString().lastIndexOf('.')+1)} permission is needed$customMessage. Please grant the permission!'); + await Future.delayed(const Duration(seconds: 3)); + permission = await permissionType.request(); + } + if(permission== pHandler.PermissionStatus.permanentlyDenied) { + showFlushbar(context,'It seems, your system security explicitly denied access to your ${permissionType.toString().substring(permissionType.toString().lastIndexOf('.')+1)} permission. Please MANUALLY enable it in setings!'); + await Future.delayed(const Duration(seconds: 3)); + pHandler.openAppSettings(); + SystemChannels.platform.invokeMethod('SystemNavigator.pop'); + } + if(permission== pHandler.PermissionStatus.denied || permission== pHandler.PermissionStatus.restricted){ + showFlushbar(context,'${permissionType.toString().substring(permissionType.toString().lastIndexOf('.')+1)} permission is needed$customMessage. Please grant the permission!'); + await Future.delayed(const Duration(seconds: 3)); + permission = await permissionType.request(); + } + await permissionCheck(context,permissionType,ifGranted); + } + else{ + await ifGranted(); + } + } + showFlushbar(context,text,{color:Colors.grey}){ + Flushbar( + message: "$text", + backgroundColor: color, + duration: const Duration(seconds: 5), + ).show(context); + } + streamLocation(context)async{ + // print('checking location'); + await permissionCheck(context,pHandler.Permission.locationWhenInUse,()async{ + location.changeSettings(accuracy: LocationAccuracy.high); + bool gpsEnabled = false; + if(await location.serviceEnabled()){ + gpsEnabled = await location.serviceEnabled(); + } + else{ + // print('requesting gps'); + gpsEnabled = await location.requestService(); + await streamLocation(context); + } +// print([gpsEnabled,permissionEnabled]); + if(gpsEnabled){ + locationStream ??= location.onLocationChanged.listen((LocationData event) { + currentPosisiton = event; + }); + } + },customMessage: " to locate your REAL location"); + } +} \ No newline at end of file diff --git a/lib/Widgets/photo_viewer.dart b/lib/Widgets/photo_viewer.dart new file mode 100644 index 0000000..fbe4b7f --- /dev/null +++ b/lib/Widgets/photo_viewer.dart @@ -0,0 +1,85 @@ +import 'dart:io'; +import 'dart:typed_data'; +import 'package:image_picker/image_picker.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:photo_view/photo_view.dart'; +import '../Model/unit.dart'; +import '../main.dart'; + +class PhotoViewer extends StatefulWidget { + Uint8List byte; + String? id; + List<ImageType>? dropMenu; + bool edit; + String? jenis; + PhotoViewer(this.byte,{this.id,this.edit= true,this.dropMenu,this.jenis}); +// PhotoViewer({Key key}) : super(key: key); + @override + _PhotoViewerState createState() => _PhotoViewerState(); +} + +class _PhotoViewerState extends State<PhotoViewer> { + @override + Widget build(BuildContext context) { + return Column( + children: <Widget>[ + Flexible( + flex: 8, + child: Container( + child: Hero( + tag: widget.id??'', + child: PhotoView( + imageProvider: MemoryImage(widget.byte), + ), + ), + ), + ), + Expanded( + flex: 1, + child: Material( + color: Colors.transparent, + child: Container( + child: Text(widget.jenis??'',style: TextStyle(color: Colors.white,fontSize: 18),), + ), + ), + ), + (widget.edit)?Flexible( + flex: 1, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: <Widget>[ + TextButton( + child: Text('Cancel'), + style: TextButton.styleFrom( + backgroundColor: Colors.red + ), + onPressed: (){Navigator.pop(context);}, + ), + TextButton( + child: Text('Change'), + style: TextButton.styleFrom( + backgroundColor: Colors.green + ), + onPressed: ()async{ + XFile? temp = await ImagePicker().pickImage(source: ImageSource.camera,maxWidth: 800); + if(temp!=null){ + util.showLoading(context); + widget.byte = await temp.readAsBytes(); + setState(() { + + }); + await Future.delayed(Duration(seconds: 1)); + } + Navigator.pop(context); + if(temp!= null)Navigator.pop(context,{'byte':widget.byte}); + }, + ), + ], + ), + ):Container() + ], + ); + } +} \ No newline at end of file diff --git a/lib/cert/isrgrootx1.pem b/lib/cert/isrgrootx1.pem new file mode 100644 index 0000000..b85c803 --- /dev/null +++ b/lib/cert/isrgrootx1.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..a1615a6 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,74 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:location/location.dart'; +import 'Utils/utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'Utils/keys.dart'; +import 'package:http/http.dart'; + +import 'Pages/home_page.dart'; +import 'Pages/login_page.dart'; +import 'Pages/unit_list.dart'; +import 'Pages/unit_details.dart'; + + + +Client http = Client(); +SecurityContext clientContext = SecurityContext(); +Util util = Util(); +var prefs; +Location location = Location(); +StreamSubscription? locationStream; +LocationData? currentPosisiton; +RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>(); + + + + +registerCert()async{ + ByteData bytes = await rootBundle.load('lib/cert/isrgrootx1.pem'); + clientContext = SecurityContext() + ..setTrustedCertificatesBytes(bytes.buffer.asUint8List()); +} + +Future preLoad() async{ + prefs = await SharedPreferences.getInstance(); + await prefs?.setString(Keys.hostAddress, "https://tbg.thamringroup.web.id/ords/tbs/unit/v1"); + await prefs?.setString(Keys.restTokenAddress, "https://tbg.thamringroup.web.id/ords/tbs/oauth/token"); + await registerCert(); +} + +void main()async{ + WidgetsFlutterBinding.ensureInitialized(); + await preLoad(); + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]) + .then((_) { + runApp(const MyApp()); + }); +} + +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return MaterialApp( + navigatorObservers: [routeObserver], + title: 'Internal Unit Stocks', + debugShowCheckedModeBanner: false, + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: (prefs.getBool(Keys.loggedIn)??false)?const HomePage():const LoginPage(), + routes: { + '/home': (context) => const HomePage(), + '/units' : (context) => new UnitsPage(), + '/login' :(context) => const LoginPage(), + }, + ); + } +} + + diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..c792151 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,698 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + another_flushbar: + dependency: "direct main" + description: + name: another_flushbar + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.28" + archive: + dependency: transitive + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.1" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.0" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.8.2" + barcode_scan2: + dependency: "direct main" + description: + name: barcode_scan2 + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.0" + bloc: + dependency: "direct main" + description: + name: bloc + url: "https://pub.dartlang.org" + source: hosted + version: "8.0.3" + 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.2.0" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + 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" + cross_file: + dependency: transitive + description: + name: cross_file + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.2" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + equatable: + dependency: "direct main" + description: + name: equatable + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + 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.1.2" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.2" + fixnum: + dependency: transitive + description: + name: fixnum + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + url: "https://pub.dartlang.org" + source: hosted + version: "8.0.1" + flutter_launcher_icons: + dependency: "direct dev" + description: + name: flutter_launcher_icons + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.2" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" + 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" + http: + dependency: "direct main" + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.13.4" + 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: "3.1.3" + image_picker: + dependency: "direct main" + description: + name: image_picker + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.5" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.4+11" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.6" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.5" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.4.4" + in_app_update: + dependency: "direct main" + description: + name: in_app_update + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.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" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + location: + dependency: "direct main" + description: + name: location + url: "https://pub.dartlang.org" + source: hosted + version: "4.3.0" + location_platform_interface: + dependency: transitive + description: + name: location_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.0" + location_web: + dependency: transitive + description: + name: location_web + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.0" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + oauth2: + dependency: "direct main" + description: + name: oauth2 + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + package_info: + dependency: "direct main" + description: + name: package_info + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + 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.9" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.12" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.8" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.5" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + url: "https://pub.dartlang.org" + source: hosted + version: "9.2.0" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + url: "https://pub.dartlang.org" + source: hosted + version: "9.0.2+1" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + url: "https://pub.dartlang.org" + source: hosted + version: "9.0.4" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "3.7.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "4.4.0" + photo_view: + dependency: "direct main" + description: + name: photo_view + url: "https://pub.dartlang.org" + source: hosted + version: "0.13.0" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.4" + protobuf: + dependency: transitive + description: + name: protobuf + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + provider: + dependency: transitive + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.2" + pull_to_refresh: + dependency: "direct main" + description: + name: pull_to_refresh + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.13" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + shared_preferences_ios: + dependency: transitive + description: + name: shared_preferences_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + shared_preferences_macos: + dependency: transitive + description: + name: shared_preferences_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + 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.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.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.1" + sqflite: + dependency: "direct main" + description: + name: sqflite + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2+1" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.1+1" + 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.4.8" + 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: "6.1.0" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.15" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.15" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "2.4.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0+1" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "5.3.1" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" +sdks: + dart: ">=2.16.0 <3.0.0" + flutter: ">=2.8.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..0039efd --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,49 @@ +name: unitstocks +description: Mobile App to help auditing Units +publish_to: 'none' + +version: 1.0.6+12 + +environment: + sdk: ">=2.12.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + flutter_bloc: + bloc: + cupertino_icons: ^1.0.4 + location: ^4.3.0 + permission_handler: ^9.2.0 + barcode_scan2: ^4.2.0 + in_app_update: ^3.0.0 + oauth2: ^2.0.0 + url_launcher: ^6.1.0 + package_info: ^2.0.2 + shared_preferences: ^2.0.13 + image_picker: ^0.8.5 + photo_view: ^0.13.0 + sqflite: ^2.0.2+1 + path_provider: ^2.0.9 + another_flushbar: ^1.10.28 + http: ^0.13.4 + intl: ^0.17.0 + equatable: ^2.0.3 + pull_to_refresh: ^2.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^1.0.4 + flutter_launcher_icons: + +flutter_icons: + android: "launcher_icon" + ios: true + image_path: "asset/icon.png" + +flutter: + uses-material-design: true + + assets: + - lib/cert/isrgrootx1.pem \ No newline at end of file diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..15d77a5 --- /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:unitstocks/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // 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); + }); +}