Parcourir la source

Add existing project files to Git

master
amin il y a 1 an
Parent
révision
a30be9e651
100 fichiers modifiés avec 6600 ajouts et 0 suppressions
  1. +1
    -0
      .envrc
  2. +43
    -0
      .gitignore
  3. +45
    -0
      .metadata
  4. +28
    -0
      analysis_options.yaml
  5. +13
    -0
      android/.gitignore
  6. +44
    -0
      android/app/build.gradle
  7. +7
    -0
      android/app/src/debug/AndroidManifest.xml
  8. +45
    -0
      android/app/src/main/AndroidManifest.xml
  9. +5
    -0
      android/app/src/main/kotlin/com/example/qadirneyriz/MainActivity.kt
  10. +12
    -0
      android/app/src/main/res/drawable-v21/launch_background.xml
  11. +12
    -0
      android/app/src/main/res/drawable/launch_background.xml
  12. BIN
      android/app/src/main/res/mipmap-hdpi/ic_launcher.png
  13. BIN
      android/app/src/main/res/mipmap-mdpi/ic_launcher.png
  14. BIN
      android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
  15. BIN
      android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  16. BIN
      android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  17. +18
    -0
      android/app/src/main/res/values-night/styles.xml
  18. +18
    -0
      android/app/src/main/res/values/styles.xml
  19. +7
    -0
      android/app/src/profile/AndroidManifest.xml
  20. +18
    -0
      android/build.gradle
  21. +3
    -0
      android/gradle.properties
  22. +5
    -0
      android/gradle/wrapper/gradle-wrapper.properties
  23. +25
    -0
      android/settings.gradle
  24. BIN
      assets/fonts/Font.ttf
  25. BIN
      assets/images/iconinappbar.png
  26. BIN
      assets/images/logo.png
  27. BIN
      assets/images/placeholder.png
  28. BIN
      assets/images/template.png
  29. +34
    -0
      ios/.gitignore
  30. +26
    -0
      ios/Flutter/AppFrameworkInfo.plist
  31. +1
    -0
      ios/Flutter/Debug.xcconfig
  32. +1
    -0
      ios/Flutter/Release.xcconfig
  33. +616
    -0
      ios/Runner.xcodeproj/project.pbxproj
  34. +7
    -0
      ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  35. +8
    -0
      ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  36. +8
    -0
      ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
  37. +98
    -0
      ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
  38. +7
    -0
      ios/Runner.xcworkspace/contents.xcworkspacedata
  39. +8
    -0
      ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  40. +8
    -0
      ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
  41. +13
    -0
      ios/Runner/AppDelegate.swift
  42. +122
    -0
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
  43. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
  44. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
  45. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
  46. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
  47. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
  48. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
  49. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
  50. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
  51. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
  52. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
  53. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
  54. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
  55. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
  56. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
  57. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
  58. +23
    -0
      ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
  59. BIN
      ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
  60. BIN
      ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
  61. BIN
      ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
  62. +5
    -0
      ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
  63. +37
    -0
      ios/Runner/Base.lproj/LaunchScreen.storyboard
  64. +26
    -0
      ios/Runner/Base.lproj/Main.storyboard
  65. +49
    -0
      ios/Runner/Info.plist
  66. +1
    -0
      ios/Runner/Runner-Bridging-Header.h
  67. +12
    -0
      ios/RunnerTests/RunnerTests.swift
  68. +3
    -0
      l10n.yaml
  69. +14
    -0
      lib/config/app_config.dart
  70. +9
    -0
      lib/config/config.dart
  71. +4
    -0
      lib/config/network_config.dart
  72. +9
    -0
      lib/config/ui_config.dart
  73. +250
    -0
      lib/drawer_navigation_bar.dart
  74. +341
    -0
      lib/global_state/global_state.dart
  75. +43
    -0
      lib/l10n/app_en.arb
  76. +43
    -0
      lib/l10n/app_fa.arb
  77. +79
    -0
      lib/main.dart
  78. +193
    -0
      lib/models/home/home_models.dart
  79. +30
    -0
      lib/models/meetings/meetings_location_model.dart
  80. +71
    -0
      lib/models/meetings/meetings_managers_model.dart
  81. +287
    -0
      lib/models/meetings/meetings_model.dart
  82. +30
    -0
      lib/models/meetings/meetings_subjects_model.dart
  83. +70
    -0
      lib/models/meetings/meetings_users_model.dart
  84. +272
    -0
      lib/models/meetings/one_meeting_model.dart
  85. +92
    -0
      lib/router/router.dart
  86. +174
    -0
      lib/screens/auth/login_screen.dart
  87. +157
    -0
      lib/screens/auth/login_with_otp_screen.dart
  88. +211
    -0
      lib/screens/auth/otp_screen.dart
  89. +93
    -0
      lib/screens/auth/state/state.dart
  90. +253
    -0
      lib/screens/home/screen.dart
  91. +52
    -0
      lib/screens/home/state.dart
  92. +391
    -0
      lib/screens/meeting/diolog_meetings_filters.dart
  93. +296
    -0
      lib/screens/meeting/screen.dart
  94. +265
    -0
      lib/screens/meeting/state.dart
  95. +106
    -0
      lib/screens/meeting_edit/diolog_add_location.dart
  96. +106
    -0
      lib/screens/meeting_edit/diolog_add_subject.dart
  97. +202
    -0
      lib/screens/meeting_edit/diolog_add_user.dart
  98. +518
    -0
      lib/screens/meeting_edit/screen.dart
  99. +148
    -0
      lib/screens/meeting_edit/state.dart
  100. +329
    -0
      lib/screens/meeting_summary/screen.dart

