commit e20c87d0dd0058627221786548907e528b57c9d3 Author: mohamadmahdi jebeli Date: Tue Jul 8 08:50:20 2025 +0330 base diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..154616e --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ +ios/ +linux/ +macos/ +windows/ + +# 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 diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..e8f7bf9 --- /dev/null +++ b/.metadata @@ -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: "ea121f8859e4b13e47a8f845e4586164519588bc" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: ea121f8859e4b13e47a8f845e4586164519588bc + base_revision: ea121f8859e4b13e47a8f845e4586164519588bc + - platform: android + create_revision: ea121f8859e4b13e47a8f845e4586164519588bc + base_revision: ea121f8859e4b13e47a8f845e4586164519588bc + - platform: ios + create_revision: ea121f8859e4b13e47a8f845e4586164519588bc + base_revision: ea121f8859e4b13e47a8f845e4586164519588bc + - platform: linux + create_revision: ea121f8859e4b13e47a8f845e4586164519588bc + base_revision: ea121f8859e4b13e47a8f845e4586164519588bc + - platform: macos + create_revision: ea121f8859e4b13e47a8f845e4586164519588bc + base_revision: ea121f8859e4b13e47a8f845e4586164519588bc + - platform: web + create_revision: ea121f8859e4b13e47a8f845e4586164519588bc + base_revision: ea121f8859e4b13e47a8f845e4586164519588bc + - platform: windows + create_revision: ea121f8859e4b13e47a8f845e4586164519588bc + base_revision: ea121f8859e4b13e47a8f845e4586164519588bc + + # 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' diff --git a/README.md b/README.md new file mode 100644 index 0000000..f7a3247 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# business_panel + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/analysis_options.yaml @@ -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 diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..be3943c --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,14 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java +.cxx/ + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts new file mode 100644 index 0000000..3230651 --- /dev/null +++ b/android/app/build.gradle.kts @@ -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.business_panel" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11.toString() + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.example.business_panel" + // 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.getByName("debug") + } + } +} + +flutter { + source = "../.." +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8f06452 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/example/business_panel/MainActivity.kt b/android/app/src/main/kotlin/com/example/business_panel/MainActivity.kt new file mode 100644 index 0000000..a3b4f28 --- /dev/null +++ b/android/app/src/main/kotlin/com/example/business_panel/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.business_panel + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..17987b7 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..09d4391 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d6372e Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/build.gradle.kts b/android/build.gradle.kts new file mode 100644 index 0000000..89176ef --- /dev/null +++ b/android/build.gradle.kts @@ -0,0 +1,21 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..f018a61 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..afa1e8e --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -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.10.2-all.zip diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts new file mode 100644 index 0000000..a439442 --- /dev/null +++ b/android/settings.gradle.kts @@ -0,0 +1,25 @@ +pluginManagement { + val flutterSdkPath = run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + 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.7.0" apply false + id("org.jetbrains.kotlin.android") version "1.8.22" apply false +} + +include(":app") diff --git a/assets/fonts/dana-fanum-regular.ttf b/assets/fonts/dana-fanum-regular.ttf new file mode 100644 index 0000000..bf0454e Binary files /dev/null and b/assets/fonts/dana-fanum-regular.ttf differ diff --git a/assets/icons/Arrow - Right 2.svg b/assets/icons/Arrow - Right 2.svg new file mode 100644 index 0000000..b498268 --- /dev/null +++ b/assets/icons/Arrow - Right 2.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/CatBack.svg b/assets/icons/CatBack.svg new file mode 100644 index 0000000..8f81d46 --- /dev/null +++ b/assets/icons/CatBack.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/Cinema.svg b/assets/icons/Cinema.svg new file mode 100644 index 0000000..035407f --- /dev/null +++ b/assets/icons/Cinema.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/Coffee.svg b/assets/icons/Coffee.svg new file mode 100644 index 0000000..88cfc6a --- /dev/null +++ b/assets/icons/Coffee.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/assets/icons/Ellipse 1.svg b/assets/icons/Ellipse 1.svg new file mode 100644 index 0000000..6230dba --- /dev/null +++ b/assets/icons/Ellipse 1.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/Gold.svg b/assets/icons/Gold.svg new file mode 100644 index 0000000..d8f6d6f --- /dev/null +++ b/assets/icons/Gold.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/assets/icons/Google svg.svg b/assets/icons/Google svg.svg new file mode 100644 index 0000000..aa550cd --- /dev/null +++ b/assets/icons/Google svg.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/Line 2.svg b/assets/icons/Line 2.svg new file mode 100644 index 0000000..1f51b1d --- /dev/null +++ b/assets/icons/Line 2.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/LogoWithName.svg b/assets/icons/LogoWithName.svg new file mode 100644 index 0000000..e826ba4 --- /dev/null +++ b/assets/icons/LogoWithName.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/assets/icons/Makeup.svg b/assets/icons/Makeup.svg new file mode 100644 index 0000000..7384a73 --- /dev/null +++ b/assets/icons/Makeup.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/Mobile.svg b/assets/icons/Mobile.svg new file mode 100644 index 0000000..4e6466a --- /dev/null +++ b/assets/icons/Mobile.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/assets/icons/Pizza.svg b/assets/icons/Pizza.svg new file mode 100644 index 0000000..433eab9 --- /dev/null +++ b/assets/icons/Pizza.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/icons/Restrurant.svg b/assets/icons/Restrurant.svg new file mode 100644 index 0000000..53a2f64 --- /dev/null +++ b/assets/icons/Restrurant.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/Shoes.svg b/assets/icons/Shoes.svg new file mode 100644 index 0000000..0e2342c --- /dev/null +++ b/assets/icons/Shoes.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/assets/icons/Tria.svg b/assets/icons/Tria.svg new file mode 100644 index 0000000..d66610d --- /dev/null +++ b/assets/icons/Tria.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/Tshirt.svg b/assets/icons/Tshirt.svg new file mode 100644 index 0000000..098a6d8 --- /dev/null +++ b/assets/icons/Tshirt.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/Vector.svg b/assets/icons/Vector.svg new file mode 100644 index 0000000..a3cfce6 --- /dev/null +++ b/assets/icons/Vector.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/addImg.svg b/assets/icons/addImg.svg new file mode 100644 index 0000000..788e5b5 --- /dev/null +++ b/assets/icons/addImg.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/appbar2.svg b/assets/icons/appbar2.svg new file mode 100644 index 0000000..433fd49 --- /dev/null +++ b/assets/icons/appbar2.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/arayesh.svg b/assets/icons/arayesh.svg new file mode 100644 index 0000000..d2f202d --- /dev/null +++ b/assets/icons/arayesh.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/arrow-down.svg b/assets/icons/arrow-down.svg new file mode 100644 index 0000000..c1464f2 --- /dev/null +++ b/assets/icons/arrow-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/arrow-left.svg b/assets/icons/arrow-left.svg new file mode 100644 index 0000000..4c58540 --- /dev/null +++ b/assets/icons/arrow-left.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/arrow-up.svg b/assets/icons/arrow-up.svg new file mode 100644 index 0000000..27f0bd8 --- /dev/null +++ b/assets/icons/arrow-up.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/back.svg b/assets/icons/back.svg new file mode 100644 index 0000000..e8f85f6 --- /dev/null +++ b/assets/icons/back.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/backArrow.svg b/assets/icons/backArrow.svg new file mode 100644 index 0000000..356c09e --- /dev/null +++ b/assets/icons/backArrow.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/camera.svg b/assets/icons/camera.svg new file mode 100644 index 0000000..46b1426 --- /dev/null +++ b/assets/icons/camera.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/card-pos.svg b/assets/icons/card-pos.svg new file mode 100644 index 0000000..8abb308 --- /dev/null +++ b/assets/icons/card-pos.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/cinama.svg b/assets/icons/cinama.svg new file mode 100644 index 0000000..8f9b9e1 --- /dev/null +++ b/assets/icons/cinama.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/clock.svg b/assets/icons/clock.svg new file mode 100644 index 0000000..baf3724 --- /dev/null +++ b/assets/icons/clock.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/clockProduct.svg b/assets/icons/clockProduct.svg new file mode 100644 index 0000000..a7ce302 --- /dev/null +++ b/assets/icons/clockProduct.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/coffeeshop.svg b/assets/icons/coffeeshop.svg new file mode 100644 index 0000000..b7f9284 --- /dev/null +++ b/assets/icons/coffeeshop.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/assets/icons/digital.svg b/assets/icons/digital.svg new file mode 100644 index 0000000..1df5c4c --- /dev/null +++ b/assets/icons/digital.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/discount-shape.svg b/assets/icons/discount-shape.svg new file mode 100644 index 0000000..6028212 --- /dev/null +++ b/assets/icons/discount-shape.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/edit-02.svg b/assets/icons/edit-02.svg new file mode 100644 index 0000000..287aaee --- /dev/null +++ b/assets/icons/edit-02.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/edit.svg b/assets/icons/edit.svg new file mode 100644 index 0000000..8c2485b --- /dev/null +++ b/assets/icons/edit.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/error.svg b/assets/icons/error.svg new file mode 100644 index 0000000..2d8cf6a --- /dev/null +++ b/assets/icons/error.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/icons/fastfood.svg b/assets/icons/fastfood.svg new file mode 100644 index 0000000..8b4ae66 --- /dev/null +++ b/assets/icons/fastfood.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/gallery-add.svg b/assets/icons/gallery-add.svg new file mode 100644 index 0000000..83219b2 --- /dev/null +++ b/assets/icons/gallery-add.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/global-search.svg b/assets/icons/global-search.svg new file mode 100644 index 0000000..61da037 --- /dev/null +++ b/assets/icons/global-search.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/icons/kafsh.svg b/assets/icons/kafsh.svg new file mode 100644 index 0000000..313e8c6 --- /dev/null +++ b/assets/icons/kafsh.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/location.svg b/assets/icons/location.svg new file mode 100644 index 0000000..b3f53a7 --- /dev/null +++ b/assets/icons/location.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/logo.svg b/assets/icons/logo.svg new file mode 100644 index 0000000..de3bfab --- /dev/null +++ b/assets/icons/logo.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/assets/icons/map.svg b/assets/icons/map.svg new file mode 100644 index 0000000..f29e012 --- /dev/null +++ b/assets/icons/map.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/notification.svg b/assets/icons/notification.svg new file mode 100644 index 0000000..2c19f5b --- /dev/null +++ b/assets/icons/notification.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/pooshak.svg b/assets/icons/pooshak.svg new file mode 100644 index 0000000..5ec1383 --- /dev/null +++ b/assets/icons/pooshak.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/receipt-disscount.svg b/assets/icons/receipt-disscount.svg new file mode 100644 index 0000000..187577f --- /dev/null +++ b/assets/icons/receipt-disscount.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/resturan.svg b/assets/icons/resturan.svg new file mode 100644 index 0000000..43da68f --- /dev/null +++ b/assets/icons/resturan.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/routing.svg b/assets/icons/routing.svg new file mode 100644 index 0000000..f2ec483 --- /dev/null +++ b/assets/icons/routing.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/scan-barcode.svg b/assets/icons/scan-barcode.svg new file mode 100644 index 0000000..05f6643 --- /dev/null +++ b/assets/icons/scan-barcode.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/shop.svg b/assets/icons/shop.svg new file mode 100644 index 0000000..04decc0 --- /dev/null +++ b/assets/icons/shop.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/shopping-cart.svg b/assets/icons/shopping-cart.svg new file mode 100644 index 0000000..c974fa0 --- /dev/null +++ b/assets/icons/shopping-cart.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/star fill.svg b/assets/icons/star fill.svg new file mode 100644 index 0000000..7ec9167 --- /dev/null +++ b/assets/icons/star fill.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/star half.svg b/assets/icons/star half.svg new file mode 100644 index 0000000..86f760e --- /dev/null +++ b/assets/icons/star half.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/star.svg b/assets/icons/star.svg new file mode 100644 index 0000000..b874e53 --- /dev/null +++ b/assets/icons/star.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/star2.svg b/assets/icons/star2.svg new file mode 100644 index 0000000..981d528 --- /dev/null +++ b/assets/icons/star2.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/state-layer.svg b/assets/icons/state-layer.svg new file mode 100644 index 0000000..a98a739 --- /dev/null +++ b/assets/icons/state-layer.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/tala.svg b/assets/icons/tala.svg new file mode 100644 index 0000000..d0b39af --- /dev/null +++ b/assets/icons/tala.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/teria.svg b/assets/icons/teria.svg new file mode 100644 index 0000000..2a71d98 --- /dev/null +++ b/assets/icons/teria.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/tick-circle.svg b/assets/icons/tick-circle.svg new file mode 100644 index 0000000..5c4ee7b --- /dev/null +++ b/assets/icons/tick-circle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/tick-square.svg b/assets/icons/tick-square.svg new file mode 100644 index 0000000..36d629c --- /dev/null +++ b/assets/icons/tick-square.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/tickPb.svg b/assets/icons/tickPb.svg new file mode 100644 index 0000000..77950ee --- /dev/null +++ b/assets/icons/tickPb.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/ticket-discount.svg b/assets/icons/ticket-discount.svg new file mode 100644 index 0000000..e9535d4 --- /dev/null +++ b/assets/icons/ticket-discount.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/timer-pause.svg b/assets/icons/timer-pause.svg new file mode 100644 index 0000000..0e21de0 --- /dev/null +++ b/assets/icons/timer-pause.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/volume-high.svg b/assets/icons/volume-high.svg new file mode 100644 index 0000000..5fd9753 --- /dev/null +++ b/assets/icons/volume-high.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/warning-2.svg b/assets/icons/warning-2.svg new file mode 100644 index 0000000..7503e0a --- /dev/null +++ b/assets/icons/warning-2.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/Rectangle 1.png b/assets/images/Rectangle 1.png new file mode 100644 index 0000000..7353026 Binary files /dev/null and b/assets/images/Rectangle 1.png differ diff --git a/assets/images/Rectangle 2 M.png b/assets/images/Rectangle 2 M.png new file mode 100644 index 0000000..8287db6 Binary files /dev/null and b/assets/images/Rectangle 2 M.png differ diff --git a/assets/images/Rectangle 2.png b/assets/images/Rectangle 2.png new file mode 100644 index 0000000..d9a52e5 Binary files /dev/null and b/assets/images/Rectangle 2.png differ diff --git a/assets/images/Rectangle 3 M.png b/assets/images/Rectangle 3 M.png new file mode 100644 index 0000000..7270247 Binary files /dev/null and b/assets/images/Rectangle 3 M.png differ diff --git a/assets/images/Rectangle 3.png b/assets/images/Rectangle 3.png new file mode 100644 index 0000000..4f1537e Binary files /dev/null and b/assets/images/Rectangle 3.png differ diff --git a/assets/images/Rectangle 4.png b/assets/images/Rectangle 4.png new file mode 100644 index 0000000..c0578be Binary files /dev/null and b/assets/images/Rectangle 4.png differ diff --git a/assets/images/empty home.svg b/assets/images/empty home.svg new file mode 100644 index 0000000..d29c544 --- /dev/null +++ b/assets/images/empty home.svg @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/userinfo.png b/assets/images/userinfo.png new file mode 100644 index 0000000..3568850 Binary files /dev/null and b/assets/images/userinfo.png differ diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 0000000..fa0b357 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/lib/core/config/app_colors.dart b/lib/core/config/app_colors.dart new file mode 100644 index 0000000..5fbbd02 --- /dev/null +++ b/lib/core/config/app_colors.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; + +class AppColors { + AppColors._(); + + static const Color primary = Color.fromARGB(255, 23, 107, 173); + static const Color unselected = Color.fromARGB(255, 186, 222, 251); + static const Color border = Color.fromARGB(255, 14, 63, 102); + static const Color button = Color.fromARGB(255, 30, 137, 221); + static const Color active = Color.fromARGB(255, 33, 150, 243); + static const Color confirm = Color.fromARGB(255, 69, 159, 73); + static const Color unselectedBorder = Color.fromARGB(255, 233, 245, 254); + static const Color hint = Color.fromARGB(255, 112, 112, 110); + static const Color selectedImg = Color.fromARGB(255, 76, 175, 80); + static const Color singleOfferType = Color.fromARGB(255, 244, 67, 54); + static const Color countdown = Color.fromARGB(255, 54, 124, 57); + static const Color countdownBorderRserve = Color.fromARGB(255, 186, 222, 251); + static const Color expiryReserve = Color.fromARGB(255, 183, 28, 28); + static const Color uploadElevated = Color.fromARGB(255, 233, 245, 254); +} \ No newline at end of file diff --git a/lib/domain/entities/onboarding_entity.dart b/lib/domain/entities/onboarding_entity.dart new file mode 100644 index 0000000..8f44575 --- /dev/null +++ b/lib/domain/entities/onboarding_entity.dart @@ -0,0 +1,11 @@ +class OnboardingEntity { + final String imagePath; + final String title; + final String description; + + OnboardingEntity({ + required this.imagePath, + required this.title, + required this.description, + }); +} \ No newline at end of file diff --git a/lib/gen/assets.gen.dart b/lib/gen/assets.gen.dart new file mode 100644 index 0000000..59bb631 --- /dev/null +++ b/lib/gen/assets.gen.dart @@ -0,0 +1,402 @@ +/// GENERATED CODE - DO NOT MODIFY BY HAND +/// ***************************************************** +/// FlutterGen +/// ***************************************************** + +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use + +import 'package:flutter/widgets.dart'; + +class $AssetsIconsGen { + const $AssetsIconsGen(); + + /// File path: assets/icons/Arrow - Right 2.svg + String get arrowRight2 => 'assets/icons/Arrow - Right 2.svg'; + + /// File path: assets/icons/CatBack.svg + String get catBack => 'assets/icons/CatBack.svg'; + + /// File path: assets/icons/Cinema.svg + String get cinema => 'assets/icons/Cinema.svg'; + + /// File path: assets/icons/Coffee.svg + String get coffee => 'assets/icons/Coffee.svg'; + + /// File path: assets/icons/Ellipse 1.svg + String get ellipse1 => 'assets/icons/Ellipse 1.svg'; + + /// File path: assets/icons/Gold.svg + String get gold => 'assets/icons/Gold.svg'; + + /// File path: assets/icons/Google svg.svg + String get googleSvg => 'assets/icons/Google svg.svg'; + + /// File path: assets/icons/Line 2.svg + String get line2 => 'assets/icons/Line 2.svg'; + + /// File path: assets/icons/LogoWithName.svg + String get logoWithName => 'assets/icons/LogoWithName.svg'; + + /// File path: assets/icons/Makeup.svg + String get makeup => 'assets/icons/Makeup.svg'; + + /// File path: assets/icons/Mobile.svg + String get mobile => 'assets/icons/Mobile.svg'; + + /// File path: assets/icons/Pizza.svg + String get pizza => 'assets/icons/Pizza.svg'; + + /// File path: assets/icons/Restrurant.svg + String get restrurant => 'assets/icons/Restrurant.svg'; + + /// File path: assets/icons/Shoes.svg + String get shoes => 'assets/icons/Shoes.svg'; + + /// File path: assets/icons/Tria.svg + String get tria => 'assets/icons/Tria.svg'; + + /// File path: assets/icons/Tshirt.svg + String get tshirt => 'assets/icons/Tshirt.svg'; + + /// File path: assets/icons/Vector.svg + String get vector => 'assets/icons/Vector.svg'; + + /// File path: assets/icons/addImg.svg + String get addImg => 'assets/icons/addImg.svg'; + + /// File path: assets/icons/appbar2.svg + String get appbar2 => 'assets/icons/appbar2.svg'; + + /// File path: assets/icons/arayesh.svg + String get arayesh => 'assets/icons/arayesh.svg'; + + /// File path: assets/icons/arrow-down.svg + String get arrowDown => 'assets/icons/arrow-down.svg'; + + /// File path: assets/icons/arrow-left.svg + String get arrowLeft => 'assets/icons/arrow-left.svg'; + + /// File path: assets/icons/arrow-up.svg + String get arrowUp => 'assets/icons/arrow-up.svg'; + + /// File path: assets/icons/back.svg + String get back => 'assets/icons/back.svg'; + + /// File path: assets/icons/backArrow.svg + String get backArrow => 'assets/icons/backArrow.svg'; + + /// File path: assets/icons/camera.svg + String get camera => 'assets/icons/camera.svg'; + + /// File path: assets/icons/card-pos.svg + String get cardPos => 'assets/icons/card-pos.svg'; + + /// File path: assets/icons/cinama.svg + String get cinama => 'assets/icons/cinama.svg'; + + /// File path: assets/icons/clock.svg + String get clock => 'assets/icons/clock.svg'; + + /// File path: assets/icons/clockProduct.svg + String get clockProduct => 'assets/icons/clockProduct.svg'; + + /// File path: assets/icons/coffeeshop.svg + String get coffeeshop => 'assets/icons/coffeeshop.svg'; + + /// File path: assets/icons/digital.svg + String get digital => 'assets/icons/digital.svg'; + + /// File path: assets/icons/discount-shape.svg + String get discountShape => 'assets/icons/discount-shape.svg'; + + /// File path: assets/icons/edit-02.svg + String get edit02 => 'assets/icons/edit-02.svg'; + + /// File path: assets/icons/edit.svg + String get edit => 'assets/icons/edit.svg'; + + /// File path: assets/icons/error.svg + String get error => 'assets/icons/error.svg'; + + /// File path: assets/icons/fastfood.svg + String get fastfood => 'assets/icons/fastfood.svg'; + + /// File path: assets/icons/gallery-add.svg + String get galleryAdd => 'assets/icons/gallery-add.svg'; + + /// File path: assets/icons/global-search.svg + String get globalSearch => 'assets/icons/global-search.svg'; + + /// File path: assets/icons/kafsh.svg + String get kafsh => 'assets/icons/kafsh.svg'; + + /// File path: assets/icons/location.svg + String get location => 'assets/icons/location.svg'; + + /// File path: assets/icons/logo.svg + String get logo => 'assets/icons/logo.svg'; + + /// File path: assets/icons/map.svg + String get map => 'assets/icons/map.svg'; + + /// File path: assets/icons/notification.svg + String get notification => 'assets/icons/notification.svg'; + + /// File path: assets/icons/pooshak.svg + String get pooshak => 'assets/icons/pooshak.svg'; + + /// File path: assets/icons/receipt-disscount.svg + String get receiptDisscount => 'assets/icons/receipt-disscount.svg'; + + /// File path: assets/icons/resturan.svg + String get resturan => 'assets/icons/resturan.svg'; + + /// File path: assets/icons/routing.svg + String get routing => 'assets/icons/routing.svg'; + + /// File path: assets/icons/scan-barcode.svg + String get scanBarcode => 'assets/icons/scan-barcode.svg'; + + /// File path: assets/icons/shop.svg + String get shop => 'assets/icons/shop.svg'; + + /// File path: assets/icons/shopping-cart.svg + String get shoppingCart => 'assets/icons/shopping-cart.svg'; + + /// File path: assets/icons/star fill.svg + String get starFill => 'assets/icons/star fill.svg'; + + /// File path: assets/icons/star half.svg + String get starHalf => 'assets/icons/star half.svg'; + + /// File path: assets/icons/star.svg + String get star => 'assets/icons/star.svg'; + + /// File path: assets/icons/star2.svg + String get star2 => 'assets/icons/star2.svg'; + + /// File path: assets/icons/state-layer.svg + String get stateLayer => 'assets/icons/state-layer.svg'; + + /// File path: assets/icons/tala.svg + String get tala => 'assets/icons/tala.svg'; + + /// File path: assets/icons/teria.svg + String get teria => 'assets/icons/teria.svg'; + + /// File path: assets/icons/tick-circle.svg + String get tickCircle => 'assets/icons/tick-circle.svg'; + + /// File path: assets/icons/tick-square.svg + String get tickSquare => 'assets/icons/tick-square.svg'; + + /// File path: assets/icons/tickPb.svg + String get tickPb => 'assets/icons/tickPb.svg'; + + /// File path: assets/icons/ticket-discount.svg + String get ticketDiscount => 'assets/icons/ticket-discount.svg'; + + /// File path: assets/icons/timer-pause.svg + String get timerPause => 'assets/icons/timer-pause.svg'; + + /// File path: assets/icons/volume-high.svg + String get volumeHigh => 'assets/icons/volume-high.svg'; + + /// File path: assets/icons/warning-2.svg + String get warning2 => 'assets/icons/warning-2.svg'; + + /// List of all assets + List get values => [ + arrowRight2, + catBack, + cinema, + coffee, + ellipse1, + gold, + googleSvg, + line2, + logoWithName, + makeup, + mobile, + pizza, + restrurant, + shoes, + tria, + tshirt, + vector, + addImg, + appbar2, + arayesh, + arrowDown, + arrowLeft, + arrowUp, + back, + backArrow, + camera, + cardPos, + cinama, + clock, + clockProduct, + coffeeshop, + digital, + discountShape, + edit02, + edit, + error, + fastfood, + galleryAdd, + globalSearch, + kafsh, + location, + logo, + map, + notification, + pooshak, + receiptDisscount, + resturan, + routing, + scanBarcode, + shop, + shoppingCart, + starFill, + starHalf, + star, + star2, + stateLayer, + tala, + teria, + tickCircle, + tickSquare, + tickPb, + ticketDiscount, + timerPause, + volumeHigh, + warning2, + ]; +} + +class $AssetsImagesGen { + const $AssetsImagesGen(); + + /// File path: assets/images/Rectangle 1.png + AssetGenImage get rectangle1 => + const AssetGenImage('assets/images/Rectangle 1.png'); + + /// File path: assets/images/Rectangle 2 M.png + AssetGenImage get rectangle2M => + const AssetGenImage('assets/images/Rectangle 2 M.png'); + + /// File path: assets/images/Rectangle 2.png + AssetGenImage get rectangle2 => + const AssetGenImage('assets/images/Rectangle 2.png'); + + /// File path: assets/images/Rectangle 3 M.png + AssetGenImage get rectangle3M => + const AssetGenImage('assets/images/Rectangle 3 M.png'); + + /// File path: assets/images/Rectangle 3.png + AssetGenImage get rectangle3 => + const AssetGenImage('assets/images/Rectangle 3.png'); + + /// File path: assets/images/Rectangle 4.png + AssetGenImage get rectangle4 => + const AssetGenImage('assets/images/Rectangle 4.png'); + + /// File path: assets/images/empty home.svg + String get emptyHome => 'assets/images/empty home.svg'; + + /// File path: assets/images/userinfo.png + AssetGenImage get userinfo => + const AssetGenImage('assets/images/userinfo.png'); + + /// List of all assets + List get values => [ + rectangle1, + rectangle2M, + rectangle2, + rectangle3M, + rectangle3, + rectangle4, + emptyHome, + userinfo, + ]; +} + +class Assets { + const Assets._(); + + static const $AssetsIconsGen icons = $AssetsIconsGen(); + static const $AssetsImagesGen images = $AssetsImagesGen(); +} + +class AssetGenImage { + const AssetGenImage(this._assetName, {this.size, this.flavors = const {}}); + + final String _assetName; + + final Size? size; + final Set flavors; + + Image image({ + Key? key, + AssetBundle? bundle, + ImageFrameBuilder? frameBuilder, + ImageErrorWidgetBuilder? errorBuilder, + String? semanticLabel, + bool excludeFromSemantics = false, + double? scale, + double? width, + double? height, + Color? color, + Animation? opacity, + BlendMode? colorBlendMode, + BoxFit? fit, + AlignmentGeometry alignment = Alignment.center, + ImageRepeat repeat = ImageRepeat.noRepeat, + Rect? centerSlice, + bool matchTextDirection = false, + bool gaplessPlayback = true, + bool isAntiAlias = false, + String? package, + FilterQuality filterQuality = FilterQuality.medium, + int? cacheWidth, + int? cacheHeight, + }) { + return Image.asset( + _assetName, + key: key, + bundle: bundle, + frameBuilder: frameBuilder, + errorBuilder: errorBuilder, + semanticLabel: semanticLabel, + excludeFromSemantics: excludeFromSemantics, + scale: scale, + width: width, + height: height, + color: color, + opacity: opacity, + colorBlendMode: colorBlendMode, + fit: fit, + alignment: alignment, + repeat: repeat, + centerSlice: centerSlice, + matchTextDirection: matchTextDirection, + gaplessPlayback: gaplessPlayback, + isAntiAlias: isAntiAlias, + package: package, + filterQuality: filterQuality, + cacheWidth: cacheWidth, + cacheHeight: cacheHeight, + ); + } + + ImageProvider provider({AssetBundle? bundle, String? package}) { + return AssetImage(_assetName, bundle: bundle, package: package); + } + + String get path => _assetName; + + String get keyName => _assetName; +} diff --git a/lib/gen/fonts.gen.dart b/lib/gen/fonts.gen.dart new file mode 100644 index 0000000..9906882 --- /dev/null +++ b/lib/gen/fonts.gen.dart @@ -0,0 +1,15 @@ +/// GENERATED CODE - DO NOT MODIFY BY HAND +/// ***************************************************** +/// FlutterGen +/// ***************************************************** + +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use + +class FontFamily { + FontFamily._(); + + /// Font family: Dana + static const String dana = 'Dana'; +} diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..e260347 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,106 @@ +import 'package:business_panel/core/config/app_colors.dart'; +import 'package:business_panel/presentation/auth/bloc/auth_bloc.dart'; +import 'package:business_panel/presentation/pages/onboarding_page.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:persian_datetime_picker/persian_datetime_picker.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => AuthBloc(), + child: MaterialApp( + title: 'Proxibuy', + debugShowCheckedModeBanner: false, + localizationsDelegates: const [ + PersianMaterialLocalizations.delegate, + PersianCupertinoLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: const [ + Locale("fa", "IR"), + Locale("en", "US"), + ], + locale: const Locale("fa", "IR"), + theme: ThemeData( + fontFamily: 'Dana', + scaffoldBackgroundColor: Colors.white, + colorScheme: ColorScheme.fromSeed( + seedColor: AppColors.primary, + primary: AppColors.primary, + surface: Colors.white, + ), + appBarTheme: const AppBarTheme( + backgroundColor: AppColors.primary, + foregroundColor: Colors.white, + elevation: 0, + ), + inputDecorationTheme: InputDecorationTheme( + filled: true, + fillColor: Colors.white, + floatingLabelBehavior: FloatingLabelBehavior.always, + contentPadding: const EdgeInsets.symmetric( + vertical: 18, + horizontal: 20, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide(color: AppColors.border), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide(color: AppColors.border), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide(color: AppColors.primary, width: 2), + ), + labelStyle: const TextStyle(color: Colors.black), + hintStyle: TextStyle(color: Colors.black.withOpacity(0.8)), + ), + outlinedButtonTheme: OutlinedButtonThemeData( + style: OutlinedButton.styleFrom( + foregroundColor: Colors.black, + padding: const EdgeInsets.symmetric(vertical: 16), + side: const BorderSide(color: Colors.grey), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(50), + ), + textStyle: const TextStyle( + fontFamily: 'Dana', + fontSize: 16, + color: Colors.black, + ), + ), + ), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.button, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(50), + ), + textStyle: const TextStyle( + fontFamily: 'Dana', + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + home: const OnboardingPage(), + ), + ); + } +} diff --git a/lib/presentation/auth/bloc/auth_bloc.dart b/lib/presentation/auth/bloc/auth_bloc.dart new file mode 100644 index 0000000..7ddc22f --- /dev/null +++ b/lib/presentation/auth/bloc/auth_bloc.dart @@ -0,0 +1,40 @@ + + +import 'package:bloc/bloc.dart'; +part 'auth_event.dart'; +part 'auth_state.dart'; + +class AuthBloc extends Bloc { + AuthBloc() : super(AuthInitial()) { + on((event, emit) async { + emit(AuthLoading()); + await Future.delayed(const Duration(seconds: 1)); + if (event.phoneNumber.isNotEmpty) { + emit(AuthCodeSentSuccess()); + } else { + emit(AuthFailure('شماره موبایل معتبر نیست.')); + } + }); + + on((event, emit) async { + emit(AuthLoading()); + await Future.delayed(const Duration(seconds: 1)); + if (event.otp == '12345') { + emit(AuthVerified()); + } else { + emit(AuthFailure('کد تایید صحیح نمی‌باشد.')); + } + }); + + on((event, emit) async { + emit(AuthLoading()); + await Future.delayed(const Duration(milliseconds: 500)); + + if (event.name.trim().isEmpty) { + emit(AuthFailure('لطفاً نام خود را وارد کنید.')); + } else { + emit(UserInfoSaved()); + } + }); + } +} diff --git a/lib/presentation/auth/bloc/auth_event.dart b/lib/presentation/auth/bloc/auth_event.dart new file mode 100644 index 0000000..adf1b2b --- /dev/null +++ b/lib/presentation/auth/bloc/auth_event.dart @@ -0,0 +1,23 @@ + +part of 'auth_bloc.dart'; + +abstract class AuthEvent {} + +class SendOTPEvent extends AuthEvent { + final String phoneNumber; + + SendOTPEvent({required this.phoneNumber}); +} + +class VerifyOTPEvent extends AuthEvent { + final String otp; + + VerifyOTPEvent({required this.otp}); +} + +class SaveUserInfoEvent extends AuthEvent { + final String name; + final String gender; + + SaveUserInfoEvent({required this.name, required this.gender}); +} diff --git a/lib/presentation/auth/bloc/auth_state.dart b/lib/presentation/auth/bloc/auth_state.dart new file mode 100644 index 0000000..ff99825 --- /dev/null +++ b/lib/presentation/auth/bloc/auth_state.dart @@ -0,0 +1,20 @@ + +part of 'auth_bloc.dart'; + +abstract class AuthState {} + +class AuthInitial extends AuthState {} + +class AuthLoading extends AuthState {} + +class AuthCodeSentSuccess extends AuthState {} + +class AuthVerified extends AuthState {} + +class UserInfoSaved extends AuthState {} + +class AuthFailure extends AuthState { + final String message; + + AuthFailure(this.message); +} \ No newline at end of file diff --git a/lib/presentation/pages/login_page.dart b/lib/presentation/pages/login_page.dart new file mode 100644 index 0000000..172fa33 --- /dev/null +++ b/lib/presentation/pages/login_page.dart @@ -0,0 +1,210 @@ +import 'package:business_panel/gen/assets.gen.dart'; +import 'package:business_panel/presentation/auth/bloc/auth_bloc.dart'; +import 'package:business_panel/presentation/pages/otp_page.dart'; +import 'package:country_picker/country_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import '../../core/config/app_colors.dart'; + +class LoginPage extends StatefulWidget { + const LoginPage({super.key}); + + @override + State createState() => _LoginPageState(); +} + +class _LoginPageState extends State { + final TextEditingController _phoneController = TextEditingController(); + Country _selectedCountry = Country.parse('IR'); + bool _keepSignedIn = false; + + @override + void dispose() { + _phoneController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final textTheme = Theme.of(context).textTheme; + + return Directionality( + textDirection: TextDirection.rtl, + child: Scaffold( + backgroundColor: Colors.white, + body: SafeArea( + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + children: [ + Center( + child: SvgPicture.asset(Assets.icons.logo,height: 160,), + ), + SizedBox(height: 15,), + Text("پنل فروشگاهی شما",style: TextStyle(color: AppColors.active),) + ], + ), + SizedBox(height: 48), + Text( + "ورود", + style: textTheme.headlineMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 48), + TextField( + readOnly: true, + controller: TextEditingController( + text: _selectedCountry.name, + ), + onTap: _showCountryPicker, + decoration: InputDecoration( + labelText: "کشور", + prefixIcon: Padding( + padding: const EdgeInsets.only( + left: 12.0, + right: 8.0, + top: 7, + ), + child: Text( + _selectedCountry.flagEmoji, + style: const TextStyle(fontSize: 24), + ), + ), + suffixIcon: const Icon(Icons.keyboard_arrow_down_rounded), + ), + ), + const SizedBox(height: 16), + TextField( + controller: _phoneController, + keyboardType: TextInputType.phone, + textDirection: TextDirection.ltr, + textAlign: TextAlign.left, + decoration: InputDecoration( + labelText: "شماره موبایل", + hintText: "- - - - - - - - - -", + suffix: Padding( + padding: const EdgeInsets.symmetric(horizontal: 2.0), + child: Text( + _selectedCountry.phoneCode, + style: textTheme.bodyLarge?.copyWith( + color: Colors.black, + ), + ), + ), + ), + ), + const SizedBox(height: 16), + Row( + children: [ + Checkbox( + value: _keepSignedIn, + onChanged: + (value) => + setState(() => _keepSignedIn = value ?? false), + activeColor: AppColors.primary, + ), + Text("مرا به خاطر بسپار", style: textTheme.bodyMedium), + ], + ), + const SizedBox(height: 24), + BlocConsumer( + listener: (context, state) { + if (state is AuthFailure) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(state.message), + backgroundColor: Colors.red, + ), + ); + } + if (state is AuthCodeSentSuccess) { + final fullPhoneNumber = "0${_phoneController.text}"; + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => OtpPage(phoneNumber: fullPhoneNumber), + ), + ); + } + }, + builder: (context, state) { + if (state is AuthLoading) { + return const Center(child: CircularProgressIndicator()); + } + return SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: _sendOtp, + child: const Text("کد یکبار مصرف"), + ), + ); + }, + ), + const SizedBox(height: 32), + Row( + children: [ + const Expanded(child: Divider()), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Text( + "یا ادامه با", + style: textTheme.bodyMedium?.copyWith( + color: Colors.grey, + ), + ), + ), + const Expanded(child: Divider()), + ], + ), + const SizedBox(height: 32), + SizedBox( + width: double.infinity, + child: OutlinedButton.icon( + icon: SvgPicture.asset(Assets.icons.googleSvg,width: 24,), + label: const Text( + "ورود با حساب گوگل", + style: TextStyle(color: Colors.black), + ), + onPressed: () { + // TODO: Implement Google Sign-in + }, + ), + ), + ], + ), + ), + ), + ), + ); + } + + void _showCountryPicker() { + showCountryPicker( + context: context, + countryListTheme: CountryListThemeData( + bottomSheetHeight: 500, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + inputDecoration: InputDecoration( + labelText: 'جستجو', + hintText: 'کشور مورد نظر را جستجو کنید', + prefixIcon: const Icon(Icons.search), + ), + ), + onSelect: (Country country) => setState(() => _selectedCountry = country), + ); + } + + void _sendOtp() { + context.read().add( + SendOTPEvent(phoneNumber: _phoneController.text), + ); + } +} diff --git a/lib/presentation/pages/onboarding_page.dart b/lib/presentation/pages/onboarding_page.dart new file mode 100644 index 0000000..9c6a3f7 --- /dev/null +++ b/lib/presentation/pages/onboarding_page.dart @@ -0,0 +1,210 @@ +import 'package:business_panel/core/config/app_colors.dart'; +import 'package:business_panel/domain/entities/onboarding_entity.dart'; +import 'package:business_panel/gen/assets.gen.dart'; +import 'package:business_panel/presentation/auth/bloc/auth_bloc.dart'; +import 'package:business_panel/presentation/pages/login_page.dart'; +import 'package:business_panel/presentation/widgets/onboarding_indicator_painter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:flutter_svg/svg.dart'; + +final List onboardingPages = [ + OnboardingEntity( + imagePath: Assets.images.rectangle1.path, + title: 'قدم بزن و تخفیف بگیر', + description: + ' کافیه موقع حرکت در شهر، موقعیت‌یاب (GPS) گوشیتو روشن کنی تا تخفیف‌های اطرافت رو دریافت کنی. بدون نیاز به جستجو، توی Proxibuy، خود تخفیف‌ها به سمتت میان!', + ), + OnboardingEntity( + imagePath: Assets.images.rectangle2M.path, + title: 'همیشه یه پیشنهاد خوب نزدیکته', + description: + 'با اعلان‌های لحظه‌ای، همیشه از نزدیک‌ترین تخفیف‌ها و پیشنهادهای ویژه باخبر باش. هوشمند بخر، هوشمند زندگی کن!', + ), + OnboardingEntity( + imagePath: Assets.images.rectangle3M.path, + title: ' پیشنهادهای لحظه‌ای، تجربه‌ای نو از خرید', + description: + 'Proxibuy همیشه باهاته؛ چه در مسیر رفتن به خونه، چه در مسیر کافه مورد علاقه‌ات. همین الان خریدت رو هوشمندتر کن!', + ), + OnboardingEntity( + imagePath: Assets.images.rectangle4.path, + title: 'دنیایی از تخفیف‌های اطرافت رو کشف کن', + description: + 'می‌صرفه یه اپ هوشمنده که کمکت می‌کنه وقتی توی شهر حرکت می‌کنی، از تخفیف‌های اطرافت باخبر شی و به صرفه‌تر خرید کنی.', + ), +]; + +class OnboardingPage extends StatefulWidget { + const OnboardingPage({super.key}); + + @override + State createState() => _OnboardingPageState(); +} + +class _OnboardingPageState extends State { + final PageController _pageController = PageController(); + int _currentPage = 0; + + @override + void initState() { + super.initState(); + _pageController.addListener(() { + if (_pageController.page != null) { + setState(() { + _currentPage = _pageController.page!.round(); + }); + } + }); + } + + @override + void dispose() { + _pageController.dispose(); + super.dispose(); + } + + void _onArrowTap() { + if (_currentPage < onboardingPages.length - 1) { + _pageController.nextPage( + duration: const Duration(milliseconds: 400), + curve: Curves.easeInOut, + ); + } else { + Navigator.of(context).pushReplacement( + MaterialPageRoute( + builder: (_) => BlocProvider( + create: (context) => AuthBloc(), + child: const LoginPage(), + ), + ), + ); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + children: [ + Directionality( + textDirection: TextDirection.ltr, + child: PageView.builder( + controller: _pageController, + itemCount: onboardingPages.length, + itemBuilder: (context, index) { + final page = onboardingPages[index]; + return Image.asset( + page.imagePath, + fit: BoxFit.cover, + width: double.infinity, + height: double.infinity, + ); + }, + ), + ), + + if (_currentPage > 0) + Positioned( + top: 50, + left: 10, + child: IconButton( + icon: SvgPicture.asset(Assets.icons.backArrow, width: 25), + onPressed: () { + _pageController.previousPage( + duration: const Duration(milliseconds: 400), + curve: Curves.easeInOut, + ); + }, + ), + ), + Positioned( + bottom: 250, + left: 0, + right: 0, + child: SizedBox( + width: 80, + height: 80, + child: Stack( + alignment: Alignment.center, + children: [ + SizedBox( + width: 80, + height: 80, + child: CustomPaint( + painter: OnboardingIndicatorPainter( + pageCount: onboardingPages.length, + currentPage: _currentPage, + activeColor: AppColors.primary, + inactiveColor: AppColors.unselected, + ), + ), + ), + Stack( + children: [ + Center( + child: GestureDetector( + onTap: _onArrowTap, + child: SvgPicture.asset( + Assets.icons.ellipse1, + width: 60, + ), + ), + ), + Center( + child: GestureDetector( + onTap: _onArrowTap, + child: SvgPicture.asset( + Assets.icons.arrowRight2, + width: 45, + ), + ), + ), + ], + ), + ], + ), + ), + ), + Positioned( + bottom: 90, + left: 24, + right: 24, + child: Column( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + onboardingPages[_currentPage].title, + textAlign: TextAlign.right, + style: Theme.of( + context, + ).textTheme.headlineSmall?.copyWith( + fontWeight: FontWeight.bold, + fontSize: 18, + color: Colors.white, + ), + ), + const SizedBox(height: 16), + Text( + onboardingPages[_currentPage].description, + textAlign: TextAlign.right, + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + // ignore: deprecated_member_use + color: Colors.white.withOpacity(0.8), + fontSize: 16, + height: 1.5, + ), + ), + ], + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/presentation/pages/osm_map_picker_page.dart b/lib/presentation/pages/osm_map_picker_page.dart new file mode 100644 index 0000000..03c50a8 --- /dev/null +++ b/lib/presentation/pages/osm_map_picker_page.dart @@ -0,0 +1,118 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:latlong2/latlong.dart'; +import 'package:geolocator/geolocator.dart'; + + +class OsmMapPickerPage extends StatefulWidget { + const OsmMapPickerPage({super.key}); + + @override + State createState() => _OsmMapPickerPageState(); +} + +class _OsmMapPickerPageState extends State { + + LatLng _pickedLocation = const LatLng(32.649704, 51.668222); + final MapController _mapController = MapController(); + + @override + void initState() { + super.initState(); + _moveToCurrentLocation(); + } + + Future _moveToCurrentLocation() async { + try { + LocationPermission permission = await Geolocator.checkPermission(); + if (permission == LocationPermission.denied || + permission == LocationPermission.deniedForever) { + permission = await Geolocator.requestPermission(); + if (permission == LocationPermission.denied || + permission == LocationPermission.deniedForever) { + return; + } + } + + Position position = await Geolocator.getCurrentPosition( + desiredAccuracy: LocationAccuracy.high, + ); + + setState(() { + _pickedLocation = LatLng(position.latitude, position.longitude); + _mapController.move(_pickedLocation, 15.0); + }); + } catch (e) { + print("Error getting location: $e"); + } + } + + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("موقعیت را روی نقشه مشخص کنید"), + actions: [ + IconButton( + icon: const Icon(Icons.my_location), + onPressed: () { + _moveToCurrentLocation(); + }, + ), + ], + ), + body: Stack( + children: [ + FlutterMap( + mapController: _mapController, + options: MapOptions( + initialCenter: _pickedLocation, + initialZoom: 13.0, + + onTap: (tapPosition, point) { + setState(() { + _pickedLocation = point; + }); + }, + ), + children: [ + TileLayer( + urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', + userAgentPackageName: 'com.example.app', // نام پکیج برنامه شما + ), + MarkerLayer( + markers: [ + Marker( + width: 80.0, + height: 80.0, + point: _pickedLocation, + child: const Icon( + Icons.location_on, + color: Colors.red, + size: 50.0, + ), + ), + ], + ), + ], + ), + Positioned( + bottom: 30, + left: 50, + right: 50, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 15), + ), + onPressed: () { + Navigator.of(context).pop(_pickedLocation); + }, + child: const Text("تایید این موقعیت"), + ), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/presentation/pages/otp_page.dart b/lib/presentation/pages/otp_page.dart new file mode 100644 index 0000000..be85468 --- /dev/null +++ b/lib/presentation/pages/otp_page.dart @@ -0,0 +1,306 @@ +import 'package:business_panel/core/config/app_colors.dart'; +import 'package:business_panel/gen/assets.gen.dart'; +import 'package:business_panel/presentation/auth/bloc/auth_bloc.dart'; +import 'package:business_panel/presentation/pages/store_info.dart'; +import 'package:business_panel/presentation/store_info/bloc/store_info_bloc.dart'; +import 'package:business_panel/presentation/utils/otp_timer_helper.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; + +class OtpPage extends StatefulWidget { + final String phoneNumber; + const OtpPage({super.key, required this.phoneNumber}); + + @override + State createState() => _OtpPageState(); +} + +class _OtpPageState extends State { + final List _focusNodes = List.generate(5, (_) => FocusNode()); + final List _controllers = List.generate( + 5, + (_) => TextEditingController(), + ); + late final OtpTimerHelper _otpTimer; + + bool _hasError = false; + String? _errorMessage; + bool _isOtpComplete = false; + + @override + void initState() { + super.initState(); + _otpTimer = OtpTimerHelper()..startTimer(); + WidgetsBinding.instance.addPostFrameCallback((_) { + FocusScope.of(context).requestFocus(_focusNodes[0]); + }); + } + + @override + void dispose() { + for (final node in _focusNodes) { + node.dispose(); + } + for (final controller in _controllers) { + controller.dispose(); + } + _otpTimer.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final textTheme = Theme.of(context).textTheme; + + return Directionality( + textDirection: TextDirection.rtl, + child: Scaffold( + body: SafeArea( + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Center(child: SvgPicture.asset(Assets.icons.logo, height: 160)), + SizedBox(height: 15), + Center( + child: Text( + "پنل فروشگاهی شما", + style: TextStyle(color: AppColors.active), + ), + ), + const SizedBox(height: 40), + Text( + "کد یکبار مصرف", + style: textTheme.headlineMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 12), + Text.rich( + TextSpan( + style: textTheme.titleMedium?.copyWith( + color: Colors.grey, + height: 1.5, + ), + children: [ + const TextSpan( + text: 'کد تایید به شماره ', + style: TextStyle(fontSize: 15), + ), + TextSpan( + text: widget.phoneNumber, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 15, + ), + ), + const TextSpan( + text: ' ارسال شد.', + style: TextStyle(fontSize: 15), + ), + ], + ), + textDirection: TextDirection.rtl, + ), + SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset(Assets.icons.vector), + const SizedBox(width: 4), + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text( + "ویرایش شماره همراه", + style: TextStyle(color: AppColors.active), + ), + ), + ], + ), + const SizedBox(height: 15), + _buildOtpFields(), + const SizedBox(height: 15), + if (_errorMessage != null) + Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: Center( + child: Text( + _errorMessage!, + style: const TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ), + ) + else + const SizedBox(height: 32), + + BlocConsumer( + listener: (context, state) { + if (state is AuthFailure) { + setState(() { + _hasError = true; + _errorMessage = state.message; + }); + } + if (state is AuthVerified) { + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: + (_) => BlocProvider( + create: (context) => StoreInfoBloc(), + child: const StoreInfoPage(), + ), + ), + (route) => false, + ); + } + }, + builder: (context, state) { + if (state is AuthLoading) { + return const Center(child: CircularProgressIndicator()); + } + return SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: _isOtpComplete ? _verifyOtp : null, + child: const Text("ورود"), + ), + ); + }, + ), + const SizedBox(height: 32), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset( + Assets.icons.clock, + colorFilter: const ColorFilter.mode( + Colors.grey, + BlendMode.srcIn, + ), + ), + const SizedBox(width: 8), + ValueListenableBuilder( + valueListenable: _otpTimer.canResend, + builder: (context, canResend, child) { + return canResend + ? TextButton( + onPressed: _resendOtp, + child: const Text( + "ارسال مجدد کد", + style: TextStyle(color: AppColors.active), + ), + ) + : ValueListenableBuilder( + valueListenable: _otpTimer.remainingSeconds, + builder: + (context, seconds, child) => Text( + "${_otpTimer.formatTime()} تا دریافت مجدد", + style: const TextStyle(color: Colors.grey), + ), + ); + }, + ), + ], + ), + ], + ), + ), + ), + ), + ); + } + + Widget _buildOtpFields() { + return Directionality( + textDirection: TextDirection.ltr, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: List.generate(5, (index) { + return SizedBox( + width: 60, + height: 60, + child: TextField( + controller: _controllers[index], + focusNode: _focusNodes[index], + keyboardType: TextInputType.number, + textAlign: TextAlign.center, + maxLength: 1, + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + color: _hasError ? Colors.red : Colors.black, + ), + decoration: InputDecoration( + counterText: '', + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: + _hasError + ? Colors.red + : (Theme.of(context) + .inputDecorationTheme + .enabledBorder + ?.borderSide + .color ?? + Colors.grey), + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: _hasError ? Colors.red : AppColors.active, + width: 2, + ), + ), + ), + onChanged: (value) { + if (_hasError) { + setState(() { + _hasError = false; + _errorMessage = null; + }); + } + if (value.length == 1 && index < 4) { + FocusScope.of(context).requestFocus(_focusNodes[index + 1]); + } + if (value.isEmpty && index > 0) { + FocusScope.of(context).requestFocus(_focusNodes[index - 1]); + } + final otpCode = _controllers.map((c) => c.text).join(); + setState(() { + _isOtpComplete = otpCode.length == 5; + }); + }, + ), + ); + }), + ), + ); + } + + void _verifyOtp() { + final otpCode = _controllers.map((c) => c.text).join(); + if (otpCode.length == 5) { + context.read().add(VerifyOTPEvent(otp: otpCode)); + } + } + + void _resendOtp() { + setState(() { + _hasError = false; + _errorMessage = null; + for (var controller in _controllers) { + controller.clear(); + } + _isOtpComplete = false; + }); + context.read().add(SendOTPEvent(phoneNumber: widget.phoneNumber)); + _otpTimer.resetTimer(); + } +} diff --git a/lib/presentation/pages/store_info.dart b/lib/presentation/pages/store_info.dart new file mode 100644 index 0000000..3b8bbc3 --- /dev/null +++ b/lib/presentation/pages/store_info.dart @@ -0,0 +1,495 @@ +import 'dart:io'; +import 'package:business_panel/core/config/app_colors.dart'; +import 'package:business_panel/gen/assets.gen.dart'; +import 'package:business_panel/presentation/pages/working_hours_dialog.dart'; +import 'package:business_panel/presentation/store_info/bloc/store_info_bloc.dart'; +import 'package:business_panel/presentation/store_info/bloc/store_info_state.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:business_panel/presentation/pages/osm_map_picker_page.dart'; // ایمپورت صفحه جدید +import 'package:latlong2/latlong.dart'; +import 'package:persian_datetime_picker/persian_datetime_picker.dart'; + +class StoreInfoPage extends StatelessWidget { + const StoreInfoPage({super.key}); + + Future _pickImage(BuildContext context) async { + try { + final ImagePicker picker = ImagePicker(); + final XFile? image = await picker.pickImage( + source: ImageSource.gallery, + imageQuality: 80, + ); + + if (image != null && context.mounted) { + context.read().add(StoreLogoChanged(image.path)); + } + } on PlatformException catch (e) { + print("خطای دسترسی یا پلتفرم: ${e.message}"); + } catch (e) { + print("یک خطای ناشناخته رخ داد: $e"); + } + } + + Future _showWorkingHoursDialog(BuildContext context) async { + final result = await showDialog>( + context: context, + builder: (context) => const WorkingHoursDialog(), + ); + + if (result != null && context.mounted) { + context.read().add( + WorkingScheduleChanged( + days: result['days'] as List, + startTime: result['startTime'] as String, + endTime: result['endTime'] as String, + ), + ); + } + } + + // Future _pickWorkingHours(BuildContext context) async { + // // ۱. انتخاب تاریخ شروع + // Jalali? startDate = await showPersianDatePicker( + // context: context, + // initialDate: Jalali.now(), + // firstDate: Jalali(1400), + // lastDate: Jalali(1405), + // ); + // if (startDate == null || !context.mounted) return; + + // // ۲. انتخاب ساعت شروع + // TimeOfDay? startTime = await showTimePicker( + // context: context, + // initialTime: TimeOfDay.now(), + // ); + // if (startTime == null || !context.mounted) return; + + // // ۳. انتخاب تاریخ پایان + // Jalali? endDate = await showPersianDatePicker( + // context: context, + // initialDate: startDate, // شروع از تاریخ انتخابی قبلی + // firstDate: startDate, // تاریخ پایان نمی‌تواند قبل از شروع باشد + // lastDate: Jalali(1405), + // ); + // if (endDate == null || !context.mounted) return; + + // // ۴. انتخاب ساعت پایان + // TimeOfDay? endTime = await showTimePicker( + // context: context, + // initialTime: startTime, + // ); + // if (endTime == null || !context.mounted) return; + + // // ۵. تبدیل به آبجکت DateTime و ارسال به BLoC + // final DateTime startDateTime = startDate.toDateTime().add( + // Duration(hours: startTime.hour, minutes: startTime.minute), + // ); + // final DateTime endDateTime = endDate.toDateTime().add( + // Duration(hours: endTime.hour, minutes: endTime.minute), + // ); + + // context.read().add( + // WorkingHoursChanged( + // startDateTime: startDateTime, + // endDateTime: endDateTime, + // ), + // ); + // } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: _buildCustomAppBar(context), + body: SingleChildScrollView( + padding: const EdgeInsets.all(24.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 16), + const Row( + children: [ + Text( + "لوگوی فروشگاه", + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20), + ), + SizedBox(width: 5), + Text("(اختیاری)", style: TextStyle(color: Colors.black54)), + ], + ), + const SizedBox(height: 24), + Center( + child: GestureDetector( + onTap: () => _pickImage(context), + child: CircleAvatar( + radius: 65, + backgroundColor: AppColors.uploadElevated, + child: BlocBuilder( + builder: (context, state) { + return Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: const Color.fromARGB(255, 224, 224, 224).withOpacity(0.5), + spreadRadius: 1, + blurRadius: 40, + offset: const Offset(0, 10), + ), + ], + image: + state.logoPath != null + ? DecorationImage( + image: FileImage(File(state.logoPath!)), + fit: BoxFit.cover, + ) + : null, + ), + child: Stack( + alignment: Alignment.center, + children: [ + Align( + alignment: Alignment.bottomRight, + child: CircleAvatar( + radius: 22, + backgroundColor: Colors.white, + child: CircleAvatar( + radius: 20, + backgroundColor: Colors.white, + child: SvgPicture.asset( + Assets.icons.edit02 + ), + ), + ), + ), + ], + ), + ); + }, + ), + ), + ), + ), + const SizedBox(height: 32), + _buildSectionTitle(), + const SizedBox(height: 30), + _buildTextField( + label: "نام فروشگاه", + isRequired: true, + hint: "مثلاً کافه ایرونی", + ), + const SizedBox(height: 30), + _buildActivityTypeDropdown(context), + const SizedBox(height: 30), + Row( + children: [ + Expanded( + child: _buildTextField(label: "استان", hint: "اصفهان"), + ), + const SizedBox(width: 16), + Expanded(child: _buildTextField(label: "شهر", hint: "اصفهان")), + ], + ), + const SizedBox(height: 30), + _buildTextField( + label: "جزئیات آدرس", + maxLines: 3, + hint: "خیابان، محله، ساختمان و ....", + ), + const SizedBox(height: 30), + Row( + children: [ + Expanded(child: _buildTextField(label: "پلاک")), + const SizedBox(width: 16), + Expanded(child: _buildTextField(label: "کد پستی")), + ], + ), + const SizedBox(height: 30), + Center( + child: TextButton.icon( + onPressed: () async { + final result = await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => const OsmMapPickerPage(), + ), + ); + if (result != null) { + context.read().add( + StoreLocationChanged( + latitude: result.latitude, + longitude: result.longitude, + ), + ); + } + }, + icon: SvgPicture.asset( + Assets.icons.map, + color: AppColors.button, + height: 23, + ), + label: const Text( + "انتخاب آدرس فروشگاه روی نقشه", + style: TextStyle(color: AppColors.button), + ), + ), + ), + Center( + child: BlocBuilder( + builder: (context, state) { + if (state.latitude != null && state.longitude != null) { + return Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + "آدرس با موفقیت ثبت شد ✓", + style: TextStyle( + color: Colors.green.shade700, + fontWeight: FontWeight.bold, + ), + ), + ); + } + return const SizedBox.shrink(); + }, + ), + ), + const SizedBox(height: 30), + _buildTextField( + label: "تلفن تماس", + keyboardType: TextInputType.phone, + hint: "شماره تماس ثابت یا موبایل فروشگاه", + ), + + const SizedBox(height: 30), + _buildWorkingHoursPicker(context), + + const SizedBox(height: 30), + _buildTextField( + label: "شماره جواز کسب", + hint: "شناسه صنفی 12 رقمی یکتا", + ), + const SizedBox(height: 44), + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: () {}, + child: const Text("تایید و ادامه"), + ), + ), + const SizedBox(height: 34), + ], + ), + ), + ); + } + + Widget _buildSectionTitle() { + return const Text( + "ثبت مشخصات", + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ); + } + + Widget _buildTextField({ + required String label, + bool isRequired = false, + String? hint, + int maxLines = 1, + TextInputType? keyboardType, + }) { + return TextFormField( + maxLines: maxLines, + keyboardType: keyboardType, + decoration: InputDecoration( + hintText: hint, + hintStyle: const TextStyle(fontSize: 15, color: Colors.grey), + label: RichText( + text: TextSpan( + text: label, + style: const TextStyle( + color: Colors.black, + fontFamily: 'Dana', + fontSize: 19, + fontWeight: FontWeight.bold, + ), + children: [ + if (isRequired) + const TextSpan( + text: ' *', + style: TextStyle(color: Colors.red, fontSize: 16), + ), + ], + ), + ), + ), + ); + } + + Widget _buildWorkingHoursPicker(BuildContext context) { + return InkWell( + onTap: () => _showWorkingHoursDialog(context), + child: InputDecorator( + decoration: InputDecoration( + label: RichText( + text: const TextSpan( + text: "ساعت کار فروشگاه", + style: TextStyle( + color: Colors.black, + fontFamily: 'Dana', + fontSize: 19, + fontWeight: FontWeight.bold, + ), + children: [ + TextSpan(text: ' *', style: TextStyle(color: Colors.red, fontSize: 16)), + ], + ), + ), + contentPadding: const EdgeInsets.symmetric(vertical: 18, horizontal: 12), + ), + child: BlocBuilder( + buildWhen: (p, c) => p.workingDays != c.workingDays, + builder: (context, state) { + final hasData = state.workingDays.isNotEmpty && state.startTime != null; + + const Map dayTranslations = { + 'Saturday': 'شنبه', 'Sunday': 'یکشنبه', 'Monday': 'دوشنبه', + 'Tuesday': 'سه‌شنبه', 'Wednesday': 'چهارشنبه', 'Thursday': 'پنج‌شنبه', 'Friday': 'جمعه' + }; + final displayDays = state.workingDays.map((day) => dayTranslations[day] ?? '').join('، '); + + String displayText = hasData + ? "$displayDays\nاز ساعت ${state.startTime} تا ${state.endTime}" + : "انتخاب روز و ساعت کاری"; + + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + displayText, + style: TextStyle( + fontSize: 16, + color: hasData ? Colors.black : Colors.grey.shade600, + height: 1.5, + ), + textAlign: TextAlign.right, + ), + ), + ], + ); + }, + ), + ), + ); + } + + + Widget _buildActivityTypeDropdown(BuildContext context) { + final List activityTypes = [ + "🍔🍕 فست فود", + "👚👔 پوشاک", + "🍨🍹 تریا", + "📱📷 لوازم دیجیتال", + "🍣🍢 رستوران", + "☕🍰 کافی شاپ", + "👜👞 کیف و کفش", + "🎭🎟️ سینما", + "💄💅️ لوازم آرایشی", + "💍💎 طلا و زیورآلات", + ]; + + return Theme( + data: Theme.of(context).copyWith(canvasColor: const Color(0xFFF6F6F6)), + child: DropdownButtonFormField( + icon: SvgPicture.asset(Assets.icons.arrowDown, width: 24,color: Colors.black,), + decoration: InputDecoration( + label: RichText( + text: const TextSpan( + text: "نوع فعالیت", + style: TextStyle( + color: Colors.black, + fontFamily: 'Dana', + fontSize: 19, + fontWeight: FontWeight.bold, + ), + children: [ + TextSpan( + text: ' *', + style: TextStyle(color: Colors.red, fontSize: 16), + ), + ], + ), + ), + ), + borderRadius: BorderRadius.circular(12.0), + isExpanded: true, + items: + activityTypes.map((String value) { + return DropdownMenuItem(value: value, child: Text(value)); + }).toList(), + onChanged: (value) { + if (value != null) { + context.read().add(ActivityTypeChanged(value)); + } + }, + ), + ); + } +} + +PreferredSizeWidget _buildCustomAppBar(BuildContext context) { + return PreferredSize( + preferredSize: const Size.fromHeight(70.0), + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.vertical(bottom: Radius.circular(15)), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.08), + blurRadius: 10, + offset: const Offset(0, 4), + ), + ], + ), + child: SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: Column( + children: [ + const SizedBox(height: 15), + Row( + children: [ + Padding( + padding: const EdgeInsets.only(right: 8), + child: SvgPicture.asset(Assets.icons.logoWithName), + ), + + const Spacer(), + + Row( + children: [ + IconButton( + onPressed: () {}, + icon: SvgPicture.asset( + Assets.icons.discountShape, + color: Colors.black, + ), + ), + IconButton( + onPressed: () {}, + icon: SvgPicture.asset(Assets.icons.scanBarcode), + ), + ], + ), + ], + ), + ], + ), + ), + ), + ), + ); +} diff --git a/lib/presentation/pages/working_hours_dialog.dart b/lib/presentation/pages/working_hours_dialog.dart new file mode 100644 index 0000000..213dbf9 --- /dev/null +++ b/lib/presentation/pages/working_hours_dialog.dart @@ -0,0 +1,124 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class WorkingHoursDialog extends StatefulWidget { + const WorkingHoursDialog({super.key}); + + @override + State createState() => _WorkingHoursDialogState(); +} + +class _WorkingHoursDialogState extends State { + final Map> _days = { + 'شنبه': {'english': 'Saturday', 'isSelected': false}, + 'یکشنبه': {'english': 'Sunday', 'isSelected': false}, + 'دوشنبه': {'english': 'Monday', 'isSelected': false}, + 'سه‌شنبه': {'english': 'Tuesday', 'isSelected': false}, + 'چهارشنبه': {'english': 'Wednesday', 'isSelected': false}, + 'پنجشنبه': {'english': 'Thursday', 'isSelected': false}, + 'جمعه': {'english': 'Friday', 'isSelected': false}, + }; + + TimeOfDay? _startTime; + TimeOfDay? _endTime; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text("تنظیم روز و ساعت کاری", textAlign: TextAlign.center), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text("روزهای فعالیت را انتخاب کنید:"), + const SizedBox(height: 8), + // ویجت برای نمایش و انتخاب روزها + Wrap( + spacing: 4.0, + runSpacing: 8.0, + children: _days.keys.map((dayName) { + return ChoiceChip( + label: Text(dayName), + selected: _days[dayName]!['isSelected'], + onSelected: (isSelected) { + setState(() { + _days[dayName]!['isSelected'] = isSelected; + }); + }, + ); + }).toList(), + ), + const Divider(height: 32), + const Text("بازه ساعت کاری را مشخص کنید:"), + const SizedBox(height: 16), + // انتخابگرهای ساعت شروع و پایان + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + _buildTimePicker("پایان", _endTime, (time) { + setState(() => _endTime = time); + }), + _buildTimePicker("شروع", _startTime, (time) { + setState(() => _startTime = time); + }), + ], + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text("انصراف"), + ), + ElevatedButton( + onPressed: () { + // فقط در صورتی که تمام مقادیر انتخاب شده باشند، بازگشت انجام شود + if (_startTime != null && _endTime != null) { + final selectedDays = _days.entries + .where((entry) => entry.value['isSelected']) + .map((entry) => entry.value['english'] as String) + .toList(); + + if (selectedDays.isNotEmpty) { + // فرمت کردن ساعت به رشته "HH:mm" + final format = NumberFormat("00"); + final startTimeString = "${format.format(_startTime!.hour)}:${format.format(_startTime!.minute)}"; + final endTimeString = "${format.format(_endTime!.hour)}:${format.format(_endTime!.minute)}"; + + // بازگرداندن نتیجه به صفحه قبل + Navigator.of(context).pop({ + 'days': selectedDays, + 'startTime': startTimeString, + 'endTime': endTimeString, + }); + } + } + }, + child: const Text("تایید"), + ), + ], + ); + } + + Widget _buildTimePicker(String title, TimeOfDay? time, Function(TimeOfDay) onTimeChanged) { + return Column( + children: [ + Text(title), + const SizedBox(height: 8), + OutlinedButton( + onPressed: () async { + final newTime = await showTimePicker( + context: context, + initialTime: time ?? TimeOfDay.now(), + ); + if (newTime != null) { + onTimeChanged(newTime); + } + }, + child: Text(time?.format(context) ?? "انتخاب"), + ), + ], + ); + } +} \ No newline at end of file diff --git a/lib/presentation/store_info/bloc/store_info_bloc.dart b/lib/presentation/store_info/bloc/store_info_bloc.dart new file mode 100644 index 0000000..b725181 --- /dev/null +++ b/lib/presentation/store_info/bloc/store_info_bloc.dart @@ -0,0 +1,45 @@ +import 'package:bloc/bloc.dart'; +import 'package:business_panel/presentation/store_info/bloc/store_info_state.dart'; +part 'store_info_event.dart'; + +class StoreInfoBloc extends Bloc { + StoreInfoBloc() : super(StoreInfoState()) { + on((event, emit) { + emit(state.copyWith(logoPath: event.imagePath)); + }); + + on((event, emit) { + emit(state.copyWith(storeName: event.name)); + }); + + on((event, emit) { + emit(state.copyWith(activityType: event.activityType)); + }); + + + on((event, emit) async { + emit(state.copyWith(isSubmitting: true)); + await Future.delayed(const Duration(seconds: 2)); + + if (state.storeName.isEmpty || state.activityType.isEmpty) { + emit(state.copyWith(isSubmitting: false, errorMessage: "فیلدهای ستاره‌دار اجباری هستند.")); + } else { + emit(state.copyWith(isSubmitting: false, isSuccess: true)); + } + }); + + on((event, emit) { + emit(state.copyWith(latitude: event.latitude, longitude: event.longitude)); + }); + + on((event, emit) { + emit(state.copyWith( + workingDays: event.days, + startTime: event.startTime, + endTime: event.endTime, + )); + }); + + } + +} \ No newline at end of file diff --git a/lib/presentation/store_info/bloc/store_info_event.dart b/lib/presentation/store_info/bloc/store_info_event.dart new file mode 100644 index 0000000..46c2d34 --- /dev/null +++ b/lib/presentation/store_info/bloc/store_info_event.dart @@ -0,0 +1,75 @@ +part of 'store_info_bloc.dart'; + +abstract class StoreInfoEvent {} + +class StoreLogoChanged extends StoreInfoEvent { + final String imagePath; + StoreLogoChanged(this.imagePath); +} + +class StoreNameChanged extends StoreInfoEvent { + final String name; + StoreNameChanged(this.name); +} + +class ActivityTypeChanged extends StoreInfoEvent { + final String activityType; + ActivityTypeChanged(this.activityType); +} + +class ProvinceChanged extends StoreInfoEvent { + final String province; + ProvinceChanged(this.province); +} + +class CityChanged extends StoreInfoEvent { + final String city; + CityChanged(this.city); +} + +class AddressChanged extends StoreInfoEvent { + final String address; + AddressChanged(this.address); +} + +class PlaqueChanged extends StoreInfoEvent { + final String plaque; + PlaqueChanged(this.plaque); +} + +class PostalCodeChanged extends StoreInfoEvent { + final String postalCode; + PostalCodeChanged(this.postalCode); +} + +class StoreLocationChanged extends StoreInfoEvent { + final double latitude; + final double longitude; + + StoreLocationChanged({required this.latitude, required this.longitude}); +} + +class ContactPhoneChanged extends StoreInfoEvent { + final String phone; + ContactPhoneChanged(this.phone); +} + +class LicenseNumberChanged extends StoreInfoEvent { + final String license; + LicenseNumberChanged(this.license); +} + +class WorkingScheduleChanged extends StoreInfoEvent { + final List days; + final String startTime; + final String endTime; + + WorkingScheduleChanged({ + required this.days, + required this.startTime, + required this.endTime, + }); +} + + +class SubmitStoreInfo extends StoreInfoEvent {} \ No newline at end of file diff --git a/lib/presentation/store_info/bloc/store_info_state.dart b/lib/presentation/store_info/bloc/store_info_state.dart new file mode 100644 index 0000000..abc790f --- /dev/null +++ b/lib/presentation/store_info/bloc/store_info_state.dart @@ -0,0 +1,107 @@ +import 'package:equatable/equatable.dart'; + +class StoreInfoState { + final String? logoPath; + final String storeName; + final String activityType; + final String province; + final String city; + final String address; + final String plaque; + final String postalCode; + final bool isSubmitting; + final bool isSuccess; + final String? errorMessage; + final double? latitude; + final double? longitude; + final String? contactPhone; + final String? licenseNumber; + final List workingDays; + final String? startTime; + final String? endTime; + + StoreInfoState({ + this.logoPath, + this.storeName = '', + this.activityType = '', + this.province = '', + this.city = '', + this.address = '', + this.plaque = '', + this.postalCode = '', + this.isSubmitting = false, + this.isSuccess = false, + this.errorMessage, + this.latitude, + this.longitude, + this.contactPhone, + this.licenseNumber, + this.workingDays = const [], // مقدار اولیه یک لیست خالی است + this.startTime, + this.endTime, + }); + + StoreInfoState copyWith({ + String? logoPath, + String? storeName, + String? activityType, + String? province, + String? city, + String? address, + String? plaque, + String? postalCode, + bool? isSubmitting, + bool? isSuccess, + String? errorMessage, + double? latitude, + double? longitude, + String? contactPhone, + String? licenseNumber, + List? workingDays, + String? startTime, + String? endTime, + }) { + return StoreInfoState( + logoPath: logoPath ?? this.logoPath, + storeName: storeName ?? this.storeName, + activityType: activityType ?? this.activityType, + province: province ?? this.province, + city: city ?? this.city, + address: address ?? this.address, + plaque: plaque ?? this.plaque, + postalCode: postalCode ?? this.postalCode, + isSubmitting: isSubmitting ?? this.isSubmitting, + isSuccess: isSuccess ?? this.isSuccess, + errorMessage: errorMessage ?? this.errorMessage, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + contactPhone: contactPhone ?? this.contactPhone, + licenseNumber: licenseNumber ?? this.licenseNumber, + workingDays: workingDays ?? this.workingDays, + startTime: startTime ?? this.startTime, + endTime: endTime ?? this.endTime, + ); + } + + @override + List get props => [ + logoPath, + storeName, + activityType, + province, + city, + address, + plaque, + postalCode, + isSubmitting, + isSuccess, + errorMessage, + latitude, + longitude, + contactPhone, + licenseNumber, + workingDays, + startTime, + endTime, + ]; +} diff --git a/lib/presentation/utils/otp_timer_helper.dart b/lib/presentation/utils/otp_timer_helper.dart new file mode 100644 index 0000000..0d5d28f --- /dev/null +++ b/lib/presentation/utils/otp_timer_helper.dart @@ -0,0 +1,37 @@ +import 'dart:async'; +import 'package:flutter/foundation.dart'; + +class OtpTimerHelper { + final ValueNotifier remainingSeconds = ValueNotifier(60); + final ValueNotifier canResend = ValueNotifier(false); + Timer? _timer; + + void startTimer() { + canResend.value = false; + _timer = Timer.periodic(const Duration(seconds: 1), (timer) { + if (remainingSeconds.value > 0) { + remainingSeconds.value--; + } else { + canResend.value = true; + _timer?.cancel(); + } + }); + } + + void resetTimer() { + _timer?.cancel(); + remainingSeconds.value = 60; + startTimer(); + } + + String formatTime() { + int seconds = remainingSeconds.value; + return "0:${seconds.toString().padLeft(2, '0')}"; + } + + void dispose() { + _timer?.cancel(); + remainingSeconds.dispose(); + canResend.dispose(); + } +} \ No newline at end of file diff --git a/lib/presentation/widgets/onboarding_indicator_painter.dart b/lib/presentation/widgets/onboarding_indicator_painter.dart new file mode 100644 index 0000000..e22e33f --- /dev/null +++ b/lib/presentation/widgets/onboarding_indicator_painter.dart @@ -0,0 +1,56 @@ +import 'dart:math'; +import 'package:business_panel/core/config/app_colors.dart'; +import 'package:flutter/material.dart'; + + +class OnboardingIndicatorPainter extends CustomPainter { + final int pageCount; + final int currentPage; + final Color activeColor; + final Color inactiveColor; + final double strokeWidth; + + OnboardingIndicatorPainter({ + required this.pageCount, + required this.currentPage, + this.activeColor = AppColors.primary, + this.inactiveColor = AppColors.unselected, + this.strokeWidth = 6.0, + }); + + @override + void paint(Canvas canvas, Size size) { + final center = Offset(size.width / 2, size.height / 2); + final radius = min(size.width, size.height) / 2; + + const double gapInDegrees = 30.0; + const double gapInRadians = gapInDegrees * (pi / 180); + + const double arcAngle = (pi / 2) - gapInRadians; + + final startAngles = [-pi / 2, 0.0, pi / 2, pi]; + + for (int i = 0; i < pageCount; i++) { + final paint = Paint() + ..color = (i == currentPage) ? activeColor : inactiveColor + ..style = PaintingStyle.stroke + ..strokeWidth = strokeWidth + ..strokeCap = StrokeCap.round; + + final correctedStartAngle = startAngles[i] + (gapInRadians / 2); + + canvas.drawArc( + Rect.fromCircle(center: center, radius: radius), + correctedStartAngle, + arcAngle, + false, + paint, + ); + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return true; + } +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..e68dfa6 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,1207 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f + url: "https://pub.dev" + source: hosted + version: "85.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "01949bf52ad33f0e0f74f881fbaac4f348c556531951d92c8d16f1262aa19ff8" + url: "https://pub.dev" + source: hosted + version: "7.5.4" + archive: + dependency: transitive + description: + name: archive + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + url: "https://pub.dev" + source: hosted + version: "4.0.7" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + url: "https://pub.dev" + source: hosted + version: "2.12.0" + bloc: + dependency: "direct main" + description: + name: bloc + sha256: "52c10575f4445c61dd9e0cafcc6356fdd827c4c64dd7945ef3c4105f6b6ac189" + url: "https://pub.dev" + source: hosted + version: "9.0.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + build: + dependency: transitive + description: + name: build + sha256: "51dc711996cbf609b90cbe5b335bbce83143875a9d58e4b5c6d3c4f684d3dda7" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + build_config: + dependency: transitive + description: + name: build_config + sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa" + url: "https://pub.dev" + source: hosted + version: "4.0.4" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: ee4257b3f20c0c90e72ed2b57ad637f694ccba48839a821e87db762548c22a62 + url: "https://pub.dev" + source: hosted + version: "2.5.4" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "382a4d649addbfb7ba71a3631df0ec6a45d5ab9b098638144faf27f02778eb53" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "85fbbb1036d576d966332a3f5ce83f2ce66a40bea1a94ad2d5fc29a19a0d3792" + url: "https://pub.dev" + source: hosted + version: "9.1.2" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: "082001b5c3dc495d4a42f1d5789990505df20d8547d42507c29050af6933ee27" + url: "https://pub.dev" + source: hosted + version: "8.10.1" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" + url: "https://pub.dev" + source: hosted + version: "4.10.1" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + color: + dependency: transitive + description: + name: color + sha256: ddcdf1b3badd7008233f5acffaf20ca9f5dc2cd0172b75f68f24526a5f5725cb + url: "https://pub.dev" + source: hosted + version: "3.0.0" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + country_picker: + dependency: "direct main" + description: + name: country_picker + sha256: "9b14c04f9a35e99f6de6bcbc453a556bb98345aecb481c7a0e843c94c2bee1f8" + url: "https://pub.dev" + source: hosted + version: "2.0.27" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" + url: "https://pub.dev" + source: hosted + version: "0.3.4+2" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" + source: hosted + version: "1.0.8" + dart_earcut: + dependency: transitive + description: + name: dart_earcut + sha256: e485001bfc05dcbc437d7bfb666316182e3522d4c3f9668048e004d0eb2ce43b + url: "https://pub.dev" + source: hosted + version: "1.2.0" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "5b236382b47ee411741447c1f1e111459c941ea1b3f2b540dde54c210a3662af" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + dartx: + dependency: transitive + description: + name: dartx + sha256: "8b25435617027257d43e6508b5fe061012880ddfdaa75a71d607c3de2a13d244" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + dbus: + dependency: transitive + description: + name: dbus + sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c" + url: "https://pub.dev" + source: hosted + version: "0.7.11" + equatable: + dependency: "direct main" + description: + name: equatable + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" + url: "https://pub.dev" + source: hosted + version: "2.0.7" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" + url: "https://pub.dev" + source: hosted + version: "1.3.2" + ffi: + dependency: transitive + description: + name: ffi + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + file_selector_linux: + dependency: transitive + description: + name: file_selector_linux + sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33" + url: "https://pub.dev" + source: hosted + version: "0.9.3+2" + file_selector_macos: + dependency: transitive + description: + name: file_selector_macos + sha256: "8c9250b2bd2d8d4268e39c82543bacbaca0fda7d29e0728c3c4bbb7c820fd711" + url: "https://pub.dev" + source: hosted + version: "0.9.4+3" + file_selector_platform_interface: + dependency: transitive + description: + name: file_selector_platform_interface + sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b + url: "https://pub.dev" + source: hosted + version: "2.6.2" + file_selector_windows: + dependency: transitive + description: + name: file_selector_windows + sha256: "320fcfb6f33caa90f0b58380489fc5ac05d99ee94b61aa96ec2bff0ba81d3c2b" + url: "https://pub.dev" + source: hosted + version: "0.9.3+4" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + sha256: cf51747952201a455a1c840f8171d273be009b932c75093020f9af64f2123e38 + url: "https://pub.dev" + source: hosted + version: "9.1.1" + flutter_gen_core: + dependency: transitive + description: + name: flutter_gen_core + sha256: "3eaa2d3d8be58267ac4cd5e215ac965dd23cae0410dc073de2e82e227be32bfc" + url: "https://pub.dev" + source: hosted + version: "5.10.0" + flutter_gen_runner: + dependency: "direct dev" + description: + name: flutter_gen_runner + sha256: e74b4ead01df3e8f02e73a26ca856759dbbe8cb3fd60941ba9f4005cd0cd19c9 + url: "https://pub.dev" + source: hosted + version: "5.10.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + flutter_localization: + dependency: "direct main" + description: + name: flutter_localization + sha256: "578a73455a0deffc4169ef9372ba0562a3e2cff563e5c524ea87bc96daa519c0" + url: "https://pub.dev" + source: hosted + version: "0.3.3" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_map: + dependency: "direct main" + description: + name: flutter_map + sha256: f7d0379477274f323c3f3bc12d369a2b42eb86d1e7bd2970ae1ea3cff782449a + url: "https://pub.dev" + source: hosted + version: "8.1.1" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e + url: "https://pub.dev" + source: hosted + version: "2.0.28" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + sha256: cd57f7969b4679317c17af6fd16ee233c1e60a82ed209d8a475c54fd6fd6f845 + url: "https://pub.dev" + source: hosted + version: "2.2.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + geoclue: + dependency: transitive + description: + name: geoclue + sha256: c2a998c77474fc57aa00c6baa2928e58f4b267649057a1c76738656e9dbd2a7f + url: "https://pub.dev" + source: hosted + version: "0.1.1" + geolocator: + dependency: "direct main" + description: + name: geolocator + sha256: "79939537046c9025be47ec645f35c8090ecadb6fe98eba146a0d25e8c1357516" + url: "https://pub.dev" + source: hosted + version: "14.0.2" + geolocator_android: + dependency: transitive + description: + name: geolocator_android + sha256: "179c3cb66dfa674fc9ccbf2be872a02658724d1c067634e2c427cf6df7df901a" + url: "https://pub.dev" + source: hosted + version: "5.0.2" + geolocator_apple: + dependency: transitive + description: + name: geolocator_apple + sha256: dbdd8789d5aaf14cf69f74d4925ad1336b4433a6efdf2fce91e8955dc921bf22 + url: "https://pub.dev" + source: hosted + version: "2.3.13" + geolocator_linux: + dependency: transitive + description: + name: geolocator_linux + sha256: c4e966f0a7a87e70049eac7a2617f9e16fd4c585a26e4330bdfc3a71e6a721f3 + url: "https://pub.dev" + source: hosted + version: "0.2.3" + geolocator_platform_interface: + dependency: transitive + description: + name: geolocator_platform_interface + sha256: "30cb64f0b9adcc0fb36f628b4ebf4f731a2961a0ebd849f4b56200205056fe67" + url: "https://pub.dev" + source: hosted + version: "4.2.6" + geolocator_web: + dependency: transitive + description: + name: geolocator_web + sha256: b1ae9bdfd90f861fde8fd4f209c37b953d65e92823cb73c7dee1fa021b06f172 + url: "https://pub.dev" + source: hosted + version: "4.1.3" + geolocator_windows: + dependency: transitive + description: + name: geolocator_windows + sha256: "175435404d20278ffd220de83c2ca293b73db95eafbdc8131fe8609be1421eb6" + url: "https://pub.dev" + source: hosted + version: "0.2.5" + glob: + dependency: transitive + description: + name: glob + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.dev" + source: hosted + version: "2.1.3" + graphs: + dependency: transitive + description: + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + gsettings: + dependency: transitive + description: + name: gsettings + sha256: "1b0ce661f5436d2db1e51f3c4295a49849f03d304003a7ba177d01e3a858249c" + url: "https://pub.dev" + source: hosted + version: "0.2.8" + hashcodes: + dependency: transitive + description: + name: hashcodes + sha256: "80f9410a5b3c8e110c4b7604546034749259f5d6dcca63e0d3c17c9258f1a651" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + http: + dependency: transitive + description: + name: http + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + url: "https://pub.dev" + source: hosted + version: "3.2.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + image_picker: + dependency: "direct main" + description: + name: image_picker + sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + sha256: "317a5d961cec5b34e777b9252393f2afbd23084aa6e60fcf601dcf6341b9ebeb" + url: "https://pub.dev" + source: hosted + version: "0.8.12+23" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + sha256: "717eb042ab08c40767684327be06a5d8dbb341fe791d514e4b92c7bbe1b7bb83" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + sha256: "05da758e67bc7839e886b3959848aa6b44ff123ab4b28f67891008afe8ef9100" + url: "https://pub.dev" + source: hosted + version: "0.8.12+2" + image_picker_linux: + dependency: transitive + description: + name: image_picker_linux + sha256: "34a65f6740df08bbbeb0a1abd8e6d32107941fd4868f67a507b25601651022c9" + url: "https://pub.dev" + source: hosted + version: "0.2.1+2" + image_picker_macos: + dependency: transitive + description: + name: image_picker_macos + sha256: "1b90ebbd9dcf98fb6c1d01427e49a55bd96b5d67b8c67cf955d60a5de74207c1" + url: "https://pub.dev" + source: hosted + version: "0.2.1+2" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + sha256: "886d57f0be73c4b140004e78b9f28a8914a09e50c2d816bdd0520051a71236a0" + url: "https://pub.dev" + source: hosted + version: "2.10.1" + image_picker_windows: + dependency: transitive + description: + name: image_picker_windows + sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" + image_size_getter: + dependency: transitive + description: + name: image_size_getter + sha256: "9a299e3af2ebbcfd1baf21456c3c884037ff524316c97d8e56035ea8fdf35653" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + intl: + dependency: "direct main" + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" + source: hosted + version: "0.19.0" + io: + dependency: transitive + description: + name: io + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + url: "https://pub.dev" + source: hosted + version: "1.0.5" + js: + dependency: transitive + description: + name: js + sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" + url: "https://pub.dev" + source: hosted + version: "0.7.2" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + latlong2: + dependency: "direct main" + description: + name: latlong2 + sha256: "98227922caf49e6056f91b6c56945ea1c7b166f28ffcd5fb8e72fc0b453cc8fe" + url: "https://pub.dev" + source: hosted + version: "0.9.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + url: "https://pub.dev" + source: hosted + version: "10.0.8" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + url: "https://pub.dev" + source: hosted + version: "5.1.1" + lists: + dependency: transitive + description: + name: lists + sha256: "4ca5c19ae4350de036a7e996cdd1ee39c93ac0a2b840f4915459b7d0a7d4ab27" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + logger: + dependency: transitive + description: + name: logger + sha256: "2621da01aabaf223f8f961e751f2c943dbb374dc3559b982f200ccedadaa6999" + url: "https://pub.dev" + source: hosted + version: "2.6.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + mgrs_dart: + dependency: transitive + description: + name: mgrs_dart + sha256: fb89ae62f05fa0bb90f70c31fc870bcbcfd516c843fb554452ab3396f78586f7 + url: "https://pub.dev" + source: hosted + version: "2.0.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" + package_info_plus: + dependency: transitive + description: + name: package_info_plus + sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" + url: "https://pub.dev" + source: hosted + version: "8.3.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + persian_datetime_picker: + dependency: "direct main" + description: + name: persian_datetime_picker + sha256: "7ccbfd3a68dc89d405550f624e9fa590c914fed2aa2d48973c4f4400baab2e06" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" + url: "https://pub.dev" + source: hosted + version: "6.1.0" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + polylabel: + dependency: transitive + description: + name: polylabel + sha256: "41b9099afb2aa6c1730bdd8a0fab1400d287694ec7615dd8516935fa3144214b" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + posix: + dependency: transitive + description: + name: posix + sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" + url: "https://pub.dev" + source: hosted + version: "6.0.3" + proj4dart: + dependency: transitive + description: + name: proj4dart + sha256: c8a659ac9b6864aa47c171e78d41bbe6f5e1d7bd790a5814249e6b68bc44324e + url: "https://pub.dev" + source: hosted + version: "2.1.0" + provider: + dependency: transitive + description: + name: provider + sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" + url: "https://pub.dev" + source: hosted + version: "6.1.5" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + shamsi_date: + dependency: transitive + description: + name: shamsi_date + sha256: b6c79ff34ddfb1e9e4761347f18e30afdd7d16cc3db77defd5a40e2d93894c51 + url: "https://pub.dev" + source: hosted + version: "1.1.0" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + url: "https://pub.dev" + source: hosted + version: "2.5.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + url: "https://pub.dev" + source: hosted + version: "2.4.10" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shelf: + dependency: transitive + description: + name: shelf + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + url: "https://pub.dev" + source: hosted + version: "1.4.2" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" + source: hosted + version: "1.10.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + url: "https://pub.dev" + source: hosted + version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + url: "https://pub.dev" + source: hosted + version: "0.7.4" + time: + dependency: transitive + description: + name: time + sha256: "370572cf5d1e58adcb3e354c47515da3f7469dac3a95b447117e728e7be6f461" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + timing: + dependency: transitive + description: + name: timing + sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + unicode: + dependency: transitive + description: + name: unicode + sha256: "0f69e46593d65245774d4f17125c6084d2c20b4e473a983f6e21b7d7762218f1" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + universal_io: + dependency: transitive + description: + name: universal_io + sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + uuid: + dependency: transitive + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6 + url: "https://pub.dev" + source: hosted + version: "1.1.19" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" + url: "https://pub.dev" + source: hosted + version: "1.1.13" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "557a315b7d2a6dbb0aaaff84d857967ce6bdc96a63dc6ee2a57ce5a6ee5d3331" + url: "https://pub.dev" + source: hosted + version: "1.1.17" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + url: "https://pub.dev" + source: hosted + version: "14.3.1" + watcher: + dependency: transitive + description: + name: watcher + sha256: "0b7fd4a0bbc4b92641dbf20adfd7e3fd1398fe17102d94b674234563e110088a" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + win32: + dependency: transitive + description: + name: win32 + sha256: "329edf97fdd893e0f1e3b9e88d6a0e627128cc17cc316a8d67fda8f1451178ba" + url: "https://pub.dev" + source: hosted + version: "5.13.0" + wkt_parser: + dependency: transitive + description: + name: wkt_parser + sha256: "8a555fc60de3116c00aad67891bcab20f81a958e4219cc106e3c037aa3937f13" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + xml: + dependency: transitive + description: + name: xml + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.dev" + source: hosted + version: "6.5.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" +sdks: + dart: ">=3.7.2 <4.0.0" + flutter: ">=3.27.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..bc0fae8 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,104 @@ +name: business_panel +description: "A new Flutter project." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: ^3.7.2 + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.8 + flutter_localization: ^0.3.3 + flutter_svg: ^2.2.0 + country_picker: ^2.0.27 + bloc: ^9.0.0 + flutter_bloc: ^9.1.1 + image_picker: ^1.1.2 + flutter_map: ^8.1.1 + latlong2: ^0.9.1 + persian_datetime_picker: ^3.1.0 + equatable: ^2.0.7 + intl: ^0.19.0 + geolocator: ^14.0.2 + + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_gen_runner: + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^5.0.0 + build_runner: ^2.5.4 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + assets: + - assets/images/ + - assets/icons/ + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/to/asset-from-package + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + fonts: + - family: Dana + fonts: + - asset: assets/fonts/dana-fanum-regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/to/font-from-package diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..7947ff1 --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:business_panel/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/web/favicon.png b/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/web/favicon.png differ diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/web/icons/Icon-192.png differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/web/icons/Icon-512.png differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000..eb9b4d7 Binary files /dev/null and b/web/icons/Icon-maskable-192.png differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000..d69c566 Binary files /dev/null and b/web/icons/Icon-maskable-512.png differ diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..6d20642 --- /dev/null +++ b/web/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + business_panel + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 0000000..5bbd466 --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "business_panel", + "short_name": "business_panel", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +}