├── ios
├── Runner
│ ├── Runner-Bridging-Header.h
│ ├── Assets.xcassets
│ │ ├── LaunchImage.imageset
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ ├── README.md
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-83.5x83.5@2x.png
│ │ │ └── Contents.json
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ ├── Main.storyboard
│ │ └── LaunchScreen.storyboard
│ └── Info.plist
├── Flutter
│ ├── Debug.xcconfig
│ ├── Release.xcconfig
│ └── AppFrameworkInfo.plist
├── Runner.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── WorkspaceSettings.xcsettings
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── WorkspaceSettings.xcsettings
│ │ └── IDEWorkspaceChecks.plist
├── .gitignore
├── Podfile
└── Podfile.lock
├── web
├── favicon.png
├── icons
│ ├── Icon-192.png
│ ├── Icon-512.png
│ ├── Icon-maskable-192.png
│ └── Icon-maskable-512.png
├── manifest.json
└── index.html
├── assets
├── fonts
│ └── vazir.ttf
└── images
│ ├── benz.png
│ ├── bmw.png
│ ├── box.png
│ ├── tara.png
│ ├── qorfe.png
│ ├── home_icon.png
│ ├── home_icon2.png
│ ├── besenior_logo.png
│ ├── category_icon.png
│ ├── category_icon2.png
│ ├── bs_logo_textfield.png
│ ├── person_icon2.svg
│ ├── person_icon.svg
│ └── amazing.svg
├── android
├── gradle.properties
├── app
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── drawable
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable-v21
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── values
│ │ │ │ │ └── styles.xml
│ │ │ │ └── values-night
│ │ │ │ │ └── styles.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ ├── example
│ │ │ │ │ └── besenior_shop_course
│ │ │ │ │ │ └── MainActivity.kt
│ │ │ │ │ └── besenior
│ │ │ │ │ └── besenior_shop_course
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── AndroidManifest.xml
│ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ └── profile
│ │ │ └── AndroidManifest.xml
│ └── build.gradle
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── .gitignore
├── settings.gradle
└── build.gradle
├── macos
├── Runner
│ ├── Configs
│ │ ├── Debug.xcconfig
│ │ ├── Release.xcconfig
│ │ ├── Warnings.xcconfig
│ │ └── AppInfo.xcconfig
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ ├── app_icon_128.png
│ │ │ ├── app_icon_16.png
│ │ │ ├── app_icon_256.png
│ │ │ ├── app_icon_32.png
│ │ │ ├── app_icon_512.png
│ │ │ ├── app_icon_64.png
│ │ │ ├── app_icon_1024.png
│ │ │ └── Contents.json
│ ├── AppDelegate.swift
│ ├── Release.entitlements
│ ├── DebugProfile.entitlements
│ ├── MainFlutterWindow.swift
│ └── Info.plist
├── .gitignore
├── Flutter
│ ├── Flutter-Debug.xcconfig
│ ├── Flutter-Release.xcconfig
│ └── GeneratedPluginRegistrant.swift
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── Runner.xcodeproj
│ ├── project.xcworkspace
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
└── Podfile
├── lib
├── common
│ ├── params
│ │ ├── signup_params.dart
│ │ └── products_params.dart
│ ├── arguments
│ │ └── productsArgument.dart
│ ├── blocs
│ │ ├── bottom_nav_cubit
│ │ │ └── bottom_nav_cubit.dart
│ │ └── searchbox_cubit.dart
│ ├── utils
│ │ ├── custom_snackbar.dart
│ │ └── prefs_operator.dart
│ ├── resources
│ │ └── data_state.dart
│ ├── widgets
│ │ ├── dot_loading_widget.dart
│ │ ├── paging_loading_widget.dart
│ │ ├── main_appbar.dart
│ │ ├── main_wrapper.dart
│ │ └── bottom_nav.dart
│ └── error_handling
│ │ ├── app_exception.dart
│ │ └── check_exceptions.dart
├── features
│ ├── feature_home
│ │ ├── presentation
│ │ │ ├── utils
│ │ │ │ └── profile_list_model.dart
│ │ │ ├── bloc
│ │ │ │ ├── home_state.dart
│ │ │ │ ├── home_data_status.dart
│ │ │ │ └── home_cubit.dart
│ │ │ ├── widgets
│ │ │ │ ├── profile_list_tile.dart
│ │ │ │ └── deep_links.dart
│ │ │ └── screens
│ │ │ │ └── profile_screen.dart
│ │ ├── data
│ │ │ └── data_source
│ │ │ │ └── home_api_provider.dart
│ │ └── repositories
│ │ │ └── home_repository.dart
│ ├── feature_intro
│ │ ├── presentation
│ │ │ ├── bloc
│ │ │ │ ├── splash_cubit
│ │ │ │ │ ├── connection_status.dart
│ │ │ │ │ ├── splash_state.dart
│ │ │ │ │ └── splash_cubit.dart
│ │ │ │ └── intro_cubit
│ │ │ │ │ ├── intro_state.dart
│ │ │ │ │ └── intro_cubit.dart
│ │ │ ├── widgets
│ │ │ │ ├── get_start_btn.dart
│ │ │ │ └── intro_page.dart
│ │ │ └── screens
│ │ │ │ ├── splash_screen.dart
│ │ │ │ └── intro_main_wrapper.dart
│ │ └── repositories
│ │ │ └── splash_repository.dart
│ ├── feature_auth
│ │ ├── presentation
│ │ │ ├── bloc
│ │ │ │ ├── login_bloc
│ │ │ │ │ ├── login_event.dart
│ │ │ │ │ ├── code_check_status.dart
│ │ │ │ │ ├── login_data_status.dart
│ │ │ │ │ ├── login_state.dart
│ │ │ │ │ └── login_bloc.dart
│ │ │ │ └── signup_bloc
│ │ │ │ │ ├── signup_event.dart
│ │ │ │ │ ├── signup_data_status.dart
│ │ │ │ │ ├── call_code_status.dart
│ │ │ │ │ ├── signup_state.dart
│ │ │ │ │ └── signup_bloc.dart
│ │ │ └── widgets
│ │ │ │ └── cutom_clippath_signup.dart
│ │ ├── data
│ │ │ ├── models
│ │ │ │ ├── login_with_sms_model.dart
│ │ │ │ ├── code_model.dart
│ │ │ │ └── signup_model.dart
│ │ │ └── data_source
│ │ │ │ └── auth_api_provider.dart
│ │ └── repositories
│ │ │ └── auth_repository.dart
│ └── feature_product
│ │ ├── presentation
│ │ ├── bloc
│ │ │ ├── category_cubit
│ │ │ │ ├── category_state.dart
│ │ │ │ ├── category_data_status.dart
│ │ │ │ └── category_cubit.dart
│ │ │ └── all_poducts_cubit
│ │ │ │ ├── products_data_status.dart
│ │ │ │ ├── all_products_state.dart
│ │ │ │ └── all_products_cubit.dart
│ │ ├── screens
│ │ │ ├── all_products_screen.dart
│ │ │ └── category_screen.dart
│ │ └── widgets
│ │ │ ├── search_textfield.dart
│ │ │ └── products_grid.dart
│ │ ├── data
│ │ ├── data_source
│ │ │ ├── category_api_provider.dart
│ │ │ └── product_api_provider.dart
│ │ └── models
│ │ │ ├── categories_model.dart
│ │ │ └── all_products_model.dart
│ │ └── repositories
│ │ ├── category_repository.dart
│ │ └── all_product_repository.dart
├── test_screen.dart
├── config
│ ├── constants.dart
│ ├── responsive.dart
│ └── my_theme.dart
├── locator.dart
└── main.dart
├── README.md
├── .gitignore
├── test
└── widget_test.dart
├── .metadata
├── analysis_options.yaml
└── pubspec.yaml
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/web/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/web/favicon.png
--------------------------------------------------------------------------------
/assets/fonts/vazir.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/assets/fonts/vazir.ttf
--------------------------------------------------------------------------------
/assets/images/benz.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/assets/images/benz.png
--------------------------------------------------------------------------------
/assets/images/bmw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/assets/images/bmw.png
--------------------------------------------------------------------------------
/assets/images/box.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/assets/images/box.png
--------------------------------------------------------------------------------
/assets/images/tara.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/assets/images/tara.png
--------------------------------------------------------------------------------
/web/icons/Icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/web/icons/Icon-192.png
--------------------------------------------------------------------------------
/web/icons/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/web/icons/Icon-512.png
--------------------------------------------------------------------------------
/assets/images/qorfe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/assets/images/qorfe.png
--------------------------------------------------------------------------------
/assets/images/home_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/assets/images/home_icon.png
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/assets/images/home_icon2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/assets/images/home_icon2.png
--------------------------------------------------------------------------------
/macos/Runner/Configs/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "../../Flutter/Flutter-Debug.xcconfig"
2 | #include "Warnings.xcconfig"
3 |
--------------------------------------------------------------------------------
/assets/images/besenior_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/assets/images/besenior_logo.png
--------------------------------------------------------------------------------
/assets/images/category_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/assets/images/category_icon.png
--------------------------------------------------------------------------------
/assets/images/category_icon2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/assets/images/category_icon2.png
--------------------------------------------------------------------------------
/macos/Runner/Configs/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "../../Flutter/Flutter-Release.xcconfig"
2 | #include "Warnings.xcconfig"
3 |
--------------------------------------------------------------------------------
/web/icons/Icon-maskable-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/web/icons/Icon-maskable-192.png
--------------------------------------------------------------------------------
/web/icons/Icon-maskable-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/web/icons/Icon-maskable-512.png
--------------------------------------------------------------------------------
/macos/.gitignore:
--------------------------------------------------------------------------------
1 | # Flutter-related
2 | **/Flutter/ephemeral/
3 | **/Pods/
4 |
5 | # Xcode-related
6 | **/dgph
7 | **/xcuserdata/
8 |
--------------------------------------------------------------------------------
/assets/images/bs_logo_textfield.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/assets/images/bs_logo_textfield.png
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/macos/Flutter/Flutter-Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "ephemeral/Flutter-Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/macos/Flutter/Flutter-Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "ephemeral/Flutter-Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/lib/common/params/signup_params.dart:
--------------------------------------------------------------------------------
1 |
2 | class SignUpParams {
3 | String? username;
4 | String? phoneNumber;
5 |
6 | SignUpParams(this.username, this.phoneNumber);
7 | }
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mhmd435/besenior_shop_course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/macos/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/example/besenior_shop_course/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.besenior_shop_course
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/besenior/besenior_shop_course/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.besenior.besenior_shop_course
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/lib/common/arguments/productsArgument.dart:
--------------------------------------------------------------------------------
1 |
2 | class ProductsArguments {
3 | final int? categoryId;
4 | final int? sellerId;
5 | final String? searchTxt;
6 |
7 | ProductsArguments({this.categoryId, this.sellerId, this.searchTxt});
8 | }
--------------------------------------------------------------------------------
/lib/common/blocs/bottom_nav_cubit/bottom_nav_cubit.dart:
--------------------------------------------------------------------------------
1 | import 'package:bloc/bloc.dart';
2 |
3 | class BottomNavCubit extends Cubit {
4 | BottomNavCubit() : super(0);
5 |
6 | changeSelectedIndex(newIndex) => emit(newIndex);
7 | }
8 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
6 |
--------------------------------------------------------------------------------
/lib/common/blocs/searchbox_cubit.dart:
--------------------------------------------------------------------------------
1 | import 'package:bloc/bloc.dart';
2 |
3 |
4 | class SearchboxCubit extends Cubit {
5 | SearchboxCubit() : super(true);
6 |
7 | void changeVisibility(bool newValue){
8 | emit(newValue);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/macos/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import Cocoa
2 | import FlutterMacOS
3 |
4 | @NSApplicationMain
5 | class AppDelegate: FlutterAppDelegate {
6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
7 | return true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/macos/Runner/Release.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/features/feature_home/presentation/utils/profile_list_model.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:flutter/material.dart';
3 |
4 | class ProfileListModel {
5 | IconData iconData;
6 | String title;
7 | Function() onTap;
8 |
9 | ProfileListModel({required this.iconData,required this.title,required this.onTap});
10 |
11 |
12 | }
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/features/feature_intro/presentation/bloc/splash_cubit/connection_status.dart:
--------------------------------------------------------------------------------
1 | part of 'splash_cubit.dart';
2 |
3 | @immutable
4 | abstract class ConnectionStatus {}
5 |
6 | class ConnectionInitial extends ConnectionStatus {}
7 |
8 | class ConnectionOn extends ConnectionStatus {}
9 |
10 | class ConnectionOff extends ConnectionStatus {}
11 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/lib/common/params/products_params.dart:
--------------------------------------------------------------------------------
1 |
2 | class ProductsParams {
3 | int? start;
4 | int? step;
5 | int? categories;
6 | int? maxPrice;
7 | int? minPrice;
8 | String? sortBy;
9 | String? search;
10 |
11 | ProductsParams({this.start = 0, this.step = 10, this.categories, this.maxPrice, this.minPrice, this.sortBy = "date",this.search});
12 | }
--------------------------------------------------------------------------------
/lib/common/utils/custom_snackbar.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:flutter/material.dart';
3 |
4 | class CustomSnackBar {
5 | static showSnack(context, String message,Color color){
6 | ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message,style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontFamily: "Vazir"),),backgroundColor: color,));
7 | }
8 | }
--------------------------------------------------------------------------------
/lib/features/feature_auth/presentation/bloc/login_bloc/login_event.dart:
--------------------------------------------------------------------------------
1 | part of 'login_bloc.dart';
2 |
3 | @immutable
4 | abstract class LoginEvent {}
5 |
6 | class LoadLoginSms extends LoginEvent {
7 | final String phoneNumber;
8 | LoadLoginSms(this.phoneNumber);
9 | }
10 |
11 | class LoadCodeCheck extends LoginEvent {
12 | final String code;
13 | LoadCodeCheck(this.code);
14 | }
15 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/lib/features/feature_intro/presentation/bloc/intro_cubit/intro_state.dart:
--------------------------------------------------------------------------------
1 | part of 'intro_cubit.dart';
2 |
3 | class IntroState {
4 | bool showGetStart;
5 |
6 | IntroState({required this.showGetStart});
7 |
8 | IntroState copyWith({
9 | bool? newShowGetStart,
10 | }){
11 | return IntroState(
12 | showGetStart: newShowGetStart ?? showGetStart
13 | );
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/common/resources/data_state.dart:
--------------------------------------------------------------------------------
1 |
2 | abstract class DataState{
3 | final T? data;
4 | final String? error;
5 |
6 | const DataState(this.data, this.error);
7 | }
8 |
9 | class DataSuccess extends DataState{
10 | const DataSuccess(T data) : super(data, null);
11 | }
12 |
13 | class DataFailed extends DataState{
14 | const DataFailed(String error) : super(null, error);
15 | }
16 |
--------------------------------------------------------------------------------
/assets/images/person_icon2.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/features/feature_home/presentation/bloc/home_state.dart:
--------------------------------------------------------------------------------
1 | part of 'home_cubit.dart';
2 |
3 | class HomeState {
4 | HomeDataStatus homeDataStatus;
5 |
6 | HomeState({
7 | required this.homeDataStatus
8 | });
9 |
10 | HomeState copyWith({
11 | HomeDataStatus? newHomeDataStatus,
12 | }){
13 | return HomeState(
14 | homeDataStatus: newHomeDataStatus ?? homeDataStatus
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/features/feature_auth/presentation/bloc/signup_bloc/signup_event.dart:
--------------------------------------------------------------------------------
1 | part of 'signup_bloc.dart';
2 |
3 | @immutable
4 | abstract class SignupEvent {}
5 |
6 | class LoadSignUp extends SignupEvent {
7 | final SignUpParams signUpParams;
8 |
9 | LoadSignUp(this.signUpParams);
10 | }
11 |
12 | class LoadRegisterCodeCheck extends SignupEvent {
13 | final String mobile;
14 | LoadRegisterCodeCheck(this.mobile);
15 | }
16 |
--------------------------------------------------------------------------------
/lib/features/feature_intro/presentation/bloc/intro_cubit/intro_cubit.dart:
--------------------------------------------------------------------------------
1 | import 'package:bloc/bloc.dart';
2 | import 'package:meta/meta.dart';
3 |
4 | part 'intro_state.dart';
5 |
6 | class IntroCubit extends Cubit {
7 | IntroCubit() : super(
8 | IntroState(
9 | showGetStart: false,
10 | ));
11 |
12 | void changeGetStart(bool value) => emit(state.copyWith(newShowGetStart: value));
13 | }
14 |
--------------------------------------------------------------------------------
/macos/Runner/DebugProfile.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.cs.allow-jit
8 |
9 | com.apple.security.network.server
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/lib/test_screen.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:besenior_shop_course/common/widgets/bottom_nav.dart';
3 | import 'package:flutter/material.dart';
4 |
5 | class TestScreen extends StatelessWidget {
6 | static const routeName = "/test_screen";
7 |
8 | TestScreen({Key? key}) : super(key: key);
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | return Scaffold(
13 | appBar: AppBar(),
14 |
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/features/feature_intro/presentation/bloc/splash_cubit/splash_state.dart:
--------------------------------------------------------------------------------
1 | part of 'splash_cubit.dart';
2 |
3 | class SplashState {
4 | ConnectionStatus connectionStatus;
5 |
6 | SplashState({required this.connectionStatus});
7 |
8 | SplashState copyWith({
9 | ConnectionStatus? newConnectionStatus,
10 | }){
11 | return SplashState(
12 | connectionStatus: newConnectionStatus ?? connectionStatus
13 | );
14 | }
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/features/feature_product/presentation/bloc/category_cubit/category_state.dart:
--------------------------------------------------------------------------------
1 | part of 'category_cubit.dart';
2 |
3 | class CategoryState {
4 | CategoryDataStatus categoryDataStatus;
5 |
6 | CategoryState({required this.categoryDataStatus});
7 |
8 | CategoryState copyWith({
9 | CategoryDataStatus? newCategoryDataStatus,
10 | }){
11 | return CategoryState(
12 | categoryDataStatus: newCategoryDataStatus ?? categoryDataStatus
13 | );
14 | }
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/macos/Runner/MainFlutterWindow.swift:
--------------------------------------------------------------------------------
1 | import Cocoa
2 | import FlutterMacOS
3 |
4 | class MainFlutterWindow: NSWindow {
5 | override func awakeFromNib() {
6 | let flutterViewController = FlutterViewController.init()
7 | let windowFrame = self.frame
8 | self.contentViewController = flutterViewController
9 | self.setFrame(windowFrame, display: true)
10 |
11 | RegisterGeneratedPlugins(registry: flutterViewController)
12 |
13 | super.awakeFromNib()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/lib/features/feature_home/presentation/bloc/home_data_status.dart:
--------------------------------------------------------------------------------
1 | part of 'home_cubit.dart';
2 |
3 | @immutable
4 | abstract class HomeDataStatus {}
5 |
6 | class HomeDataInitial extends HomeDataStatus {}
7 |
8 | class HomeDataLoading extends HomeDataStatus {}
9 |
10 | class HomeDataCompleted extends HomeDataStatus {
11 | final HomeModel homeModel;
12 | HomeDataCompleted(this.homeModel);
13 | }
14 |
15 | class HomeDataError extends HomeDataStatus {
16 | final String errorMessage;
17 | HomeDataError(this.errorMessage);
18 | }
19 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/lib/config/constants.dart:
--------------------------------------------------------------------------------
1 |
2 | class Constants{
3 | static const baseUrl = "https://shopbs.besenior.ir/api/v1";
4 | // static const tapsellAppID = "gporbsshqsbpkbkmnmrranjjijddhmenocqtcagegeqgesliijajposlophsptcmfgqsif";
5 | // static const bannerAd = "6301c89827a9cb5ebea007b0";
6 | // static const championsBannerAd = "6301d64e9508ce4428a853d2";
7 | // static const buildVideoAd = "6301d8bc9e17585c89adbf03";
8 | // static const singleRuneBannerAd = "6301e0d28f178c2d6268df95";
9 | // static const itemsBannerAd = "6301ef6e8f178c2d6268dfa3";
10 |
11 | }
--------------------------------------------------------------------------------
/lib/features/feature_auth/presentation/bloc/login_bloc/code_check_status.dart:
--------------------------------------------------------------------------------
1 | part of 'login_bloc.dart';
2 |
3 | @immutable
4 | abstract class CodeCheckStatus {}
5 |
6 | class CodeCheckInitial extends CodeCheckStatus {}
7 |
8 | class CodeCheckLoading extends CodeCheckStatus {}
9 |
10 | class CodeCheckCompleted extends CodeCheckStatus {
11 | final CodeModel codeModel;
12 | CodeCheckCompleted(this.codeModel);
13 | }
14 |
15 | class CodeCheckError extends CodeCheckStatus {
16 | final String errorMessage;
17 | CodeCheckError(this.errorMessage);
18 | }
19 |
--------------------------------------------------------------------------------
/lib/features/feature_auth/presentation/bloc/signup_bloc/signup_data_status.dart:
--------------------------------------------------------------------------------
1 | part of 'signup_bloc.dart';
2 |
3 | @immutable
4 | abstract class SignUpDataStatus {}
5 |
6 | class SignUpDataInitial extends SignUpDataStatus {}
7 |
8 | class SignUpDataLoading extends SignUpDataStatus {}
9 |
10 | class SignUpCompleted extends SignUpDataStatus {
11 | final SignupModel signupModel;
12 | SignUpCompleted(this.signupModel);
13 | }
14 |
15 | class SignUpDataError extends SignUpDataStatus {
16 | final String errorMessage;
17 | SignUpDataError(this.errorMessage);
18 | }
19 |
--------------------------------------------------------------------------------
/lib/features/feature_auth/presentation/bloc/signup_bloc/call_code_status.dart:
--------------------------------------------------------------------------------
1 | part of 'signup_bloc.dart';
2 |
3 |
4 | @immutable
5 | abstract class CallCodeStatus {}
6 |
7 | class CallCodeInitial extends CallCodeStatus {}
8 |
9 | class CallCodeLoading extends CallCodeStatus {}
10 |
11 | class CallCodeCompleted extends CallCodeStatus {
12 | final LoginWithSmsModel loginWithSmsModel;
13 | CallCodeCompleted(this.loginWithSmsModel);
14 | }
15 |
16 | class CallCodeError extends CallCodeStatus {
17 | final String errorMessage;
18 | CallCodeError(this.errorMessage);
19 | }
20 |
--------------------------------------------------------------------------------
/lib/features/feature_auth/presentation/bloc/login_bloc/login_data_status.dart:
--------------------------------------------------------------------------------
1 | part of 'login_bloc.dart';
2 |
3 |
4 | @immutable
5 | abstract class LoginDataStatus {}
6 |
7 | class LoginDataInitial extends LoginDataStatus {}
8 |
9 | class LoginDataLoading extends LoginDataStatus {}
10 |
11 | class LoginDataCompleted extends LoginDataStatus {
12 | final LoginWithSmsModel loginWithSmsModel;
13 | LoginDataCompleted(this.loginWithSmsModel);
14 | }
15 |
16 | class LoginDataError extends LoginDataStatus {
17 | final String errorMessage;
18 | LoginDataError(this.errorMessage);
19 | }
20 |
--------------------------------------------------------------------------------
/assets/images/person_icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/features/feature_product/presentation/bloc/category_cubit/category_data_status.dart:
--------------------------------------------------------------------------------
1 | part of 'category_cubit.dart';
2 |
3 | @immutable
4 | abstract class CategoryDataStatus {}
5 |
6 | class CategoryDataInitial extends CategoryDataStatus {}
7 |
8 | class CategoryDataLoading extends CategoryDataStatus {}
9 |
10 | class CategoryDataCompleted extends CategoryDataStatus {
11 | final CategoriesModel categoriesModel;
12 | CategoryDataCompleted(this.categoriesModel);
13 | }
14 |
15 | class CategoryDataError extends CategoryDataStatus {
16 | final String errorMessage;
17 | CategoryDataError(this.errorMessage);
18 | }
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # besenior_shop_course
2 |
3 | A new Flutter project.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
13 |
14 | For help getting started with Flutter development, view the
15 | [online documentation](https://docs.flutter.dev/), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/lib/features/feature_product/presentation/bloc/all_poducts_cubit/products_data_status.dart:
--------------------------------------------------------------------------------
1 | part of 'all_products_cubit.dart';
2 |
3 | @immutable
4 | abstract class ProductsDataStatus {}
5 |
6 | class ProductsDataInitial extends ProductsDataStatus {}
7 |
8 | class ProductsDataLoading extends ProductsDataStatus {}
9 |
10 | class ProductsDataCompleted extends ProductsDataStatus {
11 | final AllProductsModel allProductsModel;
12 | ProductsDataCompleted(this.allProductsModel);
13 | }
14 |
15 | class ProductsDataError extends ProductsDataStatus {
16 | final String errorMessage;
17 | ProductsDataError(this.errorMessage);
18 | }
19 |
--------------------------------------------------------------------------------
/lib/features/feature_auth/presentation/bloc/login_bloc/login_state.dart:
--------------------------------------------------------------------------------
1 | part of 'login_bloc.dart';
2 |
3 | class LoginState {
4 | LoginDataStatus loginDataStatus;
5 | CodeCheckStatus codeCheckStatus;
6 |
7 | LoginState({
8 | required this.loginDataStatus,
9 | required this.codeCheckStatus,
10 | });
11 |
12 | LoginState copyWith({
13 | LoginDataStatus? newLoginDataStatus,
14 | CodeCheckStatus? newCodeCheckStatus,
15 | }){
16 | return LoginState(
17 | loginDataStatus: newLoginDataStatus ?? loginDataStatus,
18 | codeCheckStatus: newCodeCheckStatus ?? codeCheckStatus,
19 | );
20 | }
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/lib/features/feature_auth/presentation/bloc/signup_bloc/signup_state.dart:
--------------------------------------------------------------------------------
1 | part of 'signup_bloc.dart';
2 |
3 | class SignupState {
4 | SignUpDataStatus signUpDataStatus;
5 | CallCodeStatus callCodeStatus;
6 |
7 | SignupState({
8 | required this.signUpDataStatus,
9 | required this.callCodeStatus,
10 | });
11 |
12 | SignupState copyWith({
13 | SignUpDataStatus? newSignUpDataStatus,
14 | CallCodeStatus? newCallCodeStatus,
15 | }){
16 | return SignupState(
17 | signUpDataStatus: newSignUpDataStatus ?? signUpDataStatus,
18 | callCodeStatus: newCallCodeStatus ?? callCodeStatus,
19 | );
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/macos/Runner/Configs/Warnings.xcconfig:
--------------------------------------------------------------------------------
1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
2 | GCC_WARN_UNDECLARED_SELECTOR = YES
3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
6 | CLANG_WARN_PRAGMA_PACK = YES
7 | CLANG_WARN_STRICT_PROTOTYPES = YES
8 | CLANG_WARN_COMMA = YES
9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES
10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
12 | GCC_WARN_SHADOW = YES
13 | CLANG_WARN_UNREACHABLE_CODE = YES
14 |
--------------------------------------------------------------------------------
/lib/common/widgets/dot_loading_widget.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:flutter/material.dart';
3 | import 'package:loading_animation_widget/loading_animation_widget.dart';
4 |
5 | class DotLoadingWidget extends StatelessWidget {
6 | final double size;
7 | const DotLoadingWidget({Key? key,required this.size}) : super(key: key);
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | return Directionality(
12 | textDirection: TextDirection.ltr,
13 | child: Center(
14 | child: LoadingAnimationWidget.prograssiveDots(
15 | size: size,
16 | color: Colors.redAccent,
17 | ),
18 | ),
19 | );
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/lib/common/widgets/paging_loading_widget.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:flutter/material.dart';
3 | import 'package:loading_animation_widget/loading_animation_widget.dart';
4 |
5 | class PagingLoadingWidget extends StatelessWidget {
6 | final double size;
7 | const PagingLoadingWidget({Key? key,required this.size}) : super(key: key);
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | return Directionality(
12 | textDirection: TextDirection.ltr,
13 | child: Center(
14 | child: LoadingAnimationWidget.threeArchedCircle(
15 | size: size,
16 | color: Colors.redAccent,
17 | ),
18 | ),
19 | );
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/macos/Runner/Configs/AppInfo.xcconfig:
--------------------------------------------------------------------------------
1 | // Application-level settings for the Runner target.
2 | //
3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
4 | // future. If not, the values below would default to using the project name when this becomes a
5 | // 'flutter create' template.
6 |
7 | // The application's name. By default this is also the title of the Flutter window.
8 | PRODUCT_NAME = besenior_shop_course
9 |
10 | // The application's bundle identifier
11 | PRODUCT_BUNDLE_IDENTIFIER = com.besenior.beseniorShopCourse
12 |
13 | // The copyright displayed in application information
14 | PRODUCT_COPYRIGHT = Copyright © 2022 com.besenior. All rights reserved.
15 |
--------------------------------------------------------------------------------
/lib/features/feature_product/data/data_source/category_api_provider.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'dart:developer';
3 |
4 | import 'package:besenior_shop_course/common/error_handling/check_exceptions.dart';
5 | import 'package:besenior_shop_course/config/constants.dart';
6 | import 'package:dio/dio.dart';
7 |
8 | class CategoryApiProvider {
9 | Dio dio;
10 | CategoryApiProvider(this.dio);
11 |
12 | dynamic callCategories() async{
13 | try{
14 | final response = await dio.get(
15 | "${Constants.baseUrl}/categories/tree"
16 | );
17 | log(response.toString());
18 | return response;
19 | }on DioError catch(e){
20 | return CheckExceptions.response(e.response!);
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/lib/features/feature_auth/presentation/widgets/cutom_clippath_signup.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:flutter/cupertino.dart';
3 |
4 | class CustomClipPathSignUp extends CustomClipper {
5 |
6 | @override
7 | Path getClip(Size size) {
8 | double width = size.width;
9 | double height = size.height;
10 |
11 | final path = Path();
12 |
13 | path.lineTo(0, height - 50);
14 | path.quadraticBezierTo(width * 0.5, height, width, height - 50);
15 | path.lineTo(width, 0);
16 | path.close();
17 |
18 |
19 |
20 | return path;
21 | }
22 |
23 | @override
24 | bool shouldReclip(covariant CustomClipper oldClipper) {
25 | // TODO: implement shouldReclip
26 | return true;
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | **/dgph
2 | *.mode1v3
3 | *.mode2v3
4 | *.moved-aside
5 | *.pbxuser
6 | *.perspectivev3
7 | **/*sync/
8 | .sconsign.dblite
9 | .tags*
10 | **/.vagrant/
11 | **/DerivedData/
12 | Icon?
13 | **/Pods/
14 | **/.symlinks/
15 | profile
16 | xcuserdata
17 | **/.generated/
18 | Flutter/App.framework
19 | Flutter/Flutter.framework
20 | Flutter/Flutter.podspec
21 | Flutter/Generated.xcconfig
22 | Flutter/ephemeral/
23 | Flutter/app.flx
24 | Flutter/app.zip
25 | Flutter/flutter_assets/
26 | Flutter/flutter_export_environment.sh
27 | ServiceDefinitions.json
28 | Runner/GeneratedPluginRegistrant.*
29 |
30 | # Exceptions to above rules.
31 | !default.mode1v3
32 | !default.mode2v3
33 | !default.pbxuser
34 | !default.perspectivev3
35 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.6.10'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.1.2'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/lib/common/widgets/main_appbar.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:flutter/material.dart';
3 |
4 | class MainAppbar extends StatelessWidget with PreferredSizeWidget{
5 | final String title;
6 | const MainAppbar({Key? key, required this.title}) : super(key: key);
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | return AppBar(
11 | iconTheme: const IconThemeData(
12 | color: Colors.black
13 | ),
14 | backgroundColor: Colors.white,
15 | centerTitle: true,
16 | title: Text(
17 | title,
18 | style: const TextStyle(color: Colors.black),
19 | ),
20 | );
21 | }
22 |
23 | @override
24 | // TODO: implement preferredSize
25 | Size get preferredSize => const Size.fromHeight(60);
26 | }
27 |
--------------------------------------------------------------------------------
/lib/features/feature_home/data/data_source/home_api_provider.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'dart:developer';
3 |
4 | import 'package:besenior_shop_course/common/error_handling/check_exceptions.dart';
5 | import 'package:besenior_shop_course/config/constants.dart';
6 | import 'package:dio/dio.dart';
7 |
8 | class HomeApiProvider {
9 | Dio dio;
10 | HomeApiProvider(this.dio);
11 |
12 |
13 | dynamic callHomeData(lat, lon) async {
14 | final response = await dio.get(
15 | "${Constants.baseUrl}/mainData",
16 | queryParameters: {
17 | "lat" : lat,
18 | "long" : lon,
19 | }
20 | ).onError((DioError error, stackTrace){
21 | return CheckExceptions.response(error.response!);
22 | });
23 |
24 | log(response.toString());
25 |
26 | return response;
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/lib/features/feature_intro/repositories/splash_repository.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'dart:io';
3 |
4 | import 'package:connectivity_plus/connectivity_plus.dart';
5 |
6 | class SplashRepository {
7 |
8 | Future checkConnectivity() async {
9 |
10 | // try {
11 | // final result = await InternetAddress.lookup('example.com');
12 | // return result.isNotEmpty && result[0].rawAddress.isNotEmpty;
13 | // } on SocketException catch (_) {
14 | // return false;
15 | // }
16 |
17 | var connectivityResult = await (Connectivity().checkConnectivity());
18 | if (connectivityResult == ConnectivityResult.mobile) {
19 | return true;
20 | } else if (connectivityResult == ConnectivityResult.wifi) {
21 | return true;
22 | }else{
23 | return false;
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/macos/Flutter/GeneratedPluginRegistrant.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | import FlutterMacOS
6 | import Foundation
7 |
8 | import connectivity_plus
9 | import location
10 | import path_provider_foundation
11 | import shared_preferences_foundation
12 | import sqflite
13 |
14 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
15 | ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
16 | LocationPlugin.register(with: registry.registrar(forPlugin: "LocationPlugin"))
17 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
18 | SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
19 | SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
20 | }
21 |
--------------------------------------------------------------------------------
/lib/features/feature_home/presentation/widgets/profile_list_tile.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:flutter/material.dart';
3 |
4 | class ProfileListTile extends StatelessWidget {
5 | final IconData? iconData;
6 | final String title;
7 | final Function() onTap;
8 | final bool isLast;
9 | const ProfileListTile({Key? key, this.iconData = Icons.add,required this.title, required this.onTap,required this.isLast}) : super(key: key);
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return ListTile(
14 | onTap: onTap,
15 | title: Text(title, style: TextStyle(color: (isLast) ? Colors.red : Colors.grey.shade700,fontWeight: FontWeight.bold,fontSize: 14),),
16 | leading: Icon(iconData, color: (isLast) ? Colors.red : Colors.grey.shade700,),
17 | trailing: Icon(Icons.arrow_right, color: (isLast) ? Colors.red : Colors.grey.shade700,),
18 | );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | #.vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | **/doc/api/
26 | **/ios/Flutter/.last_build_id
27 | .dart_tool/
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
30 | .packages
31 | .pub-cache/
32 | .pub/
33 | /build/
34 |
35 | # Symbolication related
36 | app.*.symbols
37 |
38 | # Obfuscation related
39 | app.*.map.json
40 |
41 | # Android Studio will place build artifacts here
42 | /android/app/debug
43 | /android/app/profile
44 | /android/app/release
45 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 11.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/lib/features/feature_intro/presentation/bloc/splash_cubit/splash_cubit.dart:
--------------------------------------------------------------------------------
1 | import 'package:besenior_shop_course/features/feature_intro/repositories/splash_repository.dart';
2 | import 'package:bloc/bloc.dart';
3 | import 'package:meta/meta.dart';
4 |
5 | part 'splash_state.dart';
6 | part 'connection_status.dart';
7 |
8 | class SplashCubit extends Cubit {
9 | SplashRepository splashRepository = SplashRepository();
10 |
11 | SplashCubit() : super(
12 | SplashState(
13 | connectionStatus: ConnectionInitial()
14 | ));
15 |
16 | void checkConnectionEvent() async {
17 | emit(state.copyWith(newConnectionStatus: ConnectionInitial()));
18 |
19 | bool isConnect = await splashRepository.checkConnectivity();
20 |
21 | if(isConnect){
22 | emit(state.copyWith(newConnectionStatus: ConnectionOn()));
23 | }else{
24 | emit(state.copyWith(newConnectionStatus: ConnectionOff()));
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/features/feature_product/repositories/category_repository.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:besenior_shop_course/features/feature_product/data/models/categories_model.dart';
3 | import 'package:dio/dio.dart';
4 |
5 | import '../../../common/error_handling/app_exception.dart';
6 | import '../../../common/error_handling/check_exceptions.dart';
7 | import '../../../common/resources/data_state.dart';
8 | import '../data/data_source/category_api_provider.dart';
9 |
10 | class CategoryRepository {
11 | CategoryApiProvider apiProvider;
12 | CategoryRepository(this.apiProvider);
13 |
14 | Future> fetchCategoryData() async {
15 | try{
16 | Response response = await apiProvider.callCategories();
17 | final CategoriesModel categoriesModel = CategoriesModel.fromJson(response.data);
18 | return DataSuccess(categoriesModel);
19 | } on AppException catch(e){
20 | return await CheckExceptions.getError(e);
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/lib/features/feature_product/presentation/bloc/all_poducts_cubit/all_products_state.dart:
--------------------------------------------------------------------------------
1 | part of 'all_products_cubit.dart';
2 |
3 | class AllProductsState {
4 | ProductsDataStatus productsDataStatus;
5 | List allProducts;
6 | int nextStart;
7 | bool isLoadingPaging;
8 |
9 | AllProductsState({
10 | required this.productsDataStatus,
11 | required this.allProducts,
12 | required this.nextStart,
13 | required this.isLoadingPaging,
14 | });
15 |
16 | AllProductsState copyWith({
17 | ProductsDataStatus? newProductsDataStatus,
18 | List? newAllProducts,
19 | int? newNextStart,
20 | bool? newIsLoadingPaging,
21 | }){
22 | return AllProductsState(
23 | productsDataStatus: newProductsDataStatus ?? productsDataStatus,
24 | allProducts: newAllProducts ?? allProducts,
25 | nextStart: newNextStart ?? nextStart,
26 | isLoadingPaging: newIsLoadingPaging ?? isLoadingPaging,
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/features/feature_home/repositories/home_repository.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | import 'package:besenior_shop_course/common/error_handling/app_exception.dart';
4 | import 'package:besenior_shop_course/common/error_handling/check_exceptions.dart';
5 | import 'package:besenior_shop_course/features/feature_home/data/data_source/home_api_provider.dart';
6 | import 'package:besenior_shop_course/features/feature_home/data/models/home_model.dart';
7 | import 'package:dio/dio.dart';
8 |
9 | import '../../../common/resources/data_state.dart';
10 |
11 | class HomeRepository {
12 | HomeApiProvider apiProvider;
13 | HomeRepository(this.apiProvider);
14 |
15 | Future> fetchHomeData(lat, lon) async {
16 | try{
17 | Response response = await apiProvider.callHomeData(lat, lon);
18 | final HomeModel homeModel = HomeModel.fromJson(response.data);
19 | return DataSuccess(homeModel);
20 | } on AppException catch(e){
21 | return await CheckExceptions.getError(e);
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/web/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "besenior_shop_course",
3 | "short_name": "besenior_shop_course",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "background_color": "#0175C2",
7 | "theme_color": "#0175C2",
8 | "description": "A new Flutter project.",
9 | "orientation": "portrait-primary",
10 | "prefer_related_applications": false,
11 | "icons": [
12 | {
13 | "src": "icons/Icon-192.png",
14 | "sizes": "192x192",
15 | "type": "image/png"
16 | },
17 | {
18 | "src": "icons/Icon-512.png",
19 | "sizes": "512x512",
20 | "type": "image/png"
21 | },
22 | {
23 | "src": "icons/Icon-maskable-192.png",
24 | "sizes": "192x192",
25 | "type": "image/png",
26 | "purpose": "maskable"
27 | },
28 | {
29 | "src": "icons/Icon-maskable-512.png",
30 | "sizes": "512x512",
31 | "type": "image/png",
32 | "purpose": "maskable"
33 | }
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/lib/features/feature_home/presentation/bloc/home_cubit.dart:
--------------------------------------------------------------------------------
1 | import 'package:besenior_shop_course/common/resources/data_state.dart';
2 | import 'package:bloc/bloc.dart';
3 | import 'package:meta/meta.dart';
4 |
5 | import '../../data/models/home_model.dart';
6 | import '../../repositories/home_repository.dart';
7 |
8 | part 'home_state.dart';
9 | part 'home_data_status.dart';
10 |
11 | class HomeCubit extends Cubit {
12 | HomeRepository homeRepository;
13 | HomeCubit(this.homeRepository) : super(HomeState(homeDataStatus: HomeDataLoading()));
14 |
15 | Future callHomeDataEvent(lat, lon) async {
16 | emit(state.copyWith(newHomeDataStatus: HomeDataLoading()));
17 |
18 | DataState dataState = await homeRepository.fetchHomeData(lat, lon);
19 |
20 | if(dataState is DataSuccess){
21 | /// emit completed
22 | emit(state.copyWith(newHomeDataStatus: HomeDataCompleted(dataState.data)));
23 | }
24 |
25 | if(dataState is DataFailed){
26 | /// emit error
27 | emit(state.copyWith(newHomeDataStatus: HomeDataError(dataState.error!)));
28 | }
29 |
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/lib/features/feature_product/data/data_source/product_api_provider.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:dio/dio.dart';
3 |
4 | import '../../../../common/error_handling/check_exceptions.dart';
5 | import '../../../../common/params/products_params.dart';
6 | import '../../../../config/constants.dart';
7 |
8 | class ProductsApiProvider {
9 | Dio dio;
10 | ProductsApiProvider(this.dio);
11 |
12 | dynamic callAllProducts(ProductsParams productsParams) async {
13 | try{
14 |
15 | print(productsParams.categories);
16 |
17 | final response = await dio.post(
18 | "${Constants.baseUrl}/products",
19 | data: {
20 | "start": productsParams.start,
21 | "step": productsParams.step,
22 | "categories": productsParams.categories,
23 | "maxPrice": productsParams.maxPrice,
24 | "minPrice": productsParams.minPrice,
25 | "sortby": productsParams.sortBy,
26 | 'search': productsParams.search
27 | },
28 | );
29 |
30 | print(response);
31 | return response;
32 | } on DioError catch(e) {
33 | print(e.toString());
34 | return CheckExceptions.response(e.response!);
35 | }
36 |
37 | }
38 | }
--------------------------------------------------------------------------------
/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility in the flutter_test package. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:besenior_shop_course/main.dart';
12 |
13 | void main() {
14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(const MyApp());
17 |
18 | // Verify that our counter starts at 0.
19 | expect(find.text('0'), findsOneWidget);
20 | expect(find.text('1'), findsNothing);
21 |
22 | // Tap the '+' icon and trigger a frame.
23 | await tester.tap(find.byIcon(Icons.add));
24 | await tester.pump();
25 |
26 | // Verify that our counter has incremented.
27 | expect(find.text('0'), findsNothing);
28 | expect(find.text('1'), findsOneWidget);
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/macos/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(FLUTTER_BUILD_NAME)
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | $(PRODUCT_COPYRIGHT)
27 | NSMainNibFile
28 | MainMenu
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/lib/features/feature_product/presentation/bloc/category_cubit/category_cubit.dart:
--------------------------------------------------------------------------------
1 | import 'package:bloc/bloc.dart';
2 | import 'package:meta/meta.dart';
3 |
4 | import '../../../../../common/resources/data_state.dart';
5 | import '../../../data/models/categories_model.dart';
6 | import '../../../repositories/category_repository.dart';
7 |
8 | part 'category_state.dart';
9 | part 'category_data_status.dart';
10 |
11 | class CategoryCubit extends Cubit {
12 | CategoryRepository categoryRepository;
13 | CategoryCubit(this.categoryRepository) : super(CategoryState(categoryDataStatus: CategoryDataLoading()));
14 |
15 |
16 | Future loadCategoryEvent() async {
17 | /// emit loading
18 | emit(state.copyWith(newCategoryDataStatus: CategoryDataLoading()));
19 |
20 | DataState dataState = await categoryRepository.fetchCategoryData();
21 |
22 | if(dataState is DataSuccess){
23 | /// emit completed
24 | emit(state.copyWith(newCategoryDataStatus: CategoryDataCompleted(dataState.data)));
25 | }
26 |
27 | if(dataState is DataFailed){
28 | /// emit error
29 | emit(state.copyWith(newCategoryDataStatus: CategoryDataError(dataState.error!)));
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/common/utils/prefs_operator.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:shared_preferences/shared_preferences.dart';
3 | import '../../locator.dart';
4 |
5 | class PrefsOperator {
6 | late SharedPreferences sharedPreferences;
7 | PrefsOperator() {
8 | sharedPreferences = locator();
9 | }
10 |
11 |
12 | saveUserData(token, userName, mobile) async {
13 | sharedPreferences.setString("user_token", token);
14 | sharedPreferences.setString("user_name", userName);
15 | sharedPreferences.setString("user_mobile", mobile);
16 | sharedPreferences.setBool("loggedIn", true);
17 | }
18 |
19 | getUserToken() async {
20 | final String? userToken = sharedPreferences.getString('user_token');
21 | return userToken;
22 | }
23 |
24 | changeIntroState() async {
25 | sharedPreferences.setBool("showIntro", false);
26 | }
27 |
28 | Future getIntroState() async {
29 | return sharedPreferences.getBool("showIntro") ?? true;
30 | }
31 |
32 | Future getLoggedIn() async {
33 | return sharedPreferences.getBool("loggedIn") ?? false;
34 | }
35 |
36 | Future logout() async {
37 | sharedPreferences.clear();
38 | sharedPreferences.setBool("showIntro", false);
39 | }
40 |
41 | }
--------------------------------------------------------------------------------
/lib/features/feature_auth/data/models/login_with_sms_model.dart:
--------------------------------------------------------------------------------
1 | /// message : "کد با موفقیت ارسال شد"
2 | /// status : "success"
3 | /// data : {"code":963036,"hash":""}
4 |
5 | class LoginWithSmsModel {
6 | LoginWithSmsModel({
7 | this.message,
8 | this.status,
9 | this.data,});
10 |
11 | LoginWithSmsModel.fromJson(dynamic json) {
12 | message = json['message'];
13 | status = json['status'];
14 | data = json['data'] != null ? Data.fromJson(json['data']) : null;
15 | }
16 | String? message;
17 | String? status;
18 | Data? data;
19 |
20 | Map toJson() {
21 | final map = {};
22 | map['message'] = message;
23 | map['status'] = status;
24 | if (data != null) {
25 | map['data'] = data?.toJson();
26 | }
27 | return map;
28 | }
29 |
30 | }
31 |
32 | /// code : 963036
33 | /// hash : ""
34 |
35 | class Data {
36 | Data({
37 | this.code,
38 | this.hash,});
39 |
40 | Data.fromJson(dynamic json) {
41 | code = json['code'];
42 | hash = json['hash'];
43 | }
44 | int? code;
45 | String? hash;
46 |
47 | Map toJson() {
48 | final map = {};
49 | map['code'] = code;
50 | map['hash'] = hash;
51 | return map;
52 | }
53 |
54 | }
--------------------------------------------------------------------------------
/lib/features/feature_auth/data/models/code_model.dart:
--------------------------------------------------------------------------------
1 | /// access_token : "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvbml5YXouc2hvcFwvYXBpXC92MVwvYXV0aFwvbG9naW5XaXRoU21zXC9jaGVjayIsImlhdCI6MTY2NTMxMjA3NywiZXhwIjoxNjY1MzE1Njc3LCJuYmYiOjE2NjUzMTIwNzcsImp0aSI6IlFJZWgxOXMzWXh0Q3lLN3ciLCJzdWIiOjksInBydiI6Ijg3ZTBhZjFlZjlmZDE1ODEyZmRlYzk3MTUzYTE0ZTBiMDQ3NTQ2YWEifQ.dY6P6LFVtw6XwB50nG_pcnruUkPo9ddvCdbaLpnkBvc"
2 | /// token_type : "bearer"
3 | /// expires_in : 3600
4 |
5 | class CodeModel {
6 | CodeModel({
7 | this.accessToken,
8 | this.name,
9 | this.mobile,
10 | this.tokenType,
11 | this.expiresIn,});
12 |
13 | CodeModel.fromJson(dynamic json) {
14 | accessToken = json['access_token'];
15 | name = json['name'];
16 | mobile = json['mobile'];
17 | tokenType = json['token_type'];
18 | expiresIn = json['expires_in'];
19 | }
20 | String? accessToken;
21 | String? name;
22 | String? mobile;
23 | String? tokenType;
24 | int? expiresIn;
25 |
26 | Map toJson() {
27 | final map = {};
28 | map['access_token'] = accessToken;
29 | map['name'] = name;
30 | map['mobile'] = mobile;
31 | map['token_type'] = tokenType;
32 | map['expires_in'] = expiresIn;
33 | return map;
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/lib/features/feature_product/presentation/screens/all_products_screen.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:besenior_shop_course/features/feature_product/presentation/bloc/all_poducts_cubit/all_products_cubit.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_bloc/flutter_bloc.dart';
5 |
6 | import '../../../../common/arguments/productsArgument.dart';
7 | import '../../../../common/widgets/main_appbar.dart';
8 | import '../../../../locator.dart';
9 | import '../widgets/products_grid.dart';
10 |
11 | class AllProductsScreen extends StatelessWidget {
12 | static const routeName = '/all_product_screen';
13 |
14 | const AllProductsScreen({Key? key}) : super(key: key);
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 |
19 | /// get categoryId
20 | final arg = ModalRoute.of(context)!.settings.arguments as ProductsArguments;
21 |
22 | return MultiBlocProvider(
23 | providers: [
24 | BlocProvider(create: (_) => AllProductsCubit(locator())),
25 | // BlocProvider(create: (_) => FilterCubit()),
26 | ],
27 | child: Scaffold(
28 |
29 | appBar: const MainAppbar(title: 'محصولات',),
30 |
31 | body: ProductsGrid(categoryId: arg.categoryId, searchText: arg.searchTxt, sellerId: arg.sellerId,),
32 | ),
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/common/error_handling/app_exception.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:dio/dio.dart';
3 |
4 | class AppException implements Exception {
5 | final message;
6 | Response? response;
7 |
8 | AppException({required this.message,this.response});
9 |
10 | String getMessage() {
11 | return "$message";
12 | }
13 | }
14 |
15 | class ServerException extends AppException {
16 | ServerException({String? message}) : super(message: message ?? "مشکلی پیش آمده لطفا دوباره امتحان کنید.");
17 | }
18 |
19 | class NotFoundException extends AppException {
20 | NotFoundException({String? message}) : super(message: message ?? "صفحه مورد نظر یافت نشد.");
21 | }
22 |
23 | class DataParsingException extends AppException {
24 | DataParsingException({String? message}) : super(message: message ?? "Data has Corrupted");
25 | }
26 |
27 | class BadRequestException extends AppException {
28 | BadRequestException({String? message,Response? response}) : super(message: message ?? "bad request exception.",response: response);
29 | }
30 |
31 | class FetchDataException extends AppException {
32 | FetchDataException({String? message}) : super(message: message ?? "please check your connection...");
33 | }
34 |
35 | class UnauthorisedException extends AppException {
36 | UnauthorisedException({String? message}) : super(message: message ?? "token has been expired.");
37 | }
38 |
--------------------------------------------------------------------------------
/lib/config/responsive.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:flutter/material.dart';
3 |
4 | class Responsive extends StatelessWidget {
5 | final Widget mobile;
6 | final Widget? tablet;
7 | final Widget? desktop;
8 |
9 | const Responsive({
10 | super.key,
11 | required this.mobile,
12 | this.tablet,
13 | this.desktop,
14 | });
15 |
16 | /// This isMobile, isTablet, isDesktop helep us later
17 | static bool isMobile(BuildContext context) =>
18 | MediaQuery.of(context).size.width < 600;
19 |
20 | static bool isTablet(BuildContext context) =>
21 | MediaQuery.of(context).size.width < 1100 &&
22 | MediaQuery.of(context).size.width >= 600;
23 |
24 | static bool isDesktop(BuildContext context) =>
25 | MediaQuery.of(context).size.width >= 1100;
26 |
27 |
28 | @override
29 | Widget build(BuildContext context) {
30 | final Size size = MediaQuery.of(context).size;
31 | /// If our width is more than 1100 then we consider it a desktop
32 | if (size.width >= 1100 && desktop != null) {
33 | return desktop!;
34 | }
35 | /// If width it less then 1100 and more then 850 we consider it as tablet
36 | else if (size.width >= 600 && tablet != null) {
37 | return tablet!;
38 | }
39 | /// Or less then that we called it mobile
40 | else {
41 | return mobile;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/lib/features/feature_product/repositories/all_product_repository.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:besenior_shop_course/common/error_handling/check_exceptions.dart';
3 | import 'package:dio/dio.dart';
4 |
5 | import '../../../common/error_handling/app_exception.dart';
6 | import '../../../common/params/products_params.dart';
7 | import '../../../common/resources/data_state.dart';
8 | import '../data/data_source/product_api_provider.dart';
9 | import '../data/models/all_products_model.dart';
10 |
11 | class AllProductsRepository {
12 | ProductsApiProvider apiProvider;
13 | AllProductsRepository(this.apiProvider);
14 |
15 | Future fetchAllProductsData(ProductsParams productsParams) async {
16 | try{
17 | // convert json to models class
18 | Response response = await apiProvider.callAllProducts(productsParams);
19 | final AllProductsModel allProductsModel = AllProductsModel.fromJson(response.data);
20 | return DataSuccess(allProductsModel);
21 | } on AppException catch(e){
22 | print(e);
23 | return CheckExceptions.getError(e);
24 | }
25 | }
26 |
27 | Future> fetchAllProductsDataSearch(ProductsParams productsParams) async {
28 | Response response = await apiProvider.callAllProducts(productsParams);
29 | final AllProductsModel allProductsModel = AllProductsModel.fromJson(response.data);
30 | return allProductsModel.data![0].products!;
31 | }
32 | }
--------------------------------------------------------------------------------
/lib/common/error_handling/check_exceptions.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:dio/dio.dart';
3 | import '../resources/data_state.dart';
4 | import 'app_exception.dart';
5 |
6 | class CheckExceptions {
7 |
8 | static dynamic response(Response response) {
9 | switch (response.statusCode) {
10 | case 200:
11 | return response;
12 | case 400:
13 | throw BadRequestException(response: response);
14 | case 401:
15 | throw UnauthorisedException();
16 | case 404:
17 | throw NotFoundException();
18 | case 500:
19 | throw ServerException();
20 | default:
21 | throw FetchDataException(message: "${response.statusCode}fetch exception");
22 | }
23 | }
24 |
25 | static dynamic getError(AppException appException) async {
26 | switch (appException.runtimeType) {
27 | /// return error came from server
28 | case BadRequestException:
29 | return DataFailed(appException.message);
30 |
31 | case NotFoundException:
32 | return DataFailed(appException.message);
33 | /// get refresh token and call api again
34 | case UnauthorisedException:
35 | return DataFailed(appException.message);
36 |
37 | /// server error
38 | case ServerException:
39 | return DataFailed(appException.message);
40 |
41 | /// dio or timeout and etc error
42 | case FetchDataException:
43 | return DataFailed(appException.message);
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/macos/Podfile:
--------------------------------------------------------------------------------
1 | platform :osx, '10.11'
2 |
3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
5 |
6 | project 'Runner', {
7 | 'Debug' => :debug,
8 | 'Profile' => :release,
9 | 'Release' => :release,
10 | }
11 |
12 | def flutter_root
13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
14 | unless File.exist?(generated_xcode_build_settings_path)
15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
16 | end
17 |
18 | File.foreach(generated_xcode_build_settings_path) do |line|
19 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
20 | return matches[1].strip if matches
21 | end
22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
23 | end
24 |
25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
26 |
27 | flutter_macos_podfile_setup
28 |
29 | target 'Runner' do
30 | use_frameworks!
31 | use_modular_headers!
32 |
33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
34 | end
35 |
36 | post_install do |installer|
37 | installer.pods_project.targets.each do |target|
38 | flutter_additional_macos_build_settings(target)
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '11.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | end
36 |
37 | post_install do |installer|
38 | installer.pods_project.targets.each do |target|
39 | flutter_additional_ios_build_settings(target)
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled.
5 |
6 | version:
7 | revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
8 | channel: stable
9 |
10 | project_type: app
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
17 | base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
18 | - platform: android
19 | create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
20 | base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
21 | - platform: ios
22 | create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
23 | base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
24 | - platform: macos
25 | create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
26 | base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
27 | - platform: web
28 | create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
29 | base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
30 |
31 | # User provided section
32 |
33 | # List of Local paths (relative to this file) that should be
34 | # ignored by the migrate tool.
35 | #
36 | # Files that are not part of the templates will be ignored by default.
37 | unmanaged_files:
38 | - 'lib/main.dart'
39 | - 'ios/Runner.xcodeproj/project.pbxproj'
40 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at
17 | # https://dart-lang.github.io/linter/lints/index.html.
18 | #
19 | # Instead of disabling a lint rule for the entire project in the
20 | # section below, it can also be suppressed for a single line of code
21 | # or a specific dart file by using the `// ignore: name_of_lint` and
22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
23 | # producing the lint.
24 | rules:
25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
27 |
28 | # Additional information about this file can be found at
29 | # https://dart.dev/guides/language/analysis-options
30 |
--------------------------------------------------------------------------------
/lib/features/feature_home/presentation/widgets/deep_links.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:cached_network_image/cached_network_image.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:shimmer/shimmer.dart';
5 |
6 | import '../../../../common/widgets/dot_loading_widget.dart';
7 | import '../../../../config/responsive.dart';
8 |
9 | class DeepLinks extends StatelessWidget {
10 | final String image;
11 | final String title;
12 | const DeepLinks({Key? key, required this.image, required this.title}) : super(key: key);
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | var width = MediaQuery.of(context).size.width;
17 |
18 |
19 | return Column(
20 | crossAxisAlignment: CrossAxisAlignment.center,
21 | children: [
22 | SizedBox(
23 | width: 70,
24 | child: ClipRRect(
25 | borderRadius: BorderRadius.circular(15),
26 | child: CachedNetworkImage(
27 | imageUrl: image,
28 | placeholder: (context, string){
29 | return Shimmer.fromColors(
30 | baseColor: Colors.white,
31 | highlightColor: Colors.grey,
32 | child: const SizedBox(height: 50,width: 50,));
33 | },
34 | fit: BoxFit.cover,
35 | useOldImageOnUrlChange: true,
36 | ),
37 | ),
38 | ),
39 | const SizedBox(height: 5,),
40 | Text(title, style: TextStyle(fontFamily: 'Vazir', fontSize: Responsive.isMobile(context) ? 11 : 18),)
41 | ],
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "16x16",
5 | "idiom" : "mac",
6 | "filename" : "app_icon_16.png",
7 | "scale" : "1x"
8 | },
9 | {
10 | "size" : "16x16",
11 | "idiom" : "mac",
12 | "filename" : "app_icon_32.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "32x32",
17 | "idiom" : "mac",
18 | "filename" : "app_icon_32.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "32x32",
23 | "idiom" : "mac",
24 | "filename" : "app_icon_64.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "128x128",
29 | "idiom" : "mac",
30 | "filename" : "app_icon_128.png",
31 | "scale" : "1x"
32 | },
33 | {
34 | "size" : "128x128",
35 | "idiom" : "mac",
36 | "filename" : "app_icon_256.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "256x256",
41 | "idiom" : "mac",
42 | "filename" : "app_icon_256.png",
43 | "scale" : "1x"
44 | },
45 | {
46 | "size" : "256x256",
47 | "idiom" : "mac",
48 | "filename" : "app_icon_512.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "512x512",
53 | "idiom" : "mac",
54 | "filename" : "app_icon_512.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "512x512",
59 | "idiom" : "mac",
60 | "filename" : "app_icon_1024.png",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/lib/features/feature_home/presentation/screens/profile_screen.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:besenior_shop_course/features/feature_home/presentation/widgets/profile_list_tile.dart';
3 | import 'package:flutter/cupertino.dart';
4 | import 'package:flutter/material.dart';
5 |
6 | import '../utils/profile_list_model.dart';
7 |
8 | class ProfileScreen extends StatelessWidget {
9 | ProfileScreen({Key? key}) : super(key: key);
10 |
11 | List profileList = [
12 | ProfileListModel(iconData: Icons.person, title: "حساب کاربری شخصی", onTap: (){print("item1");}),
13 | ProfileListModel(iconData: Icons.shopping_bag_outlined, title: "حساب کاربری فروشگاهی", onTap: (){print("item2");}),
14 | ProfileListModel(iconData: CupertinoIcons.archivebox_fill, title: "سفارشات", onTap: (){print("item3");}),
15 | ProfileListModel(iconData: Icons.home_work, title: "آدرس من", onTap: (){print("item4");}),
16 | ProfileListModel(iconData: Icons.support_agent, title: "پشتیبانی", onTap: (){print("item5");}),
17 | ProfileListModel(iconData: Icons.exit_to_app, title: "خروج از حساب", onTap: (){print("item6");}),
18 | ];
19 |
20 |
21 | @override
22 | Widget build(BuildContext context) {
23 | return Column(
24 | mainAxisAlignment: MainAxisAlignment.center,
25 | children: [
26 | ListView.builder(
27 | shrinkWrap: true,
28 | itemCount: profileList.length,
29 | itemBuilder: (context, index){
30 | return ProfileListTile(
31 | iconData: profileList[index].iconData,
32 | title: profileList[index].title,
33 | onTap: profileList[index].onTap,
34 | isLast: (index == profileList.length - 1) ? true : false
35 | );
36 | }
37 | ),
38 | ],
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/lib/features/feature_intro/presentation/widgets/get_start_btn.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:flutter/material.dart';
3 |
4 | class GetStartBtn extends StatefulWidget {
5 | final String text;
6 | final Function onTap;
7 | const GetStartBtn({Key? key, required this.text, required this.onTap}) : super(key: key);
8 |
9 | @override
10 | State createState() => _GetStartBtnState();
11 | }
12 |
13 | class _GetStartBtnState extends State with TickerProviderStateMixin{
14 |
15 | late AnimationController fadeController;
16 | late Animation fadeAnimation;
17 |
18 | @override
19 | void initState() {
20 | // TODO: implement initState
21 | super.initState();
22 |
23 | fadeController = AnimationController(
24 | value: 0,
25 | duration: const Duration(milliseconds: 2000),
26 | vsync: this,
27 | );
28 | fadeAnimation = CurvedAnimation(parent: fadeController, curve: Curves.ease);
29 | fadeController.forward();
30 | }
31 |
32 | @override
33 | void dispose() {
34 | fadeController.dispose();
35 |
36 | // TODO: implement dispose
37 | super.dispose();
38 | }
39 |
40 | @override
41 | Widget build(BuildContext context) {
42 | return FadeTransition(
43 | opacity: fadeAnimation,
44 | child: SizedBox(
45 | height: 45,
46 | width: 120,
47 | child: ElevatedButton(
48 | style: ElevatedButton.styleFrom(
49 | backgroundColor: Colors.amber,
50 | shape: RoundedRectangleBorder(
51 | borderRadius: BorderRadius.circular(20), // <-- Radius
52 | ),
53 | ),
54 | onPressed: (){
55 | widget.onTap();
56 | },
57 | child: Text(widget.text, style: const TextStyle(fontSize: 17,color: Colors.black,fontWeight: FontWeight.w500, fontFamily: 'Vazir'),),),
58 | ),
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/lib/config/my_theme.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:flutter/material.dart';
3 |
4 | class MyThemes {
5 | static final darkTheme = ThemeData(
6 | textTheme: const TextTheme(
7 | titleMedium: TextStyle(fontFamily: "Vazir",fontSize: 20,fontWeight: FontWeight.bold,),
8 | bodyMedium: TextStyle(fontFamily: "Vazir",fontSize: 15,fontWeight: FontWeight.w400),
9 | ),
10 | // highlightColor: Colors.indigo,
11 | // backgroundColor: Colors.black,
12 | // canvasColor: Colors.grey,
13 | // unselectedWidgetColor: Colors.white70,
14 | // primaryColorLight: Color.fromRGBO(252, 178, 98, 1),
15 | // scaffoldBackgroundColor: Colors.grey.shade900,
16 | // primaryColor: Colors.amber.shade800,
17 | // indicatorColor: Colors.amber,
18 | // secondaryHeaderColor: Color.fromRGBO(176, 106, 2, 1),
19 | // iconTheme: IconThemeData(color: Colors.amber.shade800),
20 | // textSelectionTheme: const TextSelectionThemeData(
21 | // cursorColor: Colors.red,
22 | // selectionColor: Colors.green,
23 | // selectionHandleColor: Colors.blue,
24 | // )
25 | // colorScheme: const ColorScheme.dark()
26 | );
27 |
28 | static final lightTheme = ThemeData(
29 | textTheme: const TextTheme(
30 | titleMedium: TextStyle(fontFamily: "Vazir",fontSize: 20,fontWeight: FontWeight.bold),
31 | bodyMedium: TextStyle(fontFamily: "Vazir",fontSize: 15,fontWeight: FontWeight.w400),
32 | ),
33 | // highlightColor: Colors.indigo,
34 | // backgroundColor: Colors.black,
35 | // unselectedWidgetColor: Colors.black,
36 | // primaryColorLight: Color.fromRGBO(252, 178, 98, 1),
37 | // scaffoldBackgroundColor: Colors.white,
38 | // primaryColor: Colors.amber.shade800,
39 | // indicatorColor: Colors.amber,
40 | // secondaryHeaderColor: Color.fromRGBO(176, 106, 2, 1),
41 | // iconTheme: IconThemeData(color: Colors.amber.shade800),
42 | //
43 | // // colorScheme: const ColorScheme.light()
44 | );
45 | }
--------------------------------------------------------------------------------
/lib/features/feature_auth/data/models/signup_model.dart:
--------------------------------------------------------------------------------
1 | /// message : "ثبت نام شما با موفقیت انجام شد"
2 | /// status : "success"
3 | /// data : {"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvbml5YXouc2hvcFwvYXBpXC92MVwvcmVnaXN0ZXIiLCJpYXQiOjE2NjUzMDM0NTksImV4cCI6MTY2NTMwNzA1OSwibmJmIjoxNjY1MzAzNDU5LCJqdGkiOiJ3SGpaOXk1SGdTaU5hSHJqIiwic3ViIjoxMCwicHJ2IjoiODdlMGFmMWVmOWZkMTU4MTJmZGVjOTcxNTNhMTRlMGIwNDc1NDZhYSJ9.dRWrY2LQ5JppeGcKWoyAx--QUORBkNuz2JcGjPO84oU"}
4 |
5 | class SignupModel {
6 | SignupModel({
7 | this.message,
8 | this.status,
9 | this.data,});
10 |
11 | SignupModel.fromJson(dynamic json) {
12 | message = json['message'];
13 | status = json['status'];
14 | data = json['data'] != null ? Data.fromJson(json['data']) : null;
15 | }
16 | String? message;
17 | String? status;
18 | Data? data;
19 |
20 | Map toJson() {
21 | final map = {};
22 | map['message'] = message;
23 | map['status'] = status;
24 | if (data != null) {
25 | map['data'] = data?.toJson();
26 | }
27 | return map;
28 | }
29 |
30 | }
31 |
32 | /// token : "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvbml5YXouc2hvcFwvYXBpXC92MVwvcmVnaXN0ZXIiLCJpYXQiOjE2NjUzMDM0NTksImV4cCI6MTY2NTMwNzA1OSwibmJmIjoxNjY1MzAzNDU5LCJqdGkiOiJ3SGpaOXk1SGdTaU5hSHJqIiwic3ViIjoxMCwicHJ2IjoiODdlMGFmMWVmOWZkMTU4MTJmZGVjOTcxNTNhMTRlMGIwNDc1NDZhYSJ9.dRWrY2LQ5JppeGcKWoyAx--QUORBkNuz2JcGjPO84oU"
33 |
34 | class Data {
35 | Data({
36 | this.token,
37 | this.name,
38 | this.mobile,});
39 |
40 | Data.fromJson(dynamic json) {
41 | token = json['token'];
42 | name = json['name'];
43 | mobile = json['mobile'];
44 | }
45 | String? token;
46 | String? name;
47 | String? mobile;
48 |
49 | Map toJson() {
50 | final map = {};
51 | map['token'] = token;
52 | map['name'] = name;
53 | map['mobile'] = mobile;
54 | return map;
55 | }
56 |
57 | }
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
11 |
19 |
23 |
27 |
28 |
29 |
30 |
31 |
32 |
34 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | besenior_shop_course
33 |
34 |
35 |
39 |
40 |
41 |
42 |
43 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/lib/features/feature_product/presentation/bloc/all_poducts_cubit/all_products_cubit.dart:
--------------------------------------------------------------------------------
1 | import 'package:bloc/bloc.dart';
2 | import 'package:meta/meta.dart';
3 |
4 | import '../../../../../common/params/products_params.dart';
5 | import '../../../../../common/resources/data_state.dart';
6 | import '../../../data/models/all_products_model.dart';
7 | import '../../../repositories/all_product_repository.dart';
8 |
9 | part 'all_products_state.dart';
10 | part 'products_data_status.dart';
11 |
12 | class AllProductsCubit extends Cubit {
13 | AllProductsRepository allProductsRepository;
14 | AllProductsCubit(this.allProductsRepository) : super(AllProductsState(
15 | productsDataStatus: ProductsDataLoading(),
16 | allProducts: [],
17 | nextStart: 0,
18 | isLoadingPaging: false
19 | ));
20 |
21 |
22 | Future loadProductsData(ProductsParams productsParams) async {
23 | /// emit loading
24 | /// if it is first time emit main loading
25 | /// if it is paging time emit paging loading
26 | if(state.nextStart == 0){
27 | emit(state.copyWith(newProductsDataStatus: ProductsDataLoading()));
28 | }else{
29 | emit(state.copyWith(newIsLoadingPaging: true));
30 | }
31 |
32 | /// change next start before api call
33 | productsParams.start = state.nextStart;
34 | DataState dataState = await allProductsRepository.fetchAllProductsData(productsParams);
35 |
36 | if(dataState is DataSuccess){
37 | AllProductsModel allProductsModel = dataState.data;
38 |
39 | /// emit completed
40 | emit(state.copyWith(
41 | newProductsDataStatus: ProductsDataCompleted(allProductsModel),
42 | newAllProducts: state.allProducts..addAll(allProductsModel.data![0].products!),
43 | newNextStart: allProductsModel.data![0].nextStart,
44 | newIsLoadingPaging: false,
45 | ));
46 | }
47 |
48 | if(dataState is DataFailed){
49 | /// emit error
50 | emit(state.copyWith(
51 | newProductsDataStatus: ProductsDataError(dataState.error!),
52 | newIsLoadingPaging: false
53 | ));
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CADisableMinimumFrameDurationOnPhone
6 |
7 | CFBundleDevelopmentRegion
8 | $(DEVELOPMENT_LANGUAGE)
9 | CFBundleDisplayName
10 | Besenior Shop Course
11 | CFBundleExecutable
12 | $(EXECUTABLE_NAME)
13 | CFBundleIdentifier
14 | $(PRODUCT_BUNDLE_IDENTIFIER)
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | besenior_shop_course
19 | CFBundlePackageType
20 | APPL
21 | CFBundleShortVersionString
22 | $(FLUTTER_BUILD_NAME)
23 | CFBundleSignature
24 | ????
25 | CFBundleVersion
26 | $(FLUTTER_BUILD_NUMBER)
27 | LSRequiresIPhoneOS
28 |
29 | NSLocationAlwaysUsageDescription
30 | we need user location for set his ecommerce
31 | NSLocationWhenInUseUsageDescription
32 | we need user location for set his ecommerce
33 | UIApplicationSupportsIndirectInputEvents
34 |
35 | UILaunchStoryboardName
36 | LaunchScreen
37 | UIMainStoryboardFile
38 | Main
39 | UISupportedInterfaceOrientations
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationLandscapeLeft
43 | UIInterfaceOrientationLandscapeRight
44 |
45 | UISupportedInterfaceOrientations~ipad
46 |
47 | UIInterfaceOrientationPortrait
48 | UIInterfaceOrientationPortraitUpsideDown
49 | UIInterfaceOrientationLandscapeLeft
50 | UIInterfaceOrientationLandscapeRight
51 |
52 | UIViewControllerBasedStatusBarAppearance
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/lib/features/feature_auth/presentation/bloc/login_bloc/login_bloc.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:bloc/bloc.dart';
3 | import 'package:meta/meta.dart';
4 | import '../../../../../common/resources/data_state.dart';
5 | import '../../../data/models/code_model.dart';
6 | import '../../../data/models/login_with_sms_model.dart';
7 | import '../../../repositories/auth_repository.dart';
8 |
9 | part 'login_event.dart';
10 | part 'login_state.dart';
11 | part 'login_data_status.dart';
12 | part 'code_check_status.dart';
13 |
14 | class LoginBloc extends Bloc {
15 | AuthRepository authRepository;
16 | LoginBloc(this.authRepository) : super(
17 | LoginState(
18 | loginDataStatus: LoginDataInitial(),
19 | codeCheckStatus: CodeCheckInitial(),
20 | )) {
21 |
22 |
23 | on((event, emit) async {
24 | /// emit loading
25 | emit(
26 | state.copyWith(
27 | newLoginDataStatus: LoginDataLoading(),
28 | ));
29 |
30 | DataState dataState = await authRepository.fetchLoginSms(event.phoneNumber);
31 |
32 | if(dataState is DataSuccess){
33 | /// emit completed
34 | emit(state.copyWith(
35 | newLoginDataStatus: LoginDataCompleted(dataState.data),
36 | ));
37 | }
38 |
39 | if(dataState is DataFailed){
40 | /// emit error
41 | emit(state.copyWith(
42 | newLoginDataStatus: LoginDataError(dataState.error!),
43 | ));
44 | }
45 | });
46 |
47 | on((event, emit) async {
48 | /// emit loading
49 | emit(
50 | state.copyWith(
51 | newCodeCheckStatus: CodeCheckLoading(),
52 | ));
53 |
54 | DataState dataState = await authRepository.fetchCodeCheckData(event.code);
55 |
56 | if(dataState is DataSuccess){
57 | /// emit completed
58 | emit(state.copyWith(
59 | newCodeCheckStatus: CodeCheckCompleted(dataState.data),
60 | ));
61 | }
62 |
63 | if(dataState is DataFailed){
64 | /// emit error
65 | emit(state.copyWith(
66 | newCodeCheckStatus: CodeCheckError(dataState.error!),
67 | ));
68 | }
69 | });
70 |
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/lib/features/feature_auth/presentation/bloc/signup_bloc/signup_bloc.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:bloc/bloc.dart';
3 | import 'package:meta/meta.dart';
4 | import '../../../../../common/params/signup_params.dart';
5 | import '../../../../../common/resources/data_state.dart';
6 | import '../../../data/models/login_with_sms_model.dart';
7 | import '../../../data/models/signup_model.dart';
8 | import '../../../repositories/auth_repository.dart';
9 |
10 | part 'signup_event.dart';
11 | part 'signup_state.dart';
12 | part 'signup_data_status.dart';
13 | part 'call_code_status.dart';
14 |
15 |
16 | class SignupBloc extends Bloc {
17 | AuthRepository authRepository;
18 | SignupBloc(this.authRepository) : super(
19 | SignupState(
20 | signUpDataStatus: SignUpDataInitial(),
21 | callCodeStatus: CallCodeInitial(),
22 | )) {
23 |
24 | on((event, emit) async {
25 | /// emit loading
26 | emit(
27 | state.copyWith(
28 | newSignUpDataStatus: SignUpDataLoading(),
29 | ));
30 |
31 | DataState dataState = await authRepository.fetchSignUpData(event.signUpParams);
32 |
33 | if(dataState is DataSuccess){
34 | /// emit completed
35 | emit(state.copyWith(
36 | newSignUpDataStatus: SignUpCompleted(dataState.data),
37 | ));
38 | }
39 |
40 | if(dataState is DataFailed){
41 | /// emit error
42 | emit(state.copyWith(
43 | newSignUpDataStatus: SignUpDataError(dataState.error!),
44 | ));
45 | }
46 |
47 | });
48 |
49 | on((event, emit) async {
50 | /// emit loading
51 | emit(
52 | state.copyWith(
53 | newCallCodeStatus: CallCodeLoading(),
54 | ));
55 |
56 | DataState dataState = await authRepository.fetchRegisterCodeCheckData(event.mobile);
57 |
58 | if(dataState is DataSuccess){
59 | /// emit completed
60 | emit(state.copyWith(
61 | newCallCodeStatus: CallCodeCompleted(dataState.data),
62 | ));
63 | }
64 |
65 | if(dataState is DataFailed){
66 | /// emit error
67 | emit(state.copyWith(
68 | newCallCodeStatus: CallCodeError(dataState.error!),
69 | ));
70 | }
71 | });
72 |
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/lib/locator.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | import 'package:besenior_shop_course/common/utils/prefs_operator.dart';
4 | import 'package:besenior_shop_course/features/feature_home/data/data_source/home_api_provider.dart';
5 | import 'package:besenior_shop_course/features/feature_home/repositories/home_repository.dart';
6 | import 'package:besenior_shop_course/features/feature_product/data/data_source/category_api_provider.dart';
7 | import 'package:besenior_shop_course/features/feature_product/data/data_source/product_api_provider.dart';
8 | import 'package:besenior_shop_course/features/feature_product/repositories/all_product_repository.dart';
9 | import 'package:besenior_shop_course/features/feature_product/repositories/category_repository.dart';
10 | import 'package:dio/dio.dart';
11 | import 'package:get_it/get_it.dart';
12 | import 'package:shared_preferences/shared_preferences.dart';
13 |
14 | import 'features/feature_auth/data/data_source/auth_api_provider.dart';
15 | import 'features/feature_auth/presentation/bloc/login_bloc/login_bloc.dart';
16 | import 'features/feature_auth/presentation/bloc/signup_bloc/signup_bloc.dart';
17 | import 'features/feature_auth/repositories/auth_repository.dart';
18 |
19 | GetIt locator = GetIt.instance;
20 |
21 | Future initLocator() async{
22 | locator.registerSingleton(Dio());
23 |
24 | SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
25 | locator.registerSingleton(sharedPreferences);
26 | locator.registerSingleton(PrefsOperator());
27 |
28 |
29 | /// api provider
30 | locator.registerSingleton(HomeApiProvider(locator()));
31 | locator.registerSingleton(CategoryApiProvider(locator()));
32 | locator.registerSingleton(ProductsApiProvider(locator()));
33 | locator.registerSingleton(AuthApiProvider(locator()));
34 |
35 | /// repository
36 | locator.registerSingleton(HomeRepository(locator()));
37 | locator.registerSingleton(CategoryRepository(locator()));
38 | locator.registerSingleton(AllProductsRepository(locator()));
39 | locator.registerSingleton(AuthRepository(locator()));
40 |
41 |
42 | /// bloc
43 | locator.registerSingleton(SignupBloc(locator()));
44 | locator.registerSingleton(LoginBloc(locator()));
45 |
46 | }
--------------------------------------------------------------------------------
/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - connectivity_plus (0.0.1):
3 | - Flutter
4 | - ReachabilitySwift
5 | - Flutter (1.0.0)
6 | - flutter_keyboard_visibility (0.0.1):
7 | - Flutter
8 | - FMDB (2.7.5):
9 | - FMDB/standard (= 2.7.5)
10 | - FMDB/standard (2.7.5)
11 | - location (0.0.1):
12 | - Flutter
13 | - path_provider_foundation (0.0.1):
14 | - Flutter
15 | - FlutterMacOS
16 | - ReachabilitySwift (5.0.0)
17 | - shared_preferences_foundation (0.0.1):
18 | - Flutter
19 | - FlutterMacOS
20 | - sqflite (0.0.2):
21 | - Flutter
22 | - FMDB (>= 2.7.5)
23 |
24 | DEPENDENCIES:
25 | - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
26 | - Flutter (from `Flutter`)
27 | - flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
28 | - location (from `.symlinks/plugins/location/ios`)
29 | - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
30 | - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`)
31 | - sqflite (from `.symlinks/plugins/sqflite/ios`)
32 |
33 | SPEC REPOS:
34 | trunk:
35 | - FMDB
36 | - ReachabilitySwift
37 |
38 | EXTERNAL SOURCES:
39 | connectivity_plus:
40 | :path: ".symlinks/plugins/connectivity_plus/ios"
41 | Flutter:
42 | :path: Flutter
43 | flutter_keyboard_visibility:
44 | :path: ".symlinks/plugins/flutter_keyboard_visibility/ios"
45 | location:
46 | :path: ".symlinks/plugins/location/ios"
47 | path_provider_foundation:
48 | :path: ".symlinks/plugins/path_provider_foundation/ios"
49 | shared_preferences_foundation:
50 | :path: ".symlinks/plugins/shared_preferences_foundation/ios"
51 | sqflite:
52 | :path: ".symlinks/plugins/sqflite/ios"
53 |
54 | SPEC CHECKSUMS:
55 | connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e
56 | Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
57 | flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
58 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
59 | location: 3a2eed4dd2fab25e7b7baf2a9efefe82b512d740
60 | path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852
61 | ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
62 | shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca
63 | sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
64 |
65 | PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
66 |
67 | COCOAPODS: 1.11.2
68 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion flutter.compileSdkVersion
30 | ndkVersion flutter.ndkVersion
31 |
32 | compileOptions {
33 | sourceCompatibility JavaVersion.VERSION_1_8
34 | targetCompatibility JavaVersion.VERSION_1_8
35 | }
36 |
37 | kotlinOptions {
38 | jvmTarget = '1.8'
39 | }
40 |
41 | sourceSets {
42 | main.java.srcDirs += 'src/main/kotlin'
43 | }
44 |
45 | defaultConfig {
46 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
47 | applicationId "com.besenior.besenior_shop_course"
48 | // You can update the following values to match your application needs.
49 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
50 | minSdkVersion 19
51 | targetSdkVersion flutter.targetSdkVersion
52 | versionCode flutterVersionCode.toInteger()
53 | versionName flutterVersionName
54 | }
55 |
56 | buildTypes {
57 | release {
58 | // TODO: Add your own signing config for the release build.
59 | // Signing with the debug keys for now, so `flutter run --release` works.
60 | signingConfig signingConfigs.debug
61 | }
62 | }
63 | }
64 |
65 | flutter {
66 | source '../..'
67 | }
68 |
69 | dependencies {
70 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
71 | }
72 |
--------------------------------------------------------------------------------
/lib/common/widgets/main_wrapper.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:besenior_shop_course/common/widgets/bottom_nav.dart';
3 | import 'package:besenior_shop_course/features/feature_home/presentation/screens/home_screen.dart';
4 | import 'package:besenior_shop_course/features/feature_home/presentation/screens/profile_screen.dart';
5 | import 'package:besenior_shop_course/features/feature_product/presentation/screens/category_screen.dart';
6 | import 'package:flutter/material.dart';
7 |
8 | import '../../features/feature_product/presentation/widgets/search_textfield.dart';
9 | import '../../features/feature_product/repositories/all_product_repository.dart';
10 | import '../../locator.dart';
11 |
12 | class MainWrapper extends StatelessWidget {
13 | static const routeName = "/main_wrapper";
14 |
15 | MainWrapper({Key? key}) : super(key: key);
16 |
17 | final TextEditingController searchController = TextEditingController();
18 |
19 | PageController pageController = PageController();
20 |
21 | List topLevelScreens = [
22 | HomeScreen(),
23 | CategoryScreen(),
24 | ProfileScreen(),
25 | Container(color: Colors.green,),
26 | ];
27 |
28 |
29 | @override
30 | Widget build(BuildContext context) {
31 | return Scaffold(
32 | bottomNavigationBar: BottomNav(controller: pageController),
33 |
34 | body: SafeArea(
35 | child: Column(
36 | children: [
37 | const SizedBox(height: 10,),
38 |
39 | /// search Box
40 | Container(
41 | decoration: BoxDecoration(
42 | color: Colors.white,
43 | boxShadow: [
44 | BoxShadow(
45 | blurRadius: 2,
46 | color: Colors.grey.shade400,
47 | offset: const Offset(0, 3)
48 | )
49 | ]
50 | ),
51 | child: Padding(
52 | padding: const EdgeInsets.only(left: 10.0,right: 10, bottom: 10),
53 | child: SearchTextField(controller: searchController, allProductsRepository: locator(),),
54 | ),
55 | ),
56 | const SizedBox(height: 10,),
57 |
58 | Expanded(
59 | child: PageView(
60 | controller: pageController,
61 | children: topLevelScreens,
62 | ),
63 | ),
64 | ],
65 | ),
66 | ),
67 | );
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/lib/features/feature_intro/presentation/widgets/intro_page.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:delayed_widget/delayed_widget.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:shimmer/shimmer.dart';
5 |
6 | class IntroPage extends StatelessWidget {
7 | final String title;
8 | final String description;
9 | final String image;
10 | const IntroPage({Key? key,
11 | required this.title,
12 | required this.description,
13 | required this.image,
14 | }) : super(key: key);
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 |
19 | var textTheme = Theme.of(context).textTheme;
20 | /// get device size
21 | var height = MediaQuery.of(context).size.height;
22 | var width = MediaQuery.of(context).size.width;
23 |
24 | return Column(
25 | crossAxisAlignment: CrossAxisAlignment.start,
26 | children: [
27 |
28 | /// image
29 | SizedBox(
30 | width: width,
31 | height: height * 0.6,
32 | child: DelayedWidget(
33 | delayDuration: const Duration(milliseconds: 200),// Not required
34 | animationDuration: const Duration(seconds: 1),// Not required
35 | animation: DelayedAnimations.SLIDE_FROM_BOTTOM,//
36 | child: Image.asset(image))
37 | ),
38 | const SizedBox(height: 20,),
39 |
40 | Padding(
41 | padding: const EdgeInsets.symmetric(horizontal: 25.0),
42 | child: DelayedWidget(
43 | delayDuration: const Duration(milliseconds: 400),// Not required
44 | animationDuration: const Duration(seconds: 1),// Not required
45 | animation: DelayedAnimations.SLIDE_FROM_BOTTOM,// Not required
46 | child: Text(title, style: textTheme.titleMedium,),
47 | ),
48 | ),
49 | const SizedBox(height: 10,),
50 | Padding(
51 | padding: const EdgeInsets.symmetric(horizontal: 25.0),
52 | child: DelayedWidget(
53 | delayDuration: const Duration(milliseconds: 600),// Not required
54 | animationDuration: const Duration(seconds: 1),// Not required
55 | animation: DelayedAnimations.SLIDE_FROM_BOTTOM,// Not required
56 | child: Shimmer.fromColors(
57 | baseColor: Colors.black,
58 | highlightColor: Colors.grey,
59 | child: Text(description, style: textTheme.bodyMedium))
60 | ),
61 | ),
62 | ],
63 | );
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/lib/features/feature_auth/data/data_source/auth_api_provider.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'dart:io';
3 |
4 | import 'package:android_sms_retriever/android_sms_retriever.dart';
5 | import 'package:dio/dio.dart';
6 | import '../../../../common/error_handling/check_exceptions.dart';
7 | import '../../../../common/params/signup_params.dart';
8 | import '../../../../config/constants.dart';
9 |
10 | class AuthApiProvider {
11 | Dio dio;
12 | AuthApiProvider(this.dio);
13 |
14 | dynamic callSignUp(SignUpParams signUpParams) async {
15 | try{
16 | final response = await dio.post(
17 | "${Constants.baseUrl}/register",
18 | queryParameters: {
19 | "name" : signUpParams.username,
20 | "mobile" : signUpParams.phoneNumber
21 | }
22 | );
23 | print(response.toString());
24 |
25 | return response;
26 |
27 | }on DioError catch(e){
28 | return CheckExceptions.response(e.response!);
29 | }
30 | }
31 |
32 | dynamic callLoginWithSms(phoneNumber) async {
33 | if(Platform.isAndroid){
34 | String? sign = await AndroidSmsRetriever.getAppSignature();
35 | print("app sign : " + sign!);
36 | }
37 |
38 |
39 | try{
40 | final response = await dio.post(
41 | "${Constants.baseUrl}/auth/loginWithSms",
42 | queryParameters: {
43 | "mobile" : phoneNumber,
44 | if(Platform.isAndroid)
45 | 'hash': (await AndroidSmsRetriever.getAppSignature())
46 | }
47 | );
48 | print(response.toString());
49 |
50 | return response;
51 |
52 | }on DioError catch(e){
53 | return CheckExceptions.response(e.response!);
54 | }
55 | }
56 |
57 | dynamic callCodeCheck(code) async {
58 | try{
59 | final response = await dio.post(
60 | "${Constants.baseUrl}/auth/loginWithSms/check",
61 | queryParameters: {
62 | "code" : code,
63 | }
64 | );
65 | print(response.toString());
66 |
67 | return response;
68 |
69 | } on DioError catch(e){
70 | return CheckExceptions.response(e.response!);
71 | }
72 | }
73 |
74 | dynamic callRegisterCodeCheck(mobile) async {
75 | try{
76 | final response = await dio.post(
77 | "${Constants.baseUrl}/auth/sendcode",
78 | queryParameters: {
79 | "mobile" : mobile,
80 | if(Platform.isAndroid)
81 | 'hash': (await AndroidSmsRetriever.getAppSignature())
82 | }
83 | );
84 | print(response.toString());
85 |
86 | return response;
87 |
88 | }on DioError catch(e){
89 | return CheckExceptions.response(e.response!);
90 | }
91 | }
92 |
93 | }
--------------------------------------------------------------------------------
/lib/features/feature_auth/repositories/auth_repository.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:dio/dio.dart';
3 | import '../../../common/error_handling/app_exception.dart';
4 | import '../../../common/error_handling/check_exceptions.dart';
5 | import '../../../common/params/signup_params.dart';
6 | import '../../../common/resources/data_state.dart';
7 | import '../data/data_source/auth_api_provider.dart';
8 | import '../data/models/code_model.dart';
9 | import '../data/models/login_with_sms_model.dart';
10 | import '../data/models/signup_model.dart';
11 |
12 | class AuthRepository {
13 | AuthApiProvider apiProvider;
14 | AuthRepository(this.apiProvider);
15 |
16 | Future> fetchSignUpData(SignUpParams signUpParams) async {
17 | try{
18 | Response response = await apiProvider.callSignUp(signUpParams);
19 | if(response.data['status'].toString() == "success"){
20 | final SignupModel signupModel = SignupModel.fromJson(response.data);
21 | return DataSuccess(signupModel);
22 | }else{
23 | return DataFailed(response.data['message']);
24 | }
25 | } on AppException catch(e){
26 | return await CheckExceptions.getError(e);
27 | }
28 | }
29 |
30 |
31 | Future> fetchLoginSms(phoneNumber) async {
32 | try{
33 | Response response = await apiProvider.callLoginWithSms(phoneNumber);
34 | if(response.data['status'].toString() != "error"){
35 | // convert json to models class
36 | final LoginWithSmsModel loginWithSmsModel = LoginWithSmsModel.fromJson(response.data);
37 | return DataSuccess(loginWithSmsModel);
38 | }else{
39 | return DataFailed(response.data['message']);
40 | }
41 | } on AppException catch(e){
42 | return CheckExceptions.getError(e);
43 | }
44 | }
45 |
46 | Future> fetchCodeCheckData(code) async {
47 | try{
48 | Response response = await apiProvider.callCodeCheck(code);
49 | final CodeModel codeModel = CodeModel.fromJson(response.data);
50 | return DataSuccess(codeModel);
51 | } on AppException catch(e){
52 | return CheckExceptions.getError(e);
53 | }
54 | }
55 |
56 | Future> fetchRegisterCodeCheckData(mobile) async {
57 | try{
58 | Response response = await apiProvider.callRegisterCodeCheck(mobile);
59 | if(response.data['status'].toString() == "success"){
60 | // convert json to models class
61 | final LoginWithSmsModel loginWithSmsModel = LoginWithSmsModel.fromJson(response.data);
62 | return DataSuccess(loginWithSmsModel);
63 | }else{
64 | return DataFailed(response.data["message"]);
65 | }
66 | } on AppException catch(e){
67 | return CheckExceptions.getError(e);
68 | }
69 | }
70 |
71 | }
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:besenior_shop_course/common/blocs/bottom_nav_cubit/bottom_nav_cubit.dart';
4 | import 'package:besenior_shop_course/config/my_theme.dart';
5 | import 'package:besenior_shop_course/features/feature_auth/presentation/screens/mobile_signup_screen.dart';
6 | import 'package:besenior_shop_course/features/feature_intro/presentation/screens/intro_main_wrapper.dart';
7 | import 'package:besenior_shop_course/features/feature_intro/presentation/screens/splash_screen.dart';
8 | import 'package:besenior_shop_course/features/feature_product/presentation/screens/all_products_screen.dart';
9 | import 'package:besenior_shop_course/test_screen.dart';
10 | import 'package:flutter/material.dart';
11 | import 'package:flutter_bloc/flutter_bloc.dart';
12 | import 'package:flutter_localizations/flutter_localizations.dart';
13 |
14 | import 'common/widgets/main_wrapper.dart';
15 | import 'features/feature_auth/presentation/bloc/login_bloc/login_bloc.dart';
16 | import 'features/feature_auth/presentation/bloc/signup_bloc/signup_bloc.dart';
17 | import 'features/feature_intro/presentation/bloc/splash_cubit/splash_cubit.dart';
18 | import 'locator.dart';
19 |
20 | Future main() async {
21 | WidgetsFlutterBinding.ensureInitialized();
22 |
23 | HttpOverrides.global = MyHttpOverrides();
24 |
25 | await initLocator();
26 |
27 | runApp(MultiBlocProvider(
28 | providers: [
29 | BlocProvider(create: (_)=> SplashCubit()),
30 | BlocProvider(create: (_)=> BottomNavCubit()),
31 | BlocProvider(create: (_) => locator()),
32 | BlocProvider(create: (_) => locator()),
33 | ],
34 | child: const MyApp()
35 | ));
36 | }
37 |
38 | class MyApp extends StatelessWidget {
39 | const MyApp({Key? key}) : super(key: key);
40 |
41 | @override
42 | Widget build(BuildContext context) {
43 | return MaterialApp(
44 | themeMode: ThemeMode.light,
45 | theme: MyThemes.lightTheme,
46 | darkTheme: MyThemes.darkTheme,
47 | initialRoute: "/",
48 | locale: const Locale("fa",""),
49 | localizationsDelegates: const [
50 | GlobalMaterialLocalizations.delegate,
51 | GlobalWidgetsLocalizations.delegate,
52 | GlobalCupertinoLocalizations.delegate,
53 | ],
54 | supportedLocales: const [
55 | Locale("en",""),
56 | Locale("fa",""),
57 | ],
58 | routes: {
59 | IntroMainWrapper.routeName: (context)=> IntroMainWrapper(),
60 | TestScreen.routeName: (context)=> TestScreen(),
61 | MainWrapper.routeName: (context)=> MainWrapper(),
62 | MobileSignUpScreen.routeName: (context)=> MobileSignUpScreen(),
63 | AllProductsScreen.routeName: (context)=> AllProductsScreen(),
64 | },
65 | debugShowCheckedModeBanner: false,
66 | title: 'besenior shop',
67 | // home: MobileSignUpScreen(),
68 | home: SplashScreen(),
69 | );
70 | }
71 | }
72 |
73 | class MyHttpOverrides extends HttpOverrides{
74 | @override
75 | HttpClient createHttpClient(SecurityContext? context){
76 | return super.createHttpClient(context)
77 | ..badCertificateCallback = (X509Certificate cert, String host, int port)=> true;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/lib/features/feature_product/data/models/categories_model.dart:
--------------------------------------------------------------------------------
1 | /// message : "با موفقیت دریافت شد"
2 | /// status : "success"
3 | /// data : [{"id":1,"title":"محصولات ارگانیک","img":"https://niyaz.shop/images/image-not-found.png","childs":[{"id":2,"title":"میوه ارگانیک","img":"https://niyaz.shop/images/image-not-found.png","childs":null},{"id":3,"title":"سبزیجات ارگانیک","img":"https://niyaz.shop/images/image-not-found.png","childs":null}]},{"id":4,"title":"میوه خشک","img":"https://niyaz.shop/images/image-not-found.png","childs":null}]
4 |
5 | class CategoriesModel {
6 | CategoriesModel({
7 | this.message,
8 | this.status,
9 | this.data,});
10 |
11 | CategoriesModel.fromJson(dynamic json) {
12 | message = json['message'];
13 | status = json['status'];
14 | if (json['data'] != null) {
15 | data = [];
16 | json['data'].forEach((v) {
17 | data?.add(Data.fromJson(v));
18 | });
19 | }
20 | }
21 | String? message;
22 | String? status;
23 | List? data;
24 |
25 | Map toJson() {
26 | final map = {};
27 | map['message'] = message;
28 | map['status'] = status;
29 | if (data != null) {
30 | map['data'] = data?.map((v) => v.toJson()).toList();
31 | }
32 | return map;
33 | }
34 |
35 | }
36 |
37 | /// id : 1
38 | /// title : "محصولات ارگانیک"
39 | /// img : "https://niyaz.shop/images/image-not-found.png"
40 | /// childs : [{"id":2,"title":"میوه ارگانیک","img":"https://niyaz.shop/images/image-not-found.png","childs":null},{"id":3,"title":"سبزیجات ارگانیک","img":"https://niyaz.shop/images/image-not-found.png","childs":null}]
41 |
42 | class Data {
43 | Data({
44 | this.id,
45 | this.title,
46 | this.img,
47 | this.icon,
48 | this.childs,});
49 |
50 | Data.fromJson(dynamic json) {
51 | id = json['id'];
52 | title = json['title'];
53 | img = json['img'];
54 | icon = json['icon'];
55 | if (json['childs'] != null) {
56 | childs = [];
57 | json['childs'].forEach((v) {
58 | childs?.add(Childs.fromJson(v));
59 | });
60 | }
61 | }
62 | int? id;
63 | String? title;
64 | String? img;
65 | String? icon;
66 | List? childs;
67 |
68 | Map toJson() {
69 | final map = {};
70 | map['id'] = id;
71 | map['title'] = title;
72 | map['img'] = img;
73 | map['icon'] = icon;
74 | if (childs != null) {
75 | map['childs'] = childs?.map((v) => v.toJson()).toList();
76 | }
77 | return map;
78 | }
79 |
80 | }
81 |
82 | /// id : 2
83 | /// title : "میوه ارگانیک"
84 | /// img : "https://niyaz.shop/images/image-not-found.png"
85 | /// childs : null
86 |
87 | class Childs {
88 | Childs({
89 | this.id,
90 | this.title,
91 | this.img,
92 | this.childs,});
93 |
94 | Childs.fromJson(dynamic json) {
95 | id = json['id'];
96 | title = json['title'];
97 | img = json['img'];
98 | childs = json['childs'];
99 | }
100 | int? id;
101 | String? title;
102 | String? img;
103 | dynamic childs;
104 |
105 | Map toJson() {
106 | final map = {};
107 | map['id'] = id;
108 | map['title'] = title;
109 | map['img'] = img;
110 | map['childs'] = childs;
111 | return map;
112 | }
113 |
114 | }
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/lib/features/feature_product/data/models/all_products_model.dart:
--------------------------------------------------------------------------------
1 | /// message : "با موفقیت دریافت شد"
2 | /// status : "success"
3 | /// data : [{"products":[{"id":120,"image":"https://niyaz.shop/uploads/products/ازگیل-16633545536048089.png","name":"ازگیل","price":"42,000","priceBeforDiscount":"0","discount":0,"callStatus":0,"specialDiscount":0,"star":3,"category":"محصولات ارگانیک"}],"count":81,"nextStart":15,"more":true}]
4 |
5 | class AllProductsModel {
6 | AllProductsModel({
7 | this.message,
8 | this.status,
9 | this.data,});
10 |
11 | AllProductsModel.fromJson(dynamic json) {
12 | message = json['message'];
13 | status = json['status'];
14 | if (json['data'] != null) {
15 | data = [];
16 | json['data'].forEach((v) {
17 | data?.add(Data.fromJson(v));
18 | });
19 | }
20 | }
21 | String? message;
22 | String? status;
23 | List? data;
24 |
25 | Map toJson() {
26 | final map = {};
27 | map['message'] = message;
28 | map['status'] = status;
29 | if (data != null) {
30 | map['data'] = data?.map((v) => v.toJson()).toList();
31 | }
32 | return map;
33 | }
34 |
35 | }
36 |
37 | /// products : [{"id":120,"image":"https://niyaz.shop/uploads/products/ازگیل-16633545536048089.png","name":"ازگیل","price":"42,000","priceBeforDiscount":"0","discount":0,"callStatus":0,"specialDiscount":0,"star":3,"category":"محصولات ارگانیک"}]
38 | /// count : 81
39 | /// nextStart : 15
40 | /// more : true
41 |
42 | class Data {
43 | Data({
44 | this.products,
45 | this.count,
46 | this.nextStart,
47 | this.more,});
48 |
49 | Data.fromJson(dynamic json) {
50 | if (json['products'] != null) {
51 | products = [];
52 | json['products'].forEach((v) {
53 | products?.add(Products.fromJson(v));
54 | });
55 | }
56 | count = json['count'];
57 | nextStart = json['nextStart'];
58 | more = json['more'];
59 | }
60 | List? products;
61 | int? count;
62 | int? nextStart;
63 | bool? more;
64 |
65 | Map toJson() {
66 | final map = {};
67 | if (products != null) {
68 | map['products'] = products?.map((v) => v.toJson()).toList();
69 | }
70 | map['count'] = count;
71 | map['nextStart'] = nextStart;
72 | map['more'] = more;
73 | return map;
74 | }
75 |
76 | }
77 |
78 | /// id : 120
79 | /// image : "https://niyaz.shop/uploads/products/ازگیل-16633545536048089.png"
80 | /// name : "ازگیل"
81 | /// price : "42,000"
82 | /// priceBeforDiscount : "0"
83 | /// discount : 0
84 | /// callStatus : 0
85 | /// specialDiscount : 0
86 | /// star : 3
87 | /// category : "محصولات ارگانیک"
88 |
89 | class Products {
90 | Products({
91 | this.id,
92 | this.image,
93 | this.name,
94 | this.price,
95 | this.priceBeforDiscount,
96 | this.discount,
97 | this.callStatus,
98 | this.specialDiscount,
99 | this.star,
100 | this.category,});
101 |
102 | Products.fromJson(dynamic json) {
103 | id = json['id'];
104 | image = json['image'];
105 | name = json['name'];
106 | price = json['price'];
107 | priceBeforDiscount = json['priceBeforDiscount'];
108 | discount = json['discount'];
109 | callStatus = json['callStatus'];
110 | specialDiscount = json['specialDiscount'];
111 | star = json['star'];
112 | category = json['category'];
113 | }
114 | int? id;
115 | String? image;
116 | String? name;
117 | String? price;
118 | String? priceBeforDiscount;
119 | int? discount;
120 | int? callStatus;
121 | int? specialDiscount;
122 | int? star;
123 | String? category;
124 |
125 | Map toJson() {
126 | final map = {};
127 | map['id'] = id;
128 | map['image'] = image;
129 | map['name'] = name;
130 | map['price'] = price;
131 | map['priceBeforDiscount'] = priceBeforDiscount;
132 | map['discount'] = discount;
133 | map['callStatus'] = callStatus;
134 | map['specialDiscount'] = specialDiscount;
135 | map['star'] = star;
136 | map['category'] = category;
137 | return map;
138 | }
139 |
140 | }
--------------------------------------------------------------------------------
/lib/features/feature_intro/presentation/screens/splash_screen.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:besenior_shop_course/common/widgets/main_wrapper.dart';
3 | import 'package:besenior_shop_course/features/feature_intro/presentation/bloc/splash_cubit/splash_cubit.dart';
4 | import 'package:besenior_shop_course/features/feature_intro/presentation/screens/intro_main_wrapper.dart';
5 | import 'package:delayed_widget/delayed_widget.dart';
6 | import 'package:flutter/material.dart';
7 | import 'package:flutter_bloc/flutter_bloc.dart';
8 | import 'package:loading_animation_widget/loading_animation_widget.dart';
9 | import '../../../../common/utils/prefs_operator.dart';
10 | import '../../../../locator.dart';
11 | import '../../../../test_screen.dart';
12 |
13 | class SplashScreen extends StatefulWidget {
14 | const SplashScreen({Key? key}) : super(key: key);
15 |
16 | @override
17 | State createState() => _SplashScreenState();
18 | }
19 |
20 | class _SplashScreenState extends State {
21 |
22 | @override
23 | void initState() {
24 | // TODO: implement initState
25 | super.initState();
26 | // gotoHome();
27 |
28 | BlocProvider.of(context).checkConnectionEvent();
29 | }
30 |
31 |
32 | @override
33 | Widget build(BuildContext context) {
34 |
35 | var width = MediaQuery.of(context).size.width;
36 | return Scaffold(
37 | body: Container(
38 | width: width,
39 | color: Colors.white,
40 | child: Column(
41 | mainAxisAlignment: MainAxisAlignment.center,
42 | children: [
43 | Expanded(
44 | child: DelayedWidget(
45 | delayDuration: const Duration(milliseconds: 200),
46 | animationDuration: const Duration(milliseconds: 1000),
47 | animation: DelayedAnimations.SLIDE_FROM_BOTTOM,
48 | child: Image.asset('assets/images/besenior_logo.png',width: width * 0.8,))),
49 |
50 |
51 | BlocConsumer(
52 | builder: (context, state){
53 | /// if user is online
54 | if(state.connectionStatus is ConnectionInitial || state.connectionStatus is ConnectionOn){
55 | return Directionality(
56 | textDirection: TextDirection.ltr,
57 | child: LoadingAnimationWidget.prograssiveDots(
58 | color: Colors.red,
59 | size: 50,
60 | ),
61 | );
62 | }
63 |
64 | /// if user is offline
65 | if(state.connectionStatus is ConnectionOff){
66 | return Row(
67 | mainAxisAlignment: MainAxisAlignment.center,
68 | children: [
69 | const Text('به اینترنت متصل نیستید!', style: TextStyle(color: Colors.red, fontWeight: FontWeight.w500, fontFamily: "vazir"),),
70 | IconButton(
71 | splashColor: Colors.red,
72 | onPressed: (){
73 | /// check that we are online or not
74 | BlocProvider.of(context).checkConnectionEvent();
75 | },
76 | icon: const Icon(Icons.autorenew, color: Colors.red,))
77 | ],
78 | );
79 | }
80 |
81 | /// default value
82 | return Container();
83 |
84 | },
85 | listener: (context, state){
86 | if(state.connectionStatus is ConnectionOn){
87 | gotoHome();
88 | }
89 | }
90 | ),
91 | const SizedBox(height: 30,),
92 |
93 | ],
94 | ),
95 | ),
96 | );
97 | }
98 |
99 | Future gotoHome() async {
100 | PrefsOperator prefsOperator = locator();
101 | var shouldShowIntro = await prefsOperator.getIntroState();
102 |
103 | return Future.delayed(const Duration(seconds: 3),(){
104 |
105 | if(shouldShowIntro){
106 | Navigator.pushNamedAndRemoveUntil(context, IntroMainWrapper.routeName,ModalRoute.withName("intro_main_wrapper"),);
107 | }else{
108 | Navigator.pushNamedAndRemoveUntil(context, MainWrapper.routeName, ModalRoute.withName("main_wrapper"),);
109 | }
110 | });
111 |
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: besenior_shop_course
2 | description: A new Flutter project.
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `flutter pub publish`. This is preferred for private packages.
6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
7 |
8 | # The following defines the version and build number for your application.
9 | # A version number is three numbers separated by dots, like 1.2.43
10 | # followed by an optional build number separated by a +.
11 | # Both the version and the builder number may be overridden in flutter
12 | # build by specifying --build-name and --build-number, respectively.
13 | # In Android, build-name is used as versionName while build-number used as versionCode.
14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
16 | # Read more about iOS versioning at
17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
18 | # In Windows, build-name is used as the major, minor, and patch parts
19 | # of the product and file versions while build-number is used as the build suffix.
20 | version: 1.0.0+1
21 |
22 | environment:
23 | sdk: '>=2.18.1 <3.0.0'
24 |
25 | # Dependencies specify other packages that your package needs in order to work.
26 | # To automatically upgrade your package dependencies to the latest versions
27 | # consider running `flutter pub upgrade --major-versions`. Alternatively,
28 | # dependencies can be manually updated by changing the version numbers below to
29 | # the latest version available on pub.dev. To see which dependencies have newer
30 | # versions available, run `flutter pub outdated`.
31 | dependencies:
32 | flutter:
33 | sdk: flutter
34 |
35 |
36 | # The following adds the Cupertino Icons font to your application.
37 | # Use with the CupertinoIcons class for iOS style icons.
38 | cupertino_icons: ^1.0.2
39 | delayed_widget: ^1.1.2
40 | flutter_bloc: ^8.0.1
41 | bloc: ^8.0.3
42 | connectivity_plus: ^3.0.2
43 | loading_animation_widget: ^1.2.0+3
44 | shimmer: ^2.0.0
45 | smooth_page_indicator: ^0.3.0-nullsafety.0
46 | get_it: ^7.2.0
47 | dio: ^4.0.6
48 | shared_preferences: ^2.0.15
49 | flutter_svg: ^1.1.5
50 | font_awesome_flutter: ^10.2.1
51 | badges: ^2.0.3
52 | cached_network_image: ^3.2.2
53 | location: ^4.4.0
54 | flutter_typeahead: ^3.2.0
55 | flutter_rating_bar: ^4.0.1
56 | android_sms_retriever: ^1.3.2
57 | persian_number_utility: ^1.1.2
58 |
59 |
60 |
61 | dev_dependencies:
62 | flutter_test:
63 | sdk: flutter
64 | flutter_localizations: # Add this line
65 | sdk: flutter
66 |
67 | # The "flutter_lints" package below contains a set of recommended lints to
68 | # encourage good coding practices. The lint set provided by the package is
69 | # activated in the `analysis_options.yaml` file located at the root of your
70 | # package. See that file for information about deactivating specific lint
71 | # rules and activating additional ones.
72 | flutter_lints: ^2.0.0
73 |
74 | # For information on the generic Dart part of this file, see the
75 | # following page: https://dart.dev/tools/pub/pubspec
76 |
77 | # The following section is specific to Flutter packages.
78 | flutter:
79 |
80 | # The following line ensures that the Material Icons font is
81 | # included with your application, so that you can use the icons in
82 | # the material Icons class.
83 | uses-material-design: true
84 |
85 | # To add assets to your application, add an assets section, like this:
86 | assets:
87 | - assets/images/
88 | # - images/a_dot_ham.jpeg
89 |
90 | # An image asset can refer to one or more resolution-specific "variants", see
91 | # https://flutter.dev/assets-and-images/#resolution-aware
92 |
93 | # For details regarding adding assets from package dependencies, see
94 | # https://flutter.dev/assets-and-images/#from-packages
95 |
96 | # To add custom fonts to your application, add a fonts section here,
97 | # in this "flutter" section. Each entry in this list should have a
98 | # "family" key with the font family name, and a "fonts" key with a
99 | # list giving the asset and other descriptors for the font. For
100 | # example:
101 | fonts:
102 | - family: Vazir
103 | fonts:
104 | - asset: assets/fonts/vazir.ttf
105 | # - asset: fonts/Schyler-Italic.ttf
106 | # style: italic
107 | # - family: Trajan Pro
108 | # fonts:
109 | # - asset: fonts/TrajanPro.ttf
110 | # - asset: fonts/TrajanPro_Bold.ttf
111 | # weight: 700
112 | #
113 | # For details regarding fonts from package dependencies,
114 | # see https://flutter.dev/custom-fonts/#from-packages
115 |
--------------------------------------------------------------------------------
/lib/features/feature_product/presentation/screens/category_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:besenior_shop_course/features/feature_product/data/models/categories_model.dart';
2 | import 'package:besenior_shop_course/features/feature_product/presentation/bloc/category_cubit/category_cubit.dart';
3 | import 'package:cached_network_image/cached_network_image.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_bloc/flutter_bloc.dart';
6 |
7 | import '../../../../common/arguments/productsArgument.dart';
8 | import '../../../../common/widgets/dot_loading_widget.dart';
9 | import '../../../../locator.dart';
10 | import 'all_products_screen.dart';
11 |
12 | class CategoryScreen extends StatelessWidget {
13 | const CategoryScreen({Key? key}) : super(key: key);
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return BlocProvider(
18 | create: (context) => CategoryCubit(locator()),
19 | child: Builder(
20 | builder: (context) {
21 |
22 | /// call api
23 | BlocProvider.of(context).loadCategoryEvent();
24 |
25 |
26 | return BlocBuilder(
27 | builder: (context, state) {
28 | /// loading
29 | if(state.categoryDataStatus is CategoryDataLoading){
30 | return const Center(child: DotLoadingWidget(size: 30));
31 | }
32 |
33 | if(state.categoryDataStatus is CategoryDataCompleted){
34 | CategoryDataCompleted categoryDataCompleted = state.categoryDataStatus as CategoryDataCompleted;
35 | CategoriesModel categoriesModel = categoryDataCompleted.categoriesModel;
36 |
37 |
38 | return ListView.separated(
39 | padding: const EdgeInsets.only(top: 20),
40 | itemBuilder: (context, index){
41 | Data categoryData = categoriesModel.data![index];
42 | /// text
43 | return GestureDetector(
44 | onTap: (){
45 | /// goto All products screen
46 | Navigator.pushNamed(context, AllProductsScreen.routeName, arguments: ProductsArguments(categoryId: categoryData.id!),);
47 | },
48 | child: Padding(
49 | padding: const EdgeInsets.symmetric(horizontal: 15.0),
50 | child: Container(
51 | height: 60,
52 | decoration: BoxDecoration(
53 | color: Colors.white,
54 | borderRadius: BorderRadius.circular(20),
55 | // boxShadow: const [
56 | // BoxShadow(
57 | // blurRadius: 10,
58 | // offset: Offset(5, 5),
59 | // color: Colors.grey
60 | // )
61 | // ]
62 | ),
63 | child: Padding(
64 | padding: const EdgeInsets.symmetric(horizontal: 15.0),
65 | child: Row(
66 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
67 | children: [
68 | Row(
69 | children: [
70 | ClipRRect(
71 | borderRadius: BorderRadius.circular(20),
72 | child: CachedNetworkImage(
73 | imageUrl: categoryData.icon!,
74 | errorWidget: (context, string, dynamic){
75 | return const Icon(Icons.error,color: Colors.black,);
76 | },
77 | width: 40,
78 | height: 40,
79 | fit: BoxFit.cover,
80 | useOldImageOnUrlChange: true,
81 | ),
82 | ),
83 | const SizedBox(width: 5,),
84 | Text(categoryData.title!,style: const TextStyle(color: Colors.black, fontFamily: 'Vazir',fontWeight: FontWeight.bold),),
85 | ],
86 | ),
87 | const Icon(Icons.arrow_back_ios_new, size: 15,color: Colors.blueAccent,),
88 | ],
89 | ),
90 | ),
91 | ),
92 | ),
93 | );
94 | },
95 | separatorBuilder: (context, index){
96 | return SizedBox(height: 10);
97 | },
98 | itemCount: categoriesModel.data!.length,
99 | );
100 | }
101 |
102 |
103 | if(state.categoryDataStatus is CategoryDataError){
104 | final CategoryDataError categoryDataError = state.categoryDataStatus as CategoryDataError;
105 |
106 | return Center(
107 | child: Column(
108 | mainAxisAlignment: MainAxisAlignment.center,
109 | children: [
110 | Text(categoryDataError.errorMessage,style: const TextStyle(color: Colors.white),),
111 | const SizedBox(height: 10,),
112 | ElevatedButton(
113 | style: ElevatedButton.styleFrom(backgroundColor: Colors.amber.shade800),
114 | onPressed: (){
115 | /// call all data again
116 | BlocProvider.of(context).loadCategoryEvent();
117 | },
118 | child: const Text("تلاش دوباره"),)
119 | ],
120 | ),
121 | );
122 | }
123 | return Container();
124 | },
125 | );
126 | }
127 | ),
128 | );
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/lib/common/widgets/bottom_nav.dart:
--------------------------------------------------------------------------------
1 | import 'package:badges/badges.dart';
2 | import 'package:flutter/cupertino.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_bloc/flutter_bloc.dart';
5 | import 'package:flutter_svg/flutter_svg.dart';
6 | import 'package:font_awesome_flutter/font_awesome_flutter.dart';
7 | import 'package:shared_preferences/shared_preferences.dart';
8 |
9 | import '../blocs/bottom_nav_cubit/bottom_nav_cubit.dart';
10 |
11 | // import '../../features/feature_auth/presentation/screens/mobile_signup_screen.dart';
12 |
13 | class BottomNav extends StatelessWidget {
14 | final PageController controller;
15 |
16 | const BottomNav({Key? key, required this.controller}) : super(key: key);
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | var primaryColor = Theme.of(context).primaryColor;
21 | TextTheme textTheme = Theme.of(context).textTheme;
22 |
23 | return BottomAppBar(
24 | shape: const CircularNotchedRectangle(),
25 | notchMargin: 5,
26 | color: Colors.white,
27 | child: SizedBox(
28 | height: 72,
29 | child: BlocBuilder(
30 | builder: (context, int state) {
31 | return Row(
32 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
33 | children: [
34 | SizedBox(
35 | width: MediaQuery.of(context).size.width / 2,
36 | height: 72,
37 | child: Row(
38 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
39 | children: [
40 | Column(
41 | children: [
42 | IconButton(
43 | onPressed: () {
44 | /// change selected index
45 | BlocProvider.of(context).changeSelectedIndex(0);
46 | controller.animateToPage(0, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);
47 | },
48 | icon: Image.asset(
49 | state == 0 ? "assets/images/home_icon.png" : "assets/images/home_icon2.png",
50 | color: state == 0 ? Colors.red : Colors.grey.shade700,
51 | )
52 | ),
53 | Text(
54 | 'بیسینیور',
55 | style: TextStyle(fontSize: 14,fontFamily: 'Yekan',color: Colors.grey.shade700),
56 | ),
57 | ],
58 | ),
59 | Column(
60 | children: [
61 | IconButton(
62 | onPressed: () {
63 | BlocProvider.of(context).changeSelectedIndex(1);
64 | controller.animateToPage(1, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);
65 | },
66 | icon: Image.asset(
67 | state == 1
68 | ? "assets/images/category_icon.png"
69 | : "assets/images/category_icon2.png" ,
70 | color: state == 1 ? Colors.red : Colors.grey.shade700,
71 | width: 40,
72 | )
73 | ),
74 | Text(
75 | 'دسته بندی',
76 | style: TextStyle(fontSize: 14,fontFamily: 'Yekan',color: Colors.grey.shade700),
77 | ),
78 | ],
79 | ),
80 | ],
81 | ),
82 | ),
83 | SizedBox(
84 | width: MediaQuery.of(context).size.width / 2,
85 | height: 72,
86 | child: Row(
87 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
88 | children: [
89 | Column(
90 | children: [
91 | IconButton(
92 | onPressed: () async {
93 | BlocProvider.of(context).changeSelectedIndex(2);
94 | controller.animateToPage(2, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);
95 | },
96 | icon: SvgPicture.asset(state == 2 ? "assets/images/person_icon.svg" : "assets/images/person_icon2.svg", color: state == 2 ? Colors.red : Colors.grey.shade700,width: 48,),
97 | ),
98 | Text(
99 | 'حساب کاربری',
100 | style: TextStyle(fontSize: 14,fontFamily: 'Yekan',color: Colors.grey.shade700),
101 | ),
102 | ],
103 | ),
104 | Column(
105 | children: [
106 | IconButton(
107 | onPressed: () async {
108 | BlocProvider.of(context).changeSelectedIndex(3);
109 | controller.animateToPage(3, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);
110 | },
111 | icon: FaIcon(
112 | state == 3
113 | ? Icons.shopping_cart
114 | : Icons.shopping_cart_outlined,
115 | color: state == 3 ? Colors.red : Colors.grey.shade700,
116 | size: 27,
117 | )
118 | ),
119 | Text(
120 | 'سبد خرید',
121 | style: TextStyle(fontSize: 14,fontFamily: 'Yekan',color: Colors.grey.shade700),
122 | ),
123 | ],
124 | ),
125 | ],
126 | ),
127 | ),
128 | ],
129 | );
130 | },
131 | ),
132 | ),
133 | );
134 | }
135 |
136 |
137 | Future getDataFromPrefs() async {
138 | // Obtain shared preferences.
139 | final prefs = await SharedPreferences.getInstance();
140 | final bool loggedIn = prefs.getBool('user_loggedIn') ?? false;
141 |
142 | return loggedIn;
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/lib/features/feature_intro/presentation/screens/intro_main_wrapper.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:besenior_shop_course/common/utils/prefs_operator.dart';
3 | import 'package:besenior_shop_course/common/widgets/main_wrapper.dart';
4 | import 'package:besenior_shop_course/features/feature_intro/presentation/bloc/intro_cubit/intro_cubit.dart';
5 | import 'package:besenior_shop_course/features/feature_intro/presentation/widgets/intro_page.dart';
6 | import 'package:besenior_shop_course/test_screen.dart';
7 | import 'package:delayed_widget/delayed_widget.dart';
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_bloc/flutter_bloc.dart';
10 | import 'package:smooth_page_indicator/smooth_page_indicator.dart';
11 |
12 | import '../../../../locator.dart';
13 | import '../widgets/get_start_btn.dart';
14 |
15 | class IntroMainWrapper extends StatelessWidget {
16 | static const routeName = "/intro_main_wrapper";
17 | IntroMainWrapper({Key? key}) : super(key: key);
18 |
19 | final PageController pageController = PageController();
20 |
21 | final List introPages = [
22 | const IntroPage(title: 'تخصص حرف اول رو میزنه!', description: 'اپلیکیشن تخصصی خرید و فروش انواع قطعات یدکی خودروهای داخلی و خارجی با ضمانت اصالت کالا و نازلترین قیمت',image: "assets/images/benz.png",),
23 | const IntroPage(title: 'آسان خرید و فروش کن!', description: 'خرید و فروش سریع و آسان همراه با تیم پشتیبانی قوی',image: "assets/images/bmw.png",),
24 | const IntroPage(title: 'همه چی اینجا هست!', description: 'ثبت قطعات کمیاب و خرید و فروش عمده تنها با یک کلیک',image: "assets/images/tara.png",),
25 | ];
26 |
27 | @override
28 | Widget build(BuildContext context) {
29 | // final args = ModalRoute.of(context)!.settings.arguments as String;
30 | /// get device size
31 | var height = MediaQuery.of(context).size.height;
32 | var width = MediaQuery.of(context).size.width;
33 |
34 | return BlocProvider(
35 | create: (context) => IntroCubit(),
36 | child: Builder(
37 | builder: (context) {
38 | return Scaffold(
39 | body: Stack(
40 | children: [
41 | /// image
42 | Positioned(
43 | top: 0,
44 | child: Container(
45 | decoration: const BoxDecoration(
46 | color: Colors.amber,
47 | borderRadius: BorderRadius.only(bottomRight: Radius.circular(150))
48 | ),
49 | width: width,
50 | height: height * 0.6,
51 | )
52 | ),
53 |
54 | Positioned(
55 | bottom: height * 0.1,
56 | child: SizedBox(
57 | width: width,
58 | height: height * 0.9,
59 | child: PageView(
60 | onPageChanged: (index){
61 | if(index == 2){
62 | BlocProvider.of(context).changeGetStart(true);
63 | }else{
64 | BlocProvider.of(context).changeGetStart(false);
65 | }
66 | },
67 | controller: pageController,
68 | children: introPages,
69 | )
70 | )),
71 |
72 | /// GetStarted Btn
73 | Positioned(
74 | bottom: height * 0.07,
75 | right: 30,
76 | child: BlocBuilder(
77 | builder: (context, state) {
78 | if(state.showGetStart){
79 | return GetStartBtn(
80 | text: 'شروع کنید',
81 | onTap: (){
82 | PrefsOperator prefsOperator = locator();
83 | prefsOperator.changeIntroState();
84 |
85 | /// goto home screen
86 | Navigator.pushNamedAndRemoveUntil(context, MainWrapper.routeName, ModalRoute.withName("main_wrapper"),);
87 | },
88 | );
89 | }else{
90 | return DelayedWidget(
91 | delayDuration: const Duration(milliseconds: 500),// Not required
92 | animationDuration: const Duration(seconds: 1),// Not required
93 | animation: DelayedAnimations.SLIDE_FROM_BOTTOM,// Not required
94 | child: GetStartBtn(
95 | text: 'ورق بزن',
96 | onTap: (){
97 | if(pageController.page!.toInt() < 2){
98 | if(pageController.page!.toInt() == 1){
99 | BlocProvider.of(context).changeGetStart(true);
100 | }
101 |
102 | pageController.animateToPage(
103 | pageController.page!.toInt() + 1,
104 | duration: const Duration(milliseconds: 400),
105 | curve: Curves.easeIn
106 | );
107 | }
108 | },
109 | ),
110 | );
111 | }
112 |
113 | },
114 | ),
115 | ),
116 |
117 | Positioned(
118 | bottom: height * 0.07,
119 | left: 30,
120 | child: DelayedWidget(
121 | delayDuration: const Duration(milliseconds: 300),// Not required
122 | animationDuration: const Duration(seconds: 1),// Not required
123 | animation: DelayedAnimations.SLIDE_FROM_BOTTOM,// Not required
124 | child: SmoothPageIndicator(
125 | controller: pageController,
126 | count: 3,
127 | effect: ExpandingDotsEffect(
128 | dotWidth: 10,
129 | dotHeight: 10,
130 | spacing: 5,
131 | activeDotColor: Colors.amber
132 | ),
133 | )
134 | ),
135 | )
136 | ],
137 | ),
138 | );
139 | }
140 | ),
141 | );
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/lib/features/feature_product/presentation/widgets/search_textfield.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:besenior_shop_course/common/blocs/searchbox_cubit.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_bloc/flutter_bloc.dart';
5 | import 'package:flutter_typeahead/flutter_typeahead.dart';
6 |
7 | import '../../../../common/params/products_params.dart';
8 | import '../../data/models/all_products_model.dart';
9 | import '../../repositories/all_product_repository.dart';
10 |
11 |
12 | class SearchTextField extends StatelessWidget {
13 | final TextEditingController controller;
14 | final AllProductsRepository allProductsRepository;
15 | SearchTextField({Key? key,required this.controller,required this.allProductsRepository}) : super(key: key);
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return BlocProvider(
20 | create: (context) => SearchboxCubit(),
21 | child: Builder(
22 | builder: (context) {
23 | return Material(
24 | child: Stack(
25 | alignment: AlignmentDirectional.center,
26 | children: [
27 | /// textfield
28 | SizedBox(
29 | height: 40,
30 | child: TypeAheadField(
31 | noItemsFoundBuilder: (context){
32 | return ListTile(
33 | title: Text("محصولی یافت نشد."),
34 | );
35 | },
36 | textFieldConfiguration: TextFieldConfiguration(
37 | onTap: () {
38 | BlocProvider.of(context).changeVisibility(false);
39 |
40 | if (controller.text[controller.text.length - 1] != ' ') {
41 | controller.text = (controller.text + ' ');
42 | }
43 | if (controller.selection ==TextSelection.fromPosition(
44 | TextPosition(offset:
45 | controller.text.length - 1))) {
46 | }
47 | },
48 | onSubmitted: (String prefix) {
49 | // Navigator.pushNamed(
50 | // context,
51 | // AllProductsScreen.routeName,
52 | // arguments: ProductsArguments(searchTxt: prefix),);
53 | },
54 | controller: controller,
55 | style: const TextStyle(color: Colors.black,fontSize: 14,fontWeight: FontWeight.w400),
56 | decoration: InputDecoration(
57 | fillColor: Colors.grey.shade200,
58 | filled: true,
59 | contentPadding: EdgeInsets.all(3),
60 | prefixIcon: Container(
61 | child: IconButton(
62 | icon: const Icon(Icons.search,color: Colors.grey,),
63 | onPressed: () {
64 | // Navigator.pushNamed(
65 | // context,
66 | // AllProductsScreen.routeName,
67 | // arguments: ProductsArguments(searchTxt: controller.text),);
68 | },
69 | )
70 | ),
71 | enabledBorder:OutlineInputBorder(
72 | borderRadius: BorderRadius.circular(15.0),
73 | borderSide: const BorderSide(
74 | color: Colors.transparent,
75 | width: 0.0,
76 | ),
77 | ),
78 | border: OutlineInputBorder(
79 | borderRadius: BorderRadius.circular(15.0),
80 | borderSide: const BorderSide(
81 | color: Colors.blue,
82 | width: 0.0,
83 | ),
84 | ),
85 | focusedBorder: OutlineInputBorder(
86 | borderRadius: BorderRadius.circular(15.0),
87 | borderSide: const BorderSide(
88 | color: Colors.blue,
89 | width: 2.0,
90 | ),
91 | ),),
92 | ),
93 | suggestionsCallback: (String prefix){
94 | return allProductsRepository.fetchAllProductsDataSearch(ProductsParams(step: 6,search: prefix));
95 | },
96 | itemBuilder: (context, Products model){
97 | return ListTile(
98 | title: Text(model.name!,style: const TextStyle(color: Colors.black,fontSize: 14,fontWeight: FontWeight.w400),),
99 | );
100 | },
101 | onSuggestionSelected: (Products products){
102 | controller.text = products.name!;
103 | // Navigator.pushNamed(
104 | // context,
105 | // AllProductsScreen.routeName,
106 | // arguments: ProductsArguments(searchTxt: products.name!),);
107 | }
108 | ),
109 | ),
110 |
111 | /// text and logo
112 | IgnorePointer(
113 | child: BlocBuilder(
114 | builder: (context, state) {
115 | return Visibility(
116 | visible: state,
117 | child: Padding(
118 | padding: const EdgeInsets.only(right: 50.0),
119 | child: Row(
120 | children: [
121 | const Text('جستوجو در', style: TextStyle(fontSize: 17,fontFamily: "Yekan",color: Colors.grey, fontWeight: FontWeight.w200),),
122 | ColorFiltered(
123 | colorFilter: ColorFilter.mode(Colors.grey.shade800, BlendMode.srcIn),
124 | child: Image.asset("assets/images/bs_logo_textfield.png",height: 28,)),
125 | ],
126 | ),
127 | ),
128 | );
129 | },
130 | ),
131 | )
132 | ],
133 | ),
134 | );
135 | }
136 | ),
137 | );
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/assets/images/amazing.svg:
--------------------------------------------------------------------------------
1 |
20 |
--------------------------------------------------------------------------------
/lib/features/feature_product/presentation/widgets/products_grid.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_bloc/flutter_bloc.dart';
4 | import 'package:persian_number_utility/persian_number_utility.dart';
5 | import '../../../../../common/widgets/dot_loading_widget.dart';
6 | import 'package:cached_network_image/cached_network_image.dart';
7 | import 'package:delayed_widget/delayed_widget.dart';
8 |
9 | import '../../../../common/params/products_params.dart';
10 | import '../../../../common/widgets/paging_loading_widget.dart';
11 | import '../../data/models/all_products_model.dart';
12 | import '../bloc/all_poducts_cubit/all_products_cubit.dart';
13 |
14 | class ProductsGrid extends StatelessWidget {
15 | final int? categoryId;
16 | final int? sellerId;
17 | final String? searchText;
18 | ProductsGrid({Key? key, this.categoryId, this.sellerId, this.searchText}) : super(key: key);
19 |
20 | final ScrollController scrollController = ScrollController();
21 |
22 | @override
23 | Widget build(BuildContext context) {
24 |
25 | setupScrollController(context);
26 | /// call api for data
27 | BlocProvider.of(context).loadProductsData(ProductsParams(categories: categoryId,search: searchText ?? ""));
28 |
29 | return BlocBuilder(
30 | builder: (context, state) {
31 |
32 | if(state.productsDataStatus is ProductsDataLoading){
33 | return const Center(child: DotLoadingWidget(size: 30));
34 | }
35 |
36 | if(state.productsDataStatus is ProductsDataCompleted){
37 | ProductsDataCompleted productsDataCompleted = state.productsDataStatus as ProductsDataCompleted;
38 | AllProductsModel allProductsModel = productsDataCompleted.allProductsModel;
39 | List allProducts = state.allProducts;
40 |
41 | return RefreshIndicator(
42 | onRefresh: () async {
43 | // BlocProvider.of(context).add(ResetNextStartEvent());
44 | BlocProvider.of(context).loadProductsData(ProductsParams(categories: categoryId,search: searchText ?? ""));
45 | },
46 | child: Padding(
47 | padding: const EdgeInsets.symmetric(horizontal: 10.0),
48 | child: Column(
49 | children: [
50 | const SizedBox(height: 10,),
51 | /// filter btn
52 | SizedBox(
53 | height: 60,
54 | width: double.infinity,
55 | child: ElevatedButton(
56 | style: ElevatedButton.styleFrom(
57 | backgroundColor: Colors.white,
58 | elevation: 3,
59 | shadowColor: Colors.redAccent,
60 | shape: RoundedRectangleBorder(
61 | borderRadius: BorderRadius.circular(10)
62 | )
63 | ),
64 | onPressed: (){
65 | // showFilterBottomSheet(context);
66 | },
67 | child: Row(
68 | mainAxisAlignment: MainAxisAlignment.center,
69 | children: const [
70 | Text('فیلتر', style: TextStyle(fontFamily: 'Vazir', color: Colors.black),),
71 | Icon(Icons.filter_list_alt,color: Colors.black),
72 | ],
73 | )
74 | ),
75 | ),
76 |
77 | const SizedBox(height: 10),
78 |
79 | /// all product gridview or no products for show
80 | (allProducts.isNotEmpty)
81 | ? Expanded(
82 | child: GridView.builder(
83 | controller: scrollController,
84 | padding: const EdgeInsets.only(top: 10),
85 | itemCount: allProducts.length,
86 | // itemCount: allProductsModel.data![0].products!.length,
87 | gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
88 | crossAxisCount: 2,
89 | crossAxisSpacing: 8.0,
90 | mainAxisSpacing: 8.0,
91 | childAspectRatio: 0.65
92 | ),
93 | itemBuilder: (BuildContext context, int index){
94 | final productImage = allProducts[index].image;
95 | final productName = allProducts[index].name;
96 | final productCategoryName = allProducts[index].category;
97 | final productDiscount = allProducts[index].discount;
98 | final productPrice = allProducts[index].price;
99 | final productPriceBeforeDiscount = allProducts[index].priceBeforDiscount;
100 |
101 | return GestureDetector(
102 | onTap: (){
103 | /// goto All products screen
104 | // Navigator.pushNamed(context, ProductDetailScreen.routeName, arguments: ProductDetailArguments(allProducts[index].id!),);
105 | },
106 | child: Container(
107 | decoration: BoxDecoration(
108 | color: Colors.white,
109 | borderRadius: BorderRadius.circular(20),
110 | boxShadow: const [
111 | BoxShadow(
112 | blurRadius: 5,
113 | offset: Offset(2, 2),
114 | color: Colors.grey
115 | )
116 | ]
117 | ),
118 | child: Padding(
119 | padding: const EdgeInsets.symmetric(horizontal: 8.0),
120 | child: Column(
121 | crossAxisAlignment: CrossAxisAlignment.start,
122 | children: [
123 |
124 | /// product image
125 | ClipRRect(
126 | borderRadius: BorderRadius.circular(20),
127 | child: CachedNetworkImage(
128 | imageUrl: productImage!,
129 | placeholder: (context, string){
130 | return const SizedBox(
131 | height: 100,
132 | );
133 | },
134 | errorWidget: (context, string, dynamic){
135 | return Icon(Icons.error,color: Colors.black,);
136 | },
137 | fit: BoxFit.cover,
138 | useOldImageOnUrlChange: true,
139 | ),
140 | ),
141 |
142 | /// product name
143 | Text(productName!, style: const TextStyle(fontFamily: 'Vazir',color: Colors.black, fontWeight: FontWeight.bold,fontSize: 12),),
144 | Text(productCategoryName!, style: const TextStyle(fontFamily: 'Vazir',color: Colors.grey, fontWeight: FontWeight.bold,fontSize: 12),),
145 | SizedBox(height: 10,),
146 |
147 | /// product price and discount
148 | Row(
149 | children: [
150 |
151 | /// discount red container
152 | (productDiscount != 0)
153 | ? Container(
154 | width: 40,
155 | height: 30,
156 | decoration: BoxDecoration(
157 | color: Colors.red,
158 | borderRadius: BorderRadius.circular(20)
159 | ),
160 | child: Center(child: Text("${productDiscount.toString().toPersianDigit()}%", style: const TextStyle(fontFamily: 'Vazir',color: Colors.white, fontWeight: FontWeight.bold,fontSize: 13),)),
161 | )
162 | : Container(),
163 |
164 | const Spacer(),
165 |
166 | Row(
167 | crossAxisAlignment: CrossAxisAlignment.start,
168 | children: [
169 | Column(
170 | children: [
171 | Text(productPrice.toString().toPersianDigit(), style: const TextStyle(fontFamily: 'Vazir',color: Colors.black, fontWeight: FontWeight.bold,fontSize: 13),),
172 | (productPriceBeforeDiscount != "0")
173 | ? Text(productPriceBeforeDiscount!.toPersianDigit(), style: const TextStyle(fontFamily: 'Vazir',color: Colors.black, fontWeight: FontWeight.bold,fontSize: 11,decoration: TextDecoration.lineThrough),)
174 | : Container(),
175 | ],
176 | ),
177 | SizedBox(width: 5,),
178 | const Text('تومان', style: TextStyle(fontFamily: 'Vazir',color: Colors.black, fontWeight: FontWeight.bold,fontSize: 10),),
179 | ],
180 | ),
181 | ],
182 | ),
183 | ],
184 | ),
185 | ),
186 | ),
187 | );
188 | },
189 | ),
190 | )
191 | : const Expanded(child: Center(child: Text('محصولی برای نمایش وجود ندارد', style: TextStyle(fontFamily: 'Vazir', color: Colors.black)),)),
192 |
193 |
194 | /// paging loading
195 | (state.isLoadingPaging)
196 | ? Padding(
197 | padding: const EdgeInsets.symmetric(vertical: 15.0),
198 | child: DelayedWidget(
199 | delayDuration: const Duration(milliseconds: 100),// Not required
200 | animationDuration: const Duration(milliseconds: 500),// Not required
201 | animation: DelayedAnimations.SLIDE_FROM_BOTTOM,
202 | child: const PagingLoadingWidget(size: 40)
203 | ),
204 | )
205 | : Container(),
206 | ],
207 | ),
208 | ),
209 | );
210 | }
211 |
212 | if(state.productsDataStatus is ProductsDataError){
213 | final ProductsDataError productsDataError = state.productsDataStatus as ProductsDataError;
214 |
215 | return Center(
216 | child: Column(
217 | mainAxisAlignment: MainAxisAlignment.center,
218 | children: [
219 | Text(productsDataError.errorMessage,style: const TextStyle(color: Colors.black),),
220 | const SizedBox(height: 10,),
221 | ElevatedButton(
222 | style: ElevatedButton.styleFrom(backgroundColor: Colors.redAccent),
223 | onPressed: (){
224 | /// call all data again
225 | BlocProvider.of(context).loadProductsData(ProductsParams(categories: categoryId,search: searchText ?? ""));
226 |
227 | },
228 | child: const Text("تلاس دوباره"),)
229 | ],
230 | ),
231 | );
232 | }
233 |
234 | return Container();
235 | },
236 | );
237 | }
238 |
239 | void setupScrollController(BuildContext context){
240 | scrollController.addListener(() {
241 | if(scrollController.position.atEdge){
242 | if(scrollController.position.pixels != 0){
243 | BlocProvider.of(context).loadProductsData(ProductsParams(categories: categoryId,search: searchText ?? ""));
244 | }
245 | }
246 | });
247 | }
248 |
249 | // void showFilterBottomSheet(ct) {
250 | // showModalBottomSheet(
251 | // context: ct,
252 | // isScrollControlled: true,
253 | // shape: const RoundedRectangleBorder(
254 | // borderRadius: BorderRadius.only(topRight: Radius.circular(20),topLeft: Radius.circular(20)),
255 | // ),
256 | // builder: (context){
257 | // return BlocProvider.value(
258 | // value: BlocProvider.of(ct),
259 | // child: BlocProvider.value(
260 | // value: BlocProvider.of(ct),
261 | // child: FilterBottomSheet(categoryId: categoryId,)),
262 | // );
263 | // });
264 | // }
265 | }
266 |
--------------------------------------------------------------------------------