+ 1
- 0
.envrc Voir le fichier

@@ -0,0 +1 @@
PATH_add /Users/amin/flutter/latest/bin

+ 43
- 0
.gitignore Voir le fichier

@@ -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

+ 45
- 0
.metadata Voir le fichier

@@ -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'

+ 28
- 0
analysis_options.yaml Voir le fichier

@@ -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

+ 13
- 0
android/.gitignore Voir le fichier

@@ -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

+ 44
- 0
android/app/build.gradle Voir le fichier

@@ -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 = "../.."
}

+ 7
- 0
android/app/src/debug/AndroidManifest.xml Voir le fichier

@@ -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>

+ 45
- 0
android/app/src/main/AndroidManifest.xml Voir le fichier

@@ -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>

+ 5
- 0
android/app/src/main/kotlin/com/example/qadirneyriz/MainActivity.kt Voir le fichier

@@ -0,0 +1,5 @@
package com.example.qadirneyriz

import io.flutter.embedding.android.FlutterActivity

class MainActivity: FlutterActivity()

+ 12
- 0
android/app/src/main/res/drawable-v21/launch_background.xml Voir le fichier

@@ -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>

+ 12
- 0
android/app/src/main/res/drawable/launch_background.xml Voir le fichier

@@ -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>

BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher.png Voir le fichier

Avant Après
Largeur: 72  |  Hauteur: 72  |  Taille: 544 B

BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.png Voir le fichier

Avant Après
Largeur: 48  |  Hauteur: 48  |  Taille: 442 B

BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.png Voir le fichier

Avant Après
Largeur: 96  |  Hauteur: 96  |  Taille: 721 B

BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png Voir le fichier

Avant Après
Largeur: 144  |  Hauteur: 144  |  Taille: 1.0 KiB

BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png Voir le fichier

Avant Après
Largeur: 192  |  Hauteur: 192  |  Taille: 1.4 KiB

+ 18
- 0
android/app/src/main/res/values-night/styles.xml Voir le fichier

@@ -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>

+ 18
- 0
android/app/src/main/res/values/styles.xml Voir le fichier

@@ -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>

+ 7
- 0
android/app/src/profile/AndroidManifest.xml Voir le fichier

@@ -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>

+ 18
- 0
android/build.gradle Voir le fichier

@@ -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
}

+ 3
- 0
android/gradle.properties Voir le fichier

@@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true

+ 5
- 0
android/gradle/wrapper/gradle-wrapper.properties Voir le fichier

@@ -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

+ 25
- 0
android/settings.gradle Voir le fichier

@@ -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"

BIN
assets/fonts/Font.ttf Voir le fichier


BIN
assets/images/iconinappbar.png Voir le fichier

Avant Après
Largeur: 416  |  Hauteur: 388  |  Taille: 35 KiB

BIN
assets/images/logo.png Voir le fichier

Avant Après
Largeur: 1656  |  Hauteur: 1596  |  Taille: 624 KiB

BIN
assets/images/placeholder.png Voir le fichier

Avant Après
Largeur: 512  |  Hauteur: 300  |  Taille: 9.0 KiB

BIN
assets/images/template.png Voir le fichier

Avant Après
Largeur: 1656  |  Hauteur: 2120  |  Taille: 499 KiB

+ 34
- 0
ios/.gitignore Voir le fichier

@@ -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

+ 26
- 0
ios/Flutter/AppFrameworkInfo.plist Voir le fichier

@@ -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>

+ 1
- 0
ios/Flutter/Debug.xcconfig Voir le fichier

@@ -0,0 +1 @@
#include "Generated.xcconfig"

+ 1
- 0
ios/Flutter/Release.xcconfig Voir le fichier

@@ -0,0 +1 @@
#include "Generated.xcconfig"

+ 616
- 0
ios/Runner.xcodeproj/project.pbxproj Voir le fichier

@@ -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 */;
}

+ 7
- 0
ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata Voir le fichier

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

+ 8
- 0
ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist Voir le fichier

@@ -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>

+ 8
- 0
ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings Voir le fichier

@@ -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>

+ 98
- 0
ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme Voir le fichier

@@ -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>

+ 7
- 0
ios/Runner.xcworkspace/contents.xcworkspacedata Voir le fichier

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

+ 8
- 0
ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist Voir le fichier

@@ -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>

+ 8
- 0
ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings Voir le fichier

@@ -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>

+ 13
- 0
ios/Runner/AppDelegate.swift Voir le fichier

@@ -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)
}
}

+ 122
- 0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json Voir le fichier

@@ -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"
}
}

BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png Voir le fichier

Avant Après
Largeur: 1024  |  Hauteur: 1024  |  Taille: 11 KiB

BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png Voir le fichier

Avant Après
Largeur: 20  |  Hauteur: 20  |  Taille: 295 B

BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png Voir le fichier

Avant Après
Largeur: 40  |  Hauteur: 40  |  Taille: 406 B

BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png Voir le fichier

Avant Après
Largeur: 60  |  Hauteur: 60  |  Taille: 450 B

BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png Voir le fichier

Avant Après
Largeur: 29  |  Hauteur: 29  |  Taille: 282 B

BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png Voir le fichier

Avant Après
Largeur: 58  |  Hauteur: 58  |  Taille: 462 B

BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png Voir le fichier

Avant Après
Largeur: 87  |  Hauteur: 87  |  Taille: 704 B

BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png Voir le fichier

Avant Après
Largeur: 40  |  Hauteur: 40  |  Taille: 406 B

BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png Voir le fichier

Avant Après
Largeur: 80  |  Hauteur: 80  |  Taille: 586 B

BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png Voir le fichier

Avant Après
Largeur: 120  |  Hauteur: 120  |  Taille: 862 B

BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png Voir le fichier

Avant Après
Largeur: 120  |  Hauteur: 120  |  Taille: 862 B

BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png Voir le fichier

Avant Après
Largeur: 180  |  Hauteur: 180  |  Taille: 1.6 KiB

BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png Voir le fichier

Avant Après
Largeur: 76  |  Hauteur: 76  |  Taille: 762 B

BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png Voir le fichier

Avant Après
Largeur: 152  |  Hauteur: 152  |  Taille: 1.2 KiB

BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png Voir le fichier

Avant Après
Largeur: 167  |  Hauteur: 167  |  Taille: 1.4 KiB

+ 23
- 0
ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json Voir le fichier

@@ -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"
}
}

BIN
ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png Voir le fichier

Avant Après
Largeur: 1  |  Hauteur: 1  |  Taille: 68 B

BIN
ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png Voir le fichier

Avant Après
Largeur: 1  |  Hauteur: 1  |  Taille: 68 B

BIN
ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png Voir le fichier

Avant Après
Largeur: 1  |  Hauteur: 1  |  Taille: 68 B

+ 5
- 0
ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md Voir le fichier

@@ -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.

+ 37
- 0
ios/Runner/Base.lproj/LaunchScreen.storyboard Voir le fichier

@@ -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>

+ 26
- 0
ios/Runner/Base.lproj/Main.storyboard Voir le fichier

@@ -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>

+ 49
- 0
ios/Runner/Info.plist Voir le fichier

@@ -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>

