| @@ -0,0 +1 @@ | |||
| PATH_add /Users/amin/flutter/latest/bin | |||
| @@ -0,0 +1,43 @@ | |||
| # Miscellaneous | |||
| *.class | |||
| *.log | |||
| *.pyc | |||
| *.swp | |||
| .DS_Store | |||
| .atom/ | |||
| .buildlog/ | |||
| .history | |||
| .svn/ | |||
| migrate_working_dir/ | |||
| # IntelliJ related | |||
| *.iml | |||
| *.ipr | |||
| *.iws | |||
| .idea/ | |||
| # The .vscode folder contains launch configuration and tasks you configure in | |||
| # VS Code which you may wish to be included in version control, so this line | |||
| # is commented out by default. | |||
| #.vscode/ | |||
| # Flutter/Dart/Pub related | |||
| **/doc/api/ | |||
| **/ios/Flutter/.last_build_id | |||
| .dart_tool/ | |||
| .flutter-plugins | |||
| .flutter-plugins-dependencies | |||
| .pub-cache/ | |||
| .pub/ | |||
| /build/ | |||
| # Symbolication related | |||
| app.*.symbols | |||
| # Obfuscation related | |||
| app.*.map.json | |||
| # Android Studio will place build artifacts here | |||
| /android/app/debug | |||
| /android/app/profile | |||
| /android/app/release | |||
| @@ -0,0 +1,45 @@ | |||
| # 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: "2663184aa79047d0a33a14a3b607954f8fdd8730" | |||
| channel: "stable" | |||
| project_type: app | |||
| # Tracks metadata for the flutter migrate command | |||
| migration: | |||
| platforms: | |||
| - platform: root | |||
| create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 | |||
| base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 | |||
| - platform: android | |||
| create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 | |||
| base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 | |||
| - platform: ios | |||
| create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 | |||
| base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 | |||
| - platform: linux | |||
| create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 | |||
| base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 | |||
| - platform: macos | |||
| create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 | |||
| base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 | |||
| - platform: web | |||
| create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 | |||
| base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 | |||
| - platform: windows | |||
| create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 | |||
| base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 | |||
| # User provided section | |||
| # List of Local paths (relative to this file) that should be | |||
| # ignored by the migrate tool. | |||
| # | |||
| # Files that are not part of the templates will be ignored by default. | |||
| unmanaged_files: | |||
| - 'lib/main.dart' | |||
| - 'ios/Runner.xcodeproj/project.pbxproj' | |||
| @@ -0,0 +1,28 @@ | |||
| # This file configures the analyzer, which statically analyzes Dart code to | |||
| # check for errors, warnings, and lints. | |||
| # | |||
| # The issues identified by the analyzer are surfaced in the UI of Dart-enabled | |||
| # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be | |||
| # invoked from the command line by running `flutter analyze`. | |||
| # The following line activates a set of recommended lints for Flutter apps, | |||
| # packages, and plugins designed to encourage good coding practices. | |||
| include: package:flutter_lints/flutter.yaml | |||
| linter: | |||
| # The lint rules applied to this project can be customized in the | |||
| # section below to disable rules from the `package:flutter_lints/flutter.yaml` | |||
| # included above or to enable additional rules. A list of all available lints | |||
| # and their documentation is published at https://dart.dev/lints. | |||
| # | |||
| # Instead of disabling a lint rule for the entire project in the | |||
| # section below, it can also be suppressed for a single line of code | |||
| # or a specific dart file by using the `// ignore: name_of_lint` and | |||
| # `// ignore_for_file: name_of_lint` syntax on the line or in the file | |||
| # producing the lint. | |||
| rules: | |||
| # avoid_print: false # Uncomment to disable the `avoid_print` rule | |||
| # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule | |||
| # Additional information about this file can be found at | |||
| # https://dart.dev/guides/language/analysis-options | |||
| @@ -0,0 +1,13 @@ | |||
| gradle-wrapper.jar | |||
| /.gradle | |||
| /captures/ | |||
| /gradlew | |||
| /gradlew.bat | |||
| /local.properties | |||
| GeneratedPluginRegistrant.java | |||
| # Remember to never publicly share your keystore. | |||
| # See https://flutter.dev/to/reference-keystore | |||
| key.properties | |||
| **/*.keystore | |||
| **/*.jks | |||
| @@ -0,0 +1,44 @@ | |||
| plugins { | |||
| id "com.android.application" | |||
| id "kotlin-android" | |||
| // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. | |||
| id "dev.flutter.flutter-gradle-plugin" | |||
| } | |||
| android { | |||
| namespace = "com.example.qadirneyriz" | |||
| compileSdk = flutter.compileSdkVersion | |||
| ndkVersion = flutter.ndkVersion | |||
| compileOptions { | |||
| sourceCompatibility = JavaVersion.VERSION_1_8 | |||
| targetCompatibility = JavaVersion.VERSION_1_8 | |||
| } | |||
| kotlinOptions { | |||
| jvmTarget = JavaVersion.VERSION_1_8 | |||
| } | |||
| defaultConfig { | |||
| // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). | |||
| applicationId = "com.example.qadirneyriz" | |||
| // You can update the following values to match your application needs. | |||
| // For more information, see: https://flutter.dev/to/review-gradle-config. | |||
| minSdk = flutter.minSdkVersion | |||
| targetSdk = flutter.targetSdkVersion | |||
| versionCode = flutter.versionCode | |||
| versionName = flutter.versionName | |||
| } | |||
| buildTypes { | |||
| release { | |||
| // TODO: Add your own signing config for the release build. | |||
| // Signing with the debug keys for now, so `flutter run --release` works. | |||
| signingConfig = signingConfigs.debug | |||
| } | |||
| } | |||
| } | |||
| flutter { | |||
| source = "../.." | |||
| } | |||
| @@ -0,0 +1,7 @@ | |||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | |||
| <!-- The INTERNET permission is required for development. Specifically, | |||
| the Flutter tool needs it to communicate with the running application | |||
| to allow setting breakpoints, to provide hot reload, etc. | |||
| --> | |||
| <uses-permission android:name="android.permission.INTERNET"/> | |||
| </manifest> | |||
| @@ -0,0 +1,45 @@ | |||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | |||
| <application | |||
| android:label="qadirneyriz" | |||
| android:name="${applicationName}" | |||
| android:icon="@mipmap/ic_launcher"> | |||
| <activity | |||
| android:name=".MainActivity" | |||
| android:exported="true" | |||
| android:launchMode="singleTop" | |||
| android:taskAffinity="" | |||
| android:theme="@style/LaunchTheme" | |||
| android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" | |||
| android:hardwareAccelerated="true" | |||
| android:windowSoftInputMode="adjustResize"> | |||
| <!-- Specifies an Android theme to apply to this Activity as soon as | |||
| the Android process has started. This theme is visible to the user | |||
| while the Flutter UI initializes. After that, this theme continues | |||
| to determine the Window background behind the Flutter UI. --> | |||
| <meta-data | |||
| android:name="io.flutter.embedding.android.NormalTheme" | |||
| android:resource="@style/NormalTheme" | |||
| /> | |||
| <intent-filter> | |||
| <action android:name="android.intent.action.MAIN"/> | |||
| <category android:name="android.intent.category.LAUNCHER"/> | |||
| </intent-filter> | |||
| </activity> | |||
| <!-- Don't delete the meta-data below. | |||
| This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> | |||
| <meta-data | |||
| android:name="flutterEmbedding" | |||
| android:value="2" /> | |||
| </application> | |||
| <!-- Required to query activities that can process text, see: | |||
| https://developer.android.com/training/package-visibility and | |||
| https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT. | |||
| In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. --> | |||
| <queries> | |||
| <intent> | |||
| <action android:name="android.intent.action.PROCESS_TEXT"/> | |||
| <data android:mimeType="text/plain"/> | |||
| </intent> | |||
| </queries> | |||
| </manifest> | |||
| @@ -0,0 +1,5 @@ | |||
| package com.example.qadirneyriz | |||
| import io.flutter.embedding.android.FlutterActivity | |||
| class MainActivity: FlutterActivity() | |||
| @@ -0,0 +1,12 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <!-- Modify this file to customize your launch splash screen --> | |||
| <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> | |||
| <item android:drawable="?android:colorBackground" /> | |||
| <!-- You can insert your own image assets here --> | |||
| <!-- <item> | |||
| <bitmap | |||
| android:gravity="center" | |||
| android:src="@mipmap/launch_image" /> | |||
| </item> --> | |||
| </layer-list> | |||
| @@ -0,0 +1,12 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <!-- Modify this file to customize your launch splash screen --> | |||
| <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> | |||
| <item android:drawable="@android:color/white" /> | |||
| <!-- You can insert your own image assets here --> | |||
| <!-- <item> | |||
| <bitmap | |||
| android:gravity="center" | |||
| android:src="@mipmap/launch_image" /> | |||
| </item> --> | |||
| </layer-list> | |||
| @@ -0,0 +1,18 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <resources> | |||
| <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on --> | |||
| <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> | |||
| <!-- Show a splash screen on the activity. Automatically removed when | |||
| the Flutter engine draws its first frame --> | |||
| <item name="android:windowBackground">@drawable/launch_background</item> | |||
| </style> | |||
| <!-- Theme applied to the Android Window as soon as the process has started. | |||
| This theme determines the color of the Android Window while your | |||
| Flutter UI initializes, as well as behind your Flutter UI while its | |||
| running. | |||
| This Theme is only used starting with V2 of Flutter's Android embedding. --> | |||
| <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar"> | |||
| <item name="android:windowBackground">?android:colorBackground</item> | |||
| </style> | |||
| </resources> | |||
| @@ -0,0 +1,18 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <resources> | |||
| <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off --> | |||
| <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar"> | |||
| <!-- Show a splash screen on the activity. Automatically removed when | |||
| the Flutter engine draws its first frame --> | |||
| <item name="android:windowBackground">@drawable/launch_background</item> | |||
| </style> | |||
| <!-- Theme applied to the Android Window as soon as the process has started. | |||
| This theme determines the color of the Android Window while your | |||
| Flutter UI initializes, as well as behind your Flutter UI while its | |||
| running. | |||
| This Theme is only used starting with V2 of Flutter's Android embedding. --> | |||
| <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar"> | |||
| <item name="android:windowBackground">?android:colorBackground</item> | |||
| </style> | |||
| </resources> | |||
| @@ -0,0 +1,7 @@ | |||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | |||
| <!-- The INTERNET permission is required for development. Specifically, | |||
| the Flutter tool needs it to communicate with the running application | |||
| to allow setting breakpoints, to provide hot reload, etc. | |||
| --> | |||
| <uses-permission android:name="android.permission.INTERNET"/> | |||
| </manifest> | |||
| @@ -0,0 +1,18 @@ | |||
| allprojects { | |||
| repositories { | |||
| google() | |||
| mavenCentral() | |||
| } | |||
| } | |||
| rootProject.buildDir = "../build" | |||
| subprojects { | |||
| project.buildDir = "${rootProject.buildDir}/${project.name}" | |||
| } | |||
| subprojects { | |||
| project.evaluationDependsOn(":app") | |||
| } | |||
| tasks.register("clean", Delete) { | |||
| delete rootProject.buildDir | |||
| } | |||
| @@ -0,0 +1,3 @@ | |||
| org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError | |||
| android.useAndroidX=true | |||
| android.enableJetifier=true | |||
| @@ -0,0 +1,5 @@ | |||
| distributionBase=GRADLE_USER_HOME | |||
| distributionPath=wrapper/dists | |||
| zipStoreBase=GRADLE_USER_HOME | |||
| zipStorePath=wrapper/dists | |||
| distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip | |||
| @@ -0,0 +1,25 @@ | |||
| pluginManagement { | |||
| def flutterSdkPath = { | |||
| def properties = new Properties() | |||
| file("local.properties").withInputStream { properties.load(it) } | |||
| def flutterSdkPath = properties.getProperty("flutter.sdk") | |||
| assert flutterSdkPath != null, "flutter.sdk not set in local.properties" | |||
| return flutterSdkPath | |||
| }() | |||
| includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") | |||
| repositories { | |||
| google() | |||
| mavenCentral() | |||
| gradlePluginPortal() | |||
| } | |||
| } | |||
| plugins { | |||
| id "dev.flutter.flutter-plugin-loader" version "1.0.0" | |||
| id "com.android.application" version "8.1.0" apply false | |||
| id "org.jetbrains.kotlin.android" version "1.8.22" apply false | |||
| } | |||
| include ":app" | |||
| @@ -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 | |||
| @@ -0,0 +1,26 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||
| <plist version="1.0"> | |||
| <dict> | |||
| <key>CFBundleDevelopmentRegion</key> | |||
| <string>en</string> | |||
| <key>CFBundleExecutable</key> | |||
| <string>App</string> | |||
| <key>CFBundleIdentifier</key> | |||
| <string>io.flutter.flutter.app</string> | |||
| <key>CFBundleInfoDictionaryVersion</key> | |||
| <string>6.0</string> | |||
| <key>CFBundleName</key> | |||
| <string>App</string> | |||
| <key>CFBundlePackageType</key> | |||
| <string>FMWK</string> | |||
| <key>CFBundleShortVersionString</key> | |||
| <string>1.0</string> | |||
| <key>CFBundleSignature</key> | |||
| <string>????</string> | |||
| <key>CFBundleVersion</key> | |||
| <string>1.0</string> | |||
| <key>MinimumOSVersion</key> | |||
| <string>12.0</string> | |||
| </dict> | |||
| </plist> | |||
| @@ -0,0 +1 @@ | |||
| #include "Generated.xcconfig" | |||
| @@ -0,0 +1 @@ | |||
| #include "Generated.xcconfig" | |||
| @@ -0,0 +1,616 @@ | |||
| // !$*UTF8*$! | |||
| { | |||
| archiveVersion = 1; | |||
| classes = { | |||
| }; | |||
| objectVersion = 54; | |||
| objects = { | |||
| /* Begin PBXBuildFile section */ | |||
| 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; | |||
| 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; | |||
| 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; | |||
| 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; | |||
| 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; | |||
| 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; | |||
| 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; | |||
| /* End PBXBuildFile section */ | |||
| /* Begin PBXContainerItemProxy section */ | |||
| 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { | |||
| isa = PBXContainerItemProxy; | |||
| containerPortal = 97C146E61CF9000F007C117D /* Project object */; | |||
| proxyType = 1; | |||
| remoteGlobalIDString = 97C146ED1CF9000F007C117D; | |||
| remoteInfo = Runner; | |||
| }; | |||
| /* End PBXContainerItemProxy section */ | |||
| /* Begin PBXCopyFilesBuildPhase section */ | |||
| 9705A1C41CF9048500538489 /* Embed Frameworks */ = { | |||
| isa = PBXCopyFilesBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| dstPath = ""; | |||
| dstSubfolderSpec = 10; | |||
| files = ( | |||
| ); | |||
| name = "Embed Frameworks"; | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| /* End PBXCopyFilesBuildPhase section */ | |||
| /* Begin PBXFileReference section */ | |||
| 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; | |||
| 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; | |||
| 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; }; | |||
| 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; | |||
| 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; | |||
| 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; | |||
| 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; | |||
| 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; | |||
| 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; | |||
| 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; | |||
| 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; | |||
| 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; | |||
| 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; | |||
| /* End PBXFileReference section */ | |||
| /* Begin PBXFrameworksBuildPhase section */ | |||
| 97C146EB1CF9000F007C117D /* Frameworks */ = { | |||
| isa = PBXFrameworksBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| /* End PBXFrameworksBuildPhase section */ | |||
| /* Begin PBXGroup section */ | |||
| 331C8082294A63A400263BE5 /* RunnerTests */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 331C807B294A618700263BE5 /* RunnerTests.swift */, | |||
| ); | |||
| path = RunnerTests; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| 9740EEB11CF90186004384FC /* Flutter */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, | |||
| 9740EEB21CF90195004384FC /* Debug.xcconfig */, | |||
| 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, | |||
| 9740EEB31CF90195004384FC /* Generated.xcconfig */, | |||
| ); | |||
| name = Flutter; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| 97C146E51CF9000F007C117D = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 9740EEB11CF90186004384FC /* Flutter */, | |||
| 97C146F01CF9000F007C117D /* Runner */, | |||
| 97C146EF1CF9000F007C117D /* Products */, | |||
| 331C8082294A63A400263BE5 /* RunnerTests */, | |||
| ); | |||
| sourceTree = "<group>"; | |||
| }; | |||
| 97C146EF1CF9000F007C117D /* Products */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 97C146EE1CF9000F007C117D /* Runner.app */, | |||
| 331C8081294A63A400263BE5 /* RunnerTests.xctest */, | |||
| ); | |||
| name = Products; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| 97C146F01CF9000F007C117D /* Runner */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 97C146FA1CF9000F007C117D /* Main.storyboard */, | |||
| 97C146FD1CF9000F007C117D /* Assets.xcassets */, | |||
| 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, | |||
| 97C147021CF9000F007C117D /* Info.plist */, | |||
| 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, | |||
| 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, | |||
| 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, | |||
| 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, | |||
| ); | |||
| path = Runner; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| /* End PBXGroup section */ | |||
| /* Begin PBXNativeTarget section */ | |||
| 331C8080294A63A400263BE5 /* RunnerTests */ = { | |||
| isa = PBXNativeTarget; | |||
| buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; | |||
| buildPhases = ( | |||
| 331C807D294A63A400263BE5 /* Sources */, | |||
| 331C807F294A63A400263BE5 /* Resources */, | |||
| ); | |||
| buildRules = ( | |||
| ); | |||
| dependencies = ( | |||
| 331C8086294A63A400263BE5 /* PBXTargetDependency */, | |||
| ); | |||
| name = RunnerTests; | |||
| productName = RunnerTests; | |||
| productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; | |||
| productType = "com.apple.product-type.bundle.unit-test"; | |||
| }; | |||
| 97C146ED1CF9000F007C117D /* Runner */ = { | |||
| isa = PBXNativeTarget; | |||
| buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; | |||
| buildPhases = ( | |||
| 9740EEB61CF901F6004384FC /* Run Script */, | |||
| 97C146EA1CF9000F007C117D /* Sources */, | |||
| 97C146EB1CF9000F007C117D /* Frameworks */, | |||
| 97C146EC1CF9000F007C117D /* Resources */, | |||
| 9705A1C41CF9048500538489 /* Embed Frameworks */, | |||
| 3B06AD1E1E4923F5004D2608 /* Thin Binary */, | |||
| ); | |||
| buildRules = ( | |||
| ); | |||
| dependencies = ( | |||
| ); | |||
| name = Runner; | |||
| productName = Runner; | |||
| productReference = 97C146EE1CF9000F007C117D /* Runner.app */; | |||
| productType = "com.apple.product-type.application"; | |||
| }; | |||
| /* End PBXNativeTarget section */ | |||
| /* Begin PBXProject section */ | |||
| 97C146E61CF9000F007C117D /* Project object */ = { | |||
| isa = PBXProject; | |||
| attributes = { | |||
| BuildIndependentTargetsInParallel = YES; | |||
| LastUpgradeCheck = 1510; | |||
| ORGANIZATIONNAME = ""; | |||
| TargetAttributes = { | |||
| 331C8080294A63A400263BE5 = { | |||
| CreatedOnToolsVersion = 14.0; | |||
| TestTargetID = 97C146ED1CF9000F007C117D; | |||
| }; | |||
| 97C146ED1CF9000F007C117D = { | |||
| CreatedOnToolsVersion = 7.3.1; | |||
| LastSwiftMigration = 1100; | |||
| }; | |||
| }; | |||
| }; | |||
| buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; | |||
| compatibilityVersion = "Xcode 9.3"; | |||
| developmentRegion = en; | |||
| hasScannedForEncodings = 0; | |||
| knownRegions = ( | |||
| en, | |||
| Base, | |||
| ); | |||
| mainGroup = 97C146E51CF9000F007C117D; | |||
| productRefGroup = 97C146EF1CF9000F007C117D /* Products */; | |||
| projectDirPath = ""; | |||
| projectRoot = ""; | |||
| targets = ( | |||
| 97C146ED1CF9000F007C117D /* Runner */, | |||
| 331C8080294A63A400263BE5 /* RunnerTests */, | |||
| ); | |||
| }; | |||
| /* End PBXProject section */ | |||
| /* Begin PBXResourcesBuildPhase section */ | |||
| 331C807F294A63A400263BE5 /* Resources */ = { | |||
| isa = PBXResourcesBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| 97C146EC1CF9000F007C117D /* Resources */ = { | |||
| isa = PBXResourcesBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, | |||
| 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, | |||
| 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, | |||
| 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| /* End PBXResourcesBuildPhase section */ | |||
| /* Begin PBXShellScriptBuildPhase section */ | |||
| 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { | |||
| isa = PBXShellScriptBuildPhase; | |||
| alwaysOutOfDate = 1; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| ); | |||
| inputPaths = ( | |||
| "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", | |||
| ); | |||
| name = "Thin Binary"; | |||
| outputPaths = ( | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| shellPath = /bin/sh; | |||
| shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; | |||
| }; | |||
| 9740EEB61CF901F6004384FC /* Run Script */ = { | |||
| isa = PBXShellScriptBuildPhase; | |||
| alwaysOutOfDate = 1; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| ); | |||
| inputPaths = ( | |||
| ); | |||
| name = "Run Script"; | |||
| outputPaths = ( | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| shellPath = /bin/sh; | |||
| shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; | |||
| }; | |||
| /* End PBXShellScriptBuildPhase section */ | |||
| /* Begin PBXSourcesBuildPhase section */ | |||
| 331C807D294A63A400263BE5 /* Sources */ = { | |||
| isa = PBXSourcesBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| 97C146EA1CF9000F007C117D /* Sources */ = { | |||
| isa = PBXSourcesBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, | |||
| 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| /* End PBXSourcesBuildPhase section */ | |||
| /* Begin PBXTargetDependency section */ | |||
| 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { | |||
| isa = PBXTargetDependency; | |||
| target = 97C146ED1CF9000F007C117D /* Runner */; | |||
| targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; | |||
| }; | |||
| /* End PBXTargetDependency section */ | |||
| /* Begin PBXVariantGroup section */ | |||
| 97C146FA1CF9000F007C117D /* Main.storyboard */ = { | |||
| isa = PBXVariantGroup; | |||
| children = ( | |||
| 97C146FB1CF9000F007C117D /* Base */, | |||
| ); | |||
| name = Main.storyboard; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { | |||
| isa = PBXVariantGroup; | |||
| children = ( | |||
| 97C147001CF9000F007C117D /* Base */, | |||
| ); | |||
| name = LaunchScreen.storyboard; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| /* End PBXVariantGroup section */ | |||
| /* Begin XCBuildConfiguration section */ | |||
| 249021D3217E4FDB00AE95B9 /* Profile */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| ALWAYS_SEARCH_USER_PATHS = NO; | |||
| ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; | |||
| CLANG_ANALYZER_NONNULL = YES; | |||
| CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; | |||
| CLANG_CXX_LIBRARY = "libc++"; | |||
| CLANG_ENABLE_MODULES = YES; | |||
| CLANG_ENABLE_OBJC_ARC = YES; | |||
| CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | |||
| CLANG_WARN_BOOL_CONVERSION = YES; | |||
| CLANG_WARN_COMMA = YES; | |||
| CLANG_WARN_CONSTANT_CONVERSION = YES; | |||
| CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; | |||
| CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | |||
| CLANG_WARN_EMPTY_BODY = YES; | |||
| CLANG_WARN_ENUM_CONVERSION = YES; | |||
| CLANG_WARN_INFINITE_RECURSION = YES; | |||
| CLANG_WARN_INT_CONVERSION = YES; | |||
| CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; | |||
| CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; | |||
| CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; | |||
| CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | |||
| CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; | |||
| CLANG_WARN_STRICT_PROTOTYPES = YES; | |||
| CLANG_WARN_SUSPICIOUS_MOVE = YES; | |||
| CLANG_WARN_UNREACHABLE_CODE = YES; | |||
| CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | |||
| "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; | |||
| COPY_PHASE_STRIP = NO; | |||
| DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; | |||
| ENABLE_NS_ASSERTIONS = NO; | |||
| ENABLE_STRICT_OBJC_MSGSEND = YES; | |||
| ENABLE_USER_SCRIPT_SANDBOXING = NO; | |||
| GCC_C_LANGUAGE_STANDARD = gnu99; | |||
| GCC_NO_COMMON_BLOCKS = YES; | |||
| GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | |||
| GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | |||
| GCC_WARN_UNDECLARED_SELECTOR = YES; | |||
| GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | |||
| GCC_WARN_UNUSED_FUNCTION = YES; | |||
| GCC_WARN_UNUSED_VARIABLE = YES; | |||
| IPHONEOS_DEPLOYMENT_TARGET = 12.0; | |||
| MTL_ENABLE_DEBUG_INFO = NO; | |||
| SDKROOT = iphoneos; | |||
| SUPPORTED_PLATFORMS = iphoneos; | |||
| TARGETED_DEVICE_FAMILY = "1,2"; | |||
| VALIDATE_PRODUCT = YES; | |||
| }; | |||
| name = Profile; | |||
| }; | |||
| 249021D4217E4FDB00AE95B9 /* Profile */ = { | |||
| isa = XCBuildConfiguration; | |||
| baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; | |||
| buildSettings = { | |||
| ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | |||
| CLANG_ENABLE_MODULES = YES; | |||
| CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | |||
| ENABLE_BITCODE = NO; | |||
| INFOPLIST_FILE = Runner/Info.plist; | |||
| LD_RUNPATH_SEARCH_PATHS = ( | |||
| "$(inherited)", | |||
| "@executable_path/Frameworks", | |||
| ); | |||
| PRODUCT_BUNDLE_IDENTIFIER = com.example.qadirneyriz; | |||
| PRODUCT_NAME = "$(TARGET_NAME)"; | |||
| SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | |||
| SWIFT_VERSION = 5.0; | |||
| VERSIONING_SYSTEM = "apple-generic"; | |||
| }; | |||
| name = Profile; | |||
| }; | |||
| 331C8088294A63A400263BE5 /* Debug */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| BUNDLE_LOADER = "$(TEST_HOST)"; | |||
| CODE_SIGN_STYLE = Automatic; | |||
| CURRENT_PROJECT_VERSION = 1; | |||
| GENERATE_INFOPLIST_FILE = YES; | |||
| MARKETING_VERSION = 1.0; | |||
| PRODUCT_BUNDLE_IDENTIFIER = com.example.qadirneyriz.RunnerTests; | |||
| PRODUCT_NAME = "$(TARGET_NAME)"; | |||
| SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; | |||
| SWIFT_OPTIMIZATION_LEVEL = "-Onone"; | |||
| SWIFT_VERSION = 5.0; | |||
| TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; | |||
| }; | |||
| name = Debug; | |||
| }; | |||
| 331C8089294A63A400263BE5 /* Release */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| BUNDLE_LOADER = "$(TEST_HOST)"; | |||
| CODE_SIGN_STYLE = Automatic; | |||
| CURRENT_PROJECT_VERSION = 1; | |||
| GENERATE_INFOPLIST_FILE = YES; | |||
| MARKETING_VERSION = 1.0; | |||
| PRODUCT_BUNDLE_IDENTIFIER = com.example.qadirneyriz.RunnerTests; | |||
| PRODUCT_NAME = "$(TARGET_NAME)"; | |||
| SWIFT_VERSION = 5.0; | |||
| TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; | |||
| }; | |||
| name = Release; | |||
| }; | |||
| 331C808A294A63A400263BE5 /* Profile */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| BUNDLE_LOADER = "$(TEST_HOST)"; | |||
| CODE_SIGN_STYLE = Automatic; | |||
| CURRENT_PROJECT_VERSION = 1; | |||
| GENERATE_INFOPLIST_FILE = YES; | |||
| MARKETING_VERSION = 1.0; | |||
| PRODUCT_BUNDLE_IDENTIFIER = com.example.qadirneyriz.RunnerTests; | |||
| PRODUCT_NAME = "$(TARGET_NAME)"; | |||
| SWIFT_VERSION = 5.0; | |||
| TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; | |||
| }; | |||
| name = Profile; | |||
| }; | |||
| 97C147031CF9000F007C117D /* Debug */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| ALWAYS_SEARCH_USER_PATHS = NO; | |||
| ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; | |||
| CLANG_ANALYZER_NONNULL = YES; | |||
| CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; | |||
| CLANG_CXX_LIBRARY = "libc++"; | |||
| CLANG_ENABLE_MODULES = YES; | |||
| CLANG_ENABLE_OBJC_ARC = YES; | |||
| CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | |||
| CLANG_WARN_BOOL_CONVERSION = YES; | |||
| CLANG_WARN_COMMA = YES; | |||
| CLANG_WARN_CONSTANT_CONVERSION = YES; | |||
| CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; | |||
| CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | |||
| CLANG_WARN_EMPTY_BODY = YES; | |||
| CLANG_WARN_ENUM_CONVERSION = YES; | |||
| CLANG_WARN_INFINITE_RECURSION = YES; | |||
| CLANG_WARN_INT_CONVERSION = YES; | |||
| CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; | |||
| CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; | |||
| CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; | |||
| CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | |||
| CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; | |||
| CLANG_WARN_STRICT_PROTOTYPES = YES; | |||
| CLANG_WARN_SUSPICIOUS_MOVE = YES; | |||
| CLANG_WARN_UNREACHABLE_CODE = YES; | |||
| CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | |||
| "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; | |||
| COPY_PHASE_STRIP = NO; | |||
| DEBUG_INFORMATION_FORMAT = dwarf; | |||
| ENABLE_STRICT_OBJC_MSGSEND = YES; | |||
| ENABLE_TESTABILITY = YES; | |||
| ENABLE_USER_SCRIPT_SANDBOXING = NO; | |||
| GCC_C_LANGUAGE_STANDARD = gnu99; | |||
| GCC_DYNAMIC_NO_PIC = NO; | |||
| GCC_NO_COMMON_BLOCKS = YES; | |||
| GCC_OPTIMIZATION_LEVEL = 0; | |||
| GCC_PREPROCESSOR_DEFINITIONS = ( | |||
| "DEBUG=1", | |||
| "$(inherited)", | |||
| ); | |||
| GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | |||
| GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | |||
| GCC_WARN_UNDECLARED_SELECTOR = YES; | |||
| GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | |||
| GCC_WARN_UNUSED_FUNCTION = YES; | |||
| GCC_WARN_UNUSED_VARIABLE = YES; | |||
| IPHONEOS_DEPLOYMENT_TARGET = 12.0; | |||
| MTL_ENABLE_DEBUG_INFO = YES; | |||
| ONLY_ACTIVE_ARCH = YES; | |||
| SDKROOT = iphoneos; | |||
| TARGETED_DEVICE_FAMILY = "1,2"; | |||
| }; | |||
| name = Debug; | |||
| }; | |||
| 97C147041CF9000F007C117D /* Release */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| ALWAYS_SEARCH_USER_PATHS = NO; | |||
| ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; | |||
| CLANG_ANALYZER_NONNULL = YES; | |||
| CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; | |||
| CLANG_CXX_LIBRARY = "libc++"; | |||
| CLANG_ENABLE_MODULES = YES; | |||
| CLANG_ENABLE_OBJC_ARC = YES; | |||
| CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | |||
| CLANG_WARN_BOOL_CONVERSION = YES; | |||
| CLANG_WARN_COMMA = YES; | |||
| CLANG_WARN_CONSTANT_CONVERSION = YES; | |||
| CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; | |||
| CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | |||
| CLANG_WARN_EMPTY_BODY = YES; | |||
| CLANG_WARN_ENUM_CONVERSION = YES; | |||
| CLANG_WARN_INFINITE_RECURSION = YES; | |||
| CLANG_WARN_INT_CONVERSION = YES; | |||
| CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; | |||
| CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; | |||
| CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; | |||
| CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | |||
| CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; | |||
| CLANG_WARN_STRICT_PROTOTYPES = YES; | |||
| CLANG_WARN_SUSPICIOUS_MOVE = YES; | |||
| CLANG_WARN_UNREACHABLE_CODE = YES; | |||
| CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | |||
| "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; | |||
| COPY_PHASE_STRIP = NO; | |||
| DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; | |||
| ENABLE_NS_ASSERTIONS = NO; | |||
| ENABLE_STRICT_OBJC_MSGSEND = YES; | |||
| ENABLE_USER_SCRIPT_SANDBOXING = NO; | |||
| GCC_C_LANGUAGE_STANDARD = gnu99; | |||
| GCC_NO_COMMON_BLOCKS = YES; | |||
| GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | |||
| GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | |||
| GCC_WARN_UNDECLARED_SELECTOR = YES; | |||
| GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | |||
| GCC_WARN_UNUSED_FUNCTION = YES; | |||
| GCC_WARN_UNUSED_VARIABLE = YES; | |||
| IPHONEOS_DEPLOYMENT_TARGET = 12.0; | |||
| MTL_ENABLE_DEBUG_INFO = NO; | |||
| SDKROOT = iphoneos; | |||
| SUPPORTED_PLATFORMS = iphoneos; | |||
| SWIFT_COMPILATION_MODE = wholemodule; | |||
| SWIFT_OPTIMIZATION_LEVEL = "-O"; | |||
| TARGETED_DEVICE_FAMILY = "1,2"; | |||
| VALIDATE_PRODUCT = YES; | |||
| }; | |||
| name = Release; | |||
| }; | |||
| 97C147061CF9000F007C117D /* Debug */ = { | |||
| isa = XCBuildConfiguration; | |||
| baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; | |||
| buildSettings = { | |||
| ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | |||
| CLANG_ENABLE_MODULES = YES; | |||
| CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | |||
| ENABLE_BITCODE = NO; | |||
| INFOPLIST_FILE = Runner/Info.plist; | |||
| LD_RUNPATH_SEARCH_PATHS = ( | |||
| "$(inherited)", | |||
| "@executable_path/Frameworks", | |||
| ); | |||
| PRODUCT_BUNDLE_IDENTIFIER = com.example.qadirneyriz; | |||
| PRODUCT_NAME = "$(TARGET_NAME)"; | |||
| SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | |||
| SWIFT_OPTIMIZATION_LEVEL = "-Onone"; | |||
| SWIFT_VERSION = 5.0; | |||
| VERSIONING_SYSTEM = "apple-generic"; | |||
| }; | |||
| name = Debug; | |||
| }; | |||
| 97C147071CF9000F007C117D /* Release */ = { | |||
| isa = XCBuildConfiguration; | |||
| baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; | |||
| buildSettings = { | |||
| ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | |||
| CLANG_ENABLE_MODULES = YES; | |||
| CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | |||
| ENABLE_BITCODE = NO; | |||
| INFOPLIST_FILE = Runner/Info.plist; | |||
| LD_RUNPATH_SEARCH_PATHS = ( | |||
| "$(inherited)", | |||
| "@executable_path/Frameworks", | |||
| ); | |||
| PRODUCT_BUNDLE_IDENTIFIER = com.example.qadirneyriz; | |||
| PRODUCT_NAME = "$(TARGET_NAME)"; | |||
| SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | |||
| SWIFT_VERSION = 5.0; | |||
| VERSIONING_SYSTEM = "apple-generic"; | |||
| }; | |||
| name = Release; | |||
| }; | |||
| /* End XCBuildConfiguration section */ | |||
| /* Begin XCConfigurationList section */ | |||
| 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { | |||
| isa = XCConfigurationList; | |||
| buildConfigurations = ( | |||
| 331C8088294A63A400263BE5 /* Debug */, | |||
| 331C8089294A63A400263BE5 /* Release */, | |||
| 331C808A294A63A400263BE5 /* Profile */, | |||
| ); | |||
| defaultConfigurationIsVisible = 0; | |||
| defaultConfigurationName = Release; | |||
| }; | |||
| 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { | |||
| isa = XCConfigurationList; | |||
| buildConfigurations = ( | |||
| 97C147031CF9000F007C117D /* Debug */, | |||
| 97C147041CF9000F007C117D /* Release */, | |||
| 249021D3217E4FDB00AE95B9 /* Profile */, | |||
| ); | |||
| defaultConfigurationIsVisible = 0; | |||
| defaultConfigurationName = Release; | |||
| }; | |||
| 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { | |||
| isa = XCConfigurationList; | |||
| buildConfigurations = ( | |||
| 97C147061CF9000F007C117D /* Debug */, | |||
| 97C147071CF9000F007C117D /* Release */, | |||
| 249021D4217E4FDB00AE95B9 /* Profile */, | |||
| ); | |||
| defaultConfigurationIsVisible = 0; | |||
| defaultConfigurationName = Release; | |||
| }; | |||
| /* End XCConfigurationList section */ | |||
| }; | |||
| rootObject = 97C146E61CF9000F007C117D /* Project object */; | |||
| } | |||
| @@ -0,0 +1,7 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <Workspace | |||
| version = "1.0"> | |||
| <FileRef | |||
| location = "self:"> | |||
| </FileRef> | |||
| </Workspace> | |||
| @@ -0,0 +1,8 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||
| <plist version="1.0"> | |||
| <dict> | |||
| <key>IDEDidComputeMac32BitWarning</key> | |||
| <true/> | |||
| </dict> | |||
| </plist> | |||
| @@ -0,0 +1,8 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||
| <plist version="1.0"> | |||
| <dict> | |||
| <key>PreviewsEnabled</key> | |||
| <false/> | |||
| </dict> | |||
| </plist> | |||
| @@ -0,0 +1,98 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <Scheme | |||
| LastUpgradeVersion = "1510" | |||
| version = "1.3"> | |||
| <BuildAction | |||
| parallelizeBuildables = "YES" | |||
| buildImplicitDependencies = "YES"> | |||
| <BuildActionEntries> | |||
| <BuildActionEntry | |||
| buildForTesting = "YES" | |||
| buildForRunning = "YES" | |||
| buildForProfiling = "YES" | |||
| buildForArchiving = "YES" | |||
| buildForAnalyzing = "YES"> | |||
| <BuildableReference | |||
| BuildableIdentifier = "primary" | |||
| BlueprintIdentifier = "97C146ED1CF9000F007C117D" | |||
| BuildableName = "Runner.app" | |||
| BlueprintName = "Runner" | |||
| ReferencedContainer = "container:Runner.xcodeproj"> | |||
| </BuildableReference> | |||
| </BuildActionEntry> | |||
| </BuildActionEntries> | |||
| </BuildAction> | |||
| <TestAction | |||
| buildConfiguration = "Debug" | |||
| selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" | |||
| selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" | |||
| shouldUseLaunchSchemeArgsEnv = "YES"> | |||
| <MacroExpansion> | |||
| <BuildableReference | |||
| BuildableIdentifier = "primary" | |||
| BlueprintIdentifier = "97C146ED1CF9000F007C117D" | |||
| BuildableName = "Runner.app" | |||
| BlueprintName = "Runner" | |||
| ReferencedContainer = "container:Runner.xcodeproj"> | |||
| </BuildableReference> | |||
| </MacroExpansion> | |||
| <Testables> | |||
| <TestableReference | |||
| skipped = "NO" | |||
| parallelizable = "YES"> | |||
| <BuildableReference | |||
| BuildableIdentifier = "primary" | |||
| BlueprintIdentifier = "331C8080294A63A400263BE5" | |||
| BuildableName = "RunnerTests.xctest" | |||
| BlueprintName = "RunnerTests" | |||
| ReferencedContainer = "container:Runner.xcodeproj"> | |||
| </BuildableReference> | |||
| </TestableReference> | |||
| </Testables> | |||
| </TestAction> | |||
| <LaunchAction | |||
| buildConfiguration = "Debug" | |||
| selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" | |||
| selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" | |||
| launchStyle = "0" | |||
| useCustomWorkingDirectory = "NO" | |||
| ignoresPersistentStateOnLaunch = "NO" | |||
| debugDocumentVersioning = "YES" | |||
| debugServiceExtension = "internal" | |||
| allowLocationSimulation = "YES"> | |||
| <BuildableProductRunnable | |||
| runnableDebuggingMode = "0"> | |||
| <BuildableReference | |||
| BuildableIdentifier = "primary" | |||
| BlueprintIdentifier = "97C146ED1CF9000F007C117D" | |||
| BuildableName = "Runner.app" | |||
| BlueprintName = "Runner" | |||
| ReferencedContainer = "container:Runner.xcodeproj"> | |||
| </BuildableReference> | |||
| </BuildableProductRunnable> | |||
| </LaunchAction> | |||
| <ProfileAction | |||
| buildConfiguration = "Profile" | |||
| shouldUseLaunchSchemeArgsEnv = "YES" | |||
| savedToolIdentifier = "" | |||
| useCustomWorkingDirectory = "NO" | |||
| debugDocumentVersioning = "YES"> | |||
| <BuildableProductRunnable | |||
| runnableDebuggingMode = "0"> | |||
| <BuildableReference | |||
| BuildableIdentifier = "primary" | |||
| BlueprintIdentifier = "97C146ED1CF9000F007C117D" | |||
| BuildableName = "Runner.app" | |||
| BlueprintName = "Runner" | |||
| ReferencedContainer = "container:Runner.xcodeproj"> | |||
| </BuildableReference> | |||
| </BuildableProductRunnable> | |||
| </ProfileAction> | |||
| <AnalyzeAction | |||
| buildConfiguration = "Debug"> | |||
| </AnalyzeAction> | |||
| <ArchiveAction | |||
| buildConfiguration = "Release" | |||
| revealArchiveInOrganizer = "YES"> | |||
| </ArchiveAction> | |||
| </Scheme> | |||
| @@ -0,0 +1,7 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <Workspace | |||
| version = "1.0"> | |||
| <FileRef | |||
| location = "group:Runner.xcodeproj"> | |||
| </FileRef> | |||
| </Workspace> | |||
| @@ -0,0 +1,8 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||
| <plist version="1.0"> | |||
| <dict> | |||
| <key>IDEDidComputeMac32BitWarning</key> | |||
| <true/> | |||
| </dict> | |||
| </plist> | |||
| @@ -0,0 +1,8 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||
| <plist version="1.0"> | |||
| <dict> | |||
| <key>PreviewsEnabled</key> | |||
| <false/> | |||
| </dict> | |||
| </plist> | |||
| @@ -0,0 +1,13 @@ | |||
| import Flutter | |||
| import UIKit | |||
| @main | |||
| @objc class AppDelegate: FlutterAppDelegate { | |||
| override func application( | |||
| _ application: UIApplication, | |||
| didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? | |||
| ) -> Bool { | |||
| GeneratedPluginRegistrant.register(with: self) | |||
| return super.application(application, didFinishLaunchingWithOptions: launchOptions) | |||
| } | |||
| } | |||
| @@ -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" | |||
| } | |||
| } | |||
| @@ -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" | |||
| } | |||
| } | |||
| @@ -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. | |||
| @@ -0,0 +1,37 @@ | |||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
| <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> | |||
| <dependencies> | |||
| <deployment identifier="iOS"/> | |||
| <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/> | |||
| </dependencies> | |||
| <scenes> | |||
| <!--View Controller--> | |||
| <scene sceneID="EHf-IW-A2E"> | |||
| <objects> | |||
| <viewController id="01J-lp-oVM" sceneMemberID="viewController"> | |||
| <layoutGuides> | |||
| <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/> | |||
| <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/> | |||
| </layoutGuides> | |||
| <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> | |||
| <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | |||
| <subviews> | |||
| <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"> | |||
| </imageView> | |||
| </subviews> | |||
| <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |||
| <constraints> | |||
| <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/> | |||
| <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/> | |||
| </constraints> | |||
| </view> | |||
| </viewController> | |||
| <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> | |||
| </objects> | |||
| <point key="canvasLocation" x="53" y="375"/> | |||
| </scene> | |||
| </scenes> | |||
| <resources> | |||
| <image name="LaunchImage" width="168" height="185"/> | |||
| </resources> | |||
| </document> | |||
| @@ -0,0 +1,26 @@ | |||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
| <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r"> | |||
| <dependencies> | |||
| <deployment identifier="iOS"/> | |||
| <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/> | |||
| </dependencies> | |||
| <scenes> | |||
| <!--Flutter View Controller--> | |||
| <scene sceneID="tne-QT-ifu"> | |||
| <objects> | |||
| <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController"> | |||
| <layoutGuides> | |||
| <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/> | |||
| <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/> | |||
| </layoutGuides> | |||
| <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> | |||
| <rect key="frame" x="0.0" y="0.0" width="600" height="600"/> | |||
| <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | |||
| <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/> | |||
| </view> | |||
| </viewController> | |||
| <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> | |||
| </objects> | |||
| </scene> | |||
| </scenes> | |||
| </document> | |||
| @@ -0,0 +1,49 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||
| <plist version="1.0"> | |||
| <dict> | |||
| <key>CFBundleDevelopmentRegion</key> | |||
| <string>$(DEVELOPMENT_LANGUAGE)</string> | |||
| <key>CFBundleDisplayName</key> | |||
| <string>Qadirneyriz</string> | |||
| <key>CFBundleExecutable</key> | |||
| <string>$(EXECUTABLE_NAME)</string> | |||
| <key>CFBundleIdentifier</key> | |||
| <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | |||
| <key>CFBundleInfoDictionaryVersion</key> | |||
| <string>6.0</string> | |||
| <key>CFBundleName</key> | |||
| <string>qadirneyriz</string> | |||
| <key>CFBundlePackageType</key> | |||
| <string>APPL</string> | |||
| <key>CFBundleShortVersionString</key> | |||
| <string>$(FLUTTER_BUILD_NAME)</string> | |||
| <key>CFBundleSignature</key> | |||
| <string>????</string> | |||
| <key>CFBundleVersion</key> | |||
| <string>$(FLUTTER_BUILD_NUMBER)</string> | |||
| <key>LSRequiresIPhoneOS</key> | |||
| <true/> | |||
| <key>UILaunchStoryboardName</key> | |||
| <string>LaunchScreen</string> | |||
| <key>UIMainStoryboardFile</key> | |||
| <string>Main</string> | |||
| <key>UISupportedInterfaceOrientations</key> | |||
| <array> | |||
| <string>UIInterfaceOrientationPortrait</string> | |||
| <string>UIInterfaceOrientationLandscapeLeft</string> | |||
| <string>UIInterfaceOrientationLandscapeRight</string> | |||
| </array> | |||
| <key>UISupportedInterfaceOrientations~ipad</key> | |||
| <array> | |||
| <string>UIInterfaceOrientationPortrait</string> | |||
| <string>UIInterfaceOrientationPortraitUpsideDown</string> | |||
| <string>UIInterfaceOrientationLandscapeLeft</string> | |||
| <string>UIInterfaceOrientationLandscapeRight</string> | |||
| </array> | |||
| <key>CADisableMinimumFrameDurationOnPhone</key> | |||
| <true/> | |||
| <key>UIApplicationSupportsIndirectInputEvents</key> | |||
| <true/> | |||
| </dict> | |||
| </plist> | |||
| @@ -0,0 +1 @@ | |||
| #import "GeneratedPluginRegistrant.h" | |||
| @@ -0,0 +1,12 @@ | |||
| import Flutter | |||
| import UIKit | |||
| import XCTest | |||
| class RunnerTests: XCTestCase { | |||
| func testExample() { | |||
| // If you add code to the Runner application, consider adding tests here. | |||
| // See https://developer.apple.com/documentation/xctest for more information about using XCTest. | |||
| } | |||
| } | |||
| @@ -0,0 +1,3 @@ | |||
| arb-dir: lib/l10n | |||
| template-arb-file: app_en.arb | |||
| output-localization-file: app_localizations.dart | |||
| @@ -0,0 +1,14 @@ | |||
| import 'package:qadirneyriz/config/network_config.dart'; | |||
| import 'package:qadirneyriz/config/ui_config.dart'; | |||
| class AppConfig { | |||
| final UIConfig ui; | |||
| final NetworkConfig network; | |||
| const AppConfig({ | |||
| required this.ui, | |||
| required this.network, | |||
| }); | |||
| } | |||
| @@ -0,0 +1,9 @@ | |||
| import 'package:qadirneyriz/config/app_config.dart'; | |||
| import 'package:qadirneyriz/config/network_config.dart'; | |||
| import 'package:qadirneyriz/config/ui_config.dart'; | |||
| const config = AppConfig( | |||
| ui: UIConfig(), | |||
| network: NetworkConfig(), | |||
| ); | |||
| @@ -0,0 +1,4 @@ | |||
| class NetworkConfig { | |||
| final baseUrl = 'https://api.nghsco.com/api/'; | |||
| const NetworkConfig(); | |||
| } | |||
| @@ -0,0 +1,9 @@ | |||
| import 'package:flutter/material.dart'; | |||
| class UIConfig { | |||
| final Color mainGreen = const Color(0xff0A8754); | |||
| final Color buttongreen = const Color(0xff04A54F); | |||
| final Color secendGreen = const Color.fromARGB(255, 75, 173, 78); | |||
| final Color mainGray = const Color(0xff333333); | |||
| const UIConfig(); | |||
| } | |||
| @@ -0,0 +1,250 @@ | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | |||
| import 'package:font_awesome_flutter/font_awesome_flutter.dart'; | |||
| import 'package:provider/provider.dart'; | |||
| import 'package:qadirneyriz/config/config.dart'; | |||
| import 'package:qadirneyriz/screens/auth/state/state.dart'; | |||
| import 'package:qadirneyriz/screens/home/screen.dart'; | |||
| import 'package:qadirneyriz/screens/meeting/screen.dart'; | |||
| import 'package:qadirneyriz/setting/setting.dart'; | |||
| class CustomDrawerNavigation extends StatefulWidget { | |||
| final int activeTab; | |||
| const CustomDrawerNavigation({super.key, required this.activeTab}); | |||
| @override | |||
| _CustomDrawerNavigationState createState() => _CustomDrawerNavigationState(); | |||
| } | |||
| class _CustomDrawerNavigationState extends State<CustomDrawerNavigation> { | |||
| late final PageController _pageController; | |||
| int _selectedIndex = 0; | |||
| final String language = setting.userLocalDb.getUser().language; | |||
| String? selectedLanguage; // زبان پیشفرض فارسی | |||
| late AuthState state; | |||
| @override | |||
| void initState() { | |||
| super.initState(); | |||
| state = Provider.of(context, listen: false); | |||
| selectedLanguage = language; | |||
| _selectedIndex = widget.activeTab; | |||
| _pageController = PageController(initialPage: _selectedIndex); | |||
| } | |||
| final List<Widget> _bottomBarPages = [ | |||
| const HomeScreen(), | |||
| const MeetingsScreen() | |||
| // Add more screens here | |||
| ]; | |||
| void _onItemTapped(int index) { | |||
| setState(() { | |||
| _selectedIndex = index; | |||
| }); | |||
| _pageController.jumpToPage(index); | |||
| Navigator.pop(context); // Close the drawer | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Scaffold( | |||
| drawer: Consumer<AuthState>( | |||
| builder: (context, value, child) { | |||
| return Drawer( | |||
| child: Column( | |||
| crossAxisAlignment: CrossAxisAlignment.start, | |||
| children: <Widget>[ | |||
| Padding( | |||
| padding: const EdgeInsets.only(left: 16.0, top: 40), | |||
| child: Image.asset( | |||
| 'assets/images/iconinappbar.png', // مسیر لوگوی شما | |||
| height: 60, | |||
| ), | |||
| ), | |||
| NewSessionButton(), | |||
| Expanded( | |||
| child: ListView( | |||
| padding: EdgeInsets.zero, | |||
| children: <Widget>[ | |||
| _buildDrawerItem( | |||
| icon: FontAwesomeIcons.house, | |||
| text: 'خانه', | |||
| index: 0, | |||
| ), | |||
| _buildDrawerItem( | |||
| icon: FontAwesomeIcons.pencil, | |||
| text: 'جلسات', | |||
| index: 1, | |||
| ), | |||
| _buildDrawerItem( | |||
| icon: FontAwesomeIcons.pencil, | |||
| text: 'ملاقات ها', | |||
| index: 2, | |||
| ), | |||
| _buildDrawerItem( | |||
| icon: FontAwesomeIcons.pencil, | |||
| text: 'گزارشات', | |||
| index: 3, | |||
| ), | |||
| Padding( | |||
| padding: const EdgeInsets.all(8.0), | |||
| child: Container( | |||
| decoration: BoxDecoration( | |||
| color: Colors.grey[300], | |||
| borderRadius: BorderRadius.circular(10), | |||
| ), | |||
| child: Row( | |||
| mainAxisAlignment: MainAxisAlignment.spaceEvenly, | |||
| children: [ | |||
| _buildLanguageButton('fa', 'فارسی', () { | |||
| value.setLocale('fa'); | |||
| }), | |||
| _buildLanguageButton('en', 'English', () { | |||
| value.setLocale('en'); | |||
| }), | |||
| ], | |||
| ), | |||
| ), | |||
| ) | |||
| ], | |||
| ), | |||
| ), | |||
| const Divider(), | |||
| ], | |||
| ), | |||
| ); | |||
| }, | |||
| ), | |||
| body: PageView( | |||
| controller: _pageController, | |||
| physics: const NeverScrollableScrollPhysics(), | |||
| children: _bottomBarPages, | |||
| ), | |||
| ); | |||
| } | |||
| Widget _buildDrawerItem( | |||
| {required IconData icon, required String text, required int index}) { | |||
| bool isSelected = _selectedIndex == index; | |||
| return Padding( | |||
| padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 2), | |||
| child: Material( | |||
| color: isSelected | |||
| ? config.ui.mainGreen.withOpacity(.2) | |||
| : Colors.transparent, | |||
| borderRadius: BorderRadius.circular(8), | |||
| child: InkWell( | |||
| borderRadius: BorderRadius.circular(8), | |||
| onTap: () => _onItemTapped(index), | |||
| child: Container( | |||
| decoration: BoxDecoration( | |||
| borderRadius: BorderRadius.circular(8), | |||
| ), | |||
| child: ListTile( | |||
| leading: FaIcon( | |||
| icon, | |||
| size: 19, | |||
| color: isSelected ? config.ui.mainGreen : config.ui.mainGray, | |||
| ), | |||
| title: Text( | |||
| text, | |||
| style: TextStyle( | |||
| color: | |||
| isSelected ? config.ui.mainGreen : config.ui.mainGray, | |||
| fontWeight: | |||
| isSelected ? FontWeight.bold : FontWeight.normal, | |||
| fontSize: 15), | |||
| ), | |||
| selected: isSelected, | |||
| ), | |||
| ), | |||
| ), | |||
| ), | |||
| ); | |||
| } | |||
| Widget _buildLanguageButton( | |||
| String language, String text, void Function() onPressed) { | |||
| bool isSelected = selectedLanguage == language; | |||
| return ElevatedButton( | |||
| onPressed: () { | |||
| setState(() { | |||
| selectedLanguage = language; // بهروز کردن زبان انتخاب شده | |||
| }); | |||
| onPressed(); // اجرای متد تغییر زبان | |||
| }, | |||
| style: ElevatedButton.styleFrom( | |||
| backgroundColor: isSelected ? Colors.green : Colors.grey[300], | |||
| shape: RoundedRectangleBorder( | |||
| borderRadius: BorderRadius.circular(8.0), | |||
| ), | |||
| ), | |||
| child: Text( | |||
| text, | |||
| style: TextStyle( | |||
| color: isSelected ? Colors.white : Colors.black, | |||
| ), | |||
| ), | |||
| ); | |||
| } | |||
| @override | |||
| void dispose() { | |||
| _pageController.dispose(); | |||
| super.dispose(); | |||
| } | |||
| } | |||
| class NewSessionButton extends StatelessWidget { | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return ElevatedButton( | |||
| style: ElevatedButton.styleFrom( | |||
| backgroundColor: Colors.green, // رنگ لبهها | |||
| shape: RoundedRectangleBorder( | |||
| borderRadius: BorderRadius.circular(8), // گرد کردن گوشهها | |||
| ), | |||
| elevation: 0, // بدون سایه | |||
| padding: EdgeInsets.symmetric(vertical: 20, horizontal: 16), | |||
| ), | |||
| onPressed: () { | |||
| // کاری که باید انجام شود | |||
| }, | |||
| child: Column( | |||
| mainAxisSize: MainAxisSize.min, | |||
| children: [ | |||
| Icon( | |||
| Icons.person_outline, | |||
| color: Colors.white, // رنگ آیکون | |||
| size: 40, | |||
| ), | |||
| SizedBox(height: 8), | |||
| Text( | |||
| 'جلسه جدید', | |||
| style: TextStyle( | |||
| color: Colors.white, // رنگ متن | |||
| fontSize: 16, | |||
| fontWeight: FontWeight.bold, | |||
| ), | |||
| ), | |||
| ], | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| class CustomScalfod extends StatefulWidget { | |||
| const CustomScalfod({super.key}); | |||
| @override | |||
| State<CustomScalfod> createState() => _CustomScalfodState(); | |||
| } | |||
| class _CustomScalfodState extends State<CustomScalfod> { | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Scaffold(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,341 @@ | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:qadirneyriz/models/meetings/meetings_location_model.dart'; | |||
| import 'package:qadirneyriz/models/meetings/meetings_managers_model.dart'; | |||
| import 'package:qadirneyriz/models/meetings/meetings_subjects_model.dart'; | |||
| import 'package:qadirneyriz/models/meetings/meetings_users_model.dart'; | |||
| import 'package:qadirneyriz/screens/meeting/diolog_meetings_filters.dart'; | |||
| import 'package:qadirneyriz/setting/setting.dart'; | |||
| import 'package:qadirneyriz/utils/enums/status.dart'; | |||
| class GlobalState extends ChangeNotifier { | |||
| // users meetings | |||
| Status usersStatus = Status.empty; | |||
| List<UsersModel>? usersModel; | |||
| Future<Status> getUsers({bool refresh = false}) async { | |||
| usersStatus = Status.loading; | |||
| notifyListeners(); | |||
| if (refresh) { | |||
| usersStatus = Status.loading; | |||
| notifyListeners(); | |||
| } | |||
| if (usersModel != null && usersModel!.isNotEmpty) { | |||
| usersStatus = Status.ready; | |||
| try { | |||
| usersModel = await setting.globalServices.getUsers(); | |||
| if (usersModel != null) { | |||
| usersStatus = Status.ready; | |||
| } else { | |||
| usersStatus = Status.empty; | |||
| } | |||
| } catch (e) { | |||
| usersStatus = Status.error; | |||
| print('$e error usersModel'); | |||
| } | |||
| notifyListeners(); | |||
| } else { | |||
| try { | |||
| usersModel = await setting.globalServices.getUsers(); | |||
| if (usersModel != null) { | |||
| usersStatus = Status.ready; | |||
| } else { | |||
| usersStatus = Status.empty; | |||
| } | |||
| notifyListeners(); | |||
| } catch (e) { | |||
| usersStatus = Status.error; | |||
| print('$e error usersModel'); | |||
| } | |||
| } | |||
| notifyListeners(); | |||
| print('$usersStatus usersModel'); | |||
| return usersStatus; | |||
| } | |||
| // locations meetings | |||
| Status locationsStatus = Status.empty; | |||
| List<LocationsModel>? locationsModel; | |||
| Future<Status> getLocations({bool refresh = false}) async { | |||
| locationsStatus = Status.loading; | |||
| notifyListeners(); | |||
| if (refresh) { | |||
| locationsStatus = Status.loading; | |||
| notifyListeners(); | |||
| } | |||
| if (locationsModel != null && locationsModel!.isNotEmpty) { | |||
| locationsStatus = Status.ready; | |||
| try { | |||
| locationsModel = await setting.globalServices.getLocation(); | |||
| if (locationsModel != null) { | |||
| locationsStatus = Status.ready; | |||
| } else { | |||
| locationsStatus = Status.empty; | |||
| } | |||
| } catch (e) { | |||
| locationsStatus = Status.error; | |||
| print(e); | |||
| } | |||
| notifyListeners(); | |||
| } else { | |||
| try { | |||
| locationsModel = await setting.globalServices.getLocation(); | |||
| if (locationsModel != null) { | |||
| locationsStatus = Status.ready; | |||
| } else { | |||
| locationsStatus = Status.empty; | |||
| } | |||
| notifyListeners(); | |||
| } catch (e) { | |||
| locationsStatus = Status.error; | |||
| print(e); | |||
| } | |||
| } | |||
| notifyListeners(); | |||
| print(locationsStatus); | |||
| return locationsStatus; | |||
| } | |||
| // subjects meetings | |||
| Status subjectsStatus = Status.empty; | |||
| List<SubjectsModel>? subjectsModel; | |||
| Future<Status> getSubjects({bool refresh = false}) async { | |||
| subjectsStatus = Status.loading; | |||
| notifyListeners(); | |||
| if (refresh) { | |||
| subjectsStatus = Status.loading; | |||
| notifyListeners(); | |||
| } | |||
| if (subjectsModel != null && subjectsModel!.isNotEmpty) { | |||
| subjectsStatus = Status.ready; | |||
| try { | |||
| subjectsModel = await setting.globalServices.getSubjects(); | |||
| if (subjectsModel != null) { | |||
| subjectsStatus = Status.ready; | |||
| } else { | |||
| subjectsStatus = Status.empty; | |||
| } | |||
| } catch (e) { | |||
| subjectsStatus = Status.error; | |||
| print(e); | |||
| } | |||
| notifyListeners(); | |||
| } else { | |||
| try { | |||
| subjectsModel = await setting.globalServices.getSubjects(); | |||
| if (subjectsModel != null) { | |||
| subjectsStatus = Status.ready; | |||
| } else { | |||
| subjectsStatus = Status.empty; | |||
| } | |||
| notifyListeners(); | |||
| } catch (e) { | |||
| subjectsStatus = Status.error; | |||
| print(e); | |||
| } | |||
| } | |||
| notifyListeners(); | |||
| print(subjectsStatus); | |||
| return subjectsStatus; | |||
| } | |||
| // managers meetings | |||
| Status meetingsManagerStatus = Status.empty; | |||
| List<MeetingsMangersModel>? meetingsManagerModel; | |||
| Future<Status> getMeetingsManager({bool refresh = false}) async { | |||
| meetingsManagerStatus = Status.loading; | |||
| notifyListeners(); | |||
| if (refresh) { | |||
| meetingsManagerStatus = Status.loading; | |||
| notifyListeners(); | |||
| } | |||
| if (meetingsManagerModel != null && meetingsManagerModel!.isNotEmpty) { | |||
| meetingsManagerStatus = Status.ready; | |||
| try { | |||
| meetingsManagerModel = await setting.globalServices.getManagers(); | |||
| if (meetingsManagerModel != null) { | |||
| meetingsManagerStatus = Status.ready; | |||
| } else { | |||
| meetingsManagerStatus = Status.empty; | |||
| } | |||
| } catch (e) { | |||
| meetingsManagerStatus = Status.error; | |||
| print(e); | |||
| } | |||
| notifyListeners(); | |||
| } else { | |||
| try { | |||
| meetingsManagerModel = await setting.globalServices.getManagers(); | |||
| if (meetingsManagerModel != null) { | |||
| meetingsManagerStatus = Status.ready; | |||
| } else { | |||
| meetingsManagerStatus = Status.empty; | |||
| } | |||
| notifyListeners(); | |||
| } catch (e) { | |||
| meetingsManagerStatus = Status.error; | |||
| print(e); | |||
| } | |||
| } | |||
| notifyListeners(); | |||
| print(meetingsManagerStatus); | |||
| return meetingsManagerStatus; | |||
| } | |||
| // statuses meetings | |||
| List<MeetingsStatus> meetingStatuses = [ | |||
| MeetingsStatus(id: 1, title: 'جلسات برگذار شده'), | |||
| MeetingsStatus(id: 2, title: 'جلسات موکول شده'), | |||
| MeetingsStatus(id: 3, title: 'جلسات لغو شده'), | |||
| MeetingsStatus(id: 4, title: 'جلسات منتظر برگذاری'), | |||
| ]; | |||
| // load all items together | |||
| Status allFiltersStatus = Status.empty; | |||
| Future<Status> getAllFiltersItems({bool refresh = false}) async { | |||
| if (_isDataAlreadyLoaded()) { | |||
| allFiltersStatus = Status.ready; | |||
| } else { | |||
| allFiltersStatus = Status.loading; | |||
| } | |||
| notifyListeners(); | |||
| await _fetchAllData(refresh: refresh); | |||
| _updateAllFiltersStatus(); | |||
| notifyListeners(); | |||
| return allFiltersStatus; | |||
| } | |||
| bool _isDataAlreadyLoaded() { | |||
| return locationsModel != null && | |||
| locationsModel!.isNotEmpty && | |||
| usersModel != null && | |||
| usersModel!.isNotEmpty && | |||
| meetingsManagerModel != null && | |||
| meetingsManagerModel!.isNotEmpty && | |||
| subjectsModel != null && | |||
| subjectsModel!.isNotEmpty; | |||
| } | |||
| Future<void> _fetchAllData({required bool refresh}) async { | |||
| await Future.wait([ | |||
| getLocations(refresh: refresh), | |||
| getMeetingsManager(refresh: refresh), | |||
| getSubjects(refresh: refresh), | |||
| getUsers(refresh: refresh), | |||
| ]); | |||
| } | |||
| void _updateAllFiltersStatus() { | |||
| if (locationsStatus == Status.ready && | |||
| subjectsStatus == Status.ready && | |||
| usersStatus == Status.ready && | |||
| meetingsManagerStatus == Status.ready) { | |||
| allFiltersStatus = Status.ready; | |||
| } else { | |||
| allFiltersStatus = Status.error; | |||
| } | |||
| } | |||
| // add new subject | |||
| Status statusAddNewSubject = Status.empty; | |||
| String? messageAddNewSubject; | |||
| Map? errorsAddNewSubject; | |||
| Future<Status> addNewSubject({required String subject}) async { | |||
| statusAddNewSubject = Status.loading; | |||
| notifyListeners(); | |||
| try { | |||
| final result = | |||
| await setting.globalServices.addNewSubject(subject: subject); | |||
| if (result.isOk) { | |||
| statusAddNewSubject = Status.ready; | |||
| messageAddNewSubject = result.message; | |||
| } else if (result.isOk == false) { | |||
| errorsAddNewSubject = result.errors; | |||
| messageAddNewSubject = result.message; | |||
| statusAddNewSubject = Status.error; | |||
| } else { | |||
| statusAddNewSubject = Status.error; | |||
| } | |||
| notifyListeners(); | |||
| } catch (e) { | |||
| statusAddNewSubject = Status.error; | |||
| } | |||
| notifyListeners(); | |||
| return statusAddNewSubject; | |||
| } | |||
| // add new address | |||
| Status statusAddNewAddress = Status.empty; | |||
| String? messageAddNewAddress; | |||
| Map? errorsAddNewAddress; | |||
| Future<Status> addNewAddress({required String address}) async { | |||
| statusAddNewAddress = Status.loading; | |||
| notifyListeners(); | |||
| try { | |||
| final result = | |||
| await setting.globalServices.addNewLocation(address: address); | |||
| if (result.isOk) { | |||
| statusAddNewAddress = Status.ready; | |||
| messageAddNewAddress = result.message; | |||
| } else if (result.isOk == false) { | |||
| errorsAddNewAddress = result.errors; | |||
| messageAddNewAddress = result.message; | |||
| statusAddNewAddress = Status.error; | |||
| } else { | |||
| statusAddNewAddress = Status.error; | |||
| } | |||
| notifyListeners(); | |||
| } catch (e) { | |||
| statusAddNewAddress = Status.error; | |||
| } | |||
| notifyListeners(); | |||
| return statusAddNewAddress; | |||
| } | |||
| // add new user | |||
| Status statusAddNewUser = Status.empty; | |||
| String? messageAddNewUser; | |||
| Map? errorsAddNewUser; | |||
| Future<Status> addNewUser( | |||
| {required String name, | |||
| required String mobile, | |||
| required String password, | |||
| required int role}) async { | |||
| statusAddNewUser = Status.loading; | |||
| notifyListeners(); | |||
| try { | |||
| final result = await setting.globalServices.addNewUser( | |||
| mobile: mobile, name: name, password: password, role: role); | |||
| if (result.isOk) { | |||
| statusAddNewUser = Status.ready; | |||
| messageAddNewUser = result.message; | |||
| } else if (result.isOk == false) { | |||
| errorsAddNewUser = result.errors; | |||
| messageAddNewUser = result.message; | |||
| statusAddNewUser = Status.error; | |||
| } else { | |||
| statusAddNewUser = Status.error; | |||
| } | |||
| notifyListeners(); | |||
| } catch (e) { | |||
| statusAddNewUser = Status.error; | |||
| } | |||
| notifyListeners(); | |||
| return statusAddNewUser; | |||
| } | |||
| } | |||
| @@ -0,0 +1,43 @@ | |||
| { | |||
| "helloWorld": "Hello World!", | |||
| "phonenumber":"PhoneNumber", | |||
| "hintphonenumber":"Please enter your phonenumber ...", | |||
| "hintpass":"Please enter your password ...", | |||
| "password":"Password", | |||
| "submit":"Submit", | |||
| "submitwithotp":"Submit with OTP", | |||
| "submitwithphone":"Submit with phonenumber", | |||
| "enterotp":"Enter OTP", | |||
| "an4digitotp":"An 4 digit OTP has been sent to", | |||
| "loading":"loading ...", | |||
| "phoneerror":"Please enter your phonenumber!", | |||
| "passerror":"Please enter your password!", | |||
| "haserror":"Something went wrong. Please try again", | |||
| "resend":"Resend code!", | |||
| "today":"Today", | |||
| "to":"To", | |||
| "reports":"Reports", | |||
| "meetings":"Meetings", | |||
| "events":"Events", | |||
| "exit":"Exit", | |||
| "appname":"Foolad QadirNeyriz", | |||
| "nomeetingfortoday":"No Meetings for today", | |||
| "todaymeetings":"Today Meetings", | |||
| "empty":"The list is empty.", | |||
| "back":"Back", | |||
| "searchFor":"جستوجو براساس", | |||
| "date":"تاریخ", | |||
| "location":"مکان", | |||
| "meetingmanager":"مدیر جلسه", | |||
| "subject":"موضوع", | |||
| "donemeetings":"جلسات برگذار شده", | |||
| "adjournedmeetings":"جلسات موکول شده", | |||
| "canceldmeetings":"جلسات لغو شده", | |||
| "meetingswaitingtobeheld":"جلسات منتظر برگذاری", | |||
| "selectdate":"انتخاب تاریخ", | |||
| "editmeeting":"ویرایش جلسه", | |||
| "meetingsubject":"موضوع جلسه", | |||
| "clock":"ساعت", | |||
| "users":"کاربران", | |||
| "selectusers":"انتخاب کاربران" | |||
| } | |||
| @@ -0,0 +1,43 @@ | |||
| { | |||
| "helloWorld": "Hello World!", | |||
| "phonenumber":"شماره موبایل", | |||
| "hintphonenumber":"لطفا شماره موبایل خود را وارد کنید ...", | |||
| "hintpass":"لطفا رمزعبور خود را وارد کنید ...", | |||
| "password":"رمزعبور", | |||
| "submit":"ثبت", | |||
| "submitwithotp":"ورود با رمز یکبارمصرف", | |||
| "submitwithphone":"ورود با شماره موبایل", | |||
| "enterotp":"رمز یکبارمصرف", | |||
| "an4digitotp":"رمز ۴ رقمی ارسال شده به شماره ", | |||
| "loading":"صبر کنید ...", | |||
| "phoneerror":"لطفا شماره خود را وارد کنید!", | |||
| "passerror":"لطفا رمزعبور خود را وارد کنید!", | |||
| "haserror":"مشکلی رخ داده است! دوباره تلاش کنید!", | |||
| "resend":"دوباره کد را بفرست!", | |||
| "today":"امروز", | |||
| "to":"تا", | |||
| "reports":"گزارشات", | |||
| "meetings":"جلسات", | |||
| "events":"ملاقات ها", | |||
| "exit":"خروج", | |||
| "appname":"فولاد غدیر نیریز", | |||
| "nomeetingfortoday":"برای امروز جلسه ایی تعریف نشده است.", | |||
| "todaymeetings":"جلسه های امروز", | |||
| "empty":"داده ایی وجود ندارد.", | |||
| "back":"بازگشست", | |||
| "searchFor":"جستوجو براساس", | |||
| "date":"تاریخ", | |||
| "location":"مکان", | |||
| "meetingmanager":"مدیر جلسه", | |||
| "subject":"موضوع", | |||
| "donemeetings":"جلسات برگذار شده", | |||
| "adjournedmeetings":"جلسات موکول شده", | |||
| "canceldmeetings":"جلسات لغو شده", | |||
| "meetingswaitingtobeheld":"جلسات منتظر برگذاری", | |||
| "selectdate":"انتخاب تاریخ", | |||
| "editmeeting":"ویرایش جلسه", | |||
| "meetingsubject":"موضوع جلسه", | |||
| "clock":"ساعت", | |||
| "users":"کاربران", | |||
| "selectusers":"انتخاب کاربران" | |||
| } | |||
| @@ -0,0 +1,79 @@ | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter_localizations/flutter_localizations.dart'; | |||
| import 'package:hive_flutter/hive_flutter.dart'; | |||
| import 'package:provider/provider.dart'; | |||
| import 'package:qadirneyriz/global_state/global_state.dart'; | |||
| import 'package:qadirneyriz/router/router.dart'; | |||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | |||
| import 'package:qadirneyriz/screens/auth/state/state.dart'; | |||
| import 'package:qadirneyriz/screens/home/state.dart'; | |||
| import 'package:qadirneyriz/screens/meeting/state.dart'; | |||
| import 'package:qadirneyriz/setting/setting.dart'; | |||
| void main() async { | |||
| await Hive.initFlutter(); | |||
| await setting.userLocalDb.openBox(); | |||
| runApp( | |||
| MultiProvider( | |||
| providers: [ | |||
| ChangeNotifierProvider(create: (_) => GlobalState()), | |||
| ChangeNotifierProvider(create: (_) => AuthState()), | |||
| ChangeNotifierProvider(create: (_) => HomeState()), | |||
| ChangeNotifierProvider(create: (_) => MeetingsState()), | |||
| ], | |||
| child: const MyApp(), | |||
| ), | |||
| ); | |||
| } | |||
| class MyApp extends StatefulWidget { | |||
| const MyApp({super.key}); | |||
| @override | |||
| State<MyApp> createState() => _MyAppState(); | |||
| } | |||
| class _MyAppState extends State<MyApp> { | |||
| late AuthState state ; | |||
| String language = setting.userLocalDb.getUser().language; | |||
| @override | |||
| void initState() { | |||
| Future.delayed(Duration.zero, () async { | |||
| state = Provider.of(context, listen: false); | |||
| setState(() { | |||
| language = state.language; | |||
| }); | |||
| }); | |||
| super.initState(); | |||
| } | |||
| // This widget is the root of your application. | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Consumer<AuthState>( | |||
| builder: (context, value, child) { | |||
| return MaterialApp.router( | |||
| theme: ThemeData( | |||
| useMaterial3: true, | |||
| fontFamily: 'Font', | |||
| scaffoldBackgroundColor: Colors.white), | |||
| debugShowCheckedModeBanner: false, | |||
| routerDelegate: router.routerDelegate, | |||
| routeInformationParser: router.routeInformationParser, | |||
| routeInformationProvider: router.routeInformationProvider, | |||
| localizationsDelegates: const [ | |||
| AppLocalizations.delegate, // Add this line | |||
| GlobalMaterialLocalizations.delegate, | |||
| GlobalWidgetsLocalizations.delegate, | |||
| GlobalCupertinoLocalizations.delegate, | |||
| ], | |||
| locale: Locale(value.language), | |||
| supportedLocales: const [ | |||
| Locale('fa'), // Persian | |||
| Locale('en'), // English | |||
| ], | |||
| ); | |||
| }, | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,193 @@ | |||
| import 'dart:convert'; | |||
| class TodayMeetingModel { | |||
| List<Meeting>? meetings; | |||
| String? note; | |||
| TodayMeetingModel({ | |||
| this.meetings, | |||
| this.note, | |||
| }); | |||
| factory TodayMeetingModel.fromRawJson(String str) => TodayMeetingModel.fromJson(json.decode(str)); | |||
| String toRawJson() => json.encode(toJson()); | |||
| factory TodayMeetingModel.fromJson(Map<String, dynamic> json) => TodayMeetingModel( | |||
| meetings: json["meetings"] == null ? [] : List<Meeting>.from(json["meetings"]!.map((x) => Meeting.fromJson(x))), | |||
| note: json["note"], | |||
| ); | |||
| Map<String, dynamic> toJson() => { | |||
| "meetings": meetings == null ? [] : List<dynamic>.from(meetings!.map((x) => x.toJson())), | |||
| "note": note, | |||
| }; | |||
| } | |||
| class Meeting { | |||
| int? id; | |||
| int? locationsId; | |||
| int? subjectId; | |||
| int? managerId; | |||
| int? ownerId; | |||
| String? azHour; | |||
| String? taHour; | |||
| dynamic description; | |||
| int? status; | |||
| int? accepted; | |||
| DateTime? dateMeeting; | |||
| DateTime? endDate; | |||
| DateTime? createdAt; | |||
| DateTime? updatedAt; | |||
| String? dateJalali; | |||
| String? statusTxt; | |||
| String? az; | |||
| String? ta; | |||
| Location? location; | |||
| Subject? subject; | |||
| Meeting({ | |||
| this.id, | |||
| this.locationsId, | |||
| this.subjectId, | |||
| this.managerId, | |||
| this.ownerId, | |||
| this.azHour, | |||
| this.taHour, | |||
| this.description, | |||
| this.status, | |||
| this.accepted, | |||
| this.dateMeeting, | |||
| this.endDate, | |||
| this.createdAt, | |||
| this.updatedAt, | |||
| this.dateJalali, | |||
| this.statusTxt, | |||
| this.az, | |||
| this.ta, | |||
| this.location, | |||
| this.subject, | |||
| }); | |||
| factory Meeting.fromRawJson(String str) => Meeting.fromJson(json.decode(str)); | |||
| String toRawJson() => json.encode(toJson()); | |||
| factory Meeting.fromJson(Map<String, dynamic> json) => Meeting( | |||
| id: json["id"], | |||
| locationsId: json["locations_id"], | |||
| subjectId: json["subject_id"], | |||
| managerId: json["manager_id"], | |||
| ownerId: json["owner_id"], | |||
| azHour: json["az_hour"], | |||
| taHour: json["ta_hour"], | |||
| description: json["description"], | |||
| status: json["status"], | |||
| accepted: json["accepted"], | |||
| dateMeeting: json["date_meeting"] == null ? null : DateTime.parse(json["date_meeting"]), | |||
| endDate: json["end_date"] == null ? null : DateTime.parse(json["end_date"]), | |||
| createdAt: json["created_at"] == null ? null : DateTime.parse(json["created_at"]), | |||
| updatedAt: json["updated_at"] == null ? null : DateTime.parse(json["updated_at"]), | |||
| dateJalali: json["date_jalali"], | |||
| statusTxt: json["status_txt"], | |||
| az: json["az"], | |||
| ta: json["ta"], | |||
| location: json["location"] == null ? null : Location.fromJson(json["location"]), | |||
| subject: json["subject"] == null ? null : Subject.fromJson(json["subject"]), | |||
| ); | |||
| Map<String, dynamic> toJson() => { | |||
| "id": id, | |||
| "locations_id": locationsId, | |||
| "subject_id": subjectId, | |||
| "manager_id": managerId, | |||
| "owner_id": ownerId, | |||
| "az_hour": azHour, | |||
| "ta_hour": taHour, | |||
| "description": description, | |||
| "status": status, | |||
| "accepted": accepted, | |||
| "date_meeting": dateMeeting?.toIso8601String(), | |||
| "end_date": endDate?.toIso8601String(), | |||
| "created_at": createdAt?.toIso8601String(), | |||
| "updated_at": updatedAt?.toIso8601String(), | |||
| "date_jalali": dateJalali, | |||
| "status_txt": statusTxt, | |||
| "az": az, | |||
| "ta": ta, | |||
| "location": location?.toJson(), | |||
| "subject": subject?.toJson(), | |||
| }; | |||
| } | |||
| class Location { | |||
| int? id; | |||
| String? address; | |||
| String? addressEn; | |||
| DateTime? createdAt; | |||
| DateTime? updatedAt; | |||
| Location({ | |||
| this.id, | |||
| this.address, | |||
| this.addressEn, | |||
| this.createdAt, | |||
| this.updatedAt, | |||
| }); | |||
| factory Location.fromRawJson(String str) => Location.fromJson(json.decode(str)); | |||
| String toRawJson() => json.encode(toJson()); | |||
| factory Location.fromJson(Map<String, dynamic> json) => Location( | |||
| id: json["id"], | |||
| address: json["address"], | |||
| addressEn: json["address_en"], | |||
| createdAt: json["created_at"] == null ? null : DateTime.parse(json["created_at"]), | |||
| updatedAt: json["updated_at"] == null ? null : DateTime.parse(json["updated_at"]), | |||
| ); | |||
| Map<String, dynamic> toJson() => { | |||
| "id": id, | |||
| "address": address, | |||
| "address_en": addressEn, | |||
| "created_at": createdAt?.toIso8601String(), | |||
| "updated_at": updatedAt?.toIso8601String(), | |||
| }; | |||
| } | |||
| class Subject { | |||
| int? id; | |||
| dynamic subject; | |||
| dynamic subjectEn; | |||
| DateTime? createdAt; | |||
| DateTime? updatedAt; | |||
| Subject({ | |||
| this.id, | |||
| this.subject, | |||
| this.subjectEn, | |||
| this.createdAt, | |||
| this.updatedAt, | |||
| }); | |||
| factory Subject.fromRawJson(String str) => Subject.fromJson(json.decode(str)); | |||
| String toRawJson() => json.encode(toJson()); | |||
| factory Subject.fromJson(Map<String, dynamic> json) => Subject( | |||
| id: json["id"], | |||
| subject: json["subject"], | |||
| subjectEn: json["subject_en"], | |||
| createdAt: json["created_at"] == null ? null : DateTime.parse(json["created_at"]), | |||
| updatedAt: json["updated_at"] == null ? null : DateTime.parse(json["updated_at"]), | |||
| ); | |||
| Map<String, dynamic> toJson() => { | |||
| "id": id, | |||
| "subject": subject, | |||
| "subject_en": subjectEn, | |||
| "created_at": createdAt?.toIso8601String(), | |||
| "updated_at": updatedAt?.toIso8601String(), | |||
| }; | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| import 'dart:convert'; | |||
| class LocationsModel { | |||
| int? id; | |||
| String? address; | |||
| String? addressEn; | |||
| LocationsModel({ | |||
| this.id, | |||
| this.address, | |||
| this.addressEn, | |||
| }); | |||
| factory LocationsModel.fromRawJson(String str) => | |||
| LocationsModel.fromJson(json.decode(str)); | |||
| String toRawJson() => json.encode(toJson()); | |||
| factory LocationsModel.fromJson(Map<String, dynamic> json) => LocationsModel( | |||
| id: json["id"], | |||
| address: json["address"], | |||
| addressEn: json["address_en"], | |||
| ); | |||
| Map<String, dynamic> toJson() => { | |||
| "id": id, | |||
| "address": address, | |||
| "address_en": addressEn, | |||
| }; | |||
| } | |||
| @@ -0,0 +1,71 @@ | |||
| import 'dart:convert'; | |||
| class MeetingsMangersModel { | |||
| int? id; | |||
| String? name; | |||
| int? role; | |||
| String? mobile; | |||
| dynamic otp; | |||
| String? access; | |||
| dynamic managerId; | |||
| dynamic firebaseToken; | |||
| int? isBlock; | |||
| int? getSms; | |||
| DateTime? createdAt; | |||
| DateTime? updatedAt; | |||
| MeetingsMangersModel({ | |||
| this.id, | |||
| this.name, | |||
| this.role, | |||
| this.mobile, | |||
| this.otp, | |||
| this.access, | |||
| this.managerId, | |||
| this.firebaseToken, | |||
| this.isBlock, | |||
| this.getSms, | |||
| this.createdAt, | |||
| this.updatedAt, | |||
| }); | |||
| factory MeetingsMangersModel.fromRawJson(String str) => | |||
| MeetingsMangersModel.fromJson(json.decode(str)); | |||
| String toRawJson() => json.encode(toJson()); | |||
| factory MeetingsMangersModel.fromJson(Map<String, dynamic> json) => | |||
| MeetingsMangersModel( | |||
| id: json["id"], | |||
| name: json["name"], | |||
| role: json["role"], | |||
| mobile: json["mobile"], | |||
| otp: json["otp"], | |||
| access: json["access"], | |||
| managerId: json["manager_id"], | |||
| firebaseToken: json["firebase_token"], | |||
| isBlock: json["is_block"], | |||
| getSms: json["get_sms"], | |||
| createdAt: json["created_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["created_at"]), | |||
| updatedAt: json["updated_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["updated_at"]), | |||
| ); | |||
| Map<String, dynamic> toJson() => { | |||
| "id": id, | |||
| "name": name, | |||
| "role": role, | |||
| "mobile": mobile, | |||
| "otp": otp, | |||
| "access": access, | |||
| "manager_id": managerId, | |||
| "firebase_token": firebaseToken, | |||
| "is_block": isBlock, | |||
| "get_sms": getSms, | |||
| "created_at": createdAt?.toIso8601String(), | |||
| "updated_at": updatedAt?.toIso8601String(), | |||
| }; | |||
| } | |||
| @@ -0,0 +1,287 @@ | |||
| import 'dart:convert'; | |||
| class MeetingsModel { | |||
| List<Datum>? data; | |||
| MeetingsModel({ | |||
| this.data, | |||
| }); | |||
| factory MeetingsModel.fromRawJson(String str) => | |||
| MeetingsModel.fromJson(json.decode(str)); | |||
| String toRawJson() => json.encode(toJson()); | |||
| factory MeetingsModel.fromJson(Map<String, dynamic> json) => MeetingsModel( | |||
| data: json["data"] == null | |||
| ? [] | |||
| : List<Datum>.from(json["data"]!.map((x) => Datum.fromJson(x))), | |||
| ); | |||
| Map<String, dynamic> toJson() => { | |||
| "data": data == null | |||
| ? [] | |||
| : List<dynamic>.from(data!.map((x) => x.toJson())), | |||
| }; | |||
| hasData() => data!.isNotEmpty; | |||
| } | |||
| class Datum { | |||
| int? id; | |||
| int? locationsId; | |||
| int? subjectId; | |||
| int? managerId; | |||
| int? ownerId; | |||
| String? azHour; | |||
| String? taHour; | |||
| dynamic description; | |||
| int? status; | |||
| int? accepted; | |||
| DateTime? dateMeeting; | |||
| DateTime? endDate; | |||
| DateTime? createdAt; | |||
| DateTime? updatedAt; | |||
| String? dateJalali; | |||
| String? statusTxt; | |||
| String? az; | |||
| String? ta; | |||
| Location? location; | |||
| Subject? subject; | |||
| Manager? manager; | |||
| Datum({ | |||
| this.id, | |||
| this.locationsId, | |||
| this.subjectId, | |||
| this.managerId, | |||
| this.ownerId, | |||
| this.azHour, | |||
| this.taHour, | |||
| this.description, | |||
| this.status, | |||
| this.accepted, | |||
| this.dateMeeting, | |||
| this.endDate, | |||
| this.createdAt, | |||
| this.updatedAt, | |||
| this.dateJalali, | |||
| this.statusTxt, | |||
| this.az, | |||
| this.ta, | |||
| this.location, | |||
| this.subject, | |||
| this.manager, | |||
| }); | |||
| factory Datum.fromRawJson(String str) => Datum.fromJson(json.decode(str)); | |||
| String toRawJson() => json.encode(toJson()); | |||
| factory Datum.fromJson(Map<String, dynamic> json) => Datum( | |||
| id: json["id"], | |||
| locationsId: json["locations_id"], | |||
| subjectId: json["subject_id"], | |||
| managerId: json["manager_id"], | |||
| ownerId: json["owner_id"], | |||
| azHour: json["az_hour"], | |||
| taHour: json["ta_hour"], | |||
| description: json["description"], | |||
| status: json["status"], | |||
| accepted: json["accepted"], | |||
| dateMeeting: json["date_meeting"] == null | |||
| ? null | |||
| : DateTime.parse(json["date_meeting"]), | |||
| endDate: | |||
| json["end_date"] == null ? null : DateTime.parse(json["end_date"]), | |||
| createdAt: json["created_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["created_at"]), | |||
| updatedAt: json["updated_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["updated_at"]), | |||
| dateJalali: json["date_jalali"], | |||
| statusTxt: json["status_txt"], | |||
| az: json["az"], | |||
| ta: json["ta"], | |||
| location: json["location"] == null | |||
| ? null | |||
| : Location.fromJson(json["location"]), | |||
| subject: | |||
| json["subject"] == null ? null : Subject.fromJson(json["subject"]), | |||
| manager: | |||
| json["manager"] == null ? null : Manager.fromJson(json["manager"]), | |||
| ); | |||
| Map<String, dynamic> toJson() => { | |||
| "id": id, | |||
| "locations_id": locationsId, | |||
| "subject_id": subjectId, | |||
| "manager_id": managerId, | |||
| "owner_id": ownerId, | |||
| "az_hour": azHour, | |||
| "ta_hour": taHour, | |||
| "description": description, | |||
| "status": status, | |||
| "accepted": accepted, | |||
| "date_meeting": dateMeeting?.toIso8601String(), | |||
| "end_date": endDate?.toIso8601String(), | |||
| "created_at": createdAt?.toIso8601String(), | |||
| "updated_at": updatedAt?.toIso8601String(), | |||
| "date_jalali": dateJalali, | |||
| "status_txt": statusTxt, | |||
| "az": az, | |||
| "ta": ta, | |||
| "location": location?.toJson(), | |||
| "subject": subject?.toJson(), | |||
| "manager": manager?.toJson(), | |||
| }; | |||
| } | |||
| class Location { | |||
| int? id; | |||
| String? address; | |||
| String? addressEn; | |||
| DateTime? createdAt; | |||
| DateTime? updatedAt; | |||
| Location({ | |||
| this.id, | |||
| this.address, | |||
| this.addressEn, | |||
| this.createdAt, | |||
| this.updatedAt, | |||
| }); | |||
| factory Location.fromRawJson(String str) => | |||
| Location.fromJson(json.decode(str)); | |||
| String toRawJson() => json.encode(toJson()); | |||
| factory Location.fromJson(Map<String, dynamic> json) => Location( | |||
| id: json["id"], | |||
| address: json["address"], | |||
| addressEn: json["address_en"], | |||
| createdAt: json["created_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["created_at"]), | |||
| updatedAt: json["updated_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["updated_at"]), | |||
| ); | |||
| Map<String, dynamic> toJson() => { | |||
| "id": id, | |||
| "address": address, | |||
| "address_en": addressEn, | |||
| "created_at": createdAt?.toIso8601String(), | |||
| "updated_at": updatedAt?.toIso8601String(), | |||
| }; | |||
| } | |||
| class Manager { | |||
| int? id; | |||
| String? name; | |||
| int? role; | |||
| String? mobile; | |||
| String? otp; | |||
| dynamic access; | |||
| dynamic managerId; | |||
| dynamic firebaseToken; | |||
| int? isBlock; | |||
| int? getSms; | |||
| DateTime? createdAt; | |||
| DateTime? updatedAt; | |||
| Manager({ | |||
| this.id, | |||
| this.name, | |||
| this.role, | |||
| this.mobile, | |||
| this.otp, | |||
| this.access, | |||
| this.managerId, | |||
| this.firebaseToken, | |||
| this.isBlock, | |||
| this.getSms, | |||
| this.createdAt, | |||
| this.updatedAt, | |||
| }); | |||
| factory Manager.fromRawJson(String str) => Manager.fromJson(json.decode(str)); | |||
| String toRawJson() => json.encode(toJson()); | |||
| factory Manager.fromJson(Map<String, dynamic> json) => Manager( | |||
| id: json["id"], | |||
| name: json["name"], | |||
| role: json["role"], | |||
| mobile: json["mobile"], | |||
| otp: json["otp"], | |||
| access: json["access"], | |||
| managerId: json["manager_id"], | |||
| firebaseToken: json["firebase_token"], | |||
| isBlock: json["is_block"], | |||
| getSms: json["get_sms"], | |||
| createdAt: json["created_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["created_at"]), | |||
| updatedAt: json["updated_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["updated_at"]), | |||
| ); | |||
| Map<String, dynamic> toJson() => { | |||
| "id": id, | |||
| "name": name, | |||
| "role": role, | |||
| "mobile": mobile, | |||
| "otp": otp, | |||
| "access": access, | |||
| "manager_id": managerId, | |||
| "firebase_token": firebaseToken, | |||
| "is_block": isBlock, | |||
| "get_sms": getSms, | |||
| "created_at": createdAt?.toIso8601String(), | |||
| "updated_at": updatedAt?.toIso8601String(), | |||
| }; | |||
| } | |||
| class Subject { | |||
| int? id; | |||
| String? subject; | |||
| dynamic subjectEn; | |||
| DateTime? createdAt; | |||
| DateTime? updatedAt; | |||
| Subject({ | |||
| this.id, | |||
| this.subject, | |||
| this.subjectEn, | |||
| this.createdAt, | |||
| this.updatedAt, | |||
| }); | |||
| factory Subject.fromRawJson(String str) => Subject.fromJson(json.decode(str)); | |||
| String toRawJson() => json.encode(toJson()); | |||
| factory Subject.fromJson(Map<String, dynamic> json) => Subject( | |||
| id: json["id"], | |||
| subject: json["subject"], | |||
| subjectEn: json["subject_en"], | |||
| createdAt: json["created_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["created_at"]), | |||
| updatedAt: json["updated_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["updated_at"]), | |||
| ); | |||
| Map<String, dynamic> toJson() => { | |||
| "id": id, | |||
| "subject": subject, | |||
| "subject_en": subjectEn, | |||
| "created_at": createdAt?.toIso8601String(), | |||
| "updated_at": updatedAt?.toIso8601String(), | |||
| }; | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| import 'dart:convert'; | |||
| class SubjectsModel { | |||
| int? id; | |||
| String? subject; | |||
| dynamic subjectEn; | |||
| SubjectsModel({ | |||
| this.id, | |||
| this.subject, | |||
| this.subjectEn, | |||
| }); | |||
| factory SubjectsModel.fromRawJson(String str) => | |||
| SubjectsModel.fromJson(json.decode(str)); | |||
| String toRawJson() => json.encode(toJson()); | |||
| factory SubjectsModel.fromJson(Map<String, dynamic> json) => SubjectsModel( | |||
| id: json["id"], | |||
| subject: json["subject"], | |||
| subjectEn: json["subject_en"], | |||
| ); | |||
| Map<String, dynamic> toJson() => { | |||
| "id": id, | |||
| "subject": subject, | |||
| "subject_en": subjectEn, | |||
| }; | |||
| } | |||
| @@ -0,0 +1,70 @@ | |||
| import 'dart:convert'; | |||
| class UsersModel { | |||
| int? id; | |||
| String? name; | |||
| int? role; | |||
| String? mobile; | |||
| dynamic otp; | |||
| dynamic access; | |||
| dynamic managerId; | |||
| dynamic firebaseToken; | |||
| int? isBlock; | |||
| int? getSms; | |||
| DateTime? createdAt; | |||
| DateTime? updatedAt; | |||
| UsersModel({ | |||
| this.id, | |||
| this.name, | |||
| this.role, | |||
| this.mobile, | |||
| this.otp, | |||
| this.access, | |||
| this.managerId, | |||
| this.firebaseToken, | |||
| this.isBlock, | |||
| this.getSms, | |||
| this.createdAt, | |||
| this.updatedAt, | |||
| }); | |||
| factory UsersModel.fromRawJson(String str) => | |||
| UsersModel.fromJson(json.decode(str)); | |||
| String toRawJson() => json.encode(toJson()); | |||
| factory UsersModel.fromJson(Map<String, dynamic> json) => UsersModel( | |||
| id: json["id"], | |||
| name: json["name"], | |||
| role: json["role"], | |||
| mobile: json["mobile"], | |||
| otp: json["otp"], | |||
| access: json["access"], | |||
| managerId: json["manager_id"], | |||
| firebaseToken: json["firebase_token"], | |||
| isBlock: json["is_block"], | |||
| getSms: json["get_sms"], | |||
| createdAt: json["created_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["created_at"]), | |||
| updatedAt: json["updated_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["updated_at"]), | |||
| ); | |||
| Map<String, dynamic> toJson() => { | |||
| "id": id, | |||
| "name": name, | |||
| "role": role, | |||
| "mobile": mobile, | |||
| "otp": otp, | |||
| "access": access, | |||
| "manager_id": managerId, | |||
| "firebase_token": firebaseToken, | |||
| "is_block": isBlock, | |||
| "get_sms": getSms, | |||
| "created_at": createdAt?.toIso8601String(), | |||
| "updated_at": updatedAt?.toIso8601String(), | |||
| }; | |||
| } | |||
| @@ -0,0 +1,272 @@ | |||
| import 'dart:convert'; | |||
| class OneMeetingModel { | |||
| int? id; | |||
| int? locationsId; | |||
| int? subjectId; | |||
| int? managerId; | |||
| int? ownerId; | |||
| String? azHour; | |||
| String? taHour; | |||
| dynamic description; | |||
| int? status; | |||
| int? accepted; | |||
| DateTime? dateMeeting; | |||
| DateTime? endDate; | |||
| DateTime? createdAt; | |||
| DateTime? updatedAt; | |||
| List<Manager>? users; | |||
| String? dateJalali; | |||
| String? statusTxt; | |||
| String? az; | |||
| String? ta; | |||
| Location? location; | |||
| Subject? subject; | |||
| Manager? manager; | |||
| OneMeetingModel({ | |||
| this.id, | |||
| this.locationsId, | |||
| this.subjectId, | |||
| this.managerId, | |||
| this.ownerId, | |||
| this.azHour, | |||
| this.taHour, | |||
| this.description, | |||
| this.status, | |||
| this.accepted, | |||
| this.dateMeeting, | |||
| this.endDate, | |||
| this.createdAt, | |||
| this.updatedAt, | |||
| this.users, | |||
| this.dateJalali, | |||
| this.statusTxt, | |||
| this.az, | |||
| this.ta, | |||
| this.location, | |||
| this.subject, | |||
| this.manager, | |||
| }); | |||
| factory OneMeetingModel.fromRawJson(String str) => | |||
| OneMeetingModel.fromJson(json.decode(str)); | |||
| String toRawJson() => json.encode(toJson()); | |||
| factory OneMeetingModel.fromJson(Map<String, dynamic> json) => | |||
| OneMeetingModel( | |||
| id: json["id"], | |||
| locationsId: json["locations_id"], | |||
| subjectId: json["subject_id"], | |||
| managerId: json["manager_id"], | |||
| ownerId: json["owner_id"], | |||
| azHour: json["az_hour"], | |||
| taHour: json["ta_hour"], | |||
| description: json["description"], | |||
| status: json["status"], | |||
| accepted: json["accepted"], | |||
| dateMeeting: json["date_meeting"] == null | |||
| ? null | |||
| : DateTime.parse(json["date_meeting"]), | |||
| endDate: | |||
| json["end_date"] == null ? null : DateTime.parse(json["end_date"]), | |||
| createdAt: json["created_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["created_at"]), | |||
| updatedAt: json["updated_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["updated_at"]), | |||
| users: json["users"] == null | |||
| ? [] | |||
| : List<Manager>.from( | |||
| json["users"]!.map((x) => Manager.fromJson(x))), | |||
| dateJalali: json["date_jalali"], | |||
| statusTxt: json["status_txt"], | |||
| az: json["az"], | |||
| ta: json["ta"], | |||
| location: json["location"] == null | |||
| ? null | |||
| : Location.fromJson(json["location"]), | |||
| subject: | |||
| json["subject"] == null ? null : Subject.fromJson(json["subject"]), | |||
| manager: | |||
| json["manager"] == null ? null : Manager.fromJson(json["manager"]), | |||
| ); | |||
| Map<String, dynamic> toJson() => { | |||
| "id": id, | |||
| "locations_id": locationsId, | |||
| "subject_id": subjectId, | |||
| "manager_id": managerId, | |||
| "owner_id": ownerId, | |||
| "az_hour": azHour, | |||
| "ta_hour": taHour, | |||
| "description": description, | |||
| "status": status, | |||
| "accepted": accepted, | |||
| "date_meeting": dateMeeting?.toIso8601String(), | |||
| "end_date": endDate?.toIso8601String(), | |||
| "created_at": createdAt?.toIso8601String(), | |||
| "updated_at": updatedAt?.toIso8601String(), | |||
| "users": users == null | |||
| ? [] | |||
| : List<dynamic>.from(users!.map((x) => x.toJson())), | |||
| "date_jalali": dateJalali, | |||
| "status_txt": statusTxt, | |||
| "az": az, | |||
| "ta": ta, | |||
| "location": location?.toJson(), | |||
| "subject": subject?.toJson(), | |||
| "manager": manager?.toJson(), | |||
| }; | |||
| } | |||
| class Location { | |||
| int? id; | |||
| String? address; | |||
| String? addressEn; | |||
| DateTime? createdAt; | |||
| DateTime? updatedAt; | |||
| Location({ | |||
| this.id, | |||
| this.address, | |||
| this.addressEn, | |||
| this.createdAt, | |||
| this.updatedAt, | |||
| }); | |||
| factory Location.fromRawJson(String str) => | |||
| Location.fromJson(json.decode(str)); | |||
| String toRawJson() => json.encode(toJson()); | |||
| factory Location.fromJson(Map<String, dynamic> json) => Location( | |||
| id: json["id"], | |||
| address: json["address"], | |||
| addressEn: json["address_en"], | |||
| createdAt: json["created_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["created_at"]), | |||
| updatedAt: json["updated_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["updated_at"]), | |||
| ); | |||
| Map<String, dynamic> toJson() => { | |||
| "id": id, | |||
| "address": address, | |||
| "address_en": addressEn, | |||
| "created_at": createdAt?.toIso8601String(), | |||
| "updated_at": updatedAt?.toIso8601String(), | |||
| }; | |||
| } | |||
| class Manager { | |||
| int? id; | |||
| String? name; | |||
| int? role; | |||
| String? mobile; | |||
| dynamic otp; | |||
| dynamic access; | |||
| dynamic managerId; | |||
| dynamic firebaseToken; | |||
| int? isBlock; | |||
| int? getSms; | |||
| DateTime? createdAt; | |||
| DateTime? updatedAt; | |||
| Manager({ | |||
| this.id, | |||
| this.name, | |||
| this.role, | |||
| this.mobile, | |||
| this.otp, | |||
| this.access, | |||
| this.managerId, | |||
| this.firebaseToken, | |||
| this.isBlock, | |||
| this.getSms, | |||
| this.createdAt, | |||
| this.updatedAt, | |||
| }); | |||
| factory Manager.fromRawJson(String str) => Manager.fromJson(json.decode(str)); | |||
| String toRawJson() => json.encode(toJson()); | |||
| factory Manager.fromJson(Map<String, dynamic> json) => Manager( | |||
| id: json["id"], | |||
| name: json["name"], | |||
| role: json["role"], | |||
| mobile: json["mobile"], | |||
| otp: json["otp"], | |||
| access: json["access"], | |||
| managerId: json["manager_id"], | |||
| firebaseToken: json["firebase_token"], | |||
| isBlock: json["is_block"], | |||
| getSms: json["get_sms"], | |||
| createdAt: json["created_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["created_at"]), | |||
| updatedAt: json["updated_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["updated_at"]), | |||
| ); | |||
| Map<String, dynamic> toJson() => { | |||
| "id": id, | |||
| "name": name, | |||
| "role": role, | |||
| "mobile": mobile, | |||
| "otp": otp, | |||
| "access": access, | |||
| "manager_id": managerId, | |||
| "firebase_token": firebaseToken, | |||
| "is_block": isBlock, | |||
| "get_sms": getSms, | |||
| "created_at": createdAt?.toIso8601String(), | |||
| "updated_at": updatedAt?.toIso8601String(), | |||
| }; | |||
| } | |||
| class Subject { | |||
| int? id; | |||
| String? subject; | |||
| String? subjectEn; | |||
| DateTime? createdAt; | |||
| DateTime? updatedAt; | |||
| Subject({ | |||
| this.id, | |||
| this.subject, | |||
| this.subjectEn, | |||
| this.createdAt, | |||
| this.updatedAt, | |||
| }); | |||
| factory Subject.fromRawJson(String str) => Subject.fromJson(json.decode(str)); | |||
| String toRawJson() => json.encode(toJson()); | |||
| factory Subject.fromJson(Map<String, dynamic> json) => Subject( | |||
| id: json["id"], | |||
| subject: json["subject"], | |||
| subjectEn: json["subject_en"], | |||
| createdAt: json["created_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["created_at"]), | |||
| updatedAt: json["updated_at"] == null | |||
| ? null | |||
| : DateTime.parse(json["updated_at"]), | |||
| ); | |||
| Map<String, dynamic> toJson() => { | |||
| "id": id, | |||
| "subject": subject, | |||
| "subject_en": subjectEn, | |||
| "created_at": createdAt?.toIso8601String(), | |||
| "updated_at": updatedAt?.toIso8601String(), | |||
| }; | |||
| } | |||
| @@ -0,0 +1,92 @@ | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:go_router/go_router.dart'; | |||
| import 'package:provider/provider.dart'; | |||
| import 'package:qadirneyriz/drawer_navigation_bar.dart'; | |||
| import 'package:qadirneyriz/models/meetings/meetings_model.dart'; | |||
| import 'package:qadirneyriz/screens/auth/login_screen.dart'; | |||
| import 'package:qadirneyriz/screens/auth/login_with_otp_screen.dart'; | |||
| import 'package:qadirneyriz/screens/auth/otp_screen.dart'; | |||
| import 'package:qadirneyriz/screens/home/screen.dart'; | |||
| import 'package:qadirneyriz/screens/meeting/screen.dart'; | |||
| import 'package:qadirneyriz/screens/meeting_edit/screen.dart'; | |||
| import 'package:qadirneyriz/screens/meeting_edit/state.dart'; | |||
| import 'package:qadirneyriz/screens/meeting_summary/screen.dart'; | |||
| import 'package:qadirneyriz/screens/meeting_summary/state.dart'; | |||
| import 'package:qadirneyriz/splash_screen.dart'; | |||
| final GoRouter router = GoRouter( | |||
| initialLocation: '/route', | |||
| routes: <GoRoute>[ | |||
| GoRoute( | |||
| path: '/navigate/:tab', | |||
| name: 'navigate', | |||
| builder: (context, state) { | |||
| return CustomDrawerNavigation( | |||
| activeTab: int.parse(state.pathParameters["tab"]!), | |||
| ); | |||
| }, | |||
| ), | |||
| GoRoute( | |||
| path: '/home', | |||
| name: 'home', | |||
| builder: (context, state) { | |||
| return const HomeScreen(); | |||
| }, | |||
| ), | |||
| GoRoute( | |||
| path: '/route', | |||
| builder: (BuildContext context, GoRouterState state) { | |||
| return const SplashScreen(); | |||
| }, | |||
| routes: [ | |||
| GoRoute( | |||
| path: 'login', | |||
| name: 'login', | |||
| builder: (context, state) { | |||
| return const LoginScreen(); | |||
| }, | |||
| ), | |||
| GoRoute( | |||
| path: 'loginotp', | |||
| name: 'loginotp', | |||
| builder: (context, state) { | |||
| return const LoginWithOtpScreen(); | |||
| }, | |||
| ), | |||
| GoRoute( | |||
| path: 'otp/:phonenumber', | |||
| name: 'otp', | |||
| builder: (context, state) { | |||
| return OtpScreen( | |||
| phoneNumber: state.pathParameters['phonenumber']!, | |||
| ); | |||
| }, | |||
| ), | |||
| ]), | |||
| GoRoute( | |||
| path: '/meetingedit/:id', | |||
| name: 'meetingedit', | |||
| builder: (context, state) { | |||
| return ChangeNotifierProvider( | |||
| child: MeetingEditScreen( | |||
| id: int.parse(state.pathParameters['id']!), | |||
| ), | |||
| create: (context) => MeetingEditState(), | |||
| ); | |||
| }, | |||
| ), | |||
| GoRoute( | |||
| path: '/meetinsammary', | |||
| name: 'meetinsammary', | |||
| builder: (context, state) { | |||
| Datum meetingData = state.extra as Datum; | |||
| return ChangeNotifierProvider( | |||
| create: (context) => MeetingSummaryState(), | |||
| child: MeetingSummaryScreen( | |||
| meetingItem: meetingData, | |||
| ), | |||
| ); | |||
| }, | |||
| ), | |||
| ], | |||
| ); | |||
| @@ -0,0 +1,174 @@ | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:provider/provider.dart'; | |||
| import 'package:qadirneyriz/config/config.dart'; | |||
| import 'package:qadirneyriz/screens/auth/state/state.dart'; | |||
| import 'package:qadirneyriz/utils/enums/status.dart'; | |||
| import 'package:qadirneyriz/utils/tools/tools.dart'; | |||
| import 'package:qadirneyriz/widgets/custom_button.dart'; | |||
| import 'package:qadirneyriz/widgets/custom_textfield.dart'; | |||
| import 'package:go_router/go_router.dart'; | |||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | |||
| class LoginScreen extends StatefulWidget { | |||
| const LoginScreen({super.key}); | |||
| @override | |||
| State<LoginScreen> createState() => _LoginScreenState(); | |||
| } | |||
| class _LoginScreenState extends State<LoginScreen> { | |||
| TextEditingController phoneController = TextEditingController(); | |||
| TextEditingController passwordController = TextEditingController(); | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Scaffold( | |||
| body: Consumer<AuthState>( | |||
| builder: (context, value, child) { | |||
| return CustomScrollView( | |||
| slivers: <Widget>[ | |||
| SliverToBoxAdapter( | |||
| child: Container( | |||
| height: 30, | |||
| color: config.ui.mainGreen, | |||
| ), | |||
| ), | |||
| // Header Image with cut-out shape at the bottom | |||
| SliverToBoxAdapter( | |||
| child: ClipPath( | |||
| child: Image.asset( | |||
| 'assets/images/template.png', | |||
| width: double.infinity, | |||
| fit: BoxFit.cover, | |||
| ), | |||
| ), | |||
| ), | |||
| // Form section with inputs and buttons | |||
| SliverPadding( | |||
| padding: const EdgeInsets.symmetric(horizontal: 20), | |||
| sliver: SliverToBoxAdapter( | |||
| child: Column( | |||
| children: [ | |||
| Container( | |||
| padding: const EdgeInsets.all(16), | |||
| decoration: BoxDecoration( | |||
| color: Colors.white, | |||
| borderRadius: BorderRadius.circular(16), | |||
| boxShadow: [ | |||
| BoxShadow( | |||
| color: | |||
| Colors.black.withOpacity(0.1), // light shadow | |||
| blurRadius: 10, | |||
| spreadRadius: 5, | |||
| ), | |||
| ], | |||
| ), | |||
| child: Column( | |||
| crossAxisAlignment: CrossAxisAlignment.stretch, | |||
| children: [ | |||
| // Username field | |||
| CustomTextField( | |||
| label: AppLocalizations.of(context)!.phonenumber, | |||
| hintText: | |||
| AppLocalizations.of(context)!.hintphonenumber, | |||
| textEditingController: phoneController, | |||
| textInputType: TextInputType.phone, | |||
| ), | |||
| const SizedBox(height: 16), | |||
| // Password field | |||
| CustomTextField( | |||
| isPass: true, | |||
| label: AppLocalizations.of(context)!.password, | |||
| hintText: AppLocalizations.of(context)!.hintpass, | |||
| textEditingController: passwordController, | |||
| textInputType: TextInputType.visiblePassword, | |||
| ), | |||
| const SizedBox(height: 40), | |||
| // Buttons section | |||
| Row( | |||
| mainAxisAlignment: MainAxisAlignment.spaceBetween, | |||
| children: [ | |||
| // Login button (filled) | |||
| CustomButton( | |||
| hieght: 56, | |||
| fontSize: 13, | |||
| text: AppLocalizations.of(context)! | |||
| .submitwithotp, | |||
| color: Colors.white, | |||
| textColor: Colors.green, | |||
| onPressed: | |||
| value.statusLogin == Status.loading | |||
| ? null | |||
| : () { | |||
| context.pushNamed('loginotp'); | |||
| }), | |||
| const SizedBox(width: 16), | |||
| // Register button (outlined) | |||
| Expanded( | |||
| child: submitButton(context, value), | |||
| ), | |||
| ], | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| const SizedBox(height: 40), | |||
| ], | |||
| ), | |||
| ), | |||
| ), | |||
| ], | |||
| ); | |||
| }, | |||
| ), | |||
| ); | |||
| } | |||
| CustomButton submitButton(BuildContext context, AuthState state) { | |||
| switch (state.statusLogin) { | |||
| case Status.loading: | |||
| return CustomButton( | |||
| hieght: 56, | |||
| fontSize: 12, | |||
| text: AppLocalizations.of(context)!.loading, | |||
| onPressed: null); | |||
| default: | |||
| return CustomButton( | |||
| hieght: 56, | |||
| fontSize: 16, | |||
| text: AppLocalizations.of(context)!.submit, | |||
| onPressed: () async { | |||
| if (phoneController.text == '') { | |||
| Tools.showCustomSnackBar( | |||
| text: AppLocalizations.of(context)!.phoneerror, | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } else if (passwordController.text == '') { | |||
| Tools.showCustomSnackBar( | |||
| text: AppLocalizations.of(context)!.passerror, | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } else { | |||
| final status = await state.login( | |||
| mobile: phoneController.text, | |||
| password: passwordController.text); | |||
| if (status == Status.ready) { | |||
| context.goNamed('navigate', pathParameters: {'tab': '0'}); | |||
| } else { | |||
| Tools.showCustomSnackBar( | |||
| text: state.errorsLogin == null | |||
| ? state.messageLogin ?? | |||
| AppLocalizations.of(context)!.haserror | |||
| : Tools.combineErrorMessages(state.errorsLogin ?? {}), | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } | |||
| } | |||
| }, | |||
| ); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,157 @@ | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:go_router/go_router.dart'; | |||
| import 'package:provider/provider.dart'; | |||
| import 'package:qadirneyriz/config/config.dart'; | |||
| import 'package:qadirneyriz/screens/auth/state/state.dart'; | |||
| import 'package:qadirneyriz/utils/enums/status.dart'; | |||
| import 'package:qadirneyriz/utils/tools/tools.dart'; | |||
| import 'package:qadirneyriz/widgets/custom_button.dart'; | |||
| import 'package:qadirneyriz/widgets/custom_textfield.dart'; | |||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | |||
| class LoginWithOtpScreen extends StatefulWidget { | |||
| const LoginWithOtpScreen({super.key}); | |||
| @override | |||
| State<LoginWithOtpScreen> createState() => _LoginWithOtpScreenState(); | |||
| } | |||
| class _LoginWithOtpScreenState extends State<LoginWithOtpScreen> { | |||
| TextEditingController phoneController = TextEditingController(); | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Scaffold( | |||
| body: Consumer<AuthState>( | |||
| builder: (context, value, child) { | |||
| return CustomScrollView( | |||
| slivers: <Widget>[ | |||
| SliverToBoxAdapter( | |||
| child: Container( | |||
| height: 30, | |||
| color: config.ui.mainGreen, | |||
| ), | |||
| ), | |||
| // Header Image with cut-out shape at the bottom | |||
| SliverToBoxAdapter( | |||
| child: ClipPath( | |||
| child: Image.asset( | |||
| 'assets/images/template.png', | |||
| width: double.infinity, | |||
| fit: BoxFit.cover, | |||
| ), | |||
| ), | |||
| ), | |||
| // Form section with inputs and buttons | |||
| SliverPadding( | |||
| padding: const EdgeInsets.symmetric(horizontal: 20), | |||
| sliver: SliverToBoxAdapter( | |||
| child: Column( | |||
| children: [ | |||
| Container( | |||
| padding: const EdgeInsets.all(16), | |||
| decoration: BoxDecoration( | |||
| color: Colors.white, | |||
| borderRadius: BorderRadius.circular(16), | |||
| boxShadow: [ | |||
| BoxShadow( | |||
| color: | |||
| Colors.black.withOpacity(0.1), // light shadow | |||
| blurRadius: 10, | |||
| spreadRadius: 5, | |||
| ), | |||
| ], | |||
| ), | |||
| child: Column( | |||
| crossAxisAlignment: CrossAxisAlignment.stretch, | |||
| children: [ | |||
| // Username field | |||
| CustomTextField( | |||
| label: AppLocalizations.of(context)!.phonenumber, | |||
| hintText: | |||
| AppLocalizations.of(context)!.hintphonenumber, | |||
| textEditingController: phoneController, | |||
| textInputType: TextInputType.phone, | |||
| ), | |||
| const SizedBox(height: 40), | |||
| // Buttons section | |||
| Row( | |||
| mainAxisAlignment: MainAxisAlignment.spaceBetween, | |||
| children: [ | |||
| CustomButton( | |||
| hieght: 56, | |||
| fontSize: 13, | |||
| text: AppLocalizations.of(context)! | |||
| .submitwithphone, | |||
| color: Colors.white, | |||
| textColor: Colors.green, | |||
| onPressed: | |||
| value.statusSendotp == Status.loading | |||
| ? null | |||
| : () { | |||
| context.pop(); | |||
| }, | |||
| ), | |||
| const SizedBox(width: 16), | |||
| Expanded( | |||
| child: submitButton(context, value), | |||
| ), | |||
| ], | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| const SizedBox(height: 40), | |||
| ], | |||
| ), | |||
| ), | |||
| ), | |||
| ], | |||
| ); | |||
| }, | |||
| ), | |||
| ); | |||
| } | |||
| CustomButton submitButton(BuildContext context, AuthState state) { | |||
| switch (state.statusSendotp) { | |||
| case Status.loading: | |||
| return CustomButton( | |||
| hieght: 56, | |||
| fontSize: 12, | |||
| text: AppLocalizations.of(context)!.loading, | |||
| onPressed: null); | |||
| default: | |||
| return CustomButton( | |||
| hieght: 56, | |||
| fontSize: 16, | |||
| text: AppLocalizations.of(context)!.submit, | |||
| onPressed: () async { | |||
| if (phoneController.text == '') { | |||
| Tools.showCustomSnackBar( | |||
| text: AppLocalizations.of(context)!.phoneerror, | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } else { | |||
| final status = await state.sendOtp(mobile: phoneController.text); | |||
| if (status == Status.ready) { | |||
| context.pushNamed('otp', | |||
| pathParameters: {'phonenumber': phoneController.text}); | |||
| } else { | |||
| Tools.showCustomSnackBar( | |||
| text: state.errorsSendOtp == null | |||
| ? state.messageSendOtp ?? | |||
| AppLocalizations.of(context)!.haserror | |||
| : Tools.combineErrorMessages(state.errorsSendOtp ?? {}), | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } | |||
| } | |||
| }, | |||
| ); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,211 @@ | |||
| // ignore_for_file: public_member_api_docs, sort_constructors_first | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:go_router/go_router.dart'; | |||
| import 'package:provider/provider.dart'; | |||
| import 'package:qadirneyriz/config/config.dart'; | |||
| import 'package:qadirneyriz/screens/auth/state/state.dart'; | |||
| import 'package:qadirneyriz/utils/enums/status.dart'; | |||
| import 'package:qadirneyriz/utils/timer/apt_simple_timer_with_controller.dart'; | |||
| import 'package:qadirneyriz/utils/tools/tools.dart'; | |||
| import 'package:qadirneyriz/widgets/otp_textfield.dart'; | |||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | |||
| class OtpScreen extends StatefulWidget { | |||
| final String phoneNumber; | |||
| const OtpScreen({ | |||
| super.key, | |||
| required this.phoneNumber, | |||
| }); | |||
| @override | |||
| State<OtpScreen> createState() => _OtpScreenState(); | |||
| } | |||
| class _OtpScreenState extends State<OtpScreen> { | |||
| bool timerEnd = false; | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Scaffold( | |||
| body: Consumer<AuthState>( | |||
| builder: (context, value, child) { | |||
| return CustomScrollView( | |||
| slivers: <Widget>[ | |||
| SliverToBoxAdapter( | |||
| child: Container( | |||
| height: 30, | |||
| color: config.ui.mainGreen, | |||
| ), | |||
| ), | |||
| // Header Image with cut-out shape at the bottom | |||
| SliverToBoxAdapter( | |||
| child: ClipPath( | |||
| child: Image.asset( | |||
| 'assets/images/template.png', | |||
| width: double.infinity, | |||
| fit: BoxFit.cover, | |||
| ), | |||
| ), | |||
| ), | |||
| SliverPadding( | |||
| padding: const EdgeInsets.symmetric(horizontal: 20), | |||
| sliver: SliverToBoxAdapter( | |||
| child: Container( | |||
| padding: const EdgeInsets.all(16), | |||
| decoration: BoxDecoration( | |||
| color: Colors.white, | |||
| borderRadius: BorderRadius.circular(16), | |||
| boxShadow: [ | |||
| BoxShadow( | |||
| color: | |||
| Colors.black.withOpacity(0.1), // light shadow | |||
| blurRadius: 10, | |||
| spreadRadius: 5, | |||
| ), | |||
| ], | |||
| ), | |||
| child: Column( | |||
| children: [ | |||
| Text( | |||
| AppLocalizations.of(context)!.enterotp, | |||
| style: TextStyle( | |||
| color: config.ui.mainGray, | |||
| fontSize: 18, | |||
| fontWeight: FontWeight.bold), | |||
| ), | |||
| const SizedBox( | |||
| height: 5, | |||
| ), | |||
| Text( | |||
| AppLocalizations.of(context)!.an4digitotp, | |||
| style: TextStyle( | |||
| color: config.ui.mainGray.withOpacity(.5), | |||
| fontSize: 14, | |||
| ), | |||
| ), | |||
| const SizedBox( | |||
| height: 5, | |||
| ), | |||
| Row( | |||
| mainAxisAlignment: MainAxisAlignment.center, | |||
| children: [ | |||
| Text( | |||
| widget.phoneNumber, | |||
| style: TextStyle( | |||
| color: config.ui.mainGray.withOpacity(.5), | |||
| fontSize: 16, | |||
| fontWeight: FontWeight.bold), | |||
| ), | |||
| Icon( | |||
| Icons.phone_callback_outlined, | |||
| color: config.ui.mainGreen, | |||
| ) | |||
| ], | |||
| ), | |||
| const SizedBox( | |||
| height: 10, | |||
| ), | |||
| OTPTextField( | |||
| length: 4, | |||
| onSubmitted: (onSubmitted) { | |||
| otpCheckCode(onSubmitted, value); | |||
| }), | |||
| const SizedBox( | |||
| height: 20, | |||
| ), | |||
| Row( | |||
| mainAxisAlignment: MainAxisAlignment.center, | |||
| children: [ | |||
| resendOtpbutton(value), | |||
| if (!timerEnd) | |||
| Directionality( | |||
| textDirection: TextDirection.ltr, | |||
| child: AptSimpleTimerWithController( | |||
| seconds: 60, | |||
| fontSize: 16, | |||
| timerColor: const Color(0xff8FA5B6) | |||
| .withOpacity(0.6), | |||
| onTimerEnd: () { | |||
| setState(() { | |||
| timerEnd = true; | |||
| }); | |||
| }, | |||
| ), | |||
| ), | |||
| ], | |||
| ), | |||
| ], | |||
| )), | |||
| ), | |||
| ), | |||
| ], | |||
| ); | |||
| }, | |||
| ), | |||
| ); | |||
| } | |||
| Widget resendOtpbutton(AuthState value) { | |||
| return timerEnd | |||
| ? GestureDetector( | |||
| onTap: value.statusSendotp != Status.loading | |||
| ? () async { | |||
| final status = await value.sendOtp( | |||
| mobile: widget.phoneNumber, | |||
| ); | |||
| if (status == Status.ready) { | |||
| setState(() { | |||
| timerEnd = false; | |||
| }); | |||
| } else { | |||
| Tools.showCustomSnackBar( | |||
| text: value.errorsSendOtp == null | |||
| ? value.messageSendOtp ?? 'An error has occurred' | |||
| : Tools.combineErrorMessages( | |||
| value.errorsSendOtp ?? {}), | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } | |||
| } | |||
| : null, | |||
| child: Text( | |||
| value.statusSendotp == Status.loading | |||
| ? AppLocalizations.of(context)!.loading | |||
| : AppLocalizations.of(context)!.resend, | |||
| style: TextStyle( | |||
| color: config.ui.secendGreen, | |||
| fontSize: 16, | |||
| ), | |||
| ), | |||
| ) | |||
| : Container(); | |||
| } | |||
| void otpCheckCode(onSubmitted, AuthState value) async { | |||
| if (onSubmitted.length == 4) { | |||
| final status = | |||
| await value.login(mobile: widget.phoneNumber, otp: onSubmitted); | |||
| if (status == Status.ready) { | |||
| context.goNamed('navigate', pathParameters: {'tab': '0'}); | |||
| } else if (status == Status.error) { | |||
| Tools.showCustomSnackBar( | |||
| text: value.errorsLogin == null | |||
| ? value.messageLogin ?? AppLocalizations.of(context)!.haserror | |||
| : Tools.combineErrorMessages(value.errorsLogin ?? {}), | |||
| isError: true, | |||
| context, | |||
| ); | |||
| setState(() {}); | |||
| } else if (status == Status.empty) { | |||
| Tools.showCustomSnackBar( | |||
| text: value.errorsLogin == null | |||
| ? value.messageLogin ?? AppLocalizations.of(context)!.haserror | |||
| : Tools.combineErrorMessages(value.errorsLogin ?? {}), | |||
| isError: true, | |||
| context, | |||
| ); | |||
| onSubmitted = ''; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,93 @@ | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:qadirneyriz/services/auth/auth.dart'; | |||
| import 'package:qadirneyriz/setting/setting.dart'; | |||
| import 'package:qadirneyriz/utils/enums/status.dart'; | |||
| class AuthState extends ChangeNotifier { | |||
| /// set and fetch language | |||
| String language = setting.userLocalDb.getUser().language; | |||
| Future<void> setLocale(String newLanguage) async { | |||
| language = newLanguage; | |||
| await setting.userLocalDb.saveUserField('language', newLanguage); | |||
| notifyListeners(); | |||
| } | |||
| // auth seviceses class | |||
| AuthServices authServises = AuthServices(); | |||
| // login funtinulity | |||
| Status statusLogin = Status.empty; | |||
| String? messageLogin; | |||
| Map? errorsLogin; | |||
| Future<Status> login( | |||
| {required String mobile, String? password, String? otp}) async { | |||
| assert(password != null || otp != null); | |||
| statusLogin = Status.loading; | |||
| notifyListeners(); | |||
| try { | |||
| final result = await authServises.loginApi( | |||
| mobile: mobile, password: password, otp: otp); | |||
| if (result == null) { | |||
| statusLogin = Status.error; | |||
| } else { | |||
| if (result.isOk) { | |||
| statusLogin = Status.ready; | |||
| messageLogin = result.message; | |||
| } else if (result.isOk == false) { | |||
| errorsLogin = result.errors; | |||
| messageLogin = result.message; | |||
| statusLogin = Status.error; | |||
| } else { | |||
| statusLogin = Status.error; | |||
| } | |||
| notifyListeners(); | |||
| } | |||
| notifyListeners(); | |||
| } catch (e) { | |||
| statusLogin = Status.error; | |||
| } | |||
| notifyListeners(); | |||
| return statusLogin; | |||
| } | |||
| Status statusSendotp = Status.empty; | |||
| String? messageSendOtp; | |||
| Map? errorsSendOtp; | |||
| Future<Status> sendOtp({ | |||
| required String mobile, | |||
| }) async { | |||
| statusSendotp = Status.loading; | |||
| notifyListeners(); | |||
| try { | |||
| final result = await authServises.sendOtpApi(mobile: mobile); | |||
| if (result == null) { | |||
| statusSendotp = Status.error; | |||
| } else { | |||
| print(result); | |||
| if (result.isOk) { | |||
| statusSendotp = Status.ready; | |||
| messageSendOtp = result.message; | |||
| } else if (result.isOk == false) { | |||
| errorsSendOtp = result.errors; | |||
| messageSendOtp = result.message; | |||
| statusSendotp = Status.error; | |||
| } else { | |||
| statusSendotp = Status.error; | |||
| } | |||
| notifyListeners(); | |||
| } | |||
| notifyListeners(); | |||
| } catch (e) { | |||
| statusSendotp = Status.error; | |||
| print(e); | |||
| } | |||
| notifyListeners(); | |||
| print(statusSendotp); | |||
| return statusSendotp; | |||
| } | |||
| } | |||
| @@ -0,0 +1,253 @@ | |||
| // ignore_for_file: public_member_api_docs, sort_constructors_first | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; | |||
| import 'package:go_router/go_router.dart'; | |||
| import 'package:provider/provider.dart'; | |||
| import 'package:qadirneyriz/utils/tools/tools.dart'; | |||
| import 'package:qadirneyriz/widgets/card_meeting.dart'; | |||
| import 'package:qadirneyriz/widgets/custom_appbar.dart'; | |||
| import 'package:qadirneyriz/widgets/today_widget.dart'; | |||
| import 'package:shamsi_date/shamsi_date.dart'; | |||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | |||
| import 'package:qadirneyriz/config/config.dart'; | |||
| import 'package:qadirneyriz/screens/home/state.dart'; | |||
| import 'package:qadirneyriz/utils/enums/status.dart'; | |||
| import 'package:qadirneyriz/widgets/loading_widget.dart'; | |||
| class HomeScreen extends StatefulWidget { | |||
| const HomeScreen({super.key}); | |||
| @override | |||
| State<HomeScreen> createState() => _HomeScreenState(); | |||
| } | |||
| class _HomeScreenState extends State<HomeScreen> { | |||
| @override | |||
| void initState() { | |||
| super.initState(); | |||
| HomeState homeState = Provider.of(context, listen: false); | |||
| Future.delayed(Duration.zero, () async { | |||
| await homeState.getTodayMeetings(); | |||
| }); | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| final Jalali shamsi = Jalali.now(); // دریافت تاریخ شمسی کنونی | |||
| String formattedDate = | |||
| '${shamsi.day} ${Tools.getMonthName(shamsi.month)} ${shamsi.year}'; // فرمت کردن تاریخ | |||
| return Consumer<HomeState>( | |||
| builder: (context, value, child) { | |||
| switch (value.todayMettingsStatus) { | |||
| case Status.ready: | |||
| return CustomScrollView( | |||
| slivers: [ | |||
| const CustomAppbar(), | |||
| SliverToBoxAdapter( | |||
| child: Padding( | |||
| padding: const EdgeInsets.symmetric( | |||
| horizontal: 20, vertical: 10), | |||
| child: Container( | |||
| decoration: BoxDecoration( | |||
| color: const Color(0xffF4F9F6), | |||
| boxShadow: [ | |||
| BoxShadow( | |||
| color: config.ui.mainGray.withOpacity(.1), | |||
| spreadRadius: .1, | |||
| offset: const Offset(0, 5), | |||
| blurRadius: 6) | |||
| ], | |||
| borderRadius: BorderRadius.circular(25)), | |||
| width: double.infinity, | |||
| child: Padding( | |||
| padding: const EdgeInsets.all(20), | |||
| child: Row( | |||
| crossAxisAlignment: CrossAxisAlignment.start, | |||
| children: [ | |||
| Icon( | |||
| Icons.edit_outlined, | |||
| color: config.ui.mainGreen, | |||
| ), | |||
| const SizedBox( | |||
| width: 10, | |||
| ), | |||
| Expanded( | |||
| child: Text( | |||
| style: const TextStyle(fontSize: 13), | |||
| value.todayMeetingsModel!.note ?? ''), | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| ), | |||
| ), | |||
| ), | |||
| SliverToBoxAdapter( | |||
| child: TodayWidget(formattedDate: formattedDate), | |||
| ), | |||
| SliverToBoxAdapter( | |||
| child: SizedBox( | |||
| height: 165, | |||
| child: value.todayMeetingsModel!.meetings!.isNotEmpty | |||
| ? ListView.builder( | |||
| scrollDirection: Axis.horizontal, | |||
| itemCount: | |||
| value.todayMeetingsModel!.meetings!.length, | |||
| itemBuilder: (BuildContext context, int index) { | |||
| final items = | |||
| value.todayMeetingsModel!.meetings![index]; | |||
| return CustomCardMeeting( | |||
| titel: items.subject!.subject ?? '', | |||
| fromTime: items.azHour ?? '', | |||
| toTime: items.taHour ?? "", | |||
| location: items.location!.address ?? '', | |||
| date: items.dateJalali ?? '', | |||
| cardId: items.id ?? 0, | |||
| ); | |||
| }, | |||
| ) | |||
| : Center( | |||
| child: Column( | |||
| mainAxisAlignment: MainAxisAlignment.center, | |||
| children: [ | |||
| Icon(Icons.error_outline, | |||
| size: 40, | |||
| color: config.ui.mainGray.withOpacity(.5)), | |||
| const SizedBox( | |||
| height: 20, | |||
| ), | |||
| Text( | |||
| AppLocalizations.of(context)! | |||
| .nomeetingfortoday, | |||
| style: TextStyle( | |||
| color: | |||
| config.ui.mainGray.withOpacity(.5)), | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| ), | |||
| ), | |||
| SliverPadding( | |||
| padding: | |||
| const EdgeInsets.symmetric(vertical: 30, horizontal: 10), | |||
| sliver: SliverToBoxAdapter( | |||
| child: StaggeredGrid.count( | |||
| crossAxisCount: 4, | |||
| mainAxisSpacing: 4, | |||
| crossAxisSpacing: 4, | |||
| children: [ | |||
| StaggeredGridTile.count( | |||
| crossAxisCellCount: 2, | |||
| mainAxisCellCount: 1, | |||
| child: ItemInGrid( | |||
| icon: Icons.assessment, | |||
| backColor: const Color(0xff03C85F), | |||
| text: AppLocalizations.of(context)!.reports, | |||
| onTap: () {}, | |||
| ), | |||
| ), | |||
| StaggeredGridTile.count( | |||
| crossAxisCellCount: 2, | |||
| mainAxisCellCount: 2, | |||
| child: ItemInGrid( | |||
| icon: Icons.people, | |||
| backColor: const Color(0xff04A54F), | |||
| text: AppLocalizations.of(context)!.meetings, | |||
| onTap: () { | |||
| context.pushNamed('navigate', | |||
| pathParameters: {'tab': '1'}); | |||
| }, | |||
| ), | |||
| ), | |||
| StaggeredGridTile.count( | |||
| crossAxisCellCount: 2, | |||
| mainAxisCellCount: 2, | |||
| child: ItemInGrid( | |||
| icon: Icons.calendar_today, | |||
| backColor: const Color(0xff37A068), | |||
| text: AppLocalizations.of(context)!.events, | |||
| onTap: () {}, | |||
| ), | |||
| ), | |||
| StaggeredGridTile.count( | |||
| crossAxisCellCount: 2, | |||
| mainAxisCellCount: 1, | |||
| child: ItemInGrid( | |||
| icon: Icons.exit_to_app, | |||
| backColor: const Color(0xff00843D), | |||
| text: AppLocalizations.of(context)!.exit, | |||
| onTap: () {}, | |||
| ), | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| ), | |||
| ], | |||
| ); | |||
| case Status.loading: | |||
| return const LoadingWidget(); | |||
| default: | |||
| return Container(); | |||
| } | |||
| }, | |||
| ); | |||
| } | |||
| } | |||
| class ItemInGrid extends StatelessWidget { | |||
| final IconData icon; | |||
| final Color backColor; | |||
| final String text; | |||
| final void Function() onTap; | |||
| const ItemInGrid( | |||
| {super.key, | |||
| required this.icon, | |||
| required this.backColor, | |||
| required this.text, | |||
| required this.onTap}); | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Padding( | |||
| padding: const EdgeInsets.all(4.0), | |||
| child: GestureDetector( | |||
| onTap: onTap, | |||
| child: Container( | |||
| decoration: BoxDecoration( | |||
| borderRadius: BorderRadius.circular(20), | |||
| color: backColor, | |||
| boxShadow: [ | |||
| BoxShadow( | |||
| color: config.ui.mainGray.withOpacity(.5), | |||
| spreadRadius: .1, | |||
| offset: const Offset(0, 2), | |||
| blurRadius: 6) | |||
| ], | |||
| ), | |||
| child: Column( | |||
| mainAxisAlignment: MainAxisAlignment.center, | |||
| children: [ | |||
| Icon( | |||
| icon, | |||
| size: 40, | |||
| color: Colors.white, | |||
| ), | |||
| const SizedBox( | |||
| height: 8, | |||
| ), | |||
| Text( | |||
| text, | |||
| style: const TextStyle( | |||
| color: Colors.white, | |||
| ), | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,52 @@ | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:qadirneyriz/models/home/home_models.dart'; | |||
| import 'package:qadirneyriz/services/home/home.dart'; | |||
| import 'package:qadirneyriz/utils/enums/status.dart'; | |||
| class HomeState extends ChangeNotifier { | |||
| HomeApi homeApi = HomeApi(); | |||
| Status todayMettingsStatus = Status.empty; | |||
| TodayMeetingModel? todayMeetingsModel; | |||
| getTodayMeetings({bool refresh = false}) async { | |||
| todayMettingsStatus = Status.loading; | |||
| notifyListeners(); | |||
| if (refresh) { | |||
| todayMettingsStatus = Status.loading; | |||
| notifyListeners(); | |||
| } | |||
| if (todayMeetingsModel != null) { | |||
| todayMettingsStatus = Status.ready; | |||
| try { | |||
| todayMeetingsModel = await homeApi.getTodayMeetings(); | |||
| if (todayMeetingsModel != null) { | |||
| todayMettingsStatus = Status.ready; | |||
| } else { | |||
| todayMettingsStatus = Status.empty; | |||
| } | |||
| } catch (e) { | |||
| todayMettingsStatus = Status.error; | |||
| // print(e); | |||
| } | |||
| notifyListeners(); | |||
| } else { | |||
| try { | |||
| todayMeetingsModel = await homeApi.getTodayMeetings(); | |||
| if (todayMeetingsModel != null) { | |||
| todayMettingsStatus = Status.ready; | |||
| } else { | |||
| todayMettingsStatus = Status.empty; | |||
| } | |||
| notifyListeners(); | |||
| } catch (e) { | |||
| todayMettingsStatus = Status.error; | |||
| print(e); | |||
| } | |||
| } | |||
| notifyListeners(); | |||
| print(todayMettingsStatus); | |||
| return todayMettingsStatus; | |||
| } | |||
| } | |||
| @@ -0,0 +1,391 @@ | |||
| // ignore_for_file: public_member_api_docs, sort_constructors_first | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:provider/provider.dart'; | |||
| import 'package:qadirneyriz/global_state/global_state.dart'; | |||
| import 'package:qadirneyriz/widgets/ExpansionTileCustom.dart'; | |||
| import 'package:qadirneyriz/widgets/error_widget.dart'; | |||
| import 'package:qadirneyriz/config/config.dart'; | |||
| import 'package:qadirneyriz/screens/meeting/state.dart'; | |||
| import 'package:qadirneyriz/utils/enums/status.dart'; | |||
| import 'package:qadirneyriz/utils/tools/tools.dart'; | |||
| import 'package:qadirneyriz/widgets/picker.dart'; | |||
| import 'package:qadirneyriz/widgets/loading_widget.dart'; | |||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | |||
| class DiologMeetingsFilters extends StatefulWidget { | |||
| const DiologMeetingsFilters({ | |||
| super.key, | |||
| }); | |||
| @override | |||
| State<DiologMeetingsFilters> createState() => _DiologMeetingsFiltersState(); | |||
| } | |||
| class _DiologMeetingsFiltersState extends State<DiologMeetingsFilters> { | |||
| MeetingsState? meetingsState; | |||
| GlobalState? globalState; | |||
| @override | |||
| void initState() { | |||
| meetingsState = Provider.of<MeetingsState>(context, listen: false); | |||
| globalState = Provider.of<GlobalState>(context, listen: false); | |||
| Future.delayed(Duration.zero, () async { | |||
| final status = await globalState!.getAllFiltersItems(); | |||
| print(status); | |||
| }); | |||
| super.initState(); | |||
| } | |||
| @override | |||
| void dispose() { | |||
| Future.delayed(Duration.zero, () async { | |||
| await meetingsState!.getMeetings( | |||
| refresh: true, | |||
| toDate: meetingsState!.toDate, | |||
| fromDate: meetingsState!.fromDate, | |||
| location: meetingsState!.selectedLocationId, | |||
| subject: meetingsState!.selectedSubjectId, | |||
| meetingManager: meetingsState!.selectedManagersId, | |||
| meetingStatus: meetingsState!.selectedStatusId); | |||
| }); | |||
| super.dispose(); | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return DraggableScrollableSheet( | |||
| initialChildSize: .7, | |||
| expand: false, | |||
| snap: false, | |||
| builder: (context, scrollController) { | |||
| return Consumer2<MeetingsState, GlobalState>( | |||
| builder: (context, meetingsState, globalState, child) { | |||
| switch (globalState.allFiltersStatus) { | |||
| case Status.ready: | |||
| return Column( | |||
| children: [ | |||
| LineButtomSheet(), | |||
| Expanded( | |||
| child: SingleChildScrollView( | |||
| controller: scrollController, | |||
| child: Column( | |||
| children: [ | |||
| Column( | |||
| mainAxisSize: MainAxisSize.max, | |||
| children: [ | |||
| Padding( | |||
| padding: const EdgeInsets.symmetric( | |||
| vertical: 25, horizontal: 15), | |||
| child: Row( | |||
| mainAxisAlignment: | |||
| MainAxisAlignment.spaceBetween, | |||
| children: [ | |||
| Text( | |||
| AppLocalizations.of(context)! | |||
| .searchFor, | |||
| ), | |||
| GestureDetector( | |||
| onTap: () { | |||
| meetingsState.clearFilters(); | |||
| }, | |||
| child: | |||
| meetingsState.hasActiveFilters() | |||
| ? Icon( | |||
| Icons.delete_outline, | |||
| color: Colors.red, | |||
| ) | |||
| : Icon( | |||
| Icons.delete_outline, | |||
| color: Colors.black | |||
| .withOpacity(.3), | |||
| )) | |||
| ], | |||
| ), | |||
| ), | |||
| Divider(), | |||
| SizedBox( | |||
| height: 10, | |||
| ), | |||
| ExpansionTileCustom( | |||
| title: AppLocalizations.of(context)!.date, | |||
| widgets: <Widget>[ | |||
| Padding( | |||
| padding: const EdgeInsets.all(20.0), | |||
| child: Row( | |||
| crossAxisAlignment: | |||
| CrossAxisAlignment.end, | |||
| mainAxisAlignment: | |||
| MainAxisAlignment.spaceBetween, | |||
| children: [ | |||
| PickerCustom( | |||
| showDate: meetingsState | |||
| .fromDate.isNotEmpty | |||
| ? meetingsState.fromDate | |||
| : AppLocalizations.of( | |||
| context)! | |||
| .selectdate, // Show selected date or prompt | |||
| onTap: () { | |||
| showDialog( | |||
| context: context, | |||
| builder: (context) { | |||
| return Dialog( | |||
| child: Tools | |||
| .shamsiDateCalendarWidget( | |||
| context, | |||
| (newDate) { | |||
| String | |||
| fromDateString = | |||
| '${newDate.year}/${newDate.month}/${newDate.day}'; | |||
| meetingsState | |||
| .setFromDates( | |||
| fromDateString); // Update the selected date | |||
| }, | |||
| ), | |||
| ); | |||
| }, | |||
| ); | |||
| }, | |||
| ), | |||
| Text( | |||
| AppLocalizations.of(context)!.to, | |||
| ), | |||
| PickerCustom( | |||
| showDate: meetingsState | |||
| .toDate.isNotEmpty | |||
| ? meetingsState.toDate | |||
| : AppLocalizations.of( | |||
| context)! | |||
| .selectdate, // Show selected date or prompt | |||
| onTap: () { | |||
| showDialog( | |||
| context: context, | |||
| builder: (context) { | |||
| return Dialog( | |||
| child: Tools | |||
| .shamsiDateCalendarWidget( | |||
| context, | |||
| (newDate) { | |||
| String toDateString = | |||
| '${newDate.year}/${newDate.month}/${newDate.day}'; | |||
| meetingsState.setToDates( | |||
| toDateString); // Update the selected date | |||
| }, | |||
| ), | |||
| ); | |||
| }, | |||
| ); | |||
| }, | |||
| ), | |||
| ], | |||
| ), | |||
| ) | |||
| ], | |||
| ), | |||
| ExpansionTileCustom( | |||
| title: | |||
| AppLocalizations.of(context)!.location, | |||
| widgets: <Widget>[ | |||
| ListView.builder( | |||
| primary: false, | |||
| physics: NeverScrollableScrollPhysics(), | |||
| shrinkWrap: true, | |||
| itemCount: | |||
| globalState.locationsModel!.length, | |||
| itemBuilder: | |||
| (BuildContext context, int index) { | |||
| final items = globalState | |||
| .locationsModel![index]; | |||
| return RadioListTile<int>( | |||
| toggleable: true, | |||
| groupValue: meetingsState | |||
| .selectedLocationId, | |||
| value: items.id ?? -1, | |||
| title: Text( | |||
| items.address ?? '', | |||
| maxLines: 1, | |||
| style: TextStyle( | |||
| fontWeight: FontWeight.w100, | |||
| fontSize: 14), | |||
| overflow: TextOverflow.ellipsis, | |||
| ), | |||
| activeColor: config.ui.secendGreen, | |||
| onChanged: (int? newValue) { | |||
| meetingsState.selectLocation( | |||
| newValue ?? null); | |||
| }, | |||
| ); | |||
| }, | |||
| ), | |||
| ], | |||
| ), | |||
| ExpansionTileCustom( | |||
| title: AppLocalizations.of(context)! | |||
| .meetingmanager, | |||
| widgets: <Widget>[ | |||
| ListView.builder( | |||
| primary: false, | |||
| physics: NeverScrollableScrollPhysics(), | |||
| shrinkWrap: true, | |||
| itemCount: | |||
| globalState.meetingsManagerModel!.length, | |||
| itemBuilder: | |||
| (BuildContext context, int index) { | |||
| final items = globalState | |||
| .meetingsManagerModel![index]; | |||
| return RadioListTile<int>( | |||
| toggleable: true, | |||
| groupValue: meetingsState | |||
| .selectedManagersId, | |||
| value: items.id ?? -1, | |||
| title: Text( | |||
| items.name ?? '', | |||
| style: TextStyle( | |||
| fontWeight: FontWeight.w100, | |||
| fontSize: 14), | |||
| maxLines: 1, | |||
| overflow: TextOverflow.ellipsis, | |||
| ), | |||
| activeColor: config.ui.secendGreen, | |||
| onChanged: (int? newValue) { | |||
| meetingsState.selectManager( | |||
| newValue ?? null); | |||
| }, | |||
| ); | |||
| }, | |||
| ), | |||
| ], | |||
| ), | |||
| ExpansionTileCustom( | |||
| title: | |||
| AppLocalizations.of(context)!.subject, | |||
| widgets: <Widget>[ | |||
| ListView.builder( | |||
| primary: false, | |||
| physics: NeverScrollableScrollPhysics(), | |||
| shrinkWrap: true, | |||
| itemCount: | |||
| globalState.subjectsModel!.length, | |||
| itemBuilder: | |||
| (BuildContext context, int index) { | |||
| final items = | |||
| globalState.subjectsModel![index]; | |||
| return RadioListTile<int>( | |||
| toggleable: true, | |||
| groupValue: | |||
| meetingsState.selectedSubjectId, | |||
| value: items.id ?? -1, | |||
| title: Text( | |||
| items.subject ?? '', | |||
| maxLines: 1, | |||
| overflow: TextOverflow.ellipsis, | |||
| style: TextStyle( | |||
| fontWeight: FontWeight.w100, | |||
| fontSize: 14), | |||
| ), | |||
| activeColor: config.ui.secendGreen, | |||
| onChanged: (int? newValue) { | |||
| meetingsState.selectSubject( | |||
| newValue ?? null); | |||
| }, | |||
| ); | |||
| }, | |||
| ), | |||
| ], | |||
| ), | |||
| SizedBox( | |||
| height: 10, | |||
| ), | |||
| Divider(), | |||
| Column( | |||
| children: [ | |||
| SizedBox( | |||
| height: 250, | |||
| child: ListView.builder( | |||
| physics: | |||
| NeverScrollableScrollPhysics(), | |||
| shrinkWrap: true, | |||
| primary: false, | |||
| itemCount: globalState | |||
| .meetingStatuses.length, | |||
| itemBuilder: (BuildContext context, | |||
| int index) { | |||
| final items = globalState | |||
| .meetingStatuses[index]; | |||
| return RadioListTile<int>( | |||
| toggleable: true, | |||
| groupValue: meetingsState | |||
| .selectedStatusId, | |||
| value: items.id, | |||
| title: Text( | |||
| items.title, | |||
| maxLines: 1, | |||
| style: TextStyle( | |||
| fontWeight: FontWeight.w100, | |||
| fontSize: 14), | |||
| overflow: TextOverflow.ellipsis, | |||
| ), | |||
| activeColor: | |||
| config.ui.secendGreen, | |||
| onChanged: (int? newValue) { | |||
| meetingsState | |||
| .selectStatusMeeting( | |||
| newValue ?? null); | |||
| }, | |||
| ); | |||
| }, | |||
| ), | |||
| ), | |||
| ], | |||
| ) | |||
| ], | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| ), | |||
| ], | |||
| ); | |||
| case Status.loading: | |||
| return const LoadingWidget(); | |||
| case Status.error: | |||
| return CustomErrorWidget( | |||
| onPressed: () async { | |||
| // await meetingsState!.getAllFiltersItems(refresh: true); | |||
| }, | |||
| ); | |||
| default: | |||
| return Container(); | |||
| } | |||
| }, | |||
| ); | |||
| }); | |||
| } | |||
| } | |||
| class LineButtomSheet extends StatelessWidget { | |||
| const LineButtomSheet({ | |||
| super.key, | |||
| }); | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Container( | |||
| margin: const EdgeInsets.only(top: 8.0), | |||
| width: 30.0, | |||
| height: 3.0, | |||
| decoration: BoxDecoration( | |||
| color: Colors.grey.shade400, | |||
| borderRadius: BorderRadius.circular(24.0), | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| class MeetingsStatus { | |||
| int id; | |||
| String title; | |||
| MeetingsStatus({ | |||
| required this.id, | |||
| required this.title, | |||
| }); | |||
| } | |||
| @@ -0,0 +1,296 @@ | |||
| // ignore_for_file: public_member_api_docs, sort_constructors_first | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | |||
| import 'package:font_awesome_flutter/font_awesome_flutter.dart'; | |||
| import 'package:go_router/go_router.dart'; | |||
| import 'package:provider/provider.dart'; | |||
| import 'package:qadirneyriz/config/config.dart'; | |||
| import 'package:qadirneyriz/screens/meeting/diolog_meetings_filters.dart'; | |||
| import 'package:qadirneyriz/screens/meeting/state.dart'; | |||
| import 'package:qadirneyriz/utils/enums/status.dart'; | |||
| import 'package:qadirneyriz/utils/tools/tools.dart'; | |||
| import 'package:qadirneyriz/widgets/card_meeting.dart'; | |||
| import 'package:qadirneyriz/widgets/custom_appbar.dart'; | |||
| import 'package:qadirneyriz/widgets/empty_widget.dart'; | |||
| import 'package:qadirneyriz/widgets/error_widget.dart'; | |||
| import 'package:qadirneyriz/widgets/icon_button.dart'; | |||
| import 'package:qadirneyriz/widgets/loading_widget.dart'; | |||
| import 'package:qadirneyriz/widgets/today_widget.dart'; | |||
| import 'package:shamsi_date/shamsi_date.dart'; | |||
| class MeetingsScreen extends StatefulWidget { | |||
| const MeetingsScreen({super.key}); | |||
| @override | |||
| State<MeetingsScreen> createState() => _MeetingsScreenState(); | |||
| } | |||
| class _MeetingsScreenState extends State<MeetingsScreen> { | |||
| late ScrollController _scrollController; | |||
| late MeetingsState meetingsState; | |||
| @override | |||
| void initState() { | |||
| _scrollController = ScrollController(); | |||
| _scrollController.addListener(_scrollListener); | |||
| meetingsState = Provider.of<MeetingsState>(context, listen: false); | |||
| Future.delayed(Duration.zero, () async { | |||
| await meetingsState.getMeetings(); | |||
| meetingsState.clearFilters(); | |||
| }); | |||
| super.initState(); | |||
| } | |||
| @override | |||
| void dispose() { | |||
| _scrollController.dispose(); | |||
| super.dispose(); | |||
| } | |||
| _scrollListener() async { | |||
| if (_scrollController.offset >= | |||
| _scrollController.position.maxScrollExtent && | |||
| !_scrollController.position.outOfRange) { | |||
| if (!meetingsState.pageEndedMeetings) { | |||
| await meetingsState.nextPageMeetings( | |||
| toDate: meetingsState.toDate, | |||
| fromDate: meetingsState.fromDate, | |||
| location: meetingsState.selectedLocationId, | |||
| subject: meetingsState.selectedSubjectId, | |||
| meetingManager: meetingsState.selectedManagersId, | |||
| meetingStatus: meetingsState.selectedStatusId); | |||
| } | |||
| } | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| Jalali nowShamsi = Jalali.now(); | |||
| String todayDateForShow = | |||
| '${nowShamsi.day} ${Tools.getMonthName(nowShamsi.month)} ${nowShamsi.year}'; | |||
| return Consumer<MeetingsState>( | |||
| builder: (context, value, child) { | |||
| return CustomScrollView( | |||
| controller: _scrollController, | |||
| slivers: <Widget>[ | |||
| const CustomAppbar(), | |||
| SliverToBoxAdapter( | |||
| child: TodayWidget(formattedDate: todayDateForShow), | |||
| ), | |||
| SliverToBoxAdapter( | |||
| child: Padding( | |||
| padding: | |||
| const EdgeInsets.symmetric(vertical: 30, horizontal: 15), | |||
| child: Row( | |||
| mainAxisAlignment: MainAxisAlignment.spaceBetween, | |||
| crossAxisAlignment: CrossAxisAlignment.center, | |||
| children: [ | |||
| Text( | |||
| style: const TextStyle(fontSize: 14), | |||
| AppLocalizations.of(context)!.meetings, | |||
| ), | |||
| IconButtonCustom( | |||
| iconColor: value.hasActiveFilters() | |||
| ? Colors.white | |||
| : config.ui.secendGreen, | |||
| backColor: value.hasActiveFilters() | |||
| ? config.ui.secendGreen | |||
| : Colors.white, | |||
| icon: FontAwesomeIcons.sliders, | |||
| onTap: () { | |||
| showModalBottomSheet( | |||
| isScrollControlled: true, | |||
| useSafeArea: true, | |||
| context: context, | |||
| builder: (context) { | |||
| return DiologMeetingsFilters(); | |||
| }, | |||
| ); | |||
| }, | |||
| ) | |||
| ], | |||
| ), | |||
| ), | |||
| ), | |||
| meetingsList(value), | |||
| (value.paginationMeetings == Status.ready || | |||
| value.paginationMeetings == Status.empty) | |||
| ? const SliverToBoxAdapter() | |||
| : const SliverToBoxAdapter( | |||
| child: Center( | |||
| child: LoadingWidget( | |||
| size: 10, | |||
| ), | |||
| ), | |||
| ) | |||
| ], | |||
| ); | |||
| }, | |||
| ); | |||
| } | |||
| Widget meetingsList(MeetingsState state) { | |||
| switch (state.statusMeetings) { | |||
| case Status.ready: | |||
| return SliverList.builder( | |||
| itemBuilder: (context, index) { | |||
| final items = state.meetingsModel!.data![index]; | |||
| return Padding( | |||
| padding: const EdgeInsets.all(8.0), | |||
| child: CustomCardMeeting( | |||
| titel: items.subject!.subject ?? '', | |||
| fromTime: items.azHour ?? '', | |||
| toTime: items.taHour ?? "", | |||
| location: items.location!.address ?? '', | |||
| date: items.dateJalali ?? '', | |||
| cardId: items.id ?? 0, | |||
| onSelectedMoreButton: (value) async { | |||
| switch (value) { | |||
| case 'edit': | |||
| await context.pushNamed('meetingedit', | |||
| pathParameters: {'id': items.id.toString()}); | |||
| meetingsState.getMeetings(refresh: true); | |||
| case 'confirm': | |||
| acceptMeeting(state, context, items.id ?? -1); | |||
| case 'cancel': | |||
| cancelMeeting(state, context, items.id ?? -1); | |||
| case 'report': | |||
| await context.pushNamed( | |||
| 'meetinsammary', | |||
| extra: items, // `items` should be a Datum instance | |||
| ); | |||
| default: | |||
| } | |||
| }, | |||
| itemBuilderMoreButton: (context) => [ | |||
| PopupMenuItem( | |||
| value: 'edit', | |||
| child: Row( | |||
| children: const [ | |||
| Icon( | |||
| Icons.edit, | |||
| color: Colors.green, | |||
| size: 17, | |||
| ), | |||
| SizedBox(width: 8), | |||
| Text( | |||
| 'ویرایش قرار', | |||
| style: TextStyle(fontSize: 12), | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| PopupMenuItem( | |||
| enabled: state.statusAcceptMeeting != Status.loading, | |||
| value: 'confirm', | |||
| child: Row( | |||
| children: const [ | |||
| Icon( | |||
| Icons.check_circle, | |||
| color: Colors.green, | |||
| size: 17, | |||
| ), | |||
| SizedBox(width: 8), | |||
| Text( | |||
| 'تایید جلسه', | |||
| style: TextStyle(fontSize: 12), | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| PopupMenuItem( | |||
| enabled: state.statusCancelMeeting != Status.loading, | |||
| value: 'cancel', | |||
| child: Row( | |||
| children: const [ | |||
| Icon( | |||
| Icons.cancel, | |||
| color: Colors.green, | |||
| size: 17, | |||
| ), | |||
| SizedBox(width: 8), | |||
| Text( | |||
| 'لغو قرار', | |||
| style: TextStyle(fontSize: 12), | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| PopupMenuItem( | |||
| value: 'report', | |||
| child: Row( | |||
| children: const [ | |||
| Icon( | |||
| Icons.receipt_long, | |||
| color: Colors.green, | |||
| size: 17, | |||
| ), | |||
| SizedBox(width: 8), | |||
| Text( | |||
| 'صورت جلسه', | |||
| style: TextStyle(fontSize: 12), | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| ]), | |||
| ); | |||
| }, | |||
| itemCount: state.meetingsModel!.data!.length, | |||
| ); | |||
| case Status.loading: | |||
| return SliverFillRemaining(child: const LoadingWidget()); | |||
| case Status.error: | |||
| return SliverFillRemaining( | |||
| child: CustomErrorWidget( | |||
| onPressed: () async { | |||
| await state.getMeetings(refresh: true); | |||
| }, | |||
| ), | |||
| ); | |||
| case Status.empty: | |||
| return SliverFillRemaining(child: EmptyStateWidget()); | |||
| default: | |||
| return Container(); | |||
| } | |||
| } | |||
| void cancelMeeting( | |||
| MeetingsState state, BuildContext context, int cardId) async { | |||
| final status = await state.cancelMeeting(id: cardId); | |||
| if (status == Status.ready) { | |||
| Tools.showCustomSnackBar( | |||
| text: 'جلسه لغو شد!', | |||
| isError: false, | |||
| context, | |||
| ); | |||
| } else { | |||
| Tools.showCustomSnackBar( | |||
| text: 'مشکلی رخ داده است. دوباره تلاش کنید!', | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } | |||
| } | |||
| void acceptMeeting( | |||
| MeetingsState state, BuildContext context, int cardId) async { | |||
| final status = await state.acceptMeeting(id: cardId); | |||
| if (status == Status.ready) { | |||
| Tools.showCustomSnackBar( | |||
| text: 'جلسه تایید شد!', | |||
| isError: false, | |||
| context, | |||
| ); | |||
| } else { | |||
| Tools.showCustomSnackBar( | |||
| text: 'مشکلی رخ داده است. دوباره تلاش کنید!', | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,265 @@ | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:qadirneyriz/models/meetings/meetings_model.dart'; | |||
| import 'package:qadirneyriz/services/meetings/meetings.dart'; | |||
| import 'package:qadirneyriz/utils/enums/status.dart'; | |||
| class MeetingsState extends ChangeNotifier { | |||
| // api meetings | |||
| MeetingsApi meetingsApi = MeetingsApi(); | |||
| // meetings list | |||
| Status statusMeetings = Status.loading; | |||
| Status paginationMeetings = Status.ready; | |||
| MeetingsModel? meetingsModel; | |||
| int pageMeetings = 1; | |||
| int limitMeetings = 10; | |||
| bool pageEndedMeetings = false; | |||
| Future<Status> getMeetings( | |||
| {bool refresh = false, | |||
| String? fromDate, | |||
| String? toDate, | |||
| int? location, | |||
| int? subject, | |||
| int? meetingManager, | |||
| int? meetingStatus}) async { | |||
| if (refresh) { | |||
| statusMeetings = Status.loading; | |||
| notifyListeners(); | |||
| } | |||
| if (meetingsModel != null && meetingsModel!.data!.isNotEmpty && !refresh) { | |||
| statusMeetings = Status.ready; | |||
| notifyListeners(); | |||
| pageMeetings = 1; | |||
| pageEndedMeetings = false; | |||
| paginationMeetings = Status.ready; | |||
| meetingsModel = await meetingsApi.getMeetings( | |||
| page: pageMeetings, | |||
| count: limitMeetings, | |||
| fromDate: fromDate, | |||
| toDate: toDate, | |||
| location: location, | |||
| subject: subject, | |||
| meetingManager: meetingManager, | |||
| status: meetingStatus); | |||
| } else { | |||
| pageMeetings = 1; | |||
| pageEndedMeetings = false; | |||
| paginationMeetings = Status.ready; | |||
| try { | |||
| statusMeetings = Status.loading; | |||
| notifyListeners(); | |||
| meetingsModel = await meetingsApi.getMeetings( | |||
| page: pageMeetings, | |||
| count: limitMeetings, | |||
| fromDate: fromDate, | |||
| toDate: toDate, | |||
| location: location, | |||
| subject: subject, | |||
| meetingManager: meetingManager, | |||
| status: meetingStatus); | |||
| if (meetingsModel != null && meetingsModel!.data!.isNotEmpty) { | |||
| if (meetingsModel!.data!.isNotEmpty) { | |||
| statusMeetings = Status.ready; | |||
| } else if (!meetingsModel!.hasData() && meetingsModel == null) { | |||
| statusMeetings = Status.empty; | |||
| } else if (meetingsModel!.data!.length < limitMeetings) { | |||
| pageEndedMeetings = true; | |||
| statusMeetings = Status.empty; | |||
| } | |||
| } else { | |||
| statusMeetings = Status.empty; | |||
| } | |||
| } catch (e) { | |||
| statusMeetings = Status.error; | |||
| } | |||
| notifyListeners(); | |||
| } | |||
| notifyListeners(); | |||
| print(statusMeetings); | |||
| return statusMeetings; | |||
| } | |||
| Future<void> nextPageMeetings( | |||
| {String? fromDate, | |||
| String? toDate, | |||
| int? location, | |||
| int? subject, | |||
| int? meetingManager, | |||
| int? meetingStatus}) async { | |||
| if (pageEndedMeetings == false && paginationMeetings == Status.ready) { | |||
| paginationMeetings = Status.loading; | |||
| notifyListeners(); | |||
| int p = pageMeetings; | |||
| pageMeetings = p + 1; | |||
| try { | |||
| final adsPaginationModel = await meetingsApi.getMeetings( | |||
| page: pageMeetings, | |||
| count: limitMeetings, | |||
| fromDate: fromDate, | |||
| toDate: toDate, | |||
| location: location, | |||
| subject: subject, | |||
| meetingManager: meetingManager, | |||
| status: meetingStatus); | |||
| if (adsPaginationModel.hasData()) { | |||
| meetingsModel!.data!.addAll(adsPaginationModel.data!); | |||
| if (adsPaginationModel.data!.length < limitMeetings) { | |||
| pageEndedMeetings = true; | |||
| paginationMeetings = Status.empty; | |||
| } else { | |||
| paginationMeetings = Status.ready; | |||
| } | |||
| } else if (!adsPaginationModel.hasData()) { | |||
| pageEndedMeetings = true; | |||
| paginationMeetings = Status.empty; | |||
| } | |||
| } catch (e) { | |||
| pageEndedMeetings = true; | |||
| paginationMeetings = Status.empty; | |||
| } | |||
| notifyListeners(); | |||
| } | |||
| } | |||
| // set date for filters | |||
| String fromDate = ''; | |||
| String toDate = ''; | |||
| void setFromDates(String? newFromDate) { | |||
| fromDate = newFromDate ?? ''; | |||
| notifyListeners(); | |||
| } | |||
| void setToDates(String? newToDate) { | |||
| toDate = newToDate ?? ''; | |||
| notifyListeners(); | |||
| } | |||
| // clear filters | |||
| void clearFilters() { | |||
| selectedLocationId = null; | |||
| selectedManagersId = null; | |||
| selectedStatusId = null; | |||
| selectedSubjectId = null; | |||
| fromDate = ''; | |||
| toDate = ''; | |||
| notifyListeners(); | |||
| } | |||
| // is filter Not empty | |||
| bool hasActiveFilters() { | |||
| return selectedLocationId != null || | |||
| selectedManagersId != null || | |||
| selectedStatusId != null || | |||
| selectedSubjectId != null || | |||
| fromDate.isNotEmpty || | |||
| toDate.isNotEmpty; | |||
| } | |||
| // get filters location meetings | |||
| int? selectedLocationId; | |||
| void selectLocation(int? locationId) { | |||
| selectedLocationId = locationId; | |||
| notifyListeners(); | |||
| } | |||
| // get filters subjects meetings | |||
| int? selectedSubjectId; | |||
| void selectSubject(int? subjectId) { | |||
| selectedSubjectId = subjectId; | |||
| notifyListeners(); | |||
| } | |||
| // get filters meeting managers | |||
| int? selectedManagersId; | |||
| void selectManager(int? managerId) { | |||
| selectedManagersId = managerId; | |||
| notifyListeners(); | |||
| } | |||
| // all meeting status filters | |||
| int? selectedStatusId; | |||
| void selectStatusMeeting(int? statusId) { | |||
| selectedStatusId = statusId; | |||
| notifyListeners(); | |||
| } | |||
| // cancel meeting | |||
| Status statusCancelMeeting = Status.empty; | |||
| String? messageCancelMeeting; | |||
| Map? errorsCancelMeeting; | |||
| Future<Status> cancelMeeting({ | |||
| required int id, | |||
| }) async { | |||
| statusCancelMeeting = Status.loading; | |||
| notifyListeners(); | |||
| try { | |||
| final result = await meetingsApi.cancelMeetingApi( | |||
| id: id, | |||
| ); | |||
| if (result.isOk) { | |||
| statusCancelMeeting = Status.ready; | |||
| messageCancelMeeting = result.message; | |||
| } else if (result.isOk == false) { | |||
| print(result.isOk); | |||
| errorsCancelMeeting = result.errors; | |||
| messageCancelMeeting = result.message; | |||
| statusCancelMeeting = Status.error; | |||
| } else { | |||
| statusCancelMeeting = Status.error; | |||
| } | |||
| notifyListeners(); | |||
| } catch (e) { | |||
| statusCancelMeeting = Status.error; | |||
| print(e); | |||
| } | |||
| notifyListeners(); | |||
| print(statusCancelMeeting); | |||
| return statusCancelMeeting; | |||
| } | |||
| // accept meeting | |||
| Status statusAcceptMeeting = Status.empty; | |||
| String? messageAcceptMeeting; | |||
| Map? errorsAcceptMeeting; | |||
| Future<Status> acceptMeeting({ | |||
| required int id, | |||
| }) async { | |||
| statusAcceptMeeting = Status.loading; | |||
| notifyListeners(); | |||
| try { | |||
| final result = await meetingsApi.acceptMeetingApi( | |||
| id: id, | |||
| ); | |||
| if (result.isOk) { | |||
| statusAcceptMeeting = Status.ready; | |||
| messageAcceptMeeting = result.message; | |||
| } else if (result.isOk == false) { | |||
| print(result.isOk); | |||
| errorsAcceptMeeting = result.errors; | |||
| messageAcceptMeeting = result.message; | |||
| statusAcceptMeeting = Status.error; | |||
| } else { | |||
| statusAcceptMeeting = Status.error; | |||
| } | |||
| notifyListeners(); | |||
| } catch (e) { | |||
| statusAcceptMeeting = Status.error; | |||
| print(e); | |||
| } | |||
| notifyListeners(); | |||
| print(statusAcceptMeeting); | |||
| return statusAcceptMeeting; | |||
| } | |||
| } | |||
| @@ -0,0 +1,106 @@ | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:go_router/go_router.dart'; | |||
| import 'package:provider/provider.dart'; | |||
| import 'package:qadirneyriz/global_state/global_state.dart'; | |||
| import 'package:qadirneyriz/utils/enums/status.dart'; | |||
| import 'package:qadirneyriz/utils/tools/tools.dart'; | |||
| import 'package:qadirneyriz/widgets/custom_button.dart'; | |||
| import 'package:qadirneyriz/widgets/custom_textfield.dart'; | |||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | |||
| class AddLocationDiolog extends StatelessWidget { | |||
| AddLocationDiolog({ | |||
| super.key, | |||
| }); | |||
| final TextEditingController addressController = TextEditingController(); | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Consumer<GlobalState>( | |||
| builder: (context, value, child) { | |||
| return Dialog( | |||
| child: Padding( | |||
| padding: const EdgeInsets.all(20.0), | |||
| child: Column( | |||
| mainAxisSize: MainAxisSize.min, // برای اندازهگیری درست دیالوگ | |||
| children: [ | |||
| Text('آدرس جدید'), | |||
| CustomTextField( | |||
| label: 'آدرس', | |||
| hintText: '', | |||
| textEditingController: addressController, | |||
| textInputType: TextInputType.text), | |||
| SizedBox( | |||
| height: 20, | |||
| ), | |||
| submit(value, context), | |||
| ], | |||
| ), | |||
| ), | |||
| ); | |||
| }, | |||
| ); | |||
| } | |||
| CustomButton submit(GlobalState state, BuildContext context) { | |||
| switch (state.statusAddNewAddress) { | |||
| case Status.loading: | |||
| return CustomButton( | |||
| hieght: 40, | |||
| width: double.infinity, | |||
| text: AppLocalizations.of(context)!.loading, | |||
| fontSize: 13, | |||
| onPressed: null, | |||
| topRightRadius: 10, | |||
| topLeftRadius: 10, | |||
| bottomLeftRadius: 10, | |||
| bottomRightRadius: 10, | |||
| ); | |||
| default: | |||
| return CustomButton( | |||
| hieght: 40, | |||
| width: double.infinity, | |||
| text: 'اظافه کردن', | |||
| fontSize: 13, | |||
| onPressed: () async { | |||
| if (addressController.text != '') { | |||
| // call add new subject | |||
| final status = | |||
| await state.addNewAddress(address: addressController.text); | |||
| if (status == Status.ready) { | |||
| // call refrresh subjects | |||
| await state.getLocations(refresh: true); | |||
| Tools.showCustomSnackBar( | |||
| text: 'آدرس اظافه شد!', | |||
| isError: false, | |||
| context, | |||
| ); | |||
| context.pop(); | |||
| } else { | |||
| Tools.showCustomSnackBar( | |||
| text: state.errorsAddNewAddress == null | |||
| ? state.messageAddNewAddress ?? | |||
| AppLocalizations.of(context)!.haserror | |||
| : Tools.combineErrorMessages( | |||
| state.errorsAddNewAddress ?? {}), | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } | |||
| } else { | |||
| Tools.showCustomSnackBar( | |||
| text: 'آدرس وارد کنید!', | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } | |||
| }, | |||
| topRightRadius: 10, | |||
| topLeftRadius: 10, | |||
| bottomLeftRadius: 10, | |||
| bottomRightRadius: 10, | |||
| ); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,106 @@ | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:go_router/go_router.dart'; | |||
| import 'package:provider/provider.dart'; | |||
| import 'package:qadirneyriz/global_state/global_state.dart'; | |||
| import 'package:qadirneyriz/utils/enums/status.dart'; | |||
| import 'package:qadirneyriz/utils/tools/tools.dart'; | |||
| import 'package:qadirneyriz/widgets/custom_button.dart'; | |||
| import 'package:qadirneyriz/widgets/custom_textfield.dart'; | |||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | |||
| class AddSubjectDiolog extends StatelessWidget { | |||
| AddSubjectDiolog({ | |||
| super.key, | |||
| }); | |||
| final TextEditingController subjectController = TextEditingController(); | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Consumer<GlobalState>( | |||
| builder: (context, value, child) { | |||
| return Dialog( | |||
| child: Padding( | |||
| padding: const EdgeInsets.all(20.0), | |||
| child: Column( | |||
| mainAxisSize: MainAxisSize.min, // برای اندازهگیری درست دیالوگ | |||
| children: [ | |||
| Text('موضوع جدید'), | |||
| CustomTextField( | |||
| label: 'موضوع', | |||
| hintText: '', | |||
| textEditingController: subjectController, | |||
| textInputType: TextInputType.text), | |||
| SizedBox( | |||
| height: 20, | |||
| ), | |||
| submit(value, context), | |||
| ], | |||
| ), | |||
| ), | |||
| ); | |||
| }, | |||
| ); | |||
| } | |||
| CustomButton submit(GlobalState state, BuildContext context) { | |||
| switch (state.statusAddNewSubject) { | |||
| case Status.loading: | |||
| return CustomButton( | |||
| hieght: 40, | |||
| width: double.infinity, | |||
| text: AppLocalizations.of(context)!.loading, | |||
| fontSize: 13, | |||
| onPressed: null, | |||
| topRightRadius: 10, | |||
| topLeftRadius: 10, | |||
| bottomLeftRadius: 10, | |||
| bottomRightRadius: 10, | |||
| ); | |||
| default: | |||
| return CustomButton( | |||
| hieght: 40, | |||
| width: double.infinity, | |||
| text: 'اظافه کردن', | |||
| fontSize: 13, | |||
| onPressed: () async { | |||
| if (subjectController.text != '') { | |||
| // call add new subject | |||
| final status = | |||
| await state.addNewSubject(subject: subjectController.text); | |||
| if (status == Status.ready) { | |||
| // call refrresh subjects | |||
| await state.getSubjects(refresh: true); | |||
| Tools.showCustomSnackBar( | |||
| text: 'موضوع اظافه شد!', | |||
| isError: false, | |||
| context, | |||
| ); | |||
| context.pop(); | |||
| } else { | |||
| Tools.showCustomSnackBar( | |||
| text: state.errorsAddNewSubject == null | |||
| ? state.messageAddNewSubject ?? | |||
| AppLocalizations.of(context)!.haserror | |||
| : Tools.combineErrorMessages( | |||
| state.errorsAddNewSubject ?? {}), | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } | |||
| } else { | |||
| Tools.showCustomSnackBar( | |||
| text: 'موضوع وارد کنید!', | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } | |||
| }, | |||
| topRightRadius: 10, | |||
| topLeftRadius: 10, | |||
| bottomLeftRadius: 10, | |||
| bottomRightRadius: 10, | |||
| ); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,202 @@ | |||
| // ignore_for_file: public_member_api_docs, sort_constructors_first | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | |||
| import 'package:go_router/go_router.dart'; | |||
| import 'package:provider/provider.dart'; | |||
| import 'package:qadirneyriz/global_state/global_state.dart'; | |||
| import 'package:qadirneyriz/screens/meeting_edit/screen.dart'; | |||
| import 'package:qadirneyriz/utils/enums/status.dart'; | |||
| import 'package:qadirneyriz/utils/tools/tools.dart'; | |||
| import 'package:qadirneyriz/widgets/ExpansionTileCustom.dart'; | |||
| import 'package:qadirneyriz/widgets/custom_button.dart'; | |||
| import 'package:qadirneyriz/widgets/custom_textfield.dart'; | |||
| class AddUserDiolog extends StatefulWidget { | |||
| AddUserDiolog({ | |||
| super.key, | |||
| }); | |||
| @override | |||
| State<AddUserDiolog> createState() => _AddUserDiologState(); | |||
| } | |||
| class _AddUserDiologState extends State<AddUserDiolog> { | |||
| final TextEditingController nameController = TextEditingController(); | |||
| final TextEditingController mobileController = TextEditingController(); | |||
| final TextEditingController passwordController = TextEditingController(); | |||
| int? selectedRole; | |||
| final List<MemberRole> roles = [ | |||
| MemberRole(roleId: 1, roleName: 'کاربر معمولی'), | |||
| MemberRole(roleId: 2, roleName: 'اپراتور'), | |||
| ]; | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Consumer<GlobalState>( | |||
| builder: (context, value, child) { | |||
| return Dialog( | |||
| child: Padding( | |||
| padding: const EdgeInsets.all(20.0), | |||
| child: SingleChildScrollView( | |||
| child: Column( | |||
| mainAxisSize: MainAxisSize.min, // برای اندازهگیری درست دیالوگ | |||
| children: [ | |||
| Text('عضو جدید'), | |||
| CustomTextField( | |||
| label: '', | |||
| hintText: 'نام و نام خانوادگی', | |||
| paddingVertical: 0, | |||
| textEditingController: nameController, | |||
| textInputType: TextInputType.text), | |||
| CustomTextField( | |||
| label: '', | |||
| paddingVertical: 0, | |||
| hintText: 'شماره موبایل', | |||
| textEditingController: mobileController, | |||
| textInputType: TextInputType.phone), | |||
| CustomTextField( | |||
| label: '', | |||
| paddingVertical: 0, | |||
| hintText: 'رمز عبور', | |||
| isPass: true, | |||
| textEditingController: passwordController, | |||
| textInputType: TextInputType.visiblePassword), | |||
| Padding( | |||
| padding: const EdgeInsets.symmetric(horizontal: 20), | |||
| child: ExpansionTileCustom( | |||
| isForm: true, | |||
| subTitile: '', | |||
| title: 'نقش کاربر', | |||
| widgets: <Widget>[ | |||
| Column( | |||
| children: roles.map((role) { | |||
| bool isSelected = selectedRole == role.roleId; | |||
| return ItemInTile( | |||
| backColor: | |||
| isSelected ? Color(0xff06CF64) : Colors.white, | |||
| textColor: | |||
| isSelected ? Colors.white : Colors.black, | |||
| text: role.roleName, | |||
| hasIcon: false, | |||
| onTap: () { | |||
| setState(() { | |||
| selectedRole = role.roleId; | |||
| }); | |||
| }, | |||
| ); | |||
| }).toList(), | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| SizedBox( | |||
| height: 60, | |||
| ), | |||
| submit(value, context), | |||
| ], | |||
| ), | |||
| ), | |||
| ), | |||
| ); | |||
| }, | |||
| ); | |||
| } | |||
| CustomButton submit(GlobalState state, BuildContext context) { | |||
| switch (state.statusAddNewUser) { | |||
| case Status.loading: | |||
| return CustomButton( | |||
| hieght: 40, | |||
| width: double.infinity, | |||
| text: AppLocalizations.of(context)!.loading, | |||
| fontSize: 13, | |||
| onPressed: null, | |||
| topRightRadius: 10, | |||
| topLeftRadius: 10, | |||
| bottomLeftRadius: 10, | |||
| bottomRightRadius: 10, | |||
| ); | |||
| default: | |||
| return CustomButton( | |||
| hieght: 40, | |||
| width: double.infinity, | |||
| text: 'اظافه کردن', | |||
| fontSize: 13, | |||
| onPressed: () async { | |||
| if (nameController.text == '') { | |||
| // call add new subject | |||
| Tools.showCustomSnackBar( | |||
| text: 'اسم وارد کنید!', | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } else if (mobileController.text == '') { | |||
| Tools.showCustomSnackBar( | |||
| text: 'موبایل وارد کنید!', | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } else if (passwordController.text == '') { | |||
| Tools.showCustomSnackBar( | |||
| text: 'پسورد وارد کنید!', | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } else if (selectedRole == null) { | |||
| Tools.showCustomSnackBar( | |||
| text: 'نقش کاربر وارد کنید!', | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } else { | |||
| final status = await state.addNewUser( | |||
| name: nameController.text, | |||
| mobile: mobileController.text, | |||
| role: selectedRole!, | |||
| password: passwordController.text); | |||
| if (status == Status.ready) { | |||
| // call refrresh users | |||
| await state.getUsers(refresh: true); | |||
| Tools.showCustomSnackBar( | |||
| text: 'کاربر اظافه شد!', | |||
| isError: false, | |||
| context, | |||
| ); | |||
| context.pop(); | |||
| } else { | |||
| Tools.showCustomSnackBar( | |||
| text: state.errorsAddNewUser == null | |||
| ? state.messageAddNewUser ?? | |||
| AppLocalizations.of(context)!.haserror | |||
| : Tools.combineErrorMessages( | |||
| state.errorsAddNewUser ?? {}), | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } | |||
| } | |||
| }, | |||
| topRightRadius: 10, | |||
| topLeftRadius: 10, | |||
| bottomLeftRadius: 10, | |||
| bottomRightRadius: 10, | |||
| ); | |||
| } | |||
| } | |||
| } | |||
| class MemberRole { | |||
| final int roleId; | |||
| final String roleName; | |||
| MemberRole({ | |||
| required this.roleId, | |||
| required this.roleName, | |||
| }); | |||
| } | |||
| @@ -0,0 +1,518 @@ | |||
| // ignore_for_file: public_member_api_docs, sort_constructors_first | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | |||
| import 'package:go_router/go_router.dart'; | |||
| import 'package:provider/provider.dart'; | |||
| import 'package:qadirneyriz/global_state/global_state.dart'; | |||
| import 'package:qadirneyriz/screens/meeting_edit/diolog_add_location.dart'; | |||
| import 'package:qadirneyriz/screens/meeting_edit/diolog_add_subject.dart'; | |||
| import 'package:qadirneyriz/screens/meeting_edit/diolog_add_user.dart'; | |||
| import 'package:qadirneyriz/screens/meeting_edit/state.dart'; | |||
| import 'package:qadirneyriz/utils/enums/status.dart'; | |||
| import 'package:qadirneyriz/utils/tools/tools.dart'; | |||
| import 'package:qadirneyriz/widgets/ExpansionTileCustom.dart'; | |||
| import 'package:qadirneyriz/widgets/custom_appbar.dart'; | |||
| import 'package:qadirneyriz/widgets/custom_button.dart'; | |||
| import 'package:qadirneyriz/widgets/ink_warpper.dart'; | |||
| import 'package:qadirneyriz/widgets/loading_widget.dart'; | |||
| import 'package:qadirneyriz/widgets/picker.dart'; | |||
| class MeetingEditScreen extends StatefulWidget { | |||
| final int id; | |||
| const MeetingEditScreen({ | |||
| Key? key, | |||
| required this.id, | |||
| }) : super(key: key); | |||
| @override | |||
| State<MeetingEditScreen> createState() => _MeetingEditScreenState(); | |||
| } | |||
| class _MeetingEditScreenState extends State<MeetingEditScreen> { | |||
| final _formKey = GlobalKey<FormState>(); // Key for form validation | |||
| // all states we have | |||
| late MeetingEditState meetingEditState; | |||
| late GlobalState globalState; | |||
| @override | |||
| void initState() { | |||
| super.initState(); | |||
| //set states | |||
| meetingEditState = Provider.of<MeetingEditState>(context, listen: false); | |||
| globalState = Provider.of<GlobalState>(context, listen: false); | |||
| Future.delayed(Duration.zero, () async { | |||
| // get items | |||
| await meetingEditState.getOneMeeting(id: widget.id); | |||
| await globalState.getAllFiltersItems(); | |||
| // set variables | |||
| meetingEditState.setAllVariablesAtStart(id: widget.id); | |||
| }); | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Scaffold( | |||
| body: Consumer2<MeetingEditState, GlobalState>( | |||
| builder: (context, meetingEditState, globalState, child) { | |||
| return CustomScrollView( | |||
| slivers: <Widget>[ | |||
| CustomAppbar(title: AppLocalizations.of(context)!.editmeeting), | |||
| SliverFillRemaining( | |||
| child: content(context, meetingEditState, globalState)), | |||
| ], | |||
| ); | |||
| }, | |||
| ), | |||
| ); | |||
| } | |||
| Widget content(BuildContext context, MeetingEditState meetingEditState, | |||
| GlobalState globalState) { | |||
| if (meetingEditState.oneMeetingStatus[widget.id] == Status.ready && | |||
| globalState.allFiltersStatus == Status.ready) { | |||
| final itemInOneMeeting = meetingEditState.oneMeetingModel![widget.id]!; | |||
| return Padding( | |||
| // This is now wrapped inside SliverToBoxAdapter | |||
| padding: const EdgeInsets.all(16.0), | |||
| child: Form( | |||
| key: _formKey, | |||
| child: SingleChildScrollView( | |||
| child: Column( | |||
| crossAxisAlignment: CrossAxisAlignment.start, | |||
| children: [ | |||
| // subject ExpansionTile | |||
| Padding( | |||
| padding: const EdgeInsets.symmetric(vertical: 8.0), | |||
| child: ExpansionTileCustom( | |||
| isForm: true, | |||
| subTitile: AppLocalizations.of(context)!.meetingsubject, | |||
| title: meetingEditState.selectedSubject.id != null | |||
| ? meetingEditState.selectedSubject.text ?? '' | |||
| : meetingEditState.oneMeetingModel![widget.id]!.subject! | |||
| .subject ?? | |||
| '', | |||
| widgets: <Widget>[ | |||
| ItemInTile( | |||
| text: 'عضو جدید', | |||
| onTap: () async { | |||
| await showDialog( | |||
| context: context, // این باید کانتکست فعلی باشد | |||
| builder: (BuildContext context) { | |||
| return AddSubjectDiolog(); | |||
| }, | |||
| ); | |||
| }, | |||
| hasIcon: true, | |||
| backColor: Colors.white, | |||
| textColor: Colors.black.withOpacity(.5), | |||
| ), | |||
| Column( | |||
| children: globalState.subjectsModel!.map((subject) { | |||
| bool isSelected = | |||
| meetingEditState.selectedSubject.id == subject.id; | |||
| return ItemInTile( | |||
| backColor: | |||
| isSelected ? Color(0xff06CF64) : Colors.white, | |||
| textColor: isSelected ? Colors.white : Colors.black, | |||
| text: subject.subject ?? '', | |||
| hasIcon: false, | |||
| onTap: () { | |||
| setState(() { | |||
| meetingEditState.selectedSubject = ItemSelected( | |||
| text: subject.subject ?? '', | |||
| id: subject.id ?? | |||
| 0); // Update selected location | |||
| }); | |||
| }, | |||
| ); | |||
| }).toList(), | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| // Date Picker | |||
| Padding( | |||
| padding: const EdgeInsets.symmetric(vertical: 8.0), | |||
| child: PickerCustom( | |||
| showDate: meetingEditState.fromDate != null | |||
| ? meetingEditState.fromDate ?? '' | |||
| : itemInOneMeeting.dateJalali ?? '', | |||
| onTap: () { | |||
| showDialog( | |||
| context: context, | |||
| builder: (context) { | |||
| return Dialog( | |||
| child: Tools.shamsiDateCalendarWidget( | |||
| context, | |||
| (newDate) { | |||
| String fromDateString = | |||
| '${newDate.year}/${newDate.month}/${newDate.day}'; | |||
| meetingEditState.setFromDate( | |||
| fromDateString); // Update the selected date | |||
| }, | |||
| ), | |||
| ); | |||
| }, | |||
| ); | |||
| }, | |||
| isForm: true, | |||
| title: AppLocalizations.of(context)!.date, | |||
| ), | |||
| ), | |||
| // From and To time Range Pickers | |||
| Padding( | |||
| padding: const EdgeInsets.symmetric(vertical: 15.0), | |||
| child: Row( | |||
| mainAxisAlignment: MainAxisAlignment.spaceBetween, | |||
| crossAxisAlignment: CrossAxisAlignment.end, | |||
| children: [ | |||
| PickerCustom( | |||
| showDate: meetingEditState.selectedStartTime != null | |||
| ? Tools.formatTime( | |||
| meetingEditState.selectedStartTime!.hour, | |||
| meetingEditState.selectedStartTime!.minute) | |||
| : itemInOneMeeting.azHour ?? '', | |||
| onTap: () async { | |||
| TimeOfDay? picked = await showTimePicker( | |||
| context: context, | |||
| initialTime: meetingEditState.selectedStartTime!, | |||
| ); | |||
| if (picked != null && | |||
| picked != meetingEditState.selectedStartTime) | |||
| setState(() { | |||
| meetingEditState.selectedStartTime = picked; | |||
| }); | |||
| }, | |||
| isForm: true, | |||
| icon: Icons.access_time_outlined, | |||
| title: AppLocalizations.of(context)!.clock, | |||
| ), | |||
| Text(AppLocalizations.of(context)!.to), | |||
| PickerCustom( | |||
| showDate: meetingEditState.selectedEndTime != null | |||
| ? Tools.formatTime( | |||
| meetingEditState.selectedEndTime!.hour, | |||
| meetingEditState.selectedEndTime!.minute) | |||
| : itemInOneMeeting.taHour ?? '', | |||
| isForm: true, | |||
| icon: Icons.access_time_outlined, | |||
| onTap: () async { | |||
| TimeOfDay? picked = await showTimePicker( | |||
| context: context, | |||
| initialTime: meetingEditState.selectedEndTime!, | |||
| ); | |||
| if (picked != null && | |||
| picked != meetingEditState.selectedEndTime) | |||
| setState(() { | |||
| meetingEditState.selectedEndTime = picked; | |||
| }); | |||
| }, | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| // Location ExpansionTile | |||
| Padding( | |||
| padding: const EdgeInsets.symmetric(vertical: 8.0), | |||
| child: ExpansionTileCustom( | |||
| isForm: true, | |||
| subTitile: AppLocalizations.of(context)!.location, | |||
| title: meetingEditState.selectedLocation.id != null | |||
| ? meetingEditState.selectedLocation.text ?? '' | |||
| : itemInOneMeeting.location!.address ?? '', | |||
| widgets: <Widget>[ | |||
| ItemInTile( | |||
| text: 'مکان جدید', | |||
| onTap: () async { | |||
| await showDialog( | |||
| context: context, // این باید کانتکست فعلی باشد | |||
| builder: (BuildContext context) { | |||
| return AddLocationDiolog(); | |||
| }, | |||
| ); | |||
| }, | |||
| hasIcon: true, | |||
| backColor: Colors.white, | |||
| textColor: Colors.black.withOpacity(.5), | |||
| ), | |||
| Column( | |||
| children: globalState.locationsModel!.map((location) { | |||
| bool isSelected = | |||
| meetingEditState.selectedLocation.id == | |||
| location.id; | |||
| return ItemInTile( | |||
| backColor: | |||
| isSelected ? Color(0xff06CF64) : Colors.white, | |||
| textColor: isSelected ? Colors.white : Colors.black, | |||
| text: location.address ?? '', | |||
| hasIcon: false, | |||
| onTap: () { | |||
| setState(() { | |||
| meetingEditState.selectedLocation = | |||
| ItemSelected( | |||
| text: location.address, | |||
| id: location | |||
| .id); // Update selected location | |||
| }); | |||
| }, | |||
| ); | |||
| }).toList(), | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| // Another ExpansionTile for users | |||
| Padding( | |||
| padding: const EdgeInsets.symmetric(vertical: 8.0), | |||
| child: ExpansionTileCustom( | |||
| isForm: true, | |||
| subTitile: AppLocalizations.of(context)!.users, | |||
| title: AppLocalizations.of(context)!.selectusers, | |||
| widgets: <Widget>[ | |||
| ItemInTile( | |||
| text: 'کاربر جدید', | |||
| onTap: () async { | |||
| await showDialog( | |||
| context: context, // این باید کانتکست فعلی باشد | |||
| builder: (BuildContext context) { | |||
| return AddUserDiolog(); | |||
| }, | |||
| ); | |||
| }, | |||
| hasIcon: true, | |||
| backColor: Colors.white, | |||
| textColor: Colors.black.withOpacity(.5), | |||
| ), | |||
| Column( | |||
| children: globalState.usersModel != null | |||
| ? globalState.usersModel!.map((user) { | |||
| bool isSelected = meetingEditState | |||
| .selectedUsersItems | |||
| .contains(user.id); | |||
| return Container( | |||
| margin: EdgeInsets.symmetric( | |||
| vertical: 5.0, horizontal: 10), | |||
| decoration: BoxDecoration( | |||
| color: isSelected | |||
| ? Color(0xff06CF64) | |||
| : Colors.white, | |||
| borderRadius: BorderRadius.circular(10), | |||
| boxShadow: [ | |||
| BoxShadow( | |||
| color: Colors.black12, | |||
| blurRadius: 8, | |||
| offset: Offset(0, 4), | |||
| ), | |||
| ], | |||
| ), | |||
| child: InkWrapper( | |||
| onTap: () { | |||
| setState(() { | |||
| if (isSelected) { | |||
| meetingEditState.selectedUsersItems | |||
| .remove(user.id); | |||
| } else { | |||
| meetingEditState.selectedUsersItems | |||
| .add(user.id ?? 0); | |||
| } | |||
| }); | |||
| }, | |||
| child: Padding( | |||
| padding: const EdgeInsets.all(10.0), | |||
| child: Row( | |||
| children: [ | |||
| Text( | |||
| maxLines: 1, | |||
| overflow: TextOverflow.ellipsis, | |||
| user.name ?? '', | |||
| style: TextStyle( | |||
| fontSize: 12, | |||
| color: isSelected | |||
| ? Colors.white | |||
| : Colors.black, | |||
| ), | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| ), | |||
| ); | |||
| }).toList() | |||
| : [], | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| // Final ExpansionTile if required | |||
| Padding( | |||
| padding: const EdgeInsets.symmetric(vertical: 10.0), | |||
| child: ExpansionTileCustom( | |||
| isForm: true, | |||
| subTitile: AppLocalizations.of(context)!.meetingmanager, | |||
| title: meetingEditState.selectedManager.id != null | |||
| ? meetingEditState.selectedManager.text ?? '' | |||
| : itemInOneMeeting.manager!.name ?? '', | |||
| widgets: <Widget>[ | |||
| Column( | |||
| children: | |||
| globalState.meetingsManagerModel!.map((manager) { | |||
| bool isSelected = | |||
| meetingEditState.selectedManager.id == manager.id; | |||
| return ItemInTile( | |||
| backColor: | |||
| isSelected ? Color(0xff06CF64) : Colors.white, | |||
| textColor: isSelected ? Colors.white : Colors.black, | |||
| text: manager.name ?? '', | |||
| hasIcon: false, | |||
| onTap: () { | |||
| setState(() { | |||
| meetingEditState.selectedManager = ItemSelected( | |||
| id: manager.id, | |||
| text: manager | |||
| .name); // Update selected manager | |||
| }); | |||
| }, | |||
| ); | |||
| }).toList(), | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| // Submit Button | |||
| SizedBox( | |||
| height: 60, | |||
| ), | |||
| submit(context) | |||
| ], | |||
| ), | |||
| ), | |||
| ), | |||
| ); | |||
| } else if (meetingEditState.oneMeetingStatus[widget.id] == Status.loading || | |||
| globalState.allFiltersStatus == Status.loading) { | |||
| return const LoadingWidget(); | |||
| } else { | |||
| return Container(); | |||
| } | |||
| } | |||
| CustomButton submit(BuildContext context) { | |||
| switch (meetingEditState.statusEitMeeting) { | |||
| case Status.loading: | |||
| return CustomButton( | |||
| width: double.infinity, | |||
| hieght: 50, | |||
| fontSize: 16, | |||
| onPressed: null, | |||
| text: AppLocalizations.of(context)!.loading); | |||
| default: | |||
| return CustomButton( | |||
| width: double.infinity, | |||
| hieght: 50, | |||
| fontSize: 16, | |||
| onPressed: () async { | |||
| final status = await meetingEditState.editMeeting( | |||
| id: widget.id, | |||
| locationId: meetingEditState.selectedLocation.id ?? -1, | |||
| subjectId: meetingEditState.selectedSubject.id ?? -1, | |||
| managerId: meetingEditState.selectedManager.id ?? -1, | |||
| fromHour: Tools.formatTime( | |||
| meetingEditState.selectedStartTime!.hour, | |||
| meetingEditState.selectedStartTime!.minute), | |||
| toHour: Tools.formatTime( | |||
| meetingEditState.selectedEndTime!.hour, | |||
| meetingEditState.selectedEndTime!.minute), | |||
| dateMeeting: meetingEditState.fromDate ?? '', | |||
| members: meetingEditState.selectedUsersItems); | |||
| if (status == Status.ready) { | |||
| context.pop(); | |||
| } else { | |||
| Tools.showCustomSnackBar( | |||
| text: meetingEditState.errorsEditMeeting == null | |||
| ? meetingEditState.messageEditMeeting ?? | |||
| AppLocalizations.of(context)!.haserror | |||
| : Tools.combineErrorMessages( | |||
| meetingEditState.errorsEditMeeting ?? {}), | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } | |||
| }, | |||
| text: AppLocalizations.of(context)!.submit); | |||
| } | |||
| } | |||
| } | |||
| class ItemInTile extends StatelessWidget { | |||
| final void Function()? onTap; | |||
| final String text; | |||
| final bool hasIcon; | |||
| final Color backColor; | |||
| final Color textColor; | |||
| const ItemInTile({ | |||
| Key? key, | |||
| this.onTap, | |||
| required this.text, | |||
| required this.hasIcon, | |||
| required this.backColor, | |||
| required this.textColor, | |||
| }) : super(key: key); | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Padding( | |||
| padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), | |||
| child: InkWrapper( | |||
| borderRadius: 10, | |||
| onTap: onTap, | |||
| child: Container( | |||
| decoration: BoxDecoration(boxShadow: [ | |||
| BoxShadow( | |||
| color: Colors.black12, | |||
| blurRadius: 8, | |||
| offset: Offset(0, 4), | |||
| ), | |||
| ], color: backColor, borderRadius: BorderRadius.circular(10)), | |||
| child: Padding( | |||
| padding: const EdgeInsets.all(10.0), | |||
| child: Row( | |||
| mainAxisAlignment: MainAxisAlignment.spaceBetween, | |||
| children: [ | |||
| Expanded( | |||
| child: Text( | |||
| maxLines: 1, | |||
| overflow: TextOverflow.ellipsis, | |||
| text, | |||
| style: TextStyle(color: textColor, fontSize: 12), | |||
| ), | |||
| ), | |||
| if (hasIcon) | |||
| Icon(Icons.add_circle_outline, | |||
| color: Colors.black.withOpacity(.3)) | |||
| ], | |||
| ), | |||
| ), | |||
| ), | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| class ItemSelected { | |||
| final String? text; | |||
| final int? id; | |||
| ItemSelected({ | |||
| this.text, | |||
| this.id, | |||
| }); | |||
| } | |||
| @@ -0,0 +1,148 @@ | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:qadirneyriz/models/meetings/one_meeting_model.dart'; | |||
| import 'package:qadirneyriz/screens/meeting_edit/screen.dart'; | |||
| import 'package:qadirneyriz/services/meetings/meetings.dart'; | |||
| import 'package:qadirneyriz/utils/enums/status.dart'; | |||
| class MeetingEditState extends ChangeNotifier { | |||
| MeetingsApi meetingApi = MeetingsApi(); | |||
| Map<int, Status> oneMeetingStatus = {}; | |||
| Map<int, OneMeetingModel>? oneMeetingModel = {}; | |||
| Future<Status> getOneMeeting({bool refresh = false, required int id}) async { | |||
| oneMeetingStatus[id] = Status.loading; | |||
| notifyListeners(); | |||
| // Ensure the status map is initialized | |||
| if (oneMeetingStatus[id] == null || refresh) { | |||
| oneMeetingStatus[id] = Status.loading; | |||
| notifyListeners(); | |||
| } | |||
| // Initialize the model map if it's null | |||
| oneMeetingModel ??= {}; | |||
| // If not refreshing and data exists, return the current state | |||
| if (!refresh && oneMeetingModel![id] != null) { | |||
| oneMeetingStatus[id] = Status.ready; | |||
| notifyListeners(); | |||
| return oneMeetingStatus[id]!; | |||
| } | |||
| // Otherwise, fetch new data from API | |||
| try { | |||
| oneMeetingModel![id] = await meetingApi.getOneMeeting(id: id); | |||
| if (oneMeetingModel![id] != null) { | |||
| oneMeetingStatus[id] = Status.ready; | |||
| } else { | |||
| oneMeetingStatus[id] = Status.empty; | |||
| } | |||
| } catch (e) { | |||
| oneMeetingStatus[id] = Status.error; | |||
| print(e); | |||
| } | |||
| notifyListeners(); | |||
| return oneMeetingStatus[id]!; | |||
| } | |||
| // date | |||
| String? fromDate; | |||
| void setFromDate(String date) { | |||
| fromDate = date; | |||
| notifyListeners(); | |||
| } | |||
| // subject | |||
| ItemSelected selectedSubject = ItemSelected(); | |||
| // location | |||
| ItemSelected selectedLocation = ItemSelected(); | |||
| // manager | |||
| ItemSelected selectedManager = ItemSelected(); | |||
| //users | |||
| List<int> selectedUsersItems = []; | |||
| // time | |||
| TimeOfDay? selectedStartTime; | |||
| TimeOfDay? selectedEndTime; | |||
| // function at start | |||
| void setAllVariablesAtStart({required int id}) { | |||
| if (oneMeetingStatus[id] == Status.ready) { | |||
| final item = oneMeetingModel![id]!; | |||
| selectedLocation = ItemSelected( | |||
| id: item.locationsId ?? -1, text: item.location!.address ?? ''); | |||
| selectedSubject = ItemSelected( | |||
| text: item.subject!.subject ?? '', id: item.subject!.id ?? -1); | |||
| selectedManager = ItemSelected( | |||
| id: item.managerId ?? -1, text: item.manager!.name ?? ''); | |||
| fromDate = item.dateJalali; | |||
| String timeStart = item.azHour ?? ':'; | |||
| List<String> timeParts = timeStart.split(':'); | |||
| int hourStart = int.parse(timeParts[0]); | |||
| int minuteStart = int.parse(timeParts[1]); | |||
| selectedStartTime = TimeOfDay(hour: hourStart, minute: minuteStart); | |||
| String timeEnd = item.taHour ?? ':'; | |||
| List<String> timeEndParts = timeEnd.split(':'); | |||
| int hourEnd = int.parse(timeEndParts[0]); | |||
| int minuteEnd = int.parse(timeEndParts[1]); | |||
| selectedEndTime = TimeOfDay(hour: hourEnd, minute: minuteEnd); | |||
| // پر کردن لیست کاربران انتخاب شده | |||
| selectedUsersItems = item.users!.map((user) => user.id ?? -1).toList(); | |||
| } | |||
| } | |||
| // send edit meeting | |||
| Status statusEitMeeting = Status.empty; | |||
| String? messageEditMeeting; | |||
| Map? errorsEditMeeting; | |||
| Future<Status> editMeeting( | |||
| {required int id, | |||
| required int locationId, | |||
| required int subjectId, | |||
| required int managerId, | |||
| required String fromHour, | |||
| required String toHour, | |||
| required String dateMeeting, | |||
| required List<int> members}) async { | |||
| statusEitMeeting = Status.loading; | |||
| notifyListeners(); | |||
| try { | |||
| final result = await meetingApi.editMeetingApi( | |||
| id: id, | |||
| locationId: locationId, | |||
| subjectId: subjectId, | |||
| managerId: managerId, | |||
| fromHour: fromHour, | |||
| toHour: toHour, | |||
| dateMeeting: dateMeeting, | |||
| members: members); | |||
| if (result.isOk) { | |||
| statusEitMeeting = Status.ready; | |||
| messageEditMeeting = result.message; | |||
| } else if (result.isOk == false) { | |||
| print(result.isOk); | |||
| errorsEditMeeting = result.errors; | |||
| messageEditMeeting = result.message; | |||
| statusEitMeeting = Status.error; | |||
| } else { | |||
| statusEitMeeting = Status.error; | |||
| } | |||
| notifyListeners(); | |||
| } catch (e) { | |||
| statusEitMeeting = Status.error; | |||
| print(e); | |||
| } | |||
| notifyListeners(); | |||
| print(statusEitMeeting); | |||
| return statusEitMeeting; | |||
| } | |||
| } | |||
| @@ -0,0 +1,329 @@ | |||
| // ignore_for_file: public_member_api_docs, sort_constructors_first | |||
| import 'package:file_picker/file_picker.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:provider/provider.dart'; | |||
| import 'package:qadirneyriz/config/config.dart'; | |||
| import 'package:qadirneyriz/models/meetings/meetings_model.dart'; | |||
| import 'package:qadirneyriz/screens/meeting_summary/state.dart'; | |||
| import 'package:qadirneyriz/utils/enums/status.dart'; | |||
| import 'package:qadirneyriz/utils/tools/tools.dart'; | |||
| import 'package:qadirneyriz/widgets/card_meeting.dart'; | |||
| import 'package:qadirneyriz/widgets/custom_appbar.dart'; | |||
| import 'package:qadirneyriz/widgets/custom_button.dart'; | |||
| class MeetingSummaryScreen extends StatefulWidget { | |||
| final Datum meetingItem; | |||
| const MeetingSummaryScreen({ | |||
| Key? key, | |||
| required this.meetingItem, | |||
| }) : super(key: key); | |||
| @override | |||
| State<MeetingSummaryScreen> createState() => _MeetingSummaryScreenState(); | |||
| } | |||
| class _MeetingSummaryScreenState extends State<MeetingSummaryScreen> { | |||
| late TextEditingController _textControllerDescription; | |||
| @override | |||
| void initState() { | |||
| super.initState(); | |||
| _textControllerDescription = TextEditingController(); | |||
| } | |||
| @override | |||
| void dispose() { | |||
| _textControllerDescription.dispose(); | |||
| super.dispose(); | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Scaffold( | |||
| body: Consumer<MeetingSummaryState>( | |||
| builder: (context, value, child) { | |||
| return CustomScrollView( | |||
| slivers: <Widget>[ | |||
| CustomAppbar( | |||
| title: 'صورت جلسه', | |||
| ), | |||
| SliverPadding( | |||
| padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 2), | |||
| sliver: SliverToBoxAdapter( | |||
| child: CustomCardMeeting( | |||
| titel: widget.meetingItem.subject!.subject ?? '', | |||
| date: widget.meetingItem.dateJalali ?? '', | |||
| location: widget.meetingItem.location!.address ?? '', | |||
| fromTime: widget.meetingItem.azHour ?? '', | |||
| toTime: widget.meetingItem.taHour ?? '', | |||
| cardId: widget.meetingItem.id ?? -1, | |||
| ), | |||
| ), | |||
| ), | |||
| SliverPadding( | |||
| padding: | |||
| const EdgeInsets.symmetric(vertical: 20, horizontal: 10), | |||
| sliver: SliverToBoxAdapter( | |||
| child: Container( | |||
| decoration: BoxDecoration( | |||
| color: const Color(0xffF4F9F6), | |||
| boxShadow: [ | |||
| BoxShadow( | |||
| color: config.ui.mainGray.withOpacity(.1), | |||
| spreadRadius: .1, | |||
| offset: const Offset(0, 2), | |||
| blurRadius: 6, | |||
| ), | |||
| ], | |||
| borderRadius: const BorderRadius.all(Radius.circular(12)), | |||
| ), | |||
| child: CustomTextArea( | |||
| hintText: 'شرح جلسه', | |||
| controller: _textControllerDescription, | |||
| ), | |||
| ), | |||
| ), | |||
| ), | |||
| SliverToBoxAdapter( | |||
| child: ReceiptUploadDialog( | |||
| state: value, | |||
| ), | |||
| ), | |||
| SliverToBoxAdapter( | |||
| child: Padding( | |||
| padding: const EdgeInsets.all(30.0), | |||
| child: submitSammaryButton(context, value), | |||
| ), | |||
| ) | |||
| ], | |||
| ); | |||
| }, | |||
| ), | |||
| ); | |||
| } | |||
| Widget submitSammaryButton(BuildContext context, MeetingSummaryState state) { | |||
| switch (state.statusMinuteMeeting) { | |||
| case Status.loading: | |||
| return CustomButton(hieght: 50, text: 'صبر کنید!'); | |||
| default: | |||
| return CustomButton( | |||
| hieght: 50, | |||
| text: 'ثبت صورت جلسه', | |||
| onPressed: () async { | |||
| if (_textControllerDescription.text == '') { | |||
| // call add new subject | |||
| Tools.showCustomSnackBar( | |||
| text: 'موضوع وارد کنید!', | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } else if (state.selectedFiles == null) { | |||
| // call add new subject | |||
| Tools.showCustomSnackBar( | |||
| text: 'فایل وارد کنید!', | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } else { | |||
| final status = await state.addMinuteMeeting( | |||
| id: widget.meetingItem.id ?? -1, | |||
| description: _textControllerDescription.text, | |||
| meetingFiles: state.selectedFiles ?? []); | |||
| if (status == Status.ready) { | |||
| // call refrresh subjects | |||
| } else { | |||
| Tools.showCustomSnackBar( | |||
| text: state.errorsMinuteMeeting == null | |||
| ? state.messageMinuteMeeting ?? | |||
| ' AppLocalizations.of(context)!.haserror' | |||
| : Tools.combineErrorMessages( | |||
| state.errorsMinuteMeeting ?? {}), | |||
| isError: true, | |||
| context, | |||
| ); | |||
| } | |||
| } | |||
| }, | |||
| ); | |||
| } | |||
| } | |||
| } | |||
| class CustomTextArea extends StatelessWidget { | |||
| final String hintText; | |||
| final TextEditingController controller; | |||
| final int maxLines; | |||
| final int minLines; | |||
| const CustomTextArea({ | |||
| Key? key, | |||
| required this.hintText, | |||
| required this.controller, | |||
| this.maxLines = 20, | |||
| this.minLines = 4, | |||
| }) : super(key: key); | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return TextField( | |||
| controller: controller, | |||
| maxLines: maxLines, | |||
| minLines: minLines, | |||
| decoration: InputDecoration( | |||
| hintText: hintText, | |||
| hintStyle: TextStyle(color: Colors.black.withOpacity(.4), fontSize: 13), | |||
| border: InputBorder.none, | |||
| contentPadding: const EdgeInsets.all(12.0), | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| class ReceiptUploadDialog extends StatefulWidget { | |||
| final MeetingSummaryState state; | |||
| const ReceiptUploadDialog({ | |||
| Key? key, | |||
| required this.state, | |||
| }) : super(key: key); | |||
| @override | |||
| _ReceiptUploadDialogState createState() => _ReceiptUploadDialogState(); | |||
| } | |||
| class _ReceiptUploadDialogState extends State<ReceiptUploadDialog> { | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Padding( | |||
| padding: const EdgeInsets.symmetric(horizontal: 10), | |||
| child: Container( | |||
| decoration: BoxDecoration( | |||
| color: const Color(0xffF4F9F6), | |||
| boxShadow: [ | |||
| BoxShadow( | |||
| color: config.ui.mainGray.withOpacity(.1), | |||
| spreadRadius: .1, | |||
| offset: const Offset(0, 2), | |||
| blurRadius: 6, | |||
| ), | |||
| ], | |||
| borderRadius: const BorderRadius.all(Radius.circular(12)), | |||
| ), | |||
| child: Padding( | |||
| padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 10), | |||
| child: Column( | |||
| mainAxisSize: MainAxisSize.min, | |||
| crossAxisAlignment: CrossAxisAlignment.start, | |||
| children: [ | |||
| Text( | |||
| 'آپلود فایل', | |||
| style: TextStyle( | |||
| fontSize: 16, | |||
| fontWeight: FontWeight.bold, | |||
| color: config.ui.mainGreen, | |||
| ), | |||
| ), | |||
| const SizedBox(height: 20), | |||
| // Upload box | |||
| FileBorderBox( | |||
| child: GestureDetector( | |||
| onTap: widget.state.pickFiles, | |||
| child: Column( | |||
| children: [ | |||
| Icon(Icons.cloud_upload_outlined, | |||
| size: 30, color: config.ui.mainGreen), | |||
| Text( | |||
| 'انتخاب فایل', | |||
| style: | |||
| TextStyle(fontSize: 12, color: config.ui.mainGreen), | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| ), | |||
| // File preview | |||
| if (widget.state.selectedFiles != null) | |||
| FilePreview( | |||
| files: widget.state.selectedFiles!, | |||
| onDelete: widget.state.removeFile, | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| class FilePreview extends StatelessWidget { | |||
| final List<PlatformFile> files; | |||
| final void Function(int) onDelete; | |||
| const FilePreview({ | |||
| super.key, | |||
| required this.files, | |||
| required this.onDelete, | |||
| }); | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return ListView.builder( | |||
| shrinkWrap: true, | |||
| itemCount: files.length, | |||
| physics: const NeverScrollableScrollPhysics(), | |||
| itemBuilder: (BuildContext context, int index) { | |||
| return Container( | |||
| padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), | |||
| margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), | |||
| decoration: BoxDecoration( | |||
| borderRadius: BorderRadius.circular(10), | |||
| border: Border(bottom: BorderSide(color: config.ui.secendGreen)), | |||
| ), | |||
| child: Row( | |||
| mainAxisAlignment: MainAxisAlignment.start, | |||
| children: [ | |||
| const Icon(Icons.file_present_outlined, color: Color(0xffD0D5ED)), | |||
| const SizedBox(width: 10), | |||
| Expanded( | |||
| child: Text( | |||
| files[index].name, | |||
| style: const TextStyle(fontSize: 12), | |||
| ), | |||
| ), | |||
| IconButton( | |||
| icon: Icon(Icons.delete, color: config.ui.secendGreen), | |||
| onPressed: () => onDelete(index), | |||
| ), | |||
| ], | |||
| ), | |||
| ); | |||
| }, | |||
| ); | |||
| } | |||
| } | |||
| class FileBorderBox extends StatelessWidget { | |||
| final Widget child; | |||
| const FileBorderBox({super.key, required this.child}); | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Container( | |||
| padding: const EdgeInsets.all(20), | |||
| decoration: BoxDecoration( | |||
| borderRadius: BorderRadius.circular(10), | |||
| border: Border.all( | |||
| color: config.ui.mainGreen, | |||
| style: BorderStyle.solid, | |||
| width: .5, | |||
| ), | |||
| ), | |||
| child: Center(child: child), | |||
| ); | |||
| } | |||
| } | |||