├── lib ├── core │ ├── route_config.dart │ ├── utils │ │ ├── type_def.dart │ │ ├── custom_snack_bar.dart │ │ └── image_picker.dart │ ├── providers │ │ ├── api_service_provider.dart │ │ ├── shared_preference_provider.dart │ │ ├── user_provider.dart │ │ ├── product_provider.dart │ │ └── repos_provider.dart │ ├── common │ │ ├── loader.dart │ │ ├── stars.dart │ │ └── bottom_bar.dart │ ├── theme │ │ └── theme.dart │ ├── constant │ │ └── constants.dart │ └── errors │ │ └── failire.dart ├── models │ ├── sales.dart │ ├── rating.dart │ ├── product.dart │ ├── order.dart │ └── user.dart ├── features │ ├── admin │ │ ├── view │ │ │ ├── widgets │ │ │ │ ├── category_product_chart.dart │ │ │ │ └── custom_app_bar_admin.dart │ │ │ ├── admin_view.dart │ │ │ └── all-orders │ │ │ │ ├── repo │ │ │ │ └── order_repo.dart │ │ │ │ ├── controller │ │ │ │ └── order_controller.dart │ │ │ │ └── orders_screen.dart │ │ ├── analytics │ │ │ ├── controller │ │ │ │ └── analytics_controller.dart │ │ │ ├── repo │ │ │ │ └── analytics_repo.dart │ │ │ └── analytics_screen.dart │ │ ├── add_product │ │ │ ├── repo │ │ │ │ └── add_product_repo.dart │ │ │ └── controller │ │ │ │ └── add_product_controller.dart │ │ └── posts │ │ │ ├── repo │ │ │ └── posts_repo.dart │ │ │ ├── controller │ │ │ └── posts_controller.dart │ │ │ └── view │ │ │ └── posts_view.dart │ ├── home │ │ ├── view │ │ │ ├── test_view.dart │ │ │ ├── widgets │ │ │ │ ├── carousel_image.dart │ │ │ │ ├── address_box.dart │ │ │ │ ├── top_categories.dart │ │ │ │ ├── custom_app_bar_home.dart │ │ │ │ └── deal_of_day.dart │ │ │ └── home_view.dart │ │ ├── repo │ │ │ └── home_repo.dart │ │ └── controllers │ │ │ └── home_controller.dart │ ├── auth │ │ ├── view │ │ │ └── login │ │ │ │ └── widgets │ │ │ │ ├── header.dart │ │ │ │ ├── custom_button.dart │ │ │ │ ├── custom_text_field.dart │ │ │ │ └── methode_selection.dart │ │ ├── repository │ │ │ └── auth_repo.dart │ │ └── controller │ │ │ └── auth_controller.dart │ ├── account │ │ ├── view │ │ │ ├── account_view.dart │ │ │ └── widgets │ │ │ │ ├── single_product.dart │ │ │ │ ├── top_button.dart │ │ │ │ ├── account_button.dart │ │ │ │ ├── below_app_bar.dart │ │ │ │ ├── custom_app_bar.dart │ │ │ │ └── orders.dart │ │ ├── repo │ │ │ └── account_repo.dart │ │ └── controller │ │ │ └── account_controller.dart │ ├── splash │ │ └── splash_view.dart │ ├── cart │ │ ├── repo │ │ │ └── cart_repo.dart │ │ ├── view │ │ │ ├── widgets │ │ │ │ ├── cart_subtotal.dart │ │ │ │ └── cart_product.dart │ │ │ └── cart_view.dart │ │ └── controller │ │ │ └── cart_controller.dart │ ├── search │ │ ├── repo │ │ │ └── search_repo.dart │ │ ├── controller │ │ │ └── search_controller.dart │ │ └── view │ │ │ └── widget │ │ │ └── searched_product.dart │ ├── category │ │ ├── repo │ │ │ └── category_repo.dart │ │ ├── controller │ │ │ └── category_controller.dart │ │ └── view │ │ │ └── category_view.dart │ ├── adress │ │ ├── repo │ │ │ └── adress_repo.dart │ │ └── controller │ │ │ └── adress_controller.dart │ └── product-detaills │ │ ├── repo │ │ └── product_detaills_repo.dart │ │ └── controller │ │ └── product_detaills_controller.dart └── main.dart ├── ios ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── 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 ├── 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 ├── .gitattributes ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── manifest.json └── index.html ├── 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 │ │ │ │ │ └── amazon_clone │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle └── build.gradle ├── assets ├── images │ ├── books.jpeg │ ├── fashion.jpeg │ ├── mobiles.jpeg │ ├── amazon_in.png │ ├── appliances.jpeg │ ├── electronics.jpeg │ └── essentials.jpeg ├── applepay.json └── gpay.json ├── server ├── package.json ├── models │ ├── rating.js │ ├── order.js │ ├── product.js │ └── user.js ├── mongoDbConnect.js ├── routes │ ├── auth.js │ ├── product.js │ ├── user.js │ └── admin.js ├── middlewars │ ├── auth.js │ └── admin.js ├── app.js └── controllers │ ├── product.js │ ├── auth.js │ ├── user.js │ └── admin.js ├── .gitignore ├── analysis_options.yaml ├── .metadata ├── README.md └── pubspec.yaml /lib/core/route_config.dart: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1FarZ1/Amazone_clone_Flutter_NodeJs/HEAD/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1FarZ1/Amazone_clone_Flutter_NodeJs/HEAD/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1FarZ1/Amazone_clone_Flutter_NodeJs/HEAD/web/icons/Icon-512.png -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /assets/images/books.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1FarZ1/Amazone_clone_Flutter_NodeJs/HEAD/assets/images/books.jpeg -------------------------------------------------------------------------------- /assets/images/fashion.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1FarZ1/Amazone_clone_Flutter_NodeJs/HEAD/assets/images/fashion.jpeg -------------------------------------------------------------------------------- /assets/images/mobiles.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1FarZ1/Amazone_clone_Flutter_NodeJs/HEAD/assets/images/mobiles.jpeg -------------------------------------------------------------------------------- /assets/images/amazon_in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1FarZ1/Amazone_clone_Flutter_NodeJs/HEAD/assets/images/amazon_in.png -------------------------------------------------------------------------------- /assets/images/appliances.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1FarZ1/Amazone_clone_Flutter_NodeJs/HEAD/assets/images/appliances.jpeg -------------------------------------------------------------------------------- /assets/images/electronics.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1FarZ1/Amazone_clone_Flutter_NodeJs/HEAD/assets/images/electronics.jpeg -------------------------------------------------------------------------------- /assets/images/essentials.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1FarZ1/Amazone_clone_Flutter_NodeJs/HEAD/assets/images/essentials.jpeg -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1FarZ1/Amazone_clone_Flutter_NodeJs/HEAD/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1FarZ1/Amazone_clone_Flutter_NodeJs/HEAD/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /lib/models/sales.dart: -------------------------------------------------------------------------------- 1 | class Sales { 2 | final String label; 3 | final int earning; 4 | 5 | Sales(this.label, this.earning); 6 | } -------------------------------------------------------------------------------- /lib/core/utils/type_def.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | 3 | import '../errors/failire.dart'; 4 | 5 | typedef FutureEither = Future>; -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1FarZ1/Amazone_clone_Flutter_NodeJs/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/1FarZ1/Amazone_clone_Flutter_NodeJs/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/1FarZ1/Amazone_clone_Flutter_NodeJs/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/1FarZ1/Amazone_clone_Flutter_NodeJs/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1FarZ1/Amazone_clone_Flutter_NodeJs/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1FarZ1/Amazone_clone_Flutter_NodeJs/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1FarZ1/Amazone_clone_Flutter_NodeJs/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1FarZ1/Amazone_clone_Flutter_NodeJs/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1FarZ1/Amazone_clone_Flutter_NodeJs/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/1FarZ1/Amazone_clone_Flutter_NodeJs/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/1FarZ1/Amazone_clone_Flutter_NodeJs/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/1FarZ1/Amazone_clone_Flutter_NodeJs/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/1FarZ1/Amazone_clone_Flutter_NodeJs/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/1FarZ1/Amazone_clone_Flutter_NodeJs/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/1FarZ1/Amazone_clone_Flutter_NodeJs/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/1FarZ1/Amazone_clone_Flutter_NodeJs/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/1FarZ1/Amazone_clone_Flutter_NodeJs/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/1FarZ1/Amazone_clone_Flutter_NodeJs/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/1FarZ1/Amazone_clone_Flutter_NodeJs/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/1FarZ1/Amazone_clone_Flutter_NodeJs/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/1FarZ1/Amazone_clone_Flutter_NodeJs/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1FarZ1/Amazone_clone_Flutter_NodeJs/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/1FarZ1/Amazone_clone_Flutter_NodeJs/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/amazon_clone/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.amazon_clone 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/core/providers/api_service_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/api_service.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | 4 | final apiServiceProvider = Provider((ref) { 5 | return ApiService(); 6 | }); -------------------------------------------------------------------------------- /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.5-all.zip 6 | -------------------------------------------------------------------------------- /lib/core/utils/custom_snack_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | void showSnackBar(BuildContext context, String text) { 4 | ScaffoldMessenger.of(context).showSnackBar( 5 | SnackBar( 6 | content: Text(text), 7 | ), 8 | ); 9 | } -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@ptkdev/logger": "^1.8.0", 4 | "bcryptjs": "^2.4.3", 5 | "express": "^4.18.2", 6 | "jsonwebtoken": "^9.0.0", 7 | "mongoose": "^7.0.4", 8 | "nodemon": "^2.0.22" 9 | }, 10 | "type": "module" 11 | } 12 | -------------------------------------------------------------------------------- /server/models/rating.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const ratingSchema = mongoose.Schema({ 4 | userId: { 5 | type: String, 6 | required: true, 7 | }, 8 | rating: { 9 | type: Number, 10 | required: true, 11 | }, 12 | }); 13 | 14 | export default ratingSchema; -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/core/common/loader.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class Loader extends StatelessWidget { 4 | const Loader({Key? key}) : super(key: key); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return const Center( 9 | child: CircularProgressIndicator(), 10 | ); 11 | } 12 | } -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/core/providers/shared_preference_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | import 'package:shared_preferences/shared_preferences.dart'; 5 | 6 | final sharedPreferenceProvider = 7 | Provider((ref) { 8 | try { 9 | var pref = SharedPreferences.getInstance(); 10 | return pref; 11 | } catch (e) { 12 | log(e.toString()); 13 | return null; 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /server/mongoDbConnect.js: -------------------------------------------------------------------------------- 1 | import {connect,set} from 'mongoose'; 2 | 3 | const dbURI= "mongodb+srv://Farz:mrfares77@cluster0.6zstawu.mongodb.net/?retryWrites=true&w=majority"; 4 | 5 | set('strictQuery', true); 6 | const connectDb = ()=>{ 7 | connect(dbURI,{useNewUrlParser:true, useUnifiedTopology:true}).then((result)=>console.log("connected to db" + result) 8 | ).catch((err)=>console.log(err)); 9 | } 10 | 11 | export default connectDb; 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/core/providers/user_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 2 | import '../../models/user.dart'; 3 | 4 | final userStateProvider = StateNotifierProvider((ref) { 5 | return UserSt(); 6 | }); 7 | 8 | class UserSt extends StateNotifier { 9 | UserSt() : super(User.empty()); 10 | 11 | void setUser(User user) { 12 | state = user; 13 | } 14 | 15 | void removeUser() { 16 | state = User.empty(); 17 | } 18 | 19 | bool isUserLoggedIn() { 20 | return !(state.equals(User.empty())); 21 | } 22 | } -------------------------------------------------------------------------------- /lib/core/theme/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/constant/constants.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class AppTheme { 5 | static ThemeData customTheme = ThemeData( 6 | scaffoldBackgroundColor: AppConsts.backgroundColor, 7 | colorScheme: const ColorScheme.dark( 8 | primary: AppConsts.secondaryColor, 9 | ), 10 | appBarTheme: const AppBarTheme( 11 | elevation: 0, 12 | iconTheme: IconThemeData( 13 | color: Colors.black, 14 | ), 15 | ), 16 | useMaterial3: true, // can remove this line 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /lib/features/admin/view/widgets/category_product_chart.dart: -------------------------------------------------------------------------------- 1 | import 'package:charts_flutter/flutter.dart' as charts; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../../../../models/sales.dart'; 5 | 6 | class CategoryProductsChart extends StatelessWidget { 7 | final List> seriesList; 8 | const CategoryProductsChart({ 9 | Key? key, 10 | required this.seriesList, 11 | }) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return charts.BarChart( 16 | seriesList, 17 | animate: true, 18 | ); 19 | } 20 | } -------------------------------------------------------------------------------- /lib/core/utils/image_picker.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'dart:developer'; 3 | import 'dart:io'; 4 | import 'package:file_picker/file_picker.dart'; 5 | 6 | Future> pickImages() async { 7 | List images = []; 8 | try { 9 | var files = await FilePicker.platform.pickFiles( 10 | type: FileType.image, 11 | allowMultiple: true, 12 | ); 13 | if (files != null && files.files.isNotEmpty) { 14 | for (int i = 0; i < files.files.length; i++) { 15 | images.add(File(files.files[i].path!)); 16 | } 17 | } 18 | } catch (e) { 19 | log(e.toString()); 20 | } 21 | return images; 22 | } -------------------------------------------------------------------------------- /server/routes/auth.js: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import { signUp, signIn,tokenValid,getUser } from "../controllers/auth.js"; 3 | import auth from "../middlewars/auth.js"; 4 | 5 | 6 | const domaineName="/api"; 7 | 8 | 9 | const router = Router(); 10 | // hada howa router , app thdr m3ah we howa yhdr m3a controller 11 | // router.post( domaineName + "/signout",signOut); 12 | router.post( domaineName + "/signup",signUp); 13 | router.post( domaineName + "/tokenIsValid",tokenValid); 14 | router.post( domaineName + "/signin",signIn); 15 | 16 | router.get("/", auth,getUser); 17 | 18 | 19 | export default router; 20 | -------------------------------------------------------------------------------- /lib/features/home/view/test_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/common/bottom_bar.dart'; 2 | import 'package:amazon_clone/features/account/view/account_view.dart'; 3 | import 'package:amazon_clone/features/cart/view/cart_view.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | import 'home_view.dart'; 7 | 8 | class TestView extends StatelessWidget { 9 | const TestView({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return const BottomBar( 14 | paramPages: [ 15 | HomeView(), 16 | AccountView(), 17 | CartView() 18 | ], 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/features/auth/view/login/widgets/header.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../../../../../core/constant/constants.dart'; 4 | import '../login_view.dart'; 5 | 6 | class Header extends StatelessWidget { 7 | const Header({ 8 | super.key, 9 | required this.auth, 10 | }); 11 | 12 | final Auth auth; 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Text( 17 | auth == Auth.signup ? "Welcome Back" : "Create Account", 18 | style: const TextStyle( 19 | color: AppConsts.textColor, 20 | fontSize: 30, 21 | fontWeight: FontWeight.w500, 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server/routes/product.js: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import {getCategoryProducts,getSearchProducts,rateProduct,getDealOfTheDay} from "../controllers/product.js"; 3 | import auth from "../middlewars/auth.js"; 4 | 5 | const productRouter = Router(); 6 | let domaineName = "/api/products"; 7 | 8 | 9 | productRouter.get(domaineName + "/getCategoryProducts",auth,getCategoryProducts); 10 | productRouter.get(domaineName + "/getSearchProducts",auth,getSearchProducts); 11 | productRouter.get(domaineName + "/get-deal-of-the-day",auth,getDealOfTheDay); 12 | productRouter.post(domaineName+ "/rate-product", auth,rateProduct); 13 | 14 | 15 | 16 | export default productRouter; 17 | -------------------------------------------------------------------------------- /server/routes/user.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import auth from '../middlewars/auth.js' 3 | import {addToCart,removeFromCart,saveUserAdress,placeOrder, allOrders} from '../controllers/user.js'; 4 | 5 | const userRouter = express.Router(); 6 | 7 | let domaineName="/api"; 8 | 9 | userRouter.post(domaineName + "/add-to-cart", auth,addToCart); 10 | userRouter.delete(domaineName + "/remove-from-cart", auth,removeFromCart); 11 | userRouter.post(domaineName + "/save-user-adress", auth,saveUserAdress); 12 | userRouter.post(domaineName + "/order",auth,placeOrder); 13 | userRouter.get(domaineName + "/orders/me", auth, allOrders); 14 | 15 | export default userRouter; -------------------------------------------------------------------------------- /lib/models/rating.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | class Rating { 4 | final String userId; 5 | final double rating; 6 | Rating({ 7 | required this.userId, 8 | required this.rating, 9 | }); 10 | 11 | Map toMap() { 12 | return { 13 | 'userId': userId, 14 | 'rating': rating, 15 | }; 16 | } 17 | 18 | factory Rating.fromMap(Map map) { 19 | return Rating( 20 | userId: map['userId'] ?? '', 21 | rating: map['rating']?.toDouble() ?? 0.0, 22 | ); 23 | } 24 | 25 | String toJson() => json.encode(toMap()); 26 | 27 | factory Rating.fromJson(String source) => Rating.fromMap(json.decode(source)); 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 | -------------------------------------------------------------------------------- /lib/core/common/stars.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/constant/constants.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_rating_bar/flutter_rating_bar.dart'; 4 | 5 | class Stars extends StatelessWidget { 6 | final double rating; 7 | const Stars({ 8 | Key? key, 9 | required this.rating, 10 | }) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return RatingBarIndicator( 15 | direction: Axis.horizontal, 16 | itemCount: 5, 17 | rating: rating, 18 | itemSize: 15, 19 | itemBuilder: (context, _) => const Icon( 20 | Icons.star, 21 | color: AppConsts.secondaryColor, 22 | ), 23 | ); 24 | } 25 | } -------------------------------------------------------------------------------- /lib/core/providers/product_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 2 | 3 | import '../../models/product.dart'; 4 | 5 | final productStProvider = StateNotifierProvider>((ref) { 6 | return ProductSt(); 7 | }); 8 | 9 | class ProductSt extends StateNotifier> { 10 | ProductSt() : super([]); 11 | 12 | void addProduct(Product p) async { 13 | state.add(p); 14 | } 15 | 16 | void removeProduct(Product p) async { 17 | state.remove(p); 18 | } 19 | 20 | void updateProduct(Product p) async { 21 | state.where((element) { 22 | if (element.id == p.id) { 23 | element = p; 24 | } 25 | return true; 26 | }); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/features/account/view/account_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'widgets/below_app_bar.dart'; 4 | import 'widgets/custom_app_bar.dart'; 5 | import 'widgets/orders.dart'; 6 | import 'widgets/top_button.dart'; 7 | 8 | class AccountView extends StatelessWidget { 9 | const AccountView({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Scaffold( 14 | appBar: const CustomAppBar(), 15 | body: Column( 16 | children: const [ 17 | BelowAppBar(), 18 | SizedBox(height: 10), 19 | TopButtons(), 20 | SizedBox(height: 20), 21 | Orders(), 22 | ], 23 | )); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.7.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.2.0' 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 | -------------------------------------------------------------------------------- /server/models/order.js: -------------------------------------------------------------------------------- 1 | import { Schema, model } from "mongoose"; 2 | import { productSchema } from "./product.js"; 3 | 4 | const orderSchema = Schema({ 5 | products: [ 6 | { 7 | product: productSchema, 8 | quantity: { 9 | type: Number, 10 | required: true, 11 | }, 12 | }, 13 | ], 14 | totalPrice: { 15 | type: Number, 16 | required: true, 17 | }, 18 | address: { 19 | type: String, 20 | required: true, 21 | }, 22 | userId: { 23 | required: true, 24 | type: String, 25 | }, 26 | orderedAt: { 27 | type: Number, 28 | required: true, 29 | }, 30 | status: { 31 | type: Number, 32 | default: 0, 33 | }, 34 | }); 35 | 36 | const Order = model("Order", orderSchema); 37 | export default Order; 38 | -------------------------------------------------------------------------------- /server/routes/admin.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import admin from '../middlewars/admin.js' 3 | import {addProduct,allProducts,changeOrderStatus,deleteProduct, getAnalytics, getOrders } from '../controllers/admin.js' 4 | 5 | let adminRoute=express.Router(); 6 | 7 | 8 | let domaineName="/admin"; 9 | 10 | adminRoute.post(domaineName + "/add-product",admin,addProduct); 11 | adminRoute.get(domaineName + "/all-products",admin,allProducts); 12 | adminRoute.post(domaineName + "/delete-product",admin,deleteProduct); 13 | adminRoute.get(domaineName + "/get-orders", admin,getOrders); 14 | adminRoute.post(domaineName + "/change-order-status", admin, changeOrderStatus); 15 | adminRoute.get(domaineName + "/analytics", admin, getAnalytics); 16 | 17 | 18 | 19 | 20 | export default adminRoute ; 21 | -------------------------------------------------------------------------------- /server/models/product.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | import ratingSchema from "./rating.js"; 3 | 4 | const productSchema = mongoose.Schema({ 5 | name: { 6 | type: String, 7 | required: true, 8 | trim: true, 9 | }, 10 | description: { 11 | type: String, 12 | required: true, 13 | trim: true, 14 | }, 15 | images: [ 16 | { 17 | type: String, 18 | required: true, 19 | }, 20 | ], 21 | quantity: { 22 | type: Number, 23 | required: true, 24 | }, 25 | price: { 26 | type: Number, 27 | required: true, 28 | }, 29 | category: { 30 | type: String, 31 | required: true, 32 | }, 33 | ratings: [ratingSchema], 34 | }); 35 | 36 | const Product = mongoose.model("Product", productSchema); 37 | export {Product , productSchema}; -------------------------------------------------------------------------------- /lib/features/admin/view/admin_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/features/admin/analytics/analytics_screen.dart'; 2 | import 'package:amazon_clone/features/admin/view/widgets/custom_app_bar_admin.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | import '../../../core/common/bottom_bar.dart'; 6 | import '../posts/view/posts_view.dart'; 7 | import 'all-orders/orders_screen.dart'; 8 | 9 | class AdminView extends StatelessWidget { 10 | const AdminView({super.key}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return const Scaffold( 15 | appBar: CustomAppBarAdmin(), 16 | body: BottomBar( 17 | paramPages: [ 18 | PostsScreen(), 19 | AnalyticsScreen(), 20 | OrdersScreen(), 21 | ], 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server/middlewars/auth.js: -------------------------------------------------------------------------------- 1 | import jwt from "jsonwebtoken"; 2 | 3 | 4 | const auth = async (req, res, next) => { 5 | try { 6 | console.log("auth middlewar trigged"); 7 | const token = req.header("x-auth-token"); 8 | if(!token){ 9 | return res.status(401).json({msg:"No Authentication Token, Authorization Denied"}); 10 | } ; 11 | const verified = jwt.verify(token, "passwordKey"); 12 | if(!verified){ 13 | return res.status(401).json({msg:"Token Verification Failed, Authorization Denied"}); 14 | } 15 | req.user = verified.id; 16 | req.token = token; 17 | next(); 18 | 19 | } catch (error) { 20 | res.status(400).json({msg:"Error" + error.message}); 21 | } 22 | }; 23 | 24 | export default auth; -------------------------------------------------------------------------------- /lib/features/splash/splash_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:go_router/go_router.dart'; 4 | 5 | class SplashView extends ConsumerStatefulWidget { 6 | const SplashView({super.key}); 7 | 8 | @override 9 | ConsumerState createState() => _SplashViewState(); 10 | } 11 | 12 | class _SplashViewState extends ConsumerState { 13 | @override 14 | void initState() { 15 | super.initState(); 16 | Future.delayed(const Duration(seconds: 1), () { 17 | GoRouter.of(context).go("/redirect"); 18 | }); 19 | } 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | return Scaffold( 24 | body: Center( 25 | child: Image.asset("assets/images/amazon_in.png"), 26 | )); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/features/home/view/widgets/carousel_image.dart: -------------------------------------------------------------------------------- 1 | import 'package:carousel_slider/carousel_slider.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../../../../core/constant/constants.dart'; 5 | 6 | class CarouselImage extends StatelessWidget { 7 | const CarouselImage({Key? key}) : super(key: key); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return CarouselSlider( 12 | items: AppConsts.carouselImages.map( 13 | (i) { 14 | return Builder( 15 | builder: (BuildContext context) => Image.network( 16 | i, 17 | fit: BoxFit.cover, 18 | height: 200, 19 | ), 20 | ); 21 | }, 22 | ).toList(), 23 | options: CarouselOptions( 24 | viewportFraction: 1.1, 25 | height: 200, 26 | ), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | 46 | server/node_modules/ 47 | -------------------------------------------------------------------------------- /lib/features/account/view/widgets/single_product.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | 4 | class SingleProduct extends StatelessWidget { 5 | final String image; 6 | const SingleProduct({ 7 | Key? key, 8 | required this.image, 9 | }) : super(key: key); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Container( 14 | padding: const EdgeInsets.symmetric(horizontal: 5), 15 | child: DecoratedBox( 16 | decoration: BoxDecoration( 17 | border: Border.all( 18 | color: Colors.black12, 19 | width: 1.5, 20 | ), 21 | borderRadius: BorderRadius.circular(5), 22 | color: Colors.white, 23 | ), 24 | child: Container( 25 | width: 180, 26 | padding: const EdgeInsets.all(10), 27 | child: Image.network( 28 | image, 29 | fit: BoxFit.fitHeight, 30 | width: 180, 31 | ), 32 | ), 33 | ), 34 | ); 35 | } 36 | } -------------------------------------------------------------------------------- /lib/features/account/repo/account_repo.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:dio/dio.dart'; 3 | 4 | import '../../../core/api_service.dart'; 5 | import '../../../core/errors/failire.dart'; 6 | import '../../../core/utils/type_def.dart'; 7 | import '../../../models/user.dart'; 8 | 9 | abstract class AccountRepo { 10 | FutureEither getOrders( 11 | {required String token}); 12 | } 13 | 14 | class AccountRepoImpl implements AccountRepo { 15 | AccountRepoImpl({required this.apiService}); 16 | final ApiService apiService; 17 | 18 | @override 19 | FutureEither getOrders( 20 | {required String token}) async { 21 | try { 22 | var res = await apiService.getOrders( 23 | token: token, 24 | ); 25 | var data = User.fromMap(res); 26 | return Right(data); 27 | } on Exception catch (e) { 28 | if (e is DioError) { 29 | return Left(ServerFailure.fromDioError(e)); 30 | } 31 | return Left(ServerFailure(e.toString())); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/features/home/view/home_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/features/home/view/widgets/custom_app_bar_home.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | import 'widgets/address_box.dart'; 5 | import 'widgets/carousel_image.dart'; 6 | import 'widgets/deal_of_day.dart'; 7 | import 'widgets/top_categories.dart'; 8 | 9 | class HomeView extends ConsumerWidget { 10 | const HomeView({super.key}); 11 | 12 | @override 13 | Widget build(BuildContext context, WidgetRef ref) { 14 | return Scaffold( 15 | appBar: const CustomAppBarHome(), 16 | body: SingleChildScrollView( 17 | child: Column( 18 | children: const [ 19 | SizedBox(height: 10), 20 | TopCategories(), 21 | SizedBox(height: 10), 22 | CarouselImage(), 23 | SizedBox(height: 10), 24 | DealOfDay(), 25 | AddressBox(), 26 | ], 27 | ), 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/features/auth/view/login/widgets/custom_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../../../../../core/constant/constants.dart'; 4 | 5 | class CustomButton extends StatelessWidget { 6 | final String text; 7 | final VoidCallback onTap; 8 | final Color? color; 9 | const CustomButton({ 10 | Key? key, 11 | required this.text, 12 | required this.onTap, 13 | this.color, 14 | }) : super(key: key); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return ElevatedButton( 19 | onPressed: onTap, 20 | style: ElevatedButton.styleFrom( 21 | minimumSize: const Size(160, 50), 22 | backgroundColor: color ?? AppConsts.secondaryColor, 23 | shape: RoundedRectangleBorder( 24 | borderRadius: BorderRadius.circular(10), 25 | ), 26 | ), 27 | child: Text( 28 | text, 29 | style: TextStyle( 30 | color: color == null ? AppConsts.textColor : Colors.black, 31 | ), 32 | ), 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/features/home/repo/home_repo.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs, sort_constructors_first 2 | import 'package:amazon_clone/core/utils/type_def.dart'; 3 | import 'package:dartz/dartz.dart'; 4 | import 'package:dio/dio.dart'; 5 | 6 | import '../../../core/api_service.dart'; 7 | import '../../../core/errors/failire.dart'; 8 | import '../../../models/product.dart'; 9 | 10 | abstract class HomeRepo { 11 | FutureEither getDealOfTheDay({required token}); 12 | } 13 | 14 | class HomeRepoImpl implements HomeRepo { 15 | final ApiService apiService; 16 | HomeRepoImpl({ 17 | required this.apiService, 18 | }); 19 | @override 20 | FutureEither getDealOfTheDay({required token}) async { 21 | try { 22 | var res = await apiService.getDealOfTheDay(token: token); 23 | var product = Product.fromMap(res); 24 | return Right(product); 25 | } on Exception catch (e) { 26 | if (e is DioError) { 27 | return Left(ServerFailure.fromDioError(e)); 28 | } 29 | return Left(ServerFailure(e.toString())); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "amazon_clone", 3 | "short_name": "amazon_clone", 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/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /server/middlewars/admin.js: -------------------------------------------------------------------------------- 1 | import jwt from "jsonwebtoken"; 2 | import User from '../models/user.js' 3 | 4 | 5 | const admin = async (req, res, next) => { 6 | try { 7 | const token = req.header("x-auth-token"); 8 | if(!token){ 9 | console.log("go out"); 10 | return res.status(401).json({msg:"No Authentication Token, Authorization Denied"}); 11 | }; 12 | 13 | const verified = jwt.verify(token, "passwordKey"); 14 | if(!verified){ 15 | return res.status(401).json({msg:"Token Verification Failed, Authorization Denied"}); 16 | } 17 | const user= User.findById(verified.id); 18 | 19 | if(user.type == "seller" ||user.type == "user" ){ 20 | return res.status(401).json({msg:"You are Unauthorized " + error.message}) 21 | } 22 | 23 | req.user = verified.id; 24 | req.token = token; 25 | next(); 26 | 27 | 28 | } catch (error) { 29 | return res.status(400).json({msg:"Error ; " + error.message}); 30 | } 31 | }; 32 | 33 | export default admin; 34 | 35 | -------------------------------------------------------------------------------- /lib/features/cart/repo/cart_repo.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:dio/dio.dart'; 3 | 4 | import '../../../core/api_service.dart'; 5 | import '../../../core/errors/failire.dart'; 6 | import '../../../core/utils/type_def.dart'; 7 | import '../../../models/user.dart'; 8 | 9 | abstract class CartRepo { 10 | FutureEither removeFromCart( 11 | {required String token, required String productId}); 12 | } 13 | 14 | class CartRepoImpl implements CartRepo { 15 | CartRepoImpl({required this.apiService}); 16 | final ApiService apiService; 17 | 18 | @override 19 | FutureEither removeFromCart( 20 | {required String token, required String productId}) async { 21 | try { 22 | var res = await apiService.removeFromCart( 23 | token: token, 24 | productId: productId, 25 | ); 26 | var data = User.fromMap(res); 27 | return Right(data); 28 | } on Exception catch (e) { 29 | if (e is DioError) { 30 | return Left(ServerFailure.fromDioError(e)); 31 | } 32 | return Left(ServerFailure(e.toString())); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/features/cart/view/widgets/cart_subtotal.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/providers/user_provider.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | 5 | class CartSubtotal extends ConsumerWidget { 6 | const CartSubtotal({Key? key}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context,ref) { 10 | final user = ref.watch(userStateProvider); 11 | int sum = 0; 12 | user.cart 13 | .map((e) => sum += e['quantity'] * e['product']['price'] as int) 14 | .toList(); 15 | 16 | return Container( 17 | margin: const EdgeInsets.all(10), 18 | child: Row( 19 | children: [ 20 | const Text( 21 | 'Subtotal ', 22 | style: TextStyle( 23 | fontSize: 20, 24 | ), 25 | ), 26 | Text( 27 | '\$$sum', 28 | style: const TextStyle( 29 | fontSize: 20, 30 | fontWeight: FontWeight.bold, 31 | ), 32 | ), 33 | ], 34 | ), 35 | ); 36 | } 37 | } -------------------------------------------------------------------------------- /lib/features/admin/view/widgets/custom_app_bar_admin.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../../../../core/constant/constants.dart'; 4 | 5 | class CustomAppBarAdmin extends StatelessWidget with PreferredSizeWidget { 6 | const CustomAppBarAdmin({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return AppBar( 11 | leading: SizedBox( 12 | height: 50, 13 | child: Image.asset("assets/images/amazon_in.png"), 14 | ), 15 | flexibleSpace: Container( 16 | decoration: const BoxDecoration( 17 | gradient: AppConsts.appBarGradient, 18 | ), 19 | ), 20 | leadingWidth: 150, 21 | actions: const [ 22 | Padding( 23 | padding: EdgeInsets.only(right: 18.0, bottom: 10), 24 | child: Text( 25 | "Admin", 26 | style: TextStyle( 27 | color: Colors.white, 28 | fontSize: 30, 29 | ), 30 | ), 31 | ) 32 | ], 33 | ); 34 | } 35 | 36 | @override 37 | Size get preferredSize => const Size.fromHeight(60); 38 | } 39 | -------------------------------------------------------------------------------- /lib/features/admin/view/all-orders/repo/order_repo.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/models/order.dart'; 2 | import 'package:dartz/dartz.dart' show Either, Left, Right; 3 | import 'package:dio/dio.dart'; 4 | 5 | import '../../../../../core/api_service.dart'; 6 | import '../../../../../core/errors/failire.dart'; 7 | import '../../../../../core/utils/type_def.dart'; 8 | 9 | abstract class OrderRepo { 10 | FutureEither getAllOrders({required String token}); 11 | } 12 | 13 | class OrderRepoImpl implements OrderRepo { 14 | OrderRepoImpl({required this.apiService}); 15 | final ApiService apiService; 16 | 17 | @override 18 | FutureEither getAllOrders({required String token}) async { 19 | try { 20 | var res = await apiService.getAllOrders( 21 | token: token, 22 | ); 23 | var data = []; 24 | for (var i in res) { 25 | data.add(Order.fromMap(i)); 26 | } 27 | return Right(data); 28 | } on Exception catch (e) { 29 | if (e is DioError) { 30 | return Left(ServerFailure.fromDioError(e)); 31 | } 32 | return Left(ServerFailure(e.toString())); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /assets/applepay.json: -------------------------------------------------------------------------------- 1 | { 2 | "provider": "apple_pay", 3 | "data": { 4 | "merchantIdentifier": "merchant.com.sams.fish", 5 | "displayName": "Sam's Fish", 6 | "merchantCapabilities": ["3DS", "debit", "credit"], 7 | "supportedNetworks": ["amex", "visa", "discover", "masterCard"], 8 | "countryCode": "US", 9 | "currencyCode": "USD", 10 | "requiredBillingContactFields": ["post"], 11 | "requiredShippingContactFields": ["post", "phone", "email", "name"], 12 | "shippingMethods": [ 13 | { 14 | "amount": "0.00", 15 | "detail": "Available within an hour", 16 | "identifier": "in_store_pickup", 17 | "label": "In-Store Pickup" 18 | }, 19 | { 20 | "amount": "4.99", 21 | "detail": "5-8 Business Days", 22 | "identifier": "flat_rate_shipping_id_2", 23 | "label": "UPS Ground" 24 | }, 25 | { 26 | "amount": "29.99", 27 | "detail": "1-3 Business Days", 28 | "identifier": "flat_rate_shipping_id_1", 29 | "label": "FedEx Priority Mail" 30 | } 31 | ] 32 | } 33 | } -------------------------------------------------------------------------------- /assets/gpay.json: -------------------------------------------------------------------------------- 1 | { 2 | "provider": "google_pay", 3 | "data": { 4 | "environment": "TEST", 5 | "apiVersion": 2, 6 | "apiVersionMinor": 0, 7 | "allowedPaymentMethods": [ 8 | { 9 | "type": "CARD", 10 | "tokenizationSpecification": { 11 | "type": "PAYMENT_GATEWAY", 12 | "parameters": { 13 | "gateway": "example", 14 | "gatewayMerchantId": "gatewayMerchantId" 15 | } 16 | }, 17 | "parameters": { 18 | "allowedCardNetworks": ["VISA", "MASTERCARD"], 19 | "allowedAuthMethods": ["PAN_ONLY", "CRYPTOGRAM_3DS"], 20 | "billingAddressRequired": true, 21 | "billingAddressParameters": { 22 | "format": "FULL", 23 | "phoneNumberRequired": true 24 | } 25 | } 26 | } 27 | ], 28 | "merchantInfo": { 29 | "merchantId": "01234567890123456789", 30 | "merchantName": "Example Merchant Name" 31 | }, 32 | "transactionInfo": { 33 | "countryCode": "US", 34 | "currencyCode": "USD" 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /lib/features/search/repo/search_repo.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:dio/dio.dart'; 3 | 4 | import '../../../core/api_service.dart'; 5 | import '../../../core/errors/failire.dart'; 6 | import '../../../core/utils/type_def.dart'; 7 | import '../../../models/product.dart'; 8 | 9 | abstract class SearchRepo { 10 | FutureEither> fetchSearchProducts({required String token, required String searchQuery}); 11 | } 12 | 13 | class SearchRepoImpl implements SearchRepo { 14 | SearchRepoImpl({required this.apiService}); 15 | final ApiService apiService; 16 | 17 | @override 18 | FutureEither> fetchSearchProducts({required String token, required String searchQuery}) async { 19 | try { 20 | List listProducts = []; 21 | var res = await apiService.getSearchProducts(token: token, searchQuery: searchQuery); 22 | for (var i in res["products"]) { 23 | listProducts.add(Product.fromMap(i)); 24 | } 25 | return Right(listProducts); 26 | } on Exception catch (e) { 27 | if (e is DioError) { 28 | return Left(ServerFailure.fromDioError(e)); 29 | } 30 | return Left(ServerFailure(e.toString())); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/features/auth/view/login/widgets/custom_text_field.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../../../../../core/constant/constants.dart'; 5 | 6 | class CustomTextField extends StatelessWidget { 7 | final TextEditingController controller; 8 | final String hintText; 9 | final int maxLines; 10 | const CustomTextField({ 11 | Key? key, 12 | required this.controller, 13 | required this.hintText, 14 | this.maxLines = 1, 15 | }) : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return TextFormField( 20 | controller: controller, 21 | decoration: InputDecoration( 22 | hintText: hintText, 23 | border: OutlineInputBorder( 24 | borderSide: BorderSide( 25 | color: AppConsts.secondaryColor.withOpacity(.5), 26 | )), 27 | enabledBorder: const OutlineInputBorder( 28 | borderSide: BorderSide( 29 | color: AppConsts.secondaryColor, 30 | ))), 31 | validator: (val) { 32 | if (val == null || val.isEmpty) { 33 | return 'Enter your $hintText'; 34 | } 35 | return null; 36 | }, 37 | maxLines: maxLines, 38 | ); 39 | } 40 | } -------------------------------------------------------------------------------- /server/app.js: -------------------------------------------------------------------------------- 1 | import express, { json, urlencoded } from "express"; 2 | import auth from "./routes/auth.js"; 3 | import admin from "./routes/admin.js"; 4 | import product from "./routes/product.js"; 5 | import user from "./routes/user.js"; 6 | import connectDb from "./mongoDbConnect.js"; 7 | 8 | 9 | // lazm trevisi middleware we tchof wchno homa 100 exacte 10 | 11 | const app=express(); 12 | const PORT=8001; 13 | 14 | 15 | app.use(json()); 16 | app.use(urlencoded({extended:true})); 17 | 18 | app.use(auth); 19 | app.use(admin); 20 | app.use(product) 21 | app.use(user) 22 | 23 | 24 | 25 | 26 | app.use((req,res,next)=>{ 27 | res.status(404).json({msg:"404 Not Found"}); 28 | }) 29 | 30 | 31 | const main = async () => { 32 | await connectDb(); 33 | app.listen(PORT, "0.0.0.0",() => { 34 | console.log("Server is running on port http://localhost:" + PORT); 35 | }) 36 | } 37 | 38 | main(); 39 | 40 | 41 | 42 | 43 | // add nodemon as dev dependency 44 | // const dbURI="mongodb://FarZ:mrfares77@ac-iazyxlm-shard-00-00.fnzafv5.mongodb.net:27017,ac-iazyxlm-shard-00-01.fnzafv5.mongodb.net:27017,ac-iazyxlm-shard-00-02.fnzafv5.mongodb.net:27017/?ssl=true&replicaSet=atlas-humg6a-shard-0&authSource=admin&retryWrites=true&w=majority"; 45 | 46 | -------------------------------------------------------------------------------- /lib/features/admin/view/all-orders/controller/order_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/providers/repos_provider.dart'; 2 | import 'package:amazon_clone/core/providers/shared_preference_provider.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | 5 | import '../repo/order_repo.dart'; 6 | 7 | 8 | final ordersControllerProvider = 9 | StateNotifierProvider((ref) { 10 | return OrderController(orderRepo: ref.watch(orderRepoProvider), ref: ref); 11 | }); 12 | 13 | class OrderController extends StateNotifier { 14 | OrderController({required this.orderRepo, required this.ref}) 15 | : super(const AsyncData(null)); 16 | final OrderRepo orderRepo; 17 | final StateNotifierProviderRef ref; 18 | 19 | void getAllOrders() async { 20 | state = const AsyncLoading(); 21 | String? token; 22 | await ref.watch(sharedPreferenceProvider)?.then((pref) async { 23 | token = pref.getString("x-auth-token"); 24 | }); 25 | var res = await orderRepo.getAllOrders( 26 | token: token ?? "", 27 | ); 28 | 29 | res.fold((l) { 30 | state = AsyncValue.error(l.errorMessage, StackTrace.empty); 31 | }, (r) { 32 | state = AsyncValue.data(r); 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/features/category/repo/category_repo.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:dio/dio.dart'; 3 | 4 | import '../../../core/api_service.dart'; 5 | import '../../../core/errors/failire.dart'; 6 | import '../../../core/utils/type_def.dart'; 7 | import '../../../models/product.dart'; 8 | 9 | abstract class CategoryRepo { 10 | FutureEither> fetchCategoryProducts( 11 | {required String token, required String category}); 12 | } 13 | 14 | class CategoryRepoImpl implements CategoryRepo { 15 | CategoryRepoImpl({required this.apiService}); 16 | final ApiService apiService; 17 | 18 | @override 19 | FutureEither> fetchCategoryProducts( 20 | {required String token, required String category}) async { 21 | try { 22 | List listProducts = []; 23 | var res = await apiService.getCategoryProducts( 24 | token: token, category: category); 25 | for (var i in res["products"]) { 26 | listProducts.add(Product.fromMap(i)); 27 | } 28 | return Right(listProducts); 29 | } on Exception catch (e) { 30 | if (e is DioError) { 31 | return Left(ServerFailure.fromDioError(e)); 32 | } 33 | return Left(ServerFailure(e.toString())); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/features/account/view/widgets/top_button.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:amazon_clone/features/account/controller/account_controller.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 | 7 | import 'account_button.dart'; 8 | 9 | class TopButtons extends ConsumerWidget { 10 | const TopButtons({super.key}); 11 | 12 | @override 13 | Widget build(BuildContext context,ref) { 14 | return Column( 15 | children: [ 16 | Row( 17 | children: [ 18 | AccountButton( 19 | text: 'Your Orders', 20 | onTap: () {}, 21 | ), 22 | AccountButton( 23 | text: 'Turn Seller', 24 | onTap: () {}, 25 | ), 26 | ], 27 | ), 28 | const SizedBox(height: 10), 29 | Row( 30 | children: [ 31 | AccountButton( 32 | text: 'Log Out', onTap: () { 33 | ref.read(accountControllerProvider.notifier).logOut(); 34 | }, 35 | 36 | 37 | ), 38 | AccountButton( 39 | text: 'Your Wish List', 40 | onTap: () {}, 41 | ), 42 | ], 43 | ), 44 | ], 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/features/account/view/widgets/account_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AccountButton extends StatelessWidget { 4 | final String text; 5 | final VoidCallback onTap; 6 | const AccountButton({ 7 | Key? key, 8 | required this.text, 9 | required this.onTap, 10 | }) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Expanded( 15 | child: Container( 16 | margin: const EdgeInsets.symmetric(horizontal: 10), 17 | height: 40, 18 | decoration: BoxDecoration( 19 | border: Border.all(color: Colors.white, width: 0.0), 20 | borderRadius: BorderRadius.circular(50), 21 | color: Colors.white, 22 | ), 23 | child: OutlinedButton( 24 | style: ElevatedButton.styleFrom( 25 | backgroundColor: Colors.black12.withOpacity(0.03), 26 | shape: RoundedRectangleBorder( 27 | borderRadius: BorderRadius.circular(50), 28 | ), 29 | ), 30 | onPressed: onTap, 31 | child: Text( 32 | text, 33 | style: const TextStyle( 34 | color: Colors.black, 35 | fontWeight: FontWeight.normal, 36 | ), 37 | ), 38 | ), 39 | ), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/features/admin/analytics/controller/analytics_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/providers/repos_provider.dart'; 2 | import 'package:amazon_clone/core/providers/shared_preference_provider.dart'; 3 | import 'package:amazon_clone/features/admin/analytics/repo/analytics_repo.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | 6 | 7 | final analyticsControllerProvider = 8 | StateNotifierProvider((ref) { 9 | return AnalyticsController(analyticsRepo: ref.watch(analyticsRepoProvider), ref: ref); 10 | }); 11 | 12 | class AnalyticsController extends StateNotifier { 13 | AnalyticsController({required this.analyticsRepo, required this.ref}) 14 | : super(const AsyncData(null)); 15 | final AnalyticsRepo analyticsRepo; 16 | final StateNotifierProviderRef ref; 17 | 18 | void getAnalytics() async { 19 | state = const AsyncLoading(); 20 | String? token; 21 | await ref.watch(sharedPreferenceProvider)?.then((pref) async { 22 | token = pref.getString("x-auth-token"); 23 | }); 24 | var res = await analyticsRepo.getAnalytics( 25 | token: token ?? "", 26 | ); 27 | 28 | res.fold((l) { 29 | state = AsyncValue.error(l.errorMessage, StackTrace.empty); 30 | }, (r) { 31 | state = AsyncValue.data(r); 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/features/home/controllers/home_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/providers/repos_provider.dart'; 2 | import 'package:amazon_clone/core/providers/shared_preference_provider.dart'; 3 | import 'package:amazon_clone/features/home/repo/home_repo.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | 6 | import '../../../models/product.dart'; 7 | 8 | final homeControllerProvider = 9 | StateNotifierProvider((ref) { 10 | return HomeController(homeRepo: ref.watch(homeRepoProvider), ref: ref); 11 | }); 12 | 13 | class HomeController extends StateNotifier> { 14 | HomeController({required this.homeRepo, required this.ref}) 15 | : super(const AsyncData(null)); 16 | final HomeRepo homeRepo; 17 | final StateNotifierProviderRef ref; 18 | String searchQuery = ''; 19 | 20 | void getDealOfTheDay() async { 21 | state = const AsyncLoading(); 22 | String? token; 23 | await ref.watch(sharedPreferenceProvider)?.then((pref) async { 24 | token = pref.getString("x-auth-token"); 25 | }); 26 | await homeRepo.getDealOfTheDay(token: token ?? "").then((value) { 27 | value.fold((failure) { 28 | state = AsyncValue.error(failure.errorMessage, StackTrace.empty); 29 | }, (products) { 30 | state = AsyncValue.data(products); 31 | }); 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/features/account/view/widgets/below_app_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/providers/user_provider.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | 5 | import '../../../../core/constant/constants.dart'; 6 | class BelowAppBar extends ConsumerWidget { 7 | const BelowAppBar({super.key}); 8 | 9 | @override 10 | Widget build(BuildContext context, ref) { 11 | var user = ref.watch(userStateProvider); 12 | return Container( 13 | decoration: const BoxDecoration( 14 | gradient: AppConsts.appBarGradient, 15 | ), 16 | padding: const EdgeInsets.only(left: 10, right: 10, bottom: 10), 17 | child: Row( 18 | children: [ 19 | RichText( 20 | text: TextSpan( 21 | text: 'Hello, ', 22 | style: const TextStyle( 23 | fontSize: 22, 24 | color: Colors.black, 25 | ), 26 | children: [ 27 | TextSpan( 28 | text: user.name, 29 | style: const TextStyle( 30 | fontSize: 22, 31 | color: Colors.black, 32 | fontWeight: FontWeight.w600, 33 | ), 34 | ), 35 | ], 36 | ), 37 | ), 38 | ], 39 | ), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/features/admin/analytics/repo/analytics_repo.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:dio/dio.dart'; 3 | 4 | import '../../../../core/api_service.dart'; 5 | import '../../../../core/errors/failire.dart'; 6 | import '../../../../core/utils/type_def.dart'; 7 | import '../../../../models/sales.dart'; 8 | 9 | abstract class AnalyticsRepo { 10 | FutureEither getAnalytics({required String token}); 11 | } 12 | 13 | class AnalyticsRepoImpl implements AnalyticsRepo { 14 | AnalyticsRepoImpl({required this.apiService}); 15 | final ApiService apiService; 16 | 17 | @override 18 | FutureEither getAnalytics({required String token}) async { 19 | try { 20 | var res = await apiService.getAnalytics( 21 | token: token, 22 | ); 23 | var totalEarning = res['totalEarnings']; 24 | var sales = [ 25 | Sales('Mobiles', res['mobileEarnings']), 26 | Sales('Essentials', res['essentialEarnings']), 27 | Sales('Books', res['booksEarnings']), 28 | Sales('Appliances', res['applianceEarnings']), 29 | Sales('Fashion', res['fashionEarnings']), 30 | ]; 31 | var temp = {'totalEarning': totalEarning, 'sales': sales}; 32 | return Right(temp); 33 | } on Exception catch (e) { 34 | if (e is DioError) { 35 | return Left(ServerFailure.fromDioError(e)); 36 | } 37 | return Left(ServerFailure(e.toString())); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/features/cart/controller/cart_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/providers/repos_provider.dart'; 2 | import 'package:amazon_clone/core/providers/shared_preference_provider.dart'; 3 | import 'package:amazon_clone/core/providers/user_provider.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | 6 | import '../../../models/product.dart'; 7 | import '../repo/cart_repo.dart'; 8 | 9 | final cartControllerProvider = 10 | StateNotifierProvider((ref) { 11 | return CartController(orderRepo: ref.watch(cartRepoProvider), ref: ref); 12 | }); 13 | 14 | class CartController extends StateNotifier { 15 | CartController({required this.orderRepo, required this.ref}) 16 | : super(const AsyncData(null)); 17 | final CartRepo orderRepo; 18 | final StateNotifierProviderRef ref; 19 | 20 | void removeFromCart({required Product product}) async { 21 | state = const AsyncLoading(); 22 | String? token; 23 | await ref.watch(sharedPreferenceProvider)?.then((pref) async { 24 | token = pref.getString("x-auth-token"); 25 | }); 26 | var res = await orderRepo.removeFromCart( 27 | productId: product.id ?? "", 28 | token: token ?? "", 29 | ); 30 | 31 | res.fold((l) { 32 | state = AsyncValue.error(l.errorMessage, StackTrace.empty); 33 | }, (r) { 34 | ref.read(userStateProvider.notifier).setUser(r); 35 | state = const AsyncValue.data('success'); 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/features/search/controller/search_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/providers/repos_provider.dart'; 2 | import 'package:amazon_clone/core/providers/shared_preference_provider.dart'; 3 | import 'package:amazon_clone/features/search/repo/search_repo.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | 6 | import '../../../models/product.dart'; 7 | 8 | final searchControllerProvider = 9 | StateNotifierProvider((ref) { 10 | return SearchController( 11 | searchRepo: ref.watch(searchRepoProvider), ref: ref); 12 | }); 13 | 14 | class SearchController extends StateNotifier?>> { 15 | SearchController({required this.searchRepo, required this.ref}) 16 | : super(const AsyncData(null)); 17 | final SearchRepo searchRepo; 18 | final StateNotifierProviderRef ref; 19 | String searchQuery = ''; 20 | 21 | void fetchSearchProducts() async { 22 | state = const AsyncLoading?>(); 23 | String? token; 24 | await ref.watch(sharedPreferenceProvider)?.then((pref) async { 25 | token = pref.getString("x-auth-token"); 26 | }); 27 | await searchRepo 28 | .fetchSearchProducts(token: token ?? "", searchQuery: searchQuery) 29 | .then((value) { 30 | value.fold((failure) { 31 | state = AsyncValue.error(failure.errorMessage, StackTrace.empty); 32 | }, (products) { 33 | state = AsyncValue.data(products); 34 | }); 35 | }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/features/account/view/widgets/custom_app_bar.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../../../../core/constant/constants.dart'; 5 | 6 | class CustomAppBar extends StatelessWidget with PreferredSizeWidget { 7 | const CustomAppBar({super.key}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return AppBar( 12 | flexibleSpace: Container( 13 | decoration: const BoxDecoration( 14 | gradient: AppConsts.appBarGradient, 15 | ), 16 | ), 17 | title: Row( 18 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 19 | children: [ 20 | Container( 21 | alignment: Alignment.topLeft, 22 | child: Image.asset( 23 | 'assets/images/amazon_in.png', 24 | width: 120, 25 | height: 45, 26 | color: Colors.black, 27 | ), 28 | ), 29 | Container( 30 | padding: const EdgeInsets.only(left: 15, right: 15), 31 | child: Row( 32 | children: const [ 33 | Padding( 34 | padding: EdgeInsets.only(right: 15), 35 | child: Icon(Icons.notifications_outlined), 36 | ), 37 | Icon( 38 | Icons.search, 39 | ), 40 | ], 41 | ), 42 | ) 43 | ], 44 | ), 45 | ); 46 | } 47 | 48 | @override 49 | Size get preferredSize => const Size.fromHeight(50); 50 | } 51 | -------------------------------------------------------------------------------- /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/category/controller/category_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/providers/repos_provider.dart'; 2 | import 'package:amazon_clone/core/providers/shared_preference_provider.dart'; 3 | import 'package:amazon_clone/features/category/repo/category_repo.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 | 7 | import '../../../models/product.dart'; 8 | 9 | final categoryControllerProvider = 10 | StateNotifierProvider((ref) { 11 | return CategoryController( 12 | categoryRepo: ref.watch(categoryRepoProvider), ref: ref); 13 | }); 14 | 15 | class CategoryController extends StateNotifier?>> { 16 | CategoryController({required this.categoryRepo, required this.ref}) 17 | : super(const AsyncData(null)); 18 | final CategoryRepo categoryRepo; 19 | final StateNotifierProviderRef ref; 20 | 21 | void fetchCategoryProducts(String category, BuildContext context) async { 22 | state = const AsyncLoading?>(); 23 | String? token; 24 | await ref.watch(sharedPreferenceProvider)?.then((pref) async { 25 | token = pref.getString("x-auth-token"); 26 | }); 27 | await categoryRepo 28 | .fetchCategoryProducts(token: token ?? "", category: category) 29 | .then((value) { 30 | value.fold((failure) { 31 | state = AsyncValue.error(failure.errorMessage, StackTrace.empty); 32 | }, (products) { 33 | state = AsyncValue.data(products); 34 | }); 35 | }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/features/admin/add_product/repo/add_product_repo.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | import 'dart:io'; 3 | 4 | import 'package:amazon_clone/core/api_service.dart'; 5 | import 'package:cloudinary_public/cloudinary_public.dart'; 6 | 7 | import '../../../../models/product.dart'; 8 | 9 | abstract class AddProductRepo { 10 | Future addProduct( 11 | {required token, 12 | required String name, 13 | required List images, 14 | required quantity, 15 | required description, 16 | required category, 17 | required price}); 18 | } 19 | 20 | class AddProductRepoImpl implements AddProductRepo { 21 | AddProductRepoImpl({required this.apiService}); 22 | final ApiService apiService; 23 | @override 24 | Future addProduct( 25 | {required token, 26 | required String name, 27 | required List images, 28 | required quantity, 29 | required description, 30 | required category, 31 | required price}) async { 32 | List imageUrl = []; 33 | CloudinaryPublic cloud = CloudinaryPublic("dcnwbszmt", "k10hhnqb"); 34 | for (var i = 0; i < images.length; i++) { 35 | var res = await cloud.uploadFile(CloudinaryFile.fromFile(images[i].path,folder:name)); 36 | imageUrl.add(res.secureUrl); 37 | } 38 | 39 | Product product = Product( 40 | name: name, 41 | description: description, 42 | quantity: quantity, 43 | images: imageUrl, 44 | category: category, 45 | price: price); 46 | var res = await apiService.addProduct(token: token, product: product); 47 | log(res.toString()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /server/models/user.js: -------------------------------------------------------------------------------- 1 | // nimportiw mongoose we njibo mnha wch ns79 2 | import mongoose from "mongoose"; 3 | import { productSchema } from "./product.js"; 4 | 5 | // hna ncriyiw instance mn shema 6 | const sh = mongoose.Schema; 7 | 8 | 9 | // hna ndiro new shema mn shema builder bwch drnalo 10 | const UserSh =new sh({ 11 | name:{ 12 | type:String, 13 | required:true, 14 | trim:true, 15 | }, 16 | email:{ 17 | type:String, 18 | required:true, 19 | trim:true, 20 | validate:{ 21 | validator: (value) => { 22 | return String(value) 23 | .toLowerCase() 24 | .match( 25 | /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ 26 | ); 27 | }, 28 | message: "Please enter a valid email adress", 29 | } 30 | } 31 | , 32 | password:{ 33 | type:String, 34 | required:true, 35 | trim:true, 36 | validator: (value) => { 37 | return value.length > 8 38 | }, 39 | }, 40 | adress:{ 41 | type:String, 42 | default:'', 43 | }, 44 | type :{ 45 | type:String, 46 | default:"user", 47 | }, 48 | cart: [ 49 | { 50 | product: productSchema, 51 | quantity: { 52 | type: Number, 53 | required: true, 54 | }, 55 | }, 56 | ], 57 | 58 | }) 59 | 60 | const User= mongoose.model("User",UserSh); 61 | export default User; -------------------------------------------------------------------------------- /lib/features/admin/add_product/controller/add_product_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/providers/repos_provider.dart'; 2 | import 'package:amazon_clone/core/providers/shared_preference_provider.dart'; 3 | import 'package:amazon_clone/features/admin/add_product/repo/add_product_repo.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 | 7 | final addProductControllerProvider = 8 | StateNotifierProvider((ref) { 9 | return AddProductController( 10 | addProductRepo: ref.watch(addProductRepoProvider), ref: ref); 11 | }); 12 | 13 | class AddProductController extends StateNotifier { 14 | AddProductController({required this.addProductRepo, required this.ref}) 15 | : super(const AsyncData(null)); 16 | final AddProductRepo addProductRepo; 17 | final StateNotifierProviderRef ref; 18 | 19 | void addProduct( 20 | {required name, 21 | required description, 22 | required quantity, 23 | required images, 24 | required String category, 25 | required double price, 26 | required BuildContext context}) async { 27 | state = const AsyncLoading(); 28 | String? token; 29 | 30 | await ref.watch(sharedPreferenceProvider)?.then((pref) async { 31 | token = pref.getString("x-auth-token"); 32 | }); 33 | 34 | state = await AsyncValue.guard(() => addProductRepo.addProduct( 35 | token: token, 36 | name: name, 37 | images: images, 38 | quantity: quantity, 39 | description: description, 40 | category: category, 41 | price: price)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/features/account/controller/account_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/providers/repos_provider.dart'; 2 | import 'package:amazon_clone/core/providers/shared_preference_provider.dart'; 3 | import 'package:amazon_clone/core/providers/user_provider.dart'; 4 | import 'package:amazon_clone/features/account/repo/account_repo.dart'; 5 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 | 7 | final accountControllerProvider = 8 | StateNotifierProvider((ref) { 9 | return AccountController( 10 | accountRepo: ref.watch(accountRepoProvider), ref: ref); 11 | }); 12 | 13 | class AccountController extends StateNotifier { 14 | AccountController({required this.accountRepo, required this.ref}) 15 | : super(const AsyncData(null)); 16 | final AccountRepo accountRepo; 17 | final StateNotifierProviderRef ref; 18 | 19 | void getOrders() async { 20 | state = const AsyncLoading(); 21 | String? token; 22 | await ref.watch(sharedPreferenceProvider)?.then((pref) async { 23 | token = pref.getString("x-auth-token"); 24 | }); 25 | var res = await accountRepo.getOrders( 26 | token: token ?? "", 27 | ); 28 | 29 | res.fold((l) { 30 | state = AsyncValue.error(l.errorMessage, StackTrace.empty); 31 | }, (r) { 32 | ref.read(userStateProvider.notifier).setUser(r); 33 | state = const AsyncValue.data('success'); 34 | }); 35 | } 36 | 37 | void logOut() async { 38 | await ref.watch(sharedPreferenceProvider)?.then((pref) async { 39 | pref.setString("x-auth-token", ""); 40 | }); 41 | 42 | ref.read(userStateProvider.notifier).removeUser(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/models/product.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import '/models/rating.dart'; 4 | 5 | class Product { 6 | final String name; 7 | final String description; 8 | final double quantity; 9 | final List images; 10 | final String category; 11 | final double price; 12 | final String? id; 13 | final List? rating; 14 | Product({ 15 | required this.name, 16 | required this.description, 17 | required this.quantity, 18 | required this.images, 19 | required this.category, 20 | required this.price, 21 | this.id, 22 | this.rating, 23 | }); 24 | 25 | Map toMap() { 26 | return { 27 | 'name': name, 28 | 'description': description, 29 | 'quantity': quantity, 30 | 'images': images, 31 | 'category': category, 32 | 'price': price, 33 | 'id': id, 34 | 'rating': rating, 35 | }; 36 | } 37 | 38 | factory Product.fromMap(Map map) { 39 | return Product( 40 | name: map['name'] ?? '', 41 | description: map['description'] ?? '', 42 | quantity: map['quantity']?.toDouble() ?? 0.0, 43 | images: List.from(map['images']), 44 | category: map['category'] ?? '', 45 | price: map['price']?.toDouble() ?? 0.0, 46 | id: map['_id'], 47 | rating: map['ratings'] != null 48 | ? List.from( 49 | map['ratings']?.map( 50 | (x) => Rating.fromMap(x), 51 | ), 52 | ) 53 | : null, 54 | ); 55 | } 56 | 57 | String toJson() => json.encode(toMap()); 58 | 59 | factory Product.fromJson(String source) => 60 | Product.fromMap(json.decode(source)); 61 | } 62 | -------------------------------------------------------------------------------- /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/models/order.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import '/models/product.dart'; 4 | 5 | class Order { 6 | final String id; 7 | final List products; 8 | final List quantity; 9 | final String address; 10 | final String userId; 11 | final int orderedAt; 12 | final int status; 13 | final double totalPrice; 14 | Order({ 15 | required this.id, 16 | required this.products, 17 | required this.quantity, 18 | required this.address, 19 | required this.userId, 20 | required this.orderedAt, 21 | required this.status, 22 | required this.totalPrice, 23 | }); 24 | 25 | Map toMap() { 26 | return { 27 | 'id': id, 28 | 'products': products.map((x) => x.toMap()).toList(), 29 | 'quantity': quantity, 30 | 'address': address, 31 | 'userId': userId, 32 | 'orderedAt': orderedAt, 33 | 'status': status, 34 | 'totalPrice': totalPrice, 35 | }; 36 | } 37 | 38 | factory Order.fromMap(Map map) { 39 | return Order( 40 | id: map['_id'] ?? '', 41 | products: List.from( 42 | map['products']?.map((x) => Product.fromMap(x['product']))), 43 | quantity: List.from( 44 | map['products']?.map( 45 | (x) => x['quantity'], 46 | ), 47 | ), 48 | address: map['address'] ?? '', 49 | userId: map['userId'] ?? '', 50 | orderedAt: map['orderedAt']?.toInt() ?? 0, 51 | status: map['status']?.toInt() ?? 0, 52 | totalPrice: map['totalPrice']?.toDouble() ?? 0.0, 53 | ); 54 | } 55 | 56 | String toJson() => json.encode(toMap()); 57 | 58 | factory Order.fromJson(String source) => Order.fromMap(json.decode(source)); 59 | } 60 | -------------------------------------------------------------------------------- /lib/features/admin/posts/repo/posts_repo.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:dio/dio.dart'; 3 | 4 | import '../../../../core/api_service.dart'; 5 | import '../../../../core/errors/failire.dart'; 6 | import '../../../../core/utils/type_def.dart'; 7 | import '../../../../models/product.dart'; 8 | 9 | abstract class PostsRepo { 10 | FutureEither> getPosts({required String token}); 11 | FutureEither> deletePosts({required String token, required id}); 12 | } 13 | 14 | class PostsRepoImpl implements PostsRepo { 15 | PostsRepoImpl({required this.apiService}); 16 | final ApiService apiService; 17 | @override 18 | FutureEither> getPosts({required token}) async { 19 | try { 20 | List listProducts = []; 21 | var res = await apiService.getPosts(token: token); 22 | for (var i in res["products"]) { 23 | listProducts.add(Product.fromMap(i)); 24 | } 25 | return Right(listProducts); 26 | } on Exception catch (e) { 27 | if (e is DioError) { 28 | return Left(ServerFailure.fromDioError(e)); 29 | } 30 | return Left(ServerFailure(e.toString())); 31 | } 32 | } 33 | 34 | @override 35 | FutureEither> deletePosts( 36 | {required String token, required id}) async { 37 | try { 38 | List listProducts = []; 39 | var res = await apiService.deletePost(token: token, id: id); 40 | for (var i in res["products"]) { 41 | listProducts.add(Product.fromMap(i)); 42 | } 43 | return Right(listProducts); 44 | } on Exception catch (e) { 45 | if (e is DioError) { 46 | return Left(ServerFailure.fromDioError(e)); 47 | } 48 | return Left(ServerFailure(e.toString())); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/features/adress/repo/adress_repo.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:dio/dio.dart'; 3 | 4 | import '../../../core/api_service.dart'; 5 | import '../../../core/errors/failire.dart'; 6 | import '../../../core/utils/type_def.dart'; 7 | import '../../../models/user.dart'; 8 | 9 | abstract class AddressRepo { 10 | 11 | FutureEither saveUserAdress({required String token, required String adress}); 12 | FutureEither placeOrder({required String token, required String adress,required amount,required cart}); 13 | } 14 | 15 | class AddressRepoImpl implements AddressRepo { 16 | AddressRepoImpl({required this.apiService}); 17 | final ApiService apiService; 18 | 19 | @override 20 | FutureEither saveUserAdress( 21 | {required String token, required String adress}) async { 22 | try { 23 | var res = await apiService.saveUserAdress( 24 | token: token, 25 | adress: adress, 26 | ); 27 | var data= User.fromMap(res); 28 | return Right(data); 29 | } on Exception catch (e) { 30 | if (e is DioError) { 31 | return Left(ServerFailure.fromDioError(e)); 32 | } 33 | return Left(ServerFailure(e.toString())); 34 | } 35 | } 36 | @override 37 | FutureEither placeOrder( 38 | {required String token, required String adress,required amount,required cart}) async { 39 | try { 40 | var res = await apiService.placeOrder( 41 | token: token, 42 | adress: adress, 43 | amount:amount, 44 | cart: cart 45 | ); 46 | var data= User.fromMap(res); 47 | return Right(data); 48 | } on Exception catch (e) { 49 | if (e is DioError) { 50 | return Left(ServerFailure.fromDioError(e)); 51 | } 52 | return Left(ServerFailure(e.toString())); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /.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: 62bd79521d8d007524e351747471ba66696fc2d4 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: 62bd79521d8d007524e351747471ba66696fc2d4 17 | base_revision: 62bd79521d8d007524e351747471ba66696fc2d4 18 | - platform: android 19 | create_revision: 62bd79521d8d007524e351747471ba66696fc2d4 20 | base_revision: 62bd79521d8d007524e351747471ba66696fc2d4 21 | - platform: ios 22 | create_revision: 62bd79521d8d007524e351747471ba66696fc2d4 23 | base_revision: 62bd79521d8d007524e351747471ba66696fc2d4 24 | - platform: linux 25 | create_revision: 62bd79521d8d007524e351747471ba66696fc2d4 26 | base_revision: 62bd79521d8d007524e351747471ba66696fc2d4 27 | - platform: macos 28 | create_revision: 62bd79521d8d007524e351747471ba66696fc2d4 29 | base_revision: 62bd79521d8d007524e351747471ba66696fc2d4 30 | - platform: web 31 | create_revision: 62bd79521d8d007524e351747471ba66696fc2d4 32 | base_revision: 62bd79521d8d007524e351747471ba66696fc2d4 33 | - platform: windows 34 | create_revision: 62bd79521d8d007524e351747471ba66696fc2d4 35 | base_revision: 62bd79521d8d007524e351747471ba66696fc2d4 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /lib/features/home/view/widgets/address_box.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/providers/user_provider.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | 5 | class AddressBox extends ConsumerWidget { 6 | const AddressBox({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context, ref) { 10 | var user = ref.watch(userStateProvider); 11 | return Container( 12 | height: 40, 13 | decoration: const BoxDecoration( 14 | gradient: LinearGradient( 15 | colors: [ 16 | Color.fromARGB(255, 114, 226, 221), 17 | Color.fromARGB(255, 162, 236, 233), 18 | ], 19 | stops: [0.5, 1.0], 20 | ), 21 | ), 22 | padding: const EdgeInsets.only(left: 10), 23 | child: Row( 24 | children: [ 25 | const Icon( 26 | Icons.location_on_outlined, 27 | size: 20, 28 | color: Colors.black54, 29 | ), 30 | Expanded( 31 | child: Padding( 32 | padding: const EdgeInsets.only(left: 5), 33 | child: Text( 34 | 'Delivery to ${user.address}', 35 | style: const TextStyle( 36 | fontWeight: FontWeight.w500, 37 | color: Colors.black54, 38 | ), 39 | overflow: TextOverflow.ellipsis, 40 | ), 41 | ), 42 | ), 43 | const Padding( 44 | padding: EdgeInsets.only( 45 | left: 5, 46 | top: 2, 47 | ), 48 | child: Icon( 49 | Icons.arrow_drop_down_outlined, 50 | size: 18, 51 | color: Colors.black54, 52 | ), 53 | ) 54 | ], 55 | ), 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 9 | 17 | 21 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Amazon Clone 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | amazon_clone 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | CADisableMinimumFrameDurationOnPhone 47 | 48 | UIApplicationSupportsIndirectInputEvents 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /lib/features/home/view/widgets/top_categories.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/constant/constants.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:go_router/go_router.dart'; 4 | 5 | class TopCategories extends StatelessWidget { 6 | const TopCategories({Key? key}) : super(key: key); 7 | 8 | void navigateToCategoryPage(BuildContext context, String category) { 9 | GoRouter.of(context).push("/category", extra:category); 10 | } 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return SizedBox( 15 | height: 60, 16 | child: ListView.builder( 17 | itemCount: AppConsts.categoryImages.length, 18 | scrollDirection: Axis.horizontal, 19 | itemExtent: 75, 20 | itemBuilder: (context, index) { 21 | return GestureDetector( 22 | onTap: () => navigateToCategoryPage( 23 | context, 24 | AppConsts.categoryImages[index]['title']!, 25 | ), 26 | child: Column( 27 | children: [ 28 | Container( 29 | padding: const EdgeInsets.symmetric(horizontal: 10), 30 | child: ClipRRect( 31 | borderRadius: BorderRadius.circular(50), 32 | child: Image.asset( 33 | AppConsts.categoryImages[index]['image']!, 34 | fit: BoxFit.cover, 35 | height: 40, 36 | width: 40, 37 | ), 38 | ), 39 | ), 40 | Text( 41 | AppConsts.categoryImages[index]['title']!, 42 | style: const TextStyle( 43 | fontSize: 12, 44 | fontWeight: FontWeight.w400, 45 | ), 46 | ), 47 | ], 48 | ), 49 | ); 50 | }, 51 | ), 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/features/product-detaills/repo/product_detaills_repo.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:dio/dio.dart'; 3 | 4 | import '../../../core/api_service.dart'; 5 | import '../../../core/errors/failire.dart'; 6 | import '../../../core/utils/type_def.dart'; 7 | import '../../../models/product.dart'; 8 | import '../../../models/user.dart'; 9 | 10 | abstract class ProductDetaillsRepo { 11 | FutureEither rateProduct( 12 | {required String token, 13 | required String productId, 14 | required double rating}); 15 | FutureEither addToCart({required String token, required String productId}); 16 | } 17 | 18 | class ProductDetaillsRepoImpl implements ProductDetaillsRepo { 19 | ProductDetaillsRepoImpl({required this.apiService}); 20 | final ApiService apiService; 21 | 22 | @override 23 | FutureEither rateProduct( 24 | {required String token, 25 | required String productId, 26 | required double rating}) async { 27 | try { 28 | var res = await apiService.rateProduct( 29 | token: token, 30 | productId: productId, 31 | rating: rating.toStringAsFixed(0)); 32 | var data = Product.fromMap(res); 33 | return Right(data); 34 | } on Exception catch (e) { 35 | if (e is DioError) { 36 | return Left(ServerFailure.fromDioError(e)); 37 | } 38 | return Left(ServerFailure(e.toString())); 39 | } 40 | } 41 | 42 | @override 43 | FutureEither addToCart( 44 | {required String token, required String productId}) async { 45 | try { 46 | var res = await apiService.addToCart( 47 | token: token, 48 | productId: productId, 49 | ); 50 | var data= User.fromMap(res); 51 | return Right(data); 52 | } on Exception catch (e) { 53 | if (e is DioError) { 54 | return Left(ServerFailure.fromDioError(e)); 55 | } 56 | return Left(ServerFailure(e.toString())); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Amazoni (Amazon Clone) 2 | ## Full Stack Amazon Clone along with Admin Panel , 3 | 4 | 5 | ## ScreenShots 6 | ![3](https://user-images.githubusercontent.com/91225280/234560031-adbe0eb6-c703-4d67-9e37-696b1d5b8b73.png) 7 | ![4](https://user-images.githubusercontent.com/91225280/235261262-c2fd1ec1-1629-4591-8b87-32bc77f6d797.png) 8 | ![5](https://user-images.githubusercontent.com/91225280/234560116-1b48a8c1-057e-4d22-9cf9-2984ffab94e7.png)![6](https://user-images.githubusercontent.com/91225280/234560151-21e52307-17dc-46f3-b420-5ea8fe9797a1.png) 9 | ![7](https://user-images.githubusercontent.com/91225280/234560191-68182160-292a-4338-bba8-b83392113f1d.png) 10 | 11 | 12 | ## Features of App 13 | - Email & Password Authentication 14 | - Persisting Auth State 15 | - Searching Products 16 | - Filtering Products (Based on Category) 17 | - Product Details 18 | - Rating 19 | - Getting Deal of the Day 20 | - Cart 21 | - Checking out with Google/Apple Pay 22 | - Viewing My Orders 23 | - Viewing Order Details & Status 24 | - Sign Out 25 | - Admin Panel 26 | - Viewing All Products 27 | - Adding Products 28 | - Deleting Products 29 | - Viewing Orders 30 | - Changing Order Status 31 | - Viewing Total Earnings 32 | - Viewing Category Based Earnings (on Graph) 33 | 34 | ## Features of Code 35 | - Maintainble and Scalable 36 | - following best Practises and Clean Code Concepts 37 | - Easy To Follow and Read 38 | - Follow The View-Controller-Repository Architecture 39 | - Feature First layer 40 | 41 | ## Author 42 | - Fares Bekkouche 43 | ## Tech used 44 | - Flutter 45 | - Flutter Riverpod 46 | - pay package 47 | - go_router 48 | - dartz (either) 49 | - dio 50 | - file_picker 51 | - dio 52 | - shared_preferences 53 | - badges 54 | - carousel_slider 55 | - NodeJs 56 | - express 57 | - bcryptjs 58 | - MongoDb and Mongoose 59 | - jWt (jsonwebtoken) 60 | - nodemon 61 | - flutter_rating_bar 62 | - Cloudinary 63 | - logger 64 | 65 | ## Contrubution 66 | - for any contrubution you re more then Welcomed 67 | -------------------------------------------------------------------------------- /lib/features/admin/posts/controller/posts_controller.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:amazon_clone/core/providers/repos_provider.dart'; 3 | import 'package:amazon_clone/core/providers/shared_preference_provider.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 | import '../../../../models/product.dart'; 7 | import '../repo/posts_repo.dart'; 8 | 9 | final postControllerProvider = 10 | StateNotifierProvider((ref) { 11 | return PostsController(postsRepo: ref.watch(postsRepoProvider), ref: ref); 12 | }); 13 | 14 | class PostsController extends StateNotifier?>> { 15 | PostsController({required this.postsRepo, required this.ref}) 16 | : super(const AsyncData(null)); 17 | final PostsRepo postsRepo; 18 | final StateNotifierProviderRef ref; 19 | 20 | Future getPosts({required BuildContext context}) async { 21 | state = const AsyncLoading?>(); 22 | String? token; 23 | await ref.watch(sharedPreferenceProvider)?.then((pref) async { 24 | token = pref.getString("x-auth-token"); 25 | }); 26 | await postsRepo.getPosts(token: token ?? "").then((value) { 27 | value.fold((failure) { 28 | state = AsyncValue.error(failure, StackTrace.empty); 29 | }, (products) { 30 | state = AsyncValue.data(products); 31 | }); 32 | }); 33 | } 34 | 35 | Future deletePost({required BuildContext context, required id}) async { 36 | state = const AsyncLoading?>(); 37 | String? token; 38 | await ref.watch(sharedPreferenceProvider)?.then((pref) async { 39 | token = pref.getString("x-auth-token"); 40 | }); 41 | await postsRepo.deletePosts(token: token ?? "", id: id).then((value) { 42 | value.fold((failure) { 43 | state = AsyncValue.error(failure.errorMessage, StackTrace.empty); 44 | }, (products) { 45 | state = AsyncValue.data(products); 46 | }); 47 | }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/core/constant/constants.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AppConsts { 4 | 5 | static const appBarGradient = LinearGradient( 6 | colors: [ 7 | Color.fromARGB(255, 29, 201, 192), 8 | Color.fromARGB(255, 125, 221, 216), 9 | ], 10 | stops: [0.5, 1.0], 11 | ); 12 | 13 | static const secondaryColor = Color.fromRGBO(255, 153, 0, 1); 14 | static const backgroundColor = Color.fromARGB(255, 29, 28, 28); 15 | static const Color greyBackgroundCOlor = Color.fromARGB(255, 0, 0, 0); 16 | static var selectedNavBarColor = Colors.cyan[800]!; 17 | static const unselectedNavBarColor = Colors.black87; 18 | static const textColor = Colors.white; 19 | 20 | // STATIC IMAGES 21 | static const List carouselImages = [ 22 | 'https://images-eu.ssl-images-amazon.com/images/G/31/img21/Wireless/WLA/TS/D37847648_Accessories_savingdays_Jan22_Cat_PC_1500.jpg', 23 | 'https://images-eu.ssl-images-amazon.com/images/G/31/img2021/Vday/bwl/English.jpg', 24 | 'https://images-eu.ssl-images-amazon.com/images/G/31/img22/Wireless/AdvantagePrime/BAU/14thJan/D37196025_IN_WL_AdvantageJustforPrime_Jan_Mob_ingress-banner_1242x450.jpg', 25 | 'https://images-na.ssl-images-amazon.com/images/G/31/Symbol/2020/00NEW/1242_450Banners/PL31_copy._CB432483346_.jpg', 26 | 'https://images-na.ssl-images-amazon.com/images/G/31/img21/shoes/September/SSW/pc-header._CB641971330_.jpg', 27 | ]; 28 | 29 | static const List> categoryImages = [ 30 | { 31 | 'title': 'Mobiles', 32 | 'image': 'assets/images/mobiles.jpeg', 33 | }, 34 | { 35 | 'title': 'Essentials', 36 | 'image': 'assets/images/essentials.jpeg', 37 | }, 38 | { 39 | 'title': 'Appliances', 40 | 'image': 'assets/images/appliances.jpeg', 41 | }, 42 | { 43 | 'title': 'Books', 44 | 'image': 'assets/images/books.jpeg', 45 | }, 46 | { 47 | 'title': 'Fashion', 48 | 'image': 'assets/images/fashion.jpeg', 49 | }, 50 | ]; 51 | } 52 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | amazon_clone 33 | 34 | 35 | 39 | 40 | 41 | 42 | 43 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /lib/core/errors/failire.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | abstract class Failure { 4 | final String errorMessage; 5 | 6 | Failure(this.errorMessage); 7 | } 8 | 9 | class ServerFailure extends Failure { 10 | ServerFailure(super.errorMessage); 11 | 12 | factory ServerFailure.fromDioError(DioError dioError) { 13 | switch (dioError.type) { 14 | case DioErrorType.connectionTimeout: 15 | return ServerFailure('Connection timeout with ApiServer'); 16 | 17 | case DioErrorType.sendTimeout: 18 | return ServerFailure('Send timeout with ApiServer'); 19 | 20 | case DioErrorType.receiveTimeout: 21 | return ServerFailure('Receive timeout with ApiServer'); 22 | 23 | case DioErrorType.badResponse: 24 | return ServerFailure.fromResponse( 25 | dioError.response!.statusCode, dioError.response!.data); 26 | case DioErrorType.cancel: 27 | return ServerFailure('Request to ApiServer was canceld'); 28 | 29 | case DioErrorType.unknown: 30 | if (dioError.message!.contains('SocketException')) { 31 | return ServerFailure('No Internet Connection'); 32 | } 33 | return ServerFailure('Unexpected Error, Please try again!'); 34 | default: 35 | return ServerFailure('Opps There was an Error, Please try again'); 36 | } 37 | } 38 | factory ServerFailure.fromResponse(int? statuscode, dynamic res) { 39 | if (statuscode == 400 || statuscode == 401 || statuscode == 403) { 40 | return ServerFailure(res["error"]["message"]); 41 | } else if (statuscode == 404) { 42 | return ServerFailure("bad request! pls try again later "); 43 | } else if (statuscode == 500) { 44 | return ServerFailure("server going down ! pls try again later "); 45 | } else { 46 | return ServerFailure("something went wrong ! pls try again later "); 47 | } 48 | } 49 | } 50 | 51 | 52 | // didnt add caching yet 53 | class CacheFailure extends Failure { 54 | CacheFailure(super.errorMessage); 55 | } 56 | 57 | 58 | // need to check if internet connection is available 59 | class NetworkFailure extends Failure { 60 | NetworkFailure(super.errorMessage); 61 | } 62 | -------------------------------------------------------------------------------- /lib/features/admin/view/all-orders/orders_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:go_router/go_router.dart'; 4 | 5 | import '../../../../core/common/loader.dart'; 6 | import '../../../account/view/widgets/single_product.dart'; 7 | import 'controller/order_controller.dart'; 8 | 9 | class OrdersScreen extends ConsumerStatefulWidget { 10 | const OrdersScreen({Key? key}) : super(key: key); 11 | 12 | @override 13 | ConsumerState createState() => _OrdersScreenState(); 14 | } 15 | 16 | class _OrdersScreenState extends ConsumerState { 17 | @override 18 | void initState() { 19 | super.initState(); 20 | WidgetsBinding.instance.addPostFrameCallback((_) { 21 | ref.read(ordersControllerProvider.notifier).getAllOrders(); 22 | }); 23 | } 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return ref.watch(ordersControllerProvider).when( 28 | data: (orders) { 29 | if (orders == null) { 30 | return const Center(child: Text("No Orders Yet")); 31 | } 32 | if (orders == []) { 33 | return const Center(child: Text("No Orders Yet")); 34 | } 35 | return GridView.builder( 36 | itemCount: orders!.length, 37 | gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( 38 | crossAxisCount: 2), 39 | itemBuilder: (context, index) { 40 | final orderData = orders[index]; 41 | return GestureDetector( 42 | onTap: () { 43 | GoRouter.of(context) 44 | .push("/order-detaills", extra: orders[index]); 45 | }, 46 | child: SizedBox( 47 | height: 140, 48 | child: SingleProduct( 49 | image: orderData.products[0].images[0], 50 | ), 51 | ), 52 | ); 53 | }, 54 | ); 55 | }, 56 | error: (error, stackTrace) { 57 | return Center(child: Text(error.toString())); 58 | }, 59 | loading: () { 60 | return const Center( 61 | child: Loader(), 62 | ); 63 | }, 64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/features/adress/controller/adress_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/providers/repos_provider.dart'; 2 | import 'package:amazon_clone/core/providers/shared_preference_provider.dart'; 3 | import 'package:amazon_clone/core/providers/user_provider.dart'; 4 | import 'package:amazon_clone/features/adress/repo/adress_repo.dart'; 5 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 | 7 | final addressControllerProvider = 8 | StateNotifierProvider((ref) { 9 | return AddressController( 10 | orderRepo: ref.watch(adressRepoProvider), ref: ref); 11 | }); 12 | 13 | class AddressController extends StateNotifier { 14 | AddressController({required this.orderRepo, required this.ref}) 15 | : super(const AsyncData(null)); 16 | final AddressRepo orderRepo; 17 | final StateNotifierProviderRef ref; 18 | 19 | void saveUserAddress({required adress}) async { 20 | state = const AsyncLoading(); 21 | String? token; 22 | await ref.watch(sharedPreferenceProvider)?.then((pref) async { 23 | token = pref.getString("x-auth-token"); 24 | }); 25 | var res = await orderRepo.saveUserAdress( 26 | adress: adress ?? "", 27 | token: token ?? "", 28 | ); 29 | 30 | res.fold((l) { 31 | state = AsyncValue.error(l.errorMessage, StackTrace.empty); 32 | }, (r) { 33 | ref.read(userStateProvider.notifier).setUser(r); 34 | state = const AsyncValue.data('success'); 35 | }); 36 | } 37 | 38 | void placeOrder({required adress, required amount}) async { 39 | state = const AsyncLoading(); 40 | String? token; 41 | await ref.watch(sharedPreferenceProvider)?.then((pref) async { 42 | token = pref.getString("x-auth-token"); 43 | }); 44 | var cart = ref.read(userStateProvider).cart; 45 | var res = await orderRepo.placeOrder( 46 | adress: adress ?? "", token: token ?? "", amount: amount, cart: cart); 47 | 48 | res.fold((l) { 49 | state = AsyncValue.error(l.errorMessage, StackTrace.empty); 50 | }, (r) { 51 | ref.read(userStateProvider).copyWith( 52 | cart: [], 53 | ); 54 | ref.read(userStateProvider.notifier).setUser(r); 55 | state = const AsyncValue.data('success'); 56 | }); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/features/auth/view/login/widgets/methode_selection.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | 5 | import '../../../../../core/constant/constants.dart'; 6 | import '../login_view.dart'; 7 | 8 | class MethodeSelectionSection extends StatelessWidget { 9 | const MethodeSelectionSection({ 10 | super.key, 11 | required this.auth, 12 | required this.ref, 13 | }); 14 | 15 | final Auth auth; 16 | final WidgetRef ref; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Column( 21 | children: [ 22 | ListTile( 23 | tileColor: auth == Auth.signin 24 | ? AppConsts.backgroundColor 25 | : AppConsts.greyBackgroundCOlor, 26 | title: const Text( 27 | 'Login', 28 | style: TextStyle( 29 | color: AppConsts.textColor, 30 | fontWeight: FontWeight.bold, 31 | ), 32 | ), 33 | leading: Radio( 34 | activeColor: AppConsts.secondaryColor, 35 | fillColor: MaterialStatePropertyAll( 36 | AppConsts.secondaryColor.withOpacity(.7)), 37 | value: Auth.signin, 38 | groupValue: auth, 39 | onChanged: (Auth? val) { 40 | ref.read(authTypeProvider.notifier).state = val!; 41 | }, 42 | ), 43 | ), 44 | ListTile( 45 | tileColor: auth == Auth.signup 46 | ? AppConsts.backgroundColor 47 | : AppConsts.greyBackgroundCOlor, 48 | title: const Text( 49 | 'Create Account', 50 | style: TextStyle( 51 | color: AppConsts.textColor, 52 | fontWeight: FontWeight.bold, 53 | ), 54 | ), 55 | leading: Radio( 56 | activeColor: AppConsts.secondaryColor, 57 | fillColor: MaterialStatePropertyAll( 58 | AppConsts.secondaryColor.withOpacity(.7)), 59 | value: Auth.signup, 60 | groupValue: auth, 61 | onChanged: (Auth? val) { 62 | ref.read(authTypeProvider.notifier).state = val!; 63 | }, 64 | ), 65 | ), 66 | ], 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /server/controllers/product.js: -------------------------------------------------------------------------------- 1 | import {Product} from '../models/product.js'; 2 | 3 | 4 | let getCategoryProducts = async (req, res) => { 5 | try { 6 | 7 | const category = req.query.category; 8 | const products = await Product.find({category:category}); 9 | return res.status(200).json({msg:"All Products of category : " + category,products:products}); 10 | } catch (error) { 11 | return res.status(400).json({msg:"error happend " + error.message}); 12 | } 13 | } 14 | let getSearchProducts = async (req, res) => { 15 | try { 16 | const searchQuery = req.query.searchQuery; 17 | const products = await Product.find({name:{$regex:searchQuery,$options:"i"}}); 18 | 19 | 20 | return res.status(200).json({msg:"All Products of That Seach Query are : " + searchQuery,products:products}); 21 | } catch (error) { 22 | return res.status(400).json({msg:"error happend " + error.message}); 23 | } 24 | } 25 | let rateProduct = async (req, res) => { 26 | try { 27 | console.log(req.body); 28 | const { id, rating } = req.body; 29 | let product = await Product.findById(id); 30 | 31 | for (let i = 0; i < product.ratings.length; i++) { 32 | if (product.ratings[i].userId == req.user) { 33 | product.ratings.splice(i, 1); 34 | break; 35 | } 36 | } 37 | const ratingSchema = { 38 | userId: req.user, 39 | rating, 40 | }; 41 | 42 | product.ratings.push(ratingSchema); 43 | product = await product.save(); 44 | res.json(product); 45 | } catch (e) { 46 | res.status(500).json({ error: e.message }); 47 | } 48 | }; 49 | let getDealOfTheDay = async (req, res) => { 50 | try { 51 | 52 | let products = await Product.find({}); 53 | 54 | products = products.sort((a, b) => { 55 | let aSum = 0; 56 | let bSum = 0; 57 | 58 | for (let i = 0; i < a.ratings.length; i++) { 59 | aSum += a.ratings[i].rating; 60 | } 61 | 62 | for (let i = 0; i < b.ratings.length; i++) { 63 | bSum += b.ratings[i].rating; 64 | } 65 | return aSum < bSum ? 1 : -1; 66 | }); 67 | 68 | res.json(products[0]); 69 | } catch (e) { 70 | res.status(500).json({ error: e.message }); 71 | } 72 | }; 73 | 74 | 75 | 76 | 77 | export {getCategoryProducts,getSearchProducts,rateProduct,getDealOfTheDay} -------------------------------------------------------------------------------- /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 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 33 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.example.amazon_clone" 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-gradle-build-configuration. 50 | minSdkVersion 21 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/features/admin/analytics/analytics_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:charts_flutter/flutter.dart' as charts; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | 5 | import '../../../core/common/loader.dart'; 6 | import '../../../models/sales.dart'; 7 | import '../view/widgets/category_product_chart.dart'; 8 | import 'controller/analytics_controller.dart'; 9 | 10 | class AnalyticsScreen extends ConsumerStatefulWidget { 11 | const AnalyticsScreen({Key? key}) : super(key: key); 12 | 13 | @override 14 | ConsumerState createState() => _AnalyticsScreenState(); 15 | } 16 | 17 | class _AnalyticsScreenState extends ConsumerState { 18 | int? totalSales; 19 | List? earnings; 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | WidgetsBinding.instance.addPostFrameCallback((_) { 25 | getEarnings(); 26 | }); 27 | } 28 | 29 | getEarnings() async { 30 | ref.read(analyticsControllerProvider.notifier).getAnalytics(); 31 | } 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | return ref.watch(analyticsControllerProvider).when( 36 | data: (earningData) { 37 | if (earningData == null) { 38 | return const Center(child: Text("No Earnings Yet")); 39 | } 40 | totalSales = earningData['totalEarnings']; 41 | earnings = earningData['sales']; 42 | return Column( 43 | children: [ 44 | Text( 45 | '\$${totalSales ?? 0}}', 46 | style: const TextStyle( 47 | fontSize: 20, 48 | fontWeight: FontWeight.bold, 49 | ), 50 | ), 51 | SizedBox( 52 | height: 250, 53 | child: CategoryProductsChart(seriesList: [ 54 | charts.Series( 55 | id: 'Sales', 56 | data: earnings!, 57 | domainFn: (Sales sales, _) => sales.label, 58 | measureFn: (Sales sales, _) => sales.earning, 59 | ), 60 | ]), 61 | ) 62 | ], 63 | ); 64 | }, 65 | error: (error, stackTrace) { 66 | return Center( 67 | child: Text(error.toString()), 68 | ); 69 | }, 70 | loading: () { 71 | return const Center( 72 | child: Loader(), 73 | ); 74 | }, 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/features/product-detaills/controller/product_detaills_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/providers/repos_provider.dart'; 2 | import 'package:amazon_clone/core/providers/shared_preference_provider.dart'; 3 | import 'package:amazon_clone/core/providers/user_provider.dart'; 4 | import 'package:amazon_clone/features/product-detaills/repo/product_detaills_repo.dart'; 5 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 | 7 | import '../../../models/product.dart'; 8 | 9 | final productDetaillsControllerProvider = 10 | StateNotifierProvider((ref) { 11 | return ProductDetaillsController( 12 | productDetaillsRepo: ref.watch(productDetaillsRepoProvider), 13 | ref: ref); 14 | }); 15 | 16 | class ProductDetaillsController extends StateNotifier> { 17 | ProductDetaillsController( 18 | {required this.productDetaillsRepo, required this.ref}) 19 | : super(const AsyncData(null)); 20 | final ProductDetaillsRepo productDetaillsRepo; 21 | final StateNotifierProviderRef ref; 22 | 23 | void rateProduct({required double rating}) async { 24 | var temp = state.value!.id; 25 | state = const AsyncLoading(); 26 | String? token; 27 | await ref.watch(sharedPreferenceProvider)?.then((pref) async { 28 | token = pref.getString("x-auth-token"); 29 | }); 30 | var res = await productDetaillsRepo.rateProduct( 31 | productId: temp ?? "", 32 | token: token ?? "", 33 | rating: rating, 34 | ); 35 | 36 | res.fold((l) { 37 | state = AsyncValue.error(l.errorMessage, StackTrace.empty); 38 | }, (r) { 39 | state = AsyncValue.data(r); 40 | }); 41 | } 42 | 43 | void addToCart() async { 44 | var tmp = state.value; 45 | var temp = tmp!.id; 46 | state = const AsyncLoading(); 47 | String? token; 48 | await ref.watch(sharedPreferenceProvider)?.then((pref) async { 49 | token = pref.getString("x-auth-token"); 50 | }); 51 | var res = await productDetaillsRepo.addToCart( 52 | productId: temp ?? "", 53 | token: token ?? "", 54 | ); 55 | 56 | res.fold((l) { 57 | state = AsyncValue.error(l.errorMessage, StackTrace.empty); 58 | }, (r) { 59 | ref.read(userStateProvider.notifier).setUser(r); 60 | state = AsyncValue.data(tmp); 61 | }); 62 | } 63 | 64 | void setProduct(Product product) { 65 | state = AsyncValue.data(product); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/models/user.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | class User { 4 | final String id; 5 | final String name; 6 | final String email; 7 | final String password; 8 | final String address; 9 | final String type; 10 | final String token; 11 | final List cart; 12 | 13 | User({ 14 | required this.id, 15 | required this.name, 16 | required this.email, 17 | required this.password, 18 | required this.address, 19 | required this.type, 20 | required this.token, 21 | required this.cart, 22 | }); 23 | 24 | Map toMap() { 25 | return { 26 | 'id': id, 27 | 'name': name, 28 | 'email': email, 29 | 'password': password, 30 | 'address': address, 31 | 'type': type, 32 | 'token': token, 33 | 'cart': cart, 34 | }; 35 | } 36 | 37 | factory User.fromMap(Map map) { 38 | return User( 39 | id: map['_id'] ?? '', 40 | name: map['name'] ?? '', 41 | email: map['email'] ?? '', 42 | password: map['password'] ?? '', 43 | address: map['address'] ?? '', 44 | type: map['type'] ?? '', 45 | token: map['token'] ?? '', 46 | cart: List>.from( 47 | map['cart']?.map( 48 | (x) => Map.from(x), 49 | ), 50 | ), 51 | ); 52 | } 53 | factory User.empty() { 54 | return User( 55 | id: '', 56 | name: '', 57 | email: '', 58 | password: '', 59 | address: '', 60 | type: '', 61 | token: '', 62 | cart: [], 63 | ); 64 | } 65 | 66 | String toJson() => json.encode(toMap()); 67 | 68 | factory User.fromJson(String source) => User.fromMap(json.decode(source)); 69 | 70 | User copyWith({ 71 | String? id, 72 | String? name, 73 | String? email, 74 | String? password, 75 | String? address, 76 | String? type, 77 | String? token, 78 | List? cart, 79 | }) { 80 | return User( 81 | id: id ?? this.id, 82 | name: name ?? this.name, 83 | email: email ?? this.email, 84 | password: password ?? this.password, 85 | address: address ?? this.address, 86 | type: type ?? this.type, 87 | token: token ?? this.token, 88 | cart: cart ?? this.cart, 89 | ); 90 | } 91 | 92 | bool equals(User other) { 93 | if (email == other.email && password == other.password) { 94 | return true; 95 | } 96 | return false; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /server/controllers/auth.js: -------------------------------------------------------------------------------- 1 | import User from "../models/user.js"; 2 | import bcrypt from "bcryptjs"; 3 | import jwt from "jsonwebtoken"; 4 | 5 | 6 | let signIn= async (req, res) => { 7 | const {email , password} = req.body; 8 | console.log(req.body); 9 | try { 10 | const existingUser = await User.findOne({email}); 11 | if(!existingUser){ 12 | return res.status(404).json({msg:"User Doesn't Exist"}); 13 | } 14 | let isPasswordCorrect = await bcrypt.compare(password, existingUser.password); 15 | if(!isPasswordCorrect){ 16 | return res.status(404).json({ 17 | nsg:"Password Inccorect" 18 | }); 19 | } 20 | 21 | 22 | const token = jwt.sign( 23 | {id: existingUser._id}, 24 | "passwordKey" 25 | ); 26 | return res.status(200).json({ 27 | msg:"User Signed In Successfully", 28 | token:token, 29 | ...existingUser._doc, 30 | 31 | }) 32 | 33 | 34 | 35 | } catch (error) { 36 | return res.status(404).json({msg : "Error" + error }) 37 | 38 | } 39 | 40 | }; 41 | 42 | let signUp= async (req, res) => { 43 | const {name, email ,password} = req.body; 44 | let hashedPass = await bcrypt.hash(password,8); 45 | try { 46 | const existingUser = await User.findOne({email}); 47 | if(existingUser){ 48 | return res.status(400).json({msg:"User With This Email Already Exists"}); 49 | } 50 | let user = new User({ 51 | email, 52 | password:hashedPass, 53 | name, 54 | }) 55 | user = await user.save(); 56 | res.status(200).json({user:user,msg:"User Created Successfully"}); 57 | 58 | } catch (error) { 59 | return res.status(404).json({msg : "Error" + error }) 60 | } 61 | }; 62 | 63 | let tokenValid = async (req, res) => { 64 | try { 65 | const token = req.header("x-auth-token"); 66 | 67 | if(!token){ 68 | return res.json(false); 69 | } 70 | const verified = jwt.verify(token,"passwordKey"); 71 | if(!verified){ 72 | return res.json(false); 73 | } 74 | const user = User.findById(verified.id); 75 | if(!user){ 76 | return res.json(false); 77 | } 78 | return res.json(true); 79 | } 80 | catch(e){ 81 | return res.status(404).json({msg:e}); 82 | } 83 | }; 84 | 85 | 86 | 87 | let getUser = async (req, res) => { 88 | const user = await User.findById(req.user); 89 | res.json({ ...user._doc, token: req.token }); 90 | } 91 | 92 | export { signUp,signIn ,tokenValid,getUser}; 93 | 94 | 95 | //JWT -------------------------------------------------------------------------------- /lib/core/providers/repos_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/providers/api_service_provider.dart'; 2 | import 'package:amazon_clone/features/account/repo/account_repo.dart'; 3 | import 'package:amazon_clone/features/admin/add_product/repo/add_product_repo.dart'; 4 | import 'package:amazon_clone/features/admin/analytics/repo/analytics_repo.dart'; 5 | import 'package:amazon_clone/features/adress/repo/adress_repo.dart'; 6 | import 'package:amazon_clone/features/auth/repository/auth_repo.dart'; 7 | import 'package:amazon_clone/features/cart/repo/cart_repo.dart'; 8 | import 'package:amazon_clone/features/category/repo/category_repo.dart'; 9 | import 'package:amazon_clone/features/home/repo/home_repo.dart'; 10 | import 'package:amazon_clone/features/admin/posts/repo/posts_repo.dart'; 11 | import 'package:amazon_clone/features/product-detaills/repo/product_detaills_repo.dart'; 12 | import 'package:amazon_clone/features/search/repo/search_repo.dart'; 13 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 14 | 15 | import '../../features/admin/view/all-orders/repo/order_repo.dart'; 16 | 17 | 18 | final authRepoProvider = Provider((ref) { 19 | return AuthRepoImpl(ref.read(apiServiceProvider)); 20 | }); 21 | final addProductRepoProvider = Provider((ref) { 22 | return AddProductRepoImpl(apiService: ref.read(apiServiceProvider)); 23 | }); 24 | final postsRepoProvider = Provider((ref) { 25 | return PostsRepoImpl(apiService: ref.read(apiServiceProvider)); 26 | }); 27 | final categoryRepoProvider = Provider((ref) { 28 | return CategoryRepoImpl(apiService: ref.read(apiServiceProvider)); 29 | }); 30 | final searchRepoProvider = Provider((ref) { 31 | return SearchRepoImpl(apiService: ref.read(apiServiceProvider)); 32 | }); 33 | final productDetaillsRepoProvider = Provider((ref) { 34 | return ProductDetaillsRepoImpl(apiService: ref.read(apiServiceProvider)); 35 | }); 36 | final homeRepoProvider = Provider((ref) { 37 | return HomeRepoImpl(apiService: ref.read(apiServiceProvider)); 38 | }); 39 | final cartRepoProvider = Provider((ref) { 40 | return CartRepoImpl(apiService: ref.read(apiServiceProvider)); 41 | }); 42 | final adressRepoProvider = Provider((ref) { 43 | return AddressRepoImpl(apiService: ref.read(apiServiceProvider)); 44 | }); 45 | final accountRepoProvider = Provider((ref) { 46 | return AccountRepoImpl(apiService: ref.read(apiServiceProvider)); 47 | }); 48 | final orderRepoProvider = Provider((ref) { 49 | return OrderRepoImpl(apiService: ref.read(apiServiceProvider)); 50 | }); 51 | final analyticsRepoProvider = Provider((ref) { 52 | return AnalyticsRepoImpl(apiService: ref.read(apiServiceProvider)); 53 | }); 54 | -------------------------------------------------------------------------------- /lib/features/auth/repository/auth_repo.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:amazon_clone/core/api_service.dart'; 4 | import 'package:amazon_clone/core/errors/failire.dart'; 5 | import 'package:amazon_clone/core/utils/type_def.dart'; 6 | import 'package:dartz/dartz.dart'; 7 | import 'package:dio/dio.dart'; 8 | 9 | import '../../../models/user.dart'; 10 | 11 | abstract class AuthRepo { 12 | FutureEither login({required email, required password}); 13 | FutureEither register( 14 | {required email, required password, required name}); 15 | FutureEither getUserData({required token}); 16 | } 17 | 18 | class AuthRepoImpl implements AuthRepo { 19 | final ApiService apiService; 20 | 21 | AuthRepoImpl(this.apiService); 22 | @override 23 | FutureEither register( 24 | {required email, required password, required name}) async { 25 | try { 26 | User user = User( 27 | id: "", 28 | name: name, 29 | email: email, 30 | password: password, 31 | address: "", 32 | type: "", 33 | token: "", cart: [], 34 | ); 35 | var data = await apiService.signUp(data: user.toJson()); 36 | user = User.fromMap(data); 37 | return Right(user); 38 | } on Exception catch (e) { 39 | if (e is DioError) { 40 | return Left(ServerFailure.fromDioError(e)); 41 | } 42 | return Left(ServerFailure(e.toString())); 43 | } 44 | } 45 | 46 | @override 47 | FutureEither login({required email, required password}) async { 48 | try { 49 | User user = User( 50 | id: "", 51 | name: "", 52 | email: email, 53 | password: password, 54 | address: "", 55 | type: "", 56 | token: "", cart: [], 57 | ); 58 | var data = await apiService.signIn(data: user.toJson()); 59 | user = User.fromMap(data); 60 | return Right(user); 61 | } on Exception catch (e) { 62 | if (e is DioError) { 63 | return Left(ServerFailure.fromDioError(e)); 64 | } 65 | return Left(ServerFailure(e.toString())); 66 | } 67 | } 68 | 69 | @override 70 | FutureEither getUserData({required token}) async { 71 | try { 72 | var res = await apiService.isValid(token: token); 73 | if (res == true) { 74 | log( 75 | "token is valid", 76 | ); 77 | var data = await apiService.getUserData(token: token); 78 | var user = User.fromMap(data); 79 | return Right(user); 80 | } 81 | log( 82 | "token not valid", 83 | ); 84 | return Left(ServerFailure("User not found")); 85 | } on Exception catch (e) { 86 | 87 | if (e is DioError) { 88 | return Left(ServerFailure.fromDioError(e)); 89 | } 90 | return Left(ServerFailure(e.toString())); 91 | } 92 | } 93 | } 94 | 95 | 96 | // FutureEither -------------------------------------------------------------------------------- /lib/features/search/view/widget/searched_product.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../../../../core/common/stars.dart'; 4 | import '../../../../models/product.dart'; 5 | 6 | class SearchedProduct extends StatelessWidget { 7 | final Product product; 8 | const SearchedProduct({ 9 | Key? key, 10 | required this.product, 11 | }) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | double totalRating = 0; 16 | for (int i = 0; i < product.rating!.length; i++) { 17 | totalRating += product.rating![i].rating; 18 | } 19 | double avgRating = 0; 20 | if (totalRating != 0) { 21 | avgRating = totalRating / product.rating!.length; 22 | } 23 | return Column( 24 | children: [ 25 | Container( 26 | margin: const EdgeInsets.symmetric( 27 | horizontal: 10, 28 | ), 29 | child: Row( 30 | children: [ 31 | Image.network( 32 | product.images[0], 33 | fit: BoxFit.contain, 34 | height: 135, 35 | width: 135, 36 | ), 37 | Column( 38 | children: [ 39 | Container( 40 | padding: const EdgeInsets.symmetric(horizontal: 10), 41 | child: Text( 42 | product.name, 43 | style: const TextStyle( 44 | fontSize: 16, 45 | ), 46 | maxLines: 2, 47 | ), 48 | ), 49 | Container( 50 | padding: const EdgeInsets.only(left: 10, top: 5), 51 | child: Stars( 52 | rating: avgRating, 53 | ), 54 | ), 55 | Container( 56 | padding: const EdgeInsets.only(left: 10, top: 5), 57 | child: Text( 58 | '\$${product.price}', 59 | style: const TextStyle( 60 | fontSize: 20, 61 | fontWeight: FontWeight.bold, 62 | ), 63 | maxLines: 2, 64 | ), 65 | ), 66 | Container( 67 | padding: const EdgeInsets.only(left: 10), 68 | child: const Text('Eligible for FREE Shipping'), 69 | ), 70 | Container( 71 | padding: const EdgeInsets.only(left: 10, top: 5), 72 | child: const Text( 73 | 'In Stock', 74 | style: TextStyle( 75 | color: Colors.teal, 76 | ), 77 | maxLines: 2, 78 | ), 79 | ), 80 | ], 81 | ), 82 | ], 83 | ), 84 | ), 85 | ], 86 | ); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /lib/features/auth/controller/auth_controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:amazon_clone/core/providers/repos_provider.dart'; 4 | import 'package:amazon_clone/core/providers/shared_preference_provider.dart'; 5 | import 'package:amazon_clone/core/providers/user_provider.dart'; 6 | import 'package:amazon_clone/core/utils/custom_snack_bar.dart'; 7 | import 'package:amazon_clone/features/auth/repository/auth_repo.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 10 | 11 | final authControllerProvider = 12 | StateNotifierProvider((ref) { 13 | return AuthController(authRepository: ref.watch(authRepoProvider), ref: ref); 14 | }); 15 | 16 | class AuthController extends StateNotifier { 17 | AuthController({required this.authRepository, required this.ref}) 18 | : super(const AsyncData(null)); 19 | final AuthRepo authRepository; 20 | final StateNotifierProviderRef ref; 21 | 22 | Future register({ 23 | required BuildContext context, 24 | required String email, 25 | required String password, 26 | required String name, 27 | }) async { 28 | state = const AsyncLoading(); 29 | 30 | state = await AsyncValue.guard(() => authRepository.register( 31 | email: email, 32 | password: password, 33 | name: name, 34 | )); 35 | } 36 | 37 | Future login( 38 | {required email, required password, required context}) async { 39 | state = const AsyncLoading(); 40 | await authRepository 41 | .login( 42 | email: email, 43 | password: password, 44 | ) 45 | .then((value) { 46 | value.fold((failure) { 47 | state = AsyncValue.error(failure.errorMessage, StackTrace.current); 48 | showSnackBar(context, failure.errorMessage); 49 | }, (user) { 50 | state = AsyncValue.data(user); 51 | 52 | ref.watch(sharedPreferenceProvider)?.then((pref) { 53 | pref.setString("x-auth-token", user.token); 54 | }); 55 | ref.read(userStateProvider.notifier).setUser(user); 56 | }); 57 | 58 | }); 59 | } 60 | 61 | Future getUserData() async { 62 | log("============================GET USER DATA CALLED====================================="); 63 | String? token; 64 | 65 | ref.watch(sharedPreferenceProvider)?.then((pref) async { 66 | token = pref.getString("x-auth-token"); 67 | if (token == null) { 68 | log("token is null"); 69 | pref.setString('x-auth-token', ''); 70 | } 71 | var res = await authRepository.getUserData(token: token); 72 | 73 | res.fold((failire) { 74 | log(failire.errorMessage.toUpperCase()); 75 | state = AsyncValue.error(failire.errorMessage, StackTrace.current); 76 | }, (data) { 77 | log(data.toJson().toString()); 78 | state = AsyncValue.data(data); 79 | ref.read(userStateProvider.notifier).setUser(data); 80 | }); 81 | }); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /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/features/account/view/widgets/orders.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/features/account/controller/account_controller.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | import 'package:go_router/go_router.dart'; 5 | 6 | import '../../../../core/common/loader.dart'; 7 | import '../../../../core/constant/constants.dart'; 8 | import 'single_product.dart'; 9 | 10 | class Orders extends ConsumerStatefulWidget { 11 | const Orders({super.key}); 12 | 13 | @override 14 | ConsumerState createState() => _OrdersState(); 15 | } 16 | 17 | class _OrdersState extends ConsumerState { 18 | @override 19 | void initState() { 20 | super.initState(); 21 | WidgetsBinding.instance.addPostFrameCallback((_) { 22 | ref.read(accountControllerProvider.notifier).getOrders(); 23 | }); 24 | } 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return ref.watch(accountControllerProvider).when( 29 | data: (orders) { 30 | if (orders == null) { 31 | return const Center(child: Text("No Orders Yet")); 32 | } 33 | return Column( 34 | children: [ 35 | Row( 36 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 37 | children: [ 38 | Container( 39 | padding: const EdgeInsets.only( 40 | left: 15, 41 | ), 42 | child: const Text( 43 | 'Your Orders', 44 | style: TextStyle( 45 | color: Colors.white, 46 | fontSize: 18, 47 | fontWeight: FontWeight.w600, 48 | ), 49 | ), 50 | ), 51 | Container( 52 | padding: const EdgeInsets.only( 53 | right: 15, 54 | ), 55 | child: Text( 56 | 'See all', 57 | style: TextStyle( 58 | color: AppConsts.selectedNavBarColor, 59 | ), 60 | ), 61 | ), 62 | ], 63 | ), 64 | // display orders 65 | Container( 66 | height: 170, 67 | padding: const EdgeInsets.only( 68 | left: 10, 69 | top: 20, 70 | right: 0, 71 | ), 72 | child: ListView.builder( 73 | scrollDirection: Axis.horizontal, 74 | itemCount: orders!.length, 75 | itemBuilder: (context, index) { 76 | return GestureDetector( 77 | onTap: () { 78 | GoRouter.of(context) 79 | .push('/order-detaills', extra: orders[index]); 80 | }, 81 | child: SingleProduct( 82 | image: orders![index].products[0].images[0], 83 | ), 84 | ); 85 | }, 86 | ), 87 | ), 88 | ], 89 | ); 90 | }, 91 | error: (error, stackTrace) { 92 | return const Center(child: Text("No Orders Yet")); 93 | }, 94 | loading: () { 95 | return const Center( 96 | child: Loader(), 97 | ); 98 | }, 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/features/admin/posts/view/posts_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/features/admin/posts/controller/posts_controller.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | import 'package:go_router/go_router.dart'; 5 | 6 | import '../../../../core/common/loader.dart'; 7 | import '../../../../models/product.dart'; 8 | import '../../../account/view/widgets/single_product.dart'; 9 | 10 | class PostsScreen extends ConsumerStatefulWidget { 11 | const PostsScreen({Key? key}) : super(key: key); 12 | 13 | @override 14 | ConsumerState createState() => _PostsScreenState(); 15 | } 16 | 17 | class _PostsScreenState extends ConsumerState { 18 | List? products; 19 | 20 | @override 21 | void initState() { 22 | super.initState(); 23 | WidgetsBinding.instance.addPostFrameCallback((_) { 24 | ref.read(postControllerProvider.notifier).getPosts(context: context); 25 | }); 26 | } 27 | 28 | void deleteProduct(Product product, int index) { 29 | ref 30 | .read(postControllerProvider.notifier) 31 | .deletePost(context: context, id: product.id); 32 | } 33 | 34 | void navigateToAddProduct() { 35 | GoRouter.of(context).push("/add-product"); 36 | } 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return Scaffold( 41 | body: ref.watch(postControllerProvider).when( 42 | data: (products) { 43 | if (products == [] || products == null) { 44 | return const Center(child: Text("No Posts Yet")); 45 | } 46 | 47 | return Scaffold( 48 | body: GridView.builder( 49 | itemCount: products!.length, 50 | gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( 51 | crossAxisCount: 2, 52 | childAspectRatio: 0.7, 53 | ), 54 | itemBuilder: (context, index) { 55 | final productData = products![index]; 56 | return Column( 57 | children: [ 58 | SizedBox( 59 | height: 140, 60 | child: SingleProduct( 61 | image: productData.images[0], 62 | ), 63 | ), 64 | Row( 65 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 66 | children: [ 67 | Expanded( 68 | child: Text( 69 | productData.name, 70 | overflow: TextOverflow.ellipsis, 71 | maxLines: 2, 72 | ), 73 | ), 74 | IconButton( 75 | onPressed: () => deleteProduct(productData, index), 76 | icon: const Icon( 77 | Icons.delete_outline, 78 | ), 79 | ), 80 | ], 81 | ), 82 | ], 83 | ); 84 | }, 85 | ), 86 | ); 87 | }, 88 | error: (_, __) { 89 | return Text(_.toString()); 90 | }, 91 | loading: () { 92 | return const Loader(); 93 | }, 94 | ), 95 | floatingActionButton: FloatingActionButton( 96 | onPressed: navigateToAddProduct, 97 | tooltip: 'Add a Product', 98 | child: const Icon(Icons.add), 99 | ), 100 | floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, 101 | ); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /lib/features/home/view/widgets/custom_app_bar_home.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:go_router/go_router.dart'; 4 | 5 | import '../../../../core/constant/constants.dart'; 6 | import '../../../search/controller/search_controller.dart'; 7 | 8 | class CustomAppBarHome extends ConsumerWidget with PreferredSizeWidget { 9 | const CustomAppBarHome({super.key}); 10 | void navigateToSearchScreen(ctx, String val) { 11 | GoRouter.of(ctx).push("/search"); 12 | } 13 | 14 | @override 15 | Widget build(BuildContext context, ref) { 16 | return AppBar( 17 | leading: null, 18 | flexibleSpace: Container( 19 | decoration: const BoxDecoration( 20 | gradient: AppConsts.appBarGradient, 21 | ), 22 | ), 23 | title: Row( 24 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 25 | children: [ 26 | Expanded( 27 | child: Container( 28 | height: 42, 29 | margin: const EdgeInsets.only(left: 15), 30 | child: Material( 31 | borderRadius: BorderRadius.circular(7), 32 | elevation: 1, 33 | child: TextFormField( 34 | onFieldSubmitted: (val) { 35 | ref.read(searchControllerProvider.notifier).searchQuery = 36 | val; 37 | 38 | navigateToSearchScreen(context, val); 39 | }, 40 | decoration: InputDecoration( 41 | 42 | prefixIcon: InkWell( 43 | onTap: () {}, 44 | child: const Padding( 45 | padding: EdgeInsets.only( 46 | left: 6, 47 | ), 48 | child: Icon( 49 | Icons.search, 50 | color: Colors.black, 51 | size: 23, 52 | ), 53 | ), 54 | ), 55 | 56 | filled: true, 57 | fillColor: Colors.white, 58 | contentPadding: const EdgeInsets.only(top: 10), 59 | border: const OutlineInputBorder( 60 | borderRadius: BorderRadius.all( 61 | Radius.circular(7), 62 | ), 63 | borderSide: BorderSide.none, 64 | ), 65 | enabledBorder: const OutlineInputBorder( 66 | borderRadius: BorderRadius.all( 67 | Radius.circular(7), 68 | ), 69 | borderSide: BorderSide( 70 | color: Colors.black38, 71 | width: 1, 72 | ), 73 | ), 74 | 75 | hintText: 'Search Amazon.in', 76 | hintStyle: const TextStyle( 77 | color: Colors.black38, 78 | fontWeight: FontWeight.w500, 79 | fontSize: 17, 80 | ), 81 | ), 82 | ), 83 | ), 84 | ), 85 | ), 86 | Container( 87 | color: Colors.transparent, 88 | height: 42, 89 | margin: const EdgeInsets.symmetric(horizontal: 10), 90 | child: const Icon(Icons.mic, color: Colors.black, size: 25), 91 | ), 92 | ], 93 | ), 94 | ); 95 | } 96 | 97 | @override 98 | Size get preferredSize => const Size.fromHeight(60); 99 | } 100 | -------------------------------------------------------------------------------- /server/controllers/user.js: -------------------------------------------------------------------------------- 1 | import User from '../models/user.js'; 2 | import {Product} from '../models/product.js'; 3 | import Order from '../models/order.js'; 4 | 5 | let addToCart = async (req, res) => { 6 | try { 7 | const { id } = req.body; 8 | const product = await Product.findById(id); 9 | let user = await User.findById(req.user); 10 | 11 | if (user.cart.length == 0) { 12 | user.cart.push({ product, quantity: 1 }); 13 | } else { 14 | let isProductFound = false; 15 | for (let i = 0; i < user.cart.length; i++) { 16 | 17 | if (user.cart[i].product._id.equals(product._id)) { 18 | isProductFound = true; 19 | } 20 | } 21 | 22 | if (isProductFound) { 23 | let producttt = user.cart.find((productt) => 24 | productt.product._id.equals(product._id) 25 | ); 26 | producttt.quantity += 1; 27 | } else { 28 | user.cart.push({ product, quantity: 1 }); 29 | } 30 | } 31 | user = await user.save(); 32 | res.json(user); 33 | } catch (e) { 34 | console.log(e.message); 35 | res.status(500).json({ error: e.message }); 36 | } 37 | } 38 | let removeFromCart = async (req, res) => { 39 | try { 40 | const { id } = req.query.productId; 41 | const product = await Product.findById(id); 42 | let user = await User.findById(req.user); 43 | 44 | for (let i = 0; i < user.cart.length; i++) { 45 | if (user.cart[i].product._id.equals(product._id)) { 46 | if (user.cart[i].quantity == 1) { 47 | user.cart.splice(i, 1); 48 | } else { 49 | user.cart[i].quantity -= 1; 50 | } 51 | } 52 | } 53 | user = await user.save(); 54 | res.json(user); 55 | } catch (e) { 56 | res.status(500).json({ error: e.message }); 57 | } 58 | } 59 | let saveUserAdress = async (req, res) => { 60 | try { 61 | const { address } = req.body; 62 | let user = await User.findById(req.user); 63 | user.address = address; 64 | user = await user.save(); 65 | res.json(user); 66 | } catch (e) { 67 | res.status(500).json({ error: e.message }); 68 | } 69 | } 70 | 71 | let placeOrder = async (req, res) => { 72 | try { 73 | const { cart, totalPrice, address } = req.body; 74 | let products = []; 75 | 76 | for (let i = 0; i < cart.length; i++) { 77 | let product = await Product.findById(cart[i].product._id); 78 | if (product.quantity >= cart[i].quantity) { 79 | product.quantity -= cart[i].quantity; 80 | products.push({ product, quantity: cart[i].quantity }); 81 | await product.save(); 82 | } else { 83 | return res 84 | .status(400) 85 | .json({ msg: `${product.name} is out of stock!` }); 86 | } 87 | } 88 | 89 | let user = await User.findById(req.user); 90 | user.cart = []; 91 | user = await user.save(); 92 | 93 | let order = new Order({ 94 | products, 95 | totalPrice, 96 | address, 97 | userId: req.user, 98 | orderedAt: new Date().getTime(), 99 | }); 100 | order = await order.save(); 101 | res.json(order); 102 | } catch (e) { 103 | res.status(500).json({ error: e.message }); 104 | } 105 | } 106 | 107 | let allOrders = async (req, res) => { 108 | try { 109 | const orders = await Order.find({ userId: req.user }); 110 | res.json(orders); 111 | } catch (e) { 112 | res.status(500).json({ error: e.message }); 113 | } 114 | } 115 | 116 | export {addToCart,removeFromCart,saveUserAdress,placeOrder,allOrders} -------------------------------------------------------------------------------- /lib/core/common/bottom_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:badges/badges.dart' as b; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../constant/constants.dart'; 5 | 6 | class BottomBar extends StatefulWidget { 7 | final List paramPages; 8 | 9 | const BottomBar({Key? key, required this.paramPages}) : super(key: key); 10 | 11 | @override 12 | State createState() => _BottomBarState(); 13 | } 14 | 15 | class _BottomBarState extends State { 16 | int _page = 0; 17 | double bottomBarWidth = 42; 18 | double bottomBarBorderWidth = 5; 19 | 20 | _BottomBarState(); 21 | 22 | void updatePage(int page) { 23 | setState(() { 24 | _page = page; 25 | }); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Scaffold( 31 | body: widget.paramPages[_page], 32 | bottomNavigationBar: BottomNavigationBar( 33 | currentIndex: _page, 34 | selectedItemColor: AppConsts.selectedNavBarColor, 35 | unselectedItemColor: const Color.fromARGB(221, 255, 255, 255), 36 | backgroundColor: const Color.fromARGB(255, 20, 230, 195), 37 | iconSize: 28, 38 | onTap: updatePage, 39 | items: [ 40 | // HOME 41 | BottomNavigationBarItem( 42 | icon: Container( 43 | width: bottomBarWidth, 44 | decoration: BoxDecoration( 45 | border: Border( 46 | top: BorderSide( 47 | color: _page == 0 48 | ? AppConsts.selectedNavBarColor 49 | : const Color.fromARGB(255, 20, 230, 195), 50 | width: bottomBarBorderWidth, 51 | ), 52 | ), 53 | ), 54 | child: const Icon( 55 | Icons.home_outlined, 56 | ), 57 | ), 58 | label: '', 59 | ), 60 | // ACCOUNT 61 | BottomNavigationBarItem( 62 | icon: Container( 63 | width: bottomBarWidth, 64 | decoration: BoxDecoration( 65 | border: Border( 66 | top: BorderSide( 67 | color: _page == 1 68 | ? AppConsts.selectedNavBarColor 69 | : const Color.fromARGB(255, 20, 230, 195), 70 | width: bottomBarBorderWidth, 71 | ), 72 | ), 73 | ), 74 | child: const Icon( 75 | Icons.person_outline_outlined, 76 | ), 77 | ), 78 | label: '', 79 | ), 80 | // CART 81 | BottomNavigationBarItem( 82 | icon: Container( 83 | width: bottomBarWidth, 84 | decoration: BoxDecoration( 85 | border: Border( 86 | top: BorderSide( 87 | color: _page == 2 88 | ? AppConsts.selectedNavBarColor 89 | : const Color.fromARGB(255, 20, 230, 195), 90 | width: bottomBarBorderWidth, 91 | ), 92 | ), 93 | ), 94 | child: b.Badge( 95 | position: b.BadgePosition.topEnd(top: 0, end: 0), 96 | showBadge: true, 97 | badgeStyle: b.BadgeStyle( 98 | padding: const EdgeInsets.all(7), 99 | borderRadius: const BorderRadius.all(Radius.circular(10)), 100 | badgeColor: !(_page == 2) 101 | ? const Color.fromARGB(255, 255, 255, 255) 102 | : AppConsts.selectedNavBarColor, 103 | ), 104 | child: const Icon( 105 | Icons.shopping_cart_outlined, 106 | ), 107 | ), 108 | ), 109 | label: '', 110 | ), 111 | ], 112 | ), 113 | ); 114 | } 115 | } 116 | 117 | // 118 | // -------------------------------------------------------------------------------- /server/controllers/admin.js: -------------------------------------------------------------------------------- 1 | import {Product} from '../models/product.js'; 2 | import {Order} from '../models/order.js'; 3 | 4 | let addProduct = async (req, res) => { 5 | try { 6 | const {name,price,description,category,quantity,images} = req.body; 7 | if(!name || !price || !description || !category || !quantity ||!images){ 8 | return res.status(400).json({msg:"Please fill all the fields"}); 9 | } 10 | const product = new Product({ 11 | name, 12 | price, 13 | description, 14 | category, 15 | quantity, 16 | images 17 | }); 18 | const savedProduct = await product.save(); 19 | return res.status(200).json({msg:"Product Added Successfully",product:savedProduct}); 20 | } catch (error) { 21 | return res.status(400).json({msg:"error happend" + error.message}); 22 | } 23 | } 24 | let allProducts = async (req, res) => { 25 | try { 26 | const products = await Product.find({}); 27 | return res.status(200).json({msg:"All Products",products:products}); 28 | } catch (error) { 29 | return res.status(400).json({msg:"error happend " + error.message}); 30 | } 31 | } 32 | let deleteProduct = async (req,res)=>{ 33 | try { 34 | const {id} = req.body; 35 | await Product.findByIdAndDelete(id); 36 | const products = await Product.find({}); 37 | return res.status(200).json({msg:"Product Deleted Successfully",products:products}); 38 | } catch (error) { 39 | console.log(error); 40 | return res.status(400).json({error:{ 41 | error:"my name is jhon cena", 42 | msg:error 43 | }}); 44 | } 45 | } 46 | let getOrders = async (req, res) => { 47 | try { 48 | const orders = await Order.find({}); 49 | res.json(orders); 50 | } catch (e) { 51 | res.status(500).json({ error: e.message }); 52 | } 53 | } 54 | 55 | let changeOrderStatus =async (req, res) => { 56 | try { 57 | const { id, status } = req.body; 58 | let order = await Order.findById(id); 59 | order.status = status; 60 | order = await order.save(); 61 | res.json(order); 62 | } catch (e) { 63 | res.status(500).json({ error: e.message }); 64 | } 65 | } 66 | 67 | let getAnalytics= async (req, res) => { 68 | try { 69 | const orders = await Order.find({}); 70 | let totalEarnings = 0; 71 | 72 | for (let i = 0; i < orders.length; i++) { 73 | for (let j = 0; j < orders[i].products.length; j++) { 74 | totalEarnings += 75 | orders[i].products[j].quantity * orders[i].products[j].product.price; 76 | } 77 | } 78 | // CATEGORY WISE ORDER FETCHING 79 | let mobileEarnings = await fetchCategoryWiseProduct("Mobiles"); 80 | let essentialEarnings = await fetchCategoryWiseProduct("Essentials"); 81 | let applianceEarnings = await fetchCategoryWiseProduct("Appliances"); 82 | let booksEarnings = await fetchCategoryWiseProduct("Books"); 83 | let fashionEarnings = await fetchCategoryWiseProduct("Fashion"); 84 | 85 | let earnings = { 86 | totalEarnings, 87 | mobileEarnings, 88 | essentialEarnings, 89 | applianceEarnings, 90 | booksEarnings, 91 | fashionEarnings, 92 | }; 93 | 94 | res.json(earnings); 95 | } catch (e) { 96 | console.log(e); 97 | res.status(500).json({ error: e.message }); 98 | } 99 | } 100 | 101 | 102 | 103 | 104 | export {addProduct,allProducts,deleteProduct,getOrders,changeOrderStatus,getAnalytics} 105 | 106 | 107 | 108 | // reusability of code 109 | async function fetchCategoryWiseProduct(category) { 110 | let earnings = 0; 111 | let categoryOrders = await Order.find({ 112 | "products.product.category": category, 113 | }); 114 | 115 | for (let i = 0; i < categoryOrders.length; i++) { 116 | for (let j = 0; j < categoryOrders[i].products.length; j++) { 117 | earnings += 118 | categoryOrders[i].products[j].quantity * 119 | categoryOrders[i].products[j].product.price; 120 | } 121 | } 122 | return earnings; 123 | } -------------------------------------------------------------------------------- /lib/features/home/view/widgets/deal_of_day.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/features/home/controllers/home_controller.dart'; 2 | import 'package:amazon_clone/features/product-detaills/controller/product_detaills_controller.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | import 'package:go_router/go_router.dart'; 6 | 7 | import '../../../../core/common/loader.dart'; 8 | import '../../../../models/product.dart'; 9 | 10 | class DealOfDay extends ConsumerStatefulWidget { 11 | const DealOfDay({Key? key}) : super(key: key); 12 | 13 | @override 14 | ConsumerState createState() => _DealOfDayState(); 15 | } 16 | 17 | class _DealOfDayState extends ConsumerState { 18 | Product? product; 19 | 20 | @override 21 | void initState() { 22 | super.initState(); 23 | WidgetsBinding.instance.addPostFrameCallback((_) { 24 | fetchDealOfDay(); 25 | }); 26 | } 27 | 28 | void fetchDealOfDay() { 29 | ref.read(homeControllerProvider.notifier).getDealOfTheDay(); 30 | } 31 | 32 | void navigateToDetailScreen(Product product) { 33 | ref.read(productDetaillsControllerProvider.notifier).setProduct(product); 34 | GoRouter.of(context).push('/product-detaills'); 35 | } 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | return ref.watch(homeControllerProvider).when(data: (product) { 40 | if (product == null) { 41 | return const Text("Loading"); 42 | } 43 | return product!.name.isEmpty 44 | ? const SizedBox() 45 | : GestureDetector( 46 | onTap: () { 47 | navigateToDetailScreen(product); 48 | }, 49 | child: Column( 50 | children: [ 51 | Container( 52 | alignment: Alignment.topLeft, 53 | padding: const EdgeInsets.only(left: 10, top: 15), 54 | child: const Text( 55 | 'Deal of the day', 56 | style: TextStyle(fontSize: 20), 57 | ), 58 | ), 59 | Image.network( 60 | product!.images[0], 61 | height: 235, 62 | fit: BoxFit.fitHeight, 63 | ), 64 | Container( 65 | padding: const EdgeInsets.only(left: 15), 66 | alignment: Alignment.topLeft, 67 | child: const Text( 68 | '\$100', 69 | style: TextStyle(fontSize: 18), 70 | ), 71 | ), 72 | Container( 73 | alignment: Alignment.topLeft, 74 | padding: const EdgeInsets.only(left: 15, top: 5, right: 40), 75 | child: const Text( 76 | 'Rivaan', 77 | maxLines: 2, 78 | overflow: TextOverflow.ellipsis, 79 | ), 80 | ), 81 | SingleChildScrollView( 82 | scrollDirection: Axis.horizontal, 83 | child: Row( 84 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 85 | children: List.generate(product.images.length, (index) { 86 | return Image.network( 87 | product.images[index], 88 | fit: BoxFit.fitHeight, 89 | height: 100, 90 | width: 100, 91 | ); 92 | }), 93 | ), 94 | ), 95 | Container( 96 | padding: const EdgeInsets.symmetric( 97 | vertical: 15, 98 | ).copyWith(left: 15), 99 | alignment: Alignment.topLeft, 100 | child: Text( 101 | 'See all deals', 102 | style: TextStyle( 103 | color: Colors.cyan[800], 104 | ), 105 | ), 106 | ), 107 | ], 108 | ), 109 | ); 110 | }, error: (error, stackTrace) { 111 | return Text(error.toString()); 112 | }, loading: () { 113 | return const Loader(); 114 | }); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/providers/user_provider.dart'; 2 | import 'package:amazon_clone/core/theme/theme.dart'; 3 | import 'package:amazon_clone/features/admin/add_product/view/add_product_view.dart'; 4 | import 'package:amazon_clone/features/admin/view/admin_view.dart'; 5 | import 'package:amazon_clone/features/adress/view/adress_view.dart'; 6 | import 'package:amazon_clone/features/account/view/order_detaills.dart'; 7 | import 'package:amazon_clone/features/auth/controller/auth_controller.dart'; 8 | import 'package:amazon_clone/features/category/view/category_view.dart'; 9 | import 'package:amazon_clone/features/home/view/test_view.dart'; 10 | import 'package:amazon_clone/features/product-detaills/view/product_detaills_view.dart'; 11 | import 'package:amazon_clone/features/search/view/search_view.dart'; 12 | import 'package:flutter/material.dart'; 13 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 14 | import 'package:go_router/go_router.dart'; 15 | 16 | import 'features/auth/view/login/login_view.dart'; 17 | import 'features/splash/splash_view.dart'; 18 | import 'models/order.dart'; 19 | 20 | void main() async { 21 | WidgetsFlutterBinding.ensureInitialized(); 22 | final container = ProviderContainer(); 23 | 24 | Future getData(ProviderContainer ref) { 25 | return ref.read(authControllerProvider.notifier).getUserData(); 26 | } 27 | 28 | await getData(container); 29 | 30 | runApp(UncontrolledProviderScope(container: container, child: const MyApp())); 31 | } 32 | 33 | class MyApp extends ConsumerStatefulWidget { 34 | const MyApp({super.key}); 35 | 36 | @override 37 | ConsumerState createState() => _MyAppState(); 38 | } 39 | 40 | class _MyAppState extends ConsumerState { 41 | @override 42 | Widget build(BuildContext context) { 43 | return MaterialApp.router( 44 | debugShowCheckedModeBanner: false, 45 | routerConfig: GoRouter(initialLocation: "/", routes: [ 46 | GoRoute( 47 | path: "/", 48 | builder: (context, state) { 49 | return const SplashView(); 50 | }), 51 | GoRoute( 52 | path: "/redirect", 53 | redirect: (context, state) { 54 | var isUserLoggedIn = ref.watch(userStateProvider.notifier 55 | .select((value) => value.isUserLoggedIn())); 56 | var typeUser = 57 | ref.watch(userStateProvider.select((value) => value.type)); 58 | if (!isUserLoggedIn) { 59 | return "/login"; 60 | } else { 61 | if (typeUser == "admin") { 62 | return "/admin"; 63 | } else { 64 | return "/home"; 65 | } 66 | } 67 | }, 68 | ), 69 | GoRoute( 70 | path: "/login", 71 | builder: (context, state) { 72 | return const LoginView(); 73 | }), 74 | GoRoute( 75 | path: "/home", 76 | builder: (context, state) { 77 | return const TestView(); 78 | }), 79 | GoRoute( 80 | path: "/admin", 81 | builder: (context, state) { 82 | return const AdminView(); 83 | }), 84 | GoRoute( 85 | path: "/add-product", 86 | builder: (context, state) { 87 | return const AddProductScreen(); 88 | }), 89 | GoRoute( 90 | path: "/category", 91 | builder: (context, state) { 92 | return CategoryDealsScreen( 93 | category: state.extra as String, 94 | ); 95 | }), 96 | GoRoute( 97 | path: "/search", 98 | builder: (context, state) { 99 | return const SearchView(); 100 | }), 101 | GoRoute( 102 | path: "/product-detaills", 103 | builder: (context, state) { 104 | return const ProductDetailScreen(); 105 | }), 106 | GoRoute( 107 | path: "/adress", 108 | builder: (context, state) { 109 | return AdressView(totalAmount: state.extra as String); 110 | }), 111 | GoRoute( 112 | path: "/order-detaills", 113 | builder: (context, state) { 114 | return OrderDetailScreen(order: state.extra as Order); 115 | }), 116 | ]), 117 | theme: AppTheme.customTheme, 118 | ); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: amazon_clone 2 | description: A new Flutter project. 3 | # The following line prevents the package from being accidentally published to 4 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 5 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 6 | 7 | # The following defines the version and build number for your application. 8 | # A version number is three numbers separated by dots, like 1.2.43 9 | # followed by an optional build number separated by a +. 10 | # Both the version and the builder number may be overridden in flutter 11 | # build by specifying --build-name and --build-number, respectively. 12 | # In Android, build-name is used as versionName while build-number used as versionCode. 13 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 14 | # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. 15 | # Read more about iOS versioning at 16 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 17 | # In Windows, build-name is used as the major, minor, and patch parts 18 | # of the product and file versions while build-number is used as the build suffix. 19 | version: 1.0.0+1 20 | 21 | environment: 22 | sdk: '>=2.19.6 <3.0.0' 23 | 24 | # Dependencies specify other packages that your package needs in order to work. 25 | # To automatically upgrade your package dependencies to the latest versions 26 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 27 | # dependencies can be manually updated by changing the version numbers below to 28 | # the latest version available on pub.dev. To see which dependencies have newer 29 | # versions available, run `flutter pub outdated`. 30 | dependencies: 31 | flutter: 32 | sdk: flutter 33 | 34 | 35 | # The following adds the Cupertino Icons font to your application. 36 | # Use with the CupertinoIcons class for iOS style icons. 37 | cupertino_icons: ^1.0.2 38 | flutter_riverpod: ^2.3.5 39 | go_router: ^6.5.7 40 | dartz: ^0.10.1 41 | file_picker: ^5.2.10 42 | dio: ^5.1.1 43 | shared_preferences: ^2.1.0 44 | badges: ^3.1.1 45 | carousel_slider: ^4.2.1 46 | dotted_border: ^2.0.0+3 47 | cloudinary_public: ^0.13.0 48 | flutter_rating_bar: ^4.0.1 49 | pay: ^1.1.1 50 | charts_flutter: ^0.12.0 51 | 52 | dev_dependencies: 53 | flutter_test: 54 | sdk: flutter 55 | 56 | # The "flutter_lints" package below contains a set of recommended lints to 57 | # encourage good coding practices. The lint set provided by the package is 58 | # activated in the `analysis_options.yaml` file located at the root of your 59 | # package. See that file for information about deactivating specific lint 60 | # rules and activating additional ones. 61 | flutter_lints: ^2.0.0 62 | 63 | # For information on the generic Dart part of this file, see the 64 | # following page: https://dart.dev/tools/pub/pubspec 65 | 66 | # The following section is specific to Flutter packages. 67 | flutter: 68 | 69 | # The following line ensures that the Material Icons font is 70 | # included with your application, so that you can use the icons in 71 | # the material Icons class. 72 | uses-material-design: true 73 | 74 | # To add assets to your application, add an assets section, like this: 75 | assets: 76 | - assets/images/ 77 | - assets/applepay.json 78 | - assets/gpay.json 79 | 80 | # An image asset can refer to one or more resolution-specific "variants", see 81 | # https://flutter.dev/assets-and-images/#resolution-aware 82 | 83 | # For details regarding adding assets from package dependencies, see 84 | # https://flutter.dev/assets-and-images/#from-packages 85 | 86 | # To add custom fonts to your application, add a fonts section here, 87 | # in this "flutter" section. Each entry in this list should have a 88 | # "family" key with the font family name, and a "fonts" key with a 89 | # list giving the asset and other descriptors for the font. For 90 | # example: 91 | # fonts: 92 | # - family: Schyler 93 | # fonts: 94 | # - asset: fonts/Schyler-Regular.ttf 95 | # - asset: fonts/Schyler-Italic.ttf 96 | # style: italic 97 | # - family: Trajan Pro 98 | # fonts: 99 | # - asset: fonts/TrajanPro.ttf 100 | # - asset: fonts/TrajanPro_Bold.ttf 101 | # weight: 700 102 | # 103 | # For details regarding fonts from package dependencies, 104 | # see https://flutter.dev/custom-fonts/#from-packages 105 | -------------------------------------------------------------------------------- /lib/features/cart/view/cart_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/constant/constants.dart'; 2 | import 'package:amazon_clone/core/providers/user_provider.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | import 'package:go_router/go_router.dart'; 6 | 7 | import '../../auth/view/login/widgets/custom_button.dart'; 8 | import '../../home/view/widgets/address_box.dart'; 9 | import '../../search/controller/search_controller.dart'; 10 | import 'widgets/cart_product.dart'; 11 | import 'widgets/cart_subtotal.dart'; 12 | 13 | class CartView extends ConsumerStatefulWidget { 14 | const CartView({Key? key}) : super(key: key); 15 | 16 | @override 17 | ConsumerState createState() => _CartViewState(); 18 | } 19 | 20 | class _CartViewState extends ConsumerState { 21 | void navigateToSearchScreen(String query) { 22 | ref.read(searchControllerProvider.notifier).searchQuery = query; 23 | GoRouter.of(context).push("/search"); 24 | } 25 | 26 | void navigateToAddress(int sum) { 27 | GoRouter.of(context).push("/adress", extra: sum.toStringAsFixed(0)); 28 | } 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | final user = ref.watch(userStateProvider); 33 | int sum = 0; 34 | user.cart 35 | .map((e) => sum += e['quantity'] * e['product']['price'] as int) 36 | .toList(); 37 | 38 | return Scaffold( 39 | appBar: PreferredSize( 40 | preferredSize: const Size.fromHeight(60), 41 | child: AppBar( 42 | flexibleSpace: Container( 43 | decoration: const BoxDecoration( 44 | gradient: AppConsts.appBarGradient, 45 | ), 46 | ), 47 | title: Row( 48 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 49 | children: [ 50 | Expanded( 51 | child: Container( 52 | height: 42, 53 | margin: const EdgeInsets.only(left: 15), 54 | child: Material( 55 | borderRadius: BorderRadius.circular(7), 56 | elevation: 1, 57 | child: TextFormField( 58 | onFieldSubmitted: (value) => 59 | navigateToSearchScreen(value), 60 | decoration: InputDecoration( 61 | prefixIcon: InkWell( 62 | onTap: () {}, 63 | child: const Padding( 64 | padding: EdgeInsets.only( 65 | left: 6, 66 | ), 67 | child: Icon( 68 | Icons.search, 69 | color: Colors.black, 70 | size: 23, 71 | ), 72 | ), 73 | ), 74 | filled: true, 75 | fillColor: Colors.white, 76 | contentPadding: const EdgeInsets.only(top: 10), 77 | border: const OutlineInputBorder( 78 | borderRadius: BorderRadius.all( 79 | Radius.circular(7), 80 | ), 81 | borderSide: BorderSide.none, 82 | ), 83 | enabledBorder: const OutlineInputBorder( 84 | borderRadius: BorderRadius.all( 85 | Radius.circular(7), 86 | ), 87 | borderSide: BorderSide( 88 | color: Colors.black38, 89 | width: 1, 90 | ), 91 | ), 92 | hintText: 'Search Amazon.in', 93 | hintStyle: const TextStyle( 94 | fontWeight: FontWeight.w500, 95 | fontSize: 17, 96 | ), 97 | ), 98 | ), 99 | ), 100 | ), 101 | ), 102 | Container( 103 | color: Colors.transparent, 104 | height: 42, 105 | margin: const EdgeInsets.symmetric(horizontal: 10), 106 | child: const Icon(Icons.mic, color: Colors.black, size: 25), 107 | ), 108 | ], 109 | ), 110 | ), 111 | ), 112 | body: SingleChildScrollView( 113 | child: Column( 114 | children: [ 115 | const AddressBox(), 116 | const CartSubtotal(), 117 | Padding( 118 | padding: const EdgeInsets.all(20.0), 119 | child: CustomButton( 120 | text: 'Proceed to Buy (${user.cart.length} items)', 121 | onTap: () => navigateToAddress(sum), 122 | color: Colors.yellow[600], 123 | ), 124 | ), 125 | const SizedBox(height: 15), 126 | Container( 127 | color: Colors.black12.withOpacity(0.08), 128 | height: 1, 129 | ), 130 | const SizedBox(height: 5), 131 | ListView.builder( 132 | itemCount: user.cart.length, 133 | shrinkWrap: true, 134 | itemBuilder: (context, index) { 135 | return CartProduct( 136 | index: index, 137 | ); 138 | }, 139 | ), 140 | ], 141 | ), 142 | ), 143 | ); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /lib/features/cart/view/widgets/cart_product.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/providers/user_provider.dart'; 2 | import 'package:amazon_clone/features/cart/controller/cart_controller.dart'; 3 | import 'package:amazon_clone/features/product-detaills/controller/product_detaills_controller.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 | 7 | import '../../../../models/product.dart'; 8 | 9 | class CartProduct extends ConsumerStatefulWidget { 10 | final int index; 11 | const CartProduct({ 12 | Key? key, 13 | required this.index, 14 | }) : super(key: key); 15 | 16 | @override 17 | ConsumerState createState() => _CartProductState(); 18 | } 19 | 20 | class _CartProductState extends ConsumerState { 21 | void increaseQuantity(Product product) { 22 | ref.read(productDetaillsControllerProvider.notifier).setProduct(product); 23 | ref.read(productDetaillsControllerProvider.notifier).addToCart(); 24 | } 25 | 26 | void decreaseQuantity(Product product) { 27 | ref.read(cartControllerProvider.notifier).removeFromCart(product: product); 28 | } 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | final productCart = ref.watch(userStateProvider).cart[widget.index]; 33 | final product = Product.fromMap(productCart['product']); 34 | final quantity = productCart['quantity']; 35 | 36 | return Column( 37 | children: [ 38 | Container( 39 | margin: const EdgeInsets.symmetric( 40 | horizontal: 10, 41 | ), 42 | width: double.infinity, 43 | child: Row( 44 | children: [ 45 | Image.network( 46 | product.images[0], 47 | fit: BoxFit.contain, 48 | height: 135, 49 | width: 135, 50 | ), 51 | Column( 52 | crossAxisAlignment: CrossAxisAlignment.start, 53 | children: [ 54 | Container( 55 | padding: const EdgeInsets.symmetric(horizontal: 10), 56 | child: Text( 57 | product.name, 58 | style: const TextStyle( 59 | fontSize: 16, 60 | ), 61 | maxLines: 2, 62 | ), 63 | ), 64 | Container( 65 | padding: const EdgeInsets.only(left: 10, top: 5), 66 | child: Text( 67 | '\$${product.price}', 68 | style: const TextStyle( 69 | fontSize: 20, 70 | fontWeight: FontWeight.bold, 71 | ), 72 | maxLines: 2, 73 | ), 74 | ), 75 | Container( 76 | padding: const EdgeInsets.only(left: 10), 77 | child: const Text( 78 | 'Eligible for FREE Shipping', 79 | ), 80 | ), 81 | Container( 82 | padding: const EdgeInsets.only(left: 10, top: 5), 83 | child: const Text( 84 | 'In Stock', 85 | style: TextStyle( 86 | color: Colors.teal, 87 | ), 88 | maxLines: 2, 89 | ), 90 | ), 91 | ], 92 | ), 93 | ], 94 | ), 95 | ), 96 | Container( 97 | margin: const EdgeInsets.all(10), 98 | child: Row( 99 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 100 | children: [ 101 | Container( 102 | decoration: BoxDecoration( 103 | border: Border.all( 104 | color: Colors.black12, 105 | width: 1.5, 106 | ), 107 | borderRadius: BorderRadius.circular(5), 108 | color: Colors.black12, 109 | ), 110 | child: Row( 111 | children: [ 112 | InkWell( 113 | onTap: () => decreaseQuantity(product), 114 | child: Container( 115 | width: 35, 116 | height: 32, 117 | alignment: Alignment.center, 118 | child: const Icon( 119 | Icons.remove, 120 | size: 18, 121 | ), 122 | ), 123 | ), 124 | DecoratedBox( 125 | decoration: BoxDecoration( 126 | border: Border.all(color: Colors.black12, width: 1.5), 127 | color: Colors.black45, 128 | borderRadius: BorderRadius.circular(0), 129 | ), 130 | child: Container( 131 | width: 35, 132 | height: 32, 133 | alignment: Alignment.center, 134 | child: Text( 135 | quantity.toString(), 136 | ), 137 | ), 138 | ), 139 | InkWell( 140 | onTap: () => increaseQuantity(product), 141 | child: Container( 142 | width: 35, 143 | height: 32, 144 | alignment: Alignment.center, 145 | child: const Icon( 146 | Icons.add, 147 | size: 18, 148 | ), 149 | ), 150 | ), 151 | ], 152 | ), 153 | ), 154 | ], 155 | ), 156 | ), 157 | ], 158 | ); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /lib/features/category/view/category_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:amazon_clone/core/constant/constants.dart'; 2 | import 'package:amazon_clone/features/category/controller/category_controller.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | import 'package:go_router/go_router.dart'; 6 | 7 | import '../../../core/common/loader.dart'; 8 | import '../../../models/product.dart'; 9 | import '../../product-detaills/controller/product_detaills_controller.dart'; 10 | 11 | class CategoryDealsScreen extends ConsumerStatefulWidget { 12 | final String category; 13 | const CategoryDealsScreen({ 14 | Key? key, 15 | required this.category, 16 | }) : super(key: key); 17 | 18 | @override 19 | ConsumerState createState() => 20 | _CategoryDealsScreenState(); 21 | } 22 | 23 | class _CategoryDealsScreenState extends ConsumerState { 24 | List? productList; 25 | 26 | @override 27 | void initState() { 28 | super.initState(); 29 | // this is called after the build method is called 30 | WidgetsBinding.instance.addPostFrameCallback((_) { 31 | fetchCategoryProducts(); 32 | }); 33 | } 34 | 35 | fetchCategoryProducts() async { 36 | ref.read(categoryControllerProvider.notifier).fetchCategoryProducts( 37 | widget.category, 38 | context, 39 | ); 40 | } 41 | 42 | void navigateToDetaillScreen(Product product) { 43 | ref.read(productDetaillsControllerProvider.notifier).setProduct(product); 44 | GoRouter.of(context).push("/product-detaills"); 45 | } 46 | 47 | @override 48 | Widget build(BuildContext context) { 49 | return Scaffold( 50 | appBar: PreferredSize( 51 | preferredSize: const Size.fromHeight(50), 52 | child: AppBar( 53 | leading: IconButton( 54 | icon: const Icon( 55 | Icons.arrow_back_ios, 56 | color: Colors.black, 57 | ), 58 | onPressed: () { 59 | GoRouter.of(context).pop(context); 60 | }, 61 | ), 62 | flexibleSpace: Container( 63 | decoration: const BoxDecoration( 64 | gradient: AppConsts.appBarGradient, 65 | ), 66 | ), 67 | title: Text( 68 | widget.category, 69 | style: const TextStyle( 70 | color: Colors.black, 71 | ), 72 | ), 73 | ), 74 | ), 75 | body: ref.watch(categoryControllerProvider).when(data: (productList) { 76 | if (productList == null) { 77 | return const Center( 78 | child: Text( 79 | "waiting for data", 80 | style: TextStyle( 81 | fontSize: 20, 82 | ), 83 | )); 84 | } 85 | 86 | if (productList.isEmpty) { 87 | return const Center( 88 | child: Text( 89 | "No products yet", 90 | style: TextStyle( 91 | fontSize: 20, 92 | ), 93 | )); 94 | } 95 | 96 | return Column( 97 | children: [ 98 | Container( 99 | padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), 100 | alignment: Alignment.topLeft, 101 | child: Text( 102 | 'Keep shopping for ${widget.category}', 103 | style: const TextStyle( 104 | fontSize: 20, 105 | ), 106 | ), 107 | ), 108 | SizedBox( 109 | height: 170, 110 | child: GridView.builder( 111 | scrollDirection: Axis.horizontal, 112 | padding: const EdgeInsets.only(left: 15), 113 | itemCount: productList!.length, 114 | gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( 115 | crossAxisCount: 1, 116 | childAspectRatio: 1.4, 117 | mainAxisSpacing: 10, 118 | ), 119 | itemBuilder: (context, index) { 120 | final product = productList![index]; 121 | return GestureDetector( 122 | onTap: () { 123 | navigateToDetaillScreen(product); 124 | }, 125 | child: Column( 126 | children: [ 127 | SizedBox( 128 | height: 130, 129 | child: DecoratedBox( 130 | decoration: BoxDecoration( 131 | border: Border.all( 132 | color: Colors.black12, 133 | width: 0.5, 134 | ), 135 | ), 136 | child: Padding( 137 | padding: const EdgeInsets.all(10), 138 | child: Image.network( 139 | product.images[0], 140 | ), 141 | ), 142 | ), 143 | ), 144 | Container( 145 | alignment: Alignment.topLeft, 146 | padding: const EdgeInsets.only( 147 | left: 0, 148 | top: 5, 149 | right: 15, 150 | ), 151 | child: Text( 152 | product.name, 153 | maxLines: 1, 154 | overflow: TextOverflow.ellipsis, 155 | ), 156 | ), 157 | ], 158 | ), 159 | ); 160 | }, 161 | ), 162 | ), 163 | ], 164 | ); 165 | }, error: ((error, stackTrace) { 166 | return Center( 167 | child: Text(error.toString()), 168 | ); 169 | }), loading: () { 170 | return const Loader(); 171 | }), 172 | ); 173 | } 174 | } 175 | --------------------------------------------------------------------------------