+ 1
- 0
ios/Runner/Runner-Bridging-Header.h Voir le fichier

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

+ 12
- 0
ios/RunnerTests/RunnerTests.swift Voir le fichier

@@ -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.
}

}

+ 3
- 0
l10n.yaml Voir le fichier

@@ -0,0 +1,3 @@
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart

+ 14
- 0
lib/config/app_config.dart Voir le fichier

@@ -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,
});
}

+ 9
- 0
lib/config/config.dart Voir le fichier

@@ -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(),
);

+ 4
- 0
lib/config/network_config.dart Voir le fichier

@@ -0,0 +1,4 @@
class NetworkConfig {
final baseUrl = 'https://api.nghsco.com/api/';
const NetworkConfig();
}

+ 9
- 0
lib/config/ui_config.dart Voir le fichier

@@ -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();
}

+ 250
- 0
lib/drawer_navigation_bar.dart Voir le fichier

@@ -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();
}
}

+ 341
- 0
lib/global_state/global_state.dart Voir le fichier

@@ -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;
}
}

+ 43
- 0
lib/l10n/app_en.arb Voir le fichier

@@ -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":"انتخاب کاربران"
}

+ 43
- 0
lib/l10n/app_fa.arb Voir le fichier

@@ -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":"انتخاب کاربران"
}

+ 79
- 0
lib/main.dart Voir le fichier

@@ -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
],
);
},
);
}
}

+ 193
- 0
lib/models/home/home_models.dart Voir le fichier

@@ -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(),
};
}

+ 30
- 0
lib/models/meetings/meetings_location_model.dart Voir le fichier

@@ -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,
};
}

+ 71
- 0
lib/models/meetings/meetings_managers_model.dart Voir le fichier

@@ -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(),
};
}

+ 287
- 0
lib/models/meetings/meetings_model.dart Voir le fichier

@@ -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(),
};
}

+ 30
- 0
lib/models/meetings/meetings_subjects_model.dart Voir le fichier

@@ -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,
};
}

+ 70
- 0
lib/models/meetings/meetings_users_model.dart Voir le fichier

@@ -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(),
};
}

+ 272
- 0
lib/models/meetings/one_meeting_model.dart Voir le fichier

@@ -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(),
};
}

+ 92
- 0
lib/router/router.dart Voir le fichier

@@ -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,
),
);
},
),
],
);

+ 174
- 0
lib/screens/auth/login_screen.dart Voir le fichier

@@ -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,
);
}
}
},
);
}
}
}

+ 157
- 0
lib/screens/auth/login_with_otp_screen.dart Voir le fichier

@@ -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,
);
}
}
},
);
}
}
}

+ 211
- 0
lib/screens/auth/otp_screen.dart Voir le fichier

@@ -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 = '';
}
}
}
}

+ 93
- 0
lib/screens/auth/state/state.dart Voir le fichier

@@ -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;
}


}

+ 253
- 0
lib/screens/home/screen.dart Voir le fichier

@@ -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,
),
),
],
),
),
),
);
}
}

+ 52
- 0
lib/screens/home/state.dart Voir le fichier

@@ -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;
}

}

+ 391
- 0
lib/screens/meeting/diolog_meetings_filters.dart Voir le fichier

@@ -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,
});
}

+ 296
- 0
lib/screens/meeting/screen.dart Voir le fichier

@@ -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,
);
}
}
}

+ 265
- 0
lib/screens/meeting/state.dart Voir le fichier

@@ -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;
}
}

+ 106
- 0
lib/screens/meeting_edit/diolog_add_location.dart Voir le fichier

@@ -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,
);
}
}
}

+ 106
- 0
lib/screens/meeting_edit/diolog_add_subject.dart Voir le fichier

@@ -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,
);
}
}
}

+ 202
- 0
lib/screens/meeting_edit/diolog_add_user.dart Voir le fichier

@@ -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,
});
}

+ 518
- 0
lib/screens/meeting_edit/screen.dart Voir le fichier

@@ -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,
});
}

+ 148
- 0
lib/screens/meeting_edit/state.dart Voir le fichier

@@ -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;
}

}

+ 329
- 0
lib/screens/meeting_summary/screen.dart Voir le fichier

@@ -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),
);
}
}

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff

Chargement…
Annuler
Enregistrer