├── 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.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── WorkspaceSettings.xcsettings
│ │ └── IDEWorkspaceChecks.plist
├── Runner.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── WorkspaceSettings.xcsettings
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
└── .gitignore
├── assets
└── Images
│ ├── cake.jpg
│ ├── fish.jpg
│ └── logo.png
├── lib
├── constants
│ ├── button_text.dart
│ ├── payment.dart
│ ├── tasks.dart
│ ├── screen_ids.dart
│ └── screen_titles.dart
├── controllers
│ ├── activity_tracker_controller.dart
│ ├── shipping_controller.dart
│ ├── error_controller.dart
│ ├── category_controller.dart
│ ├── product_controller.dart
│ ├── order_controller.dart
│ ├── auth_controller.dart
│ └── cart_controller.dart
├── models
│ ├── authdata.dart
│ ├── cart_item.dart
│ ├── category.dart
│ ├── shipping_details.dart
│ ├── product.dart
│ └── order.dart
├── utils
│ ├── validator.dart
│ └── date_util.dart
├── widgets
│ ├── dialog.dart
│ ├── global_snackbar.dart
│ ├── underlined_text..dart
│ ├── round_cart_button.dart
│ ├── cart_button.dart
│ ├── no_order_history_content.dart
│ ├── round_icon_button.dart
│ ├── category.dart
│ ├── order_history_item.dart
│ ├── guest_user_drawer_widget.dart
│ ├── drawer.dart
│ ├── product_detail_bottomsheet_content.dart
│ ├── product_card.dart
│ ├── shopping_cart_bottom_sheet.dart
│ └── auth_screen_custom_painter.dart
├── services
│ ├── category_service.dart
│ ├── paypal_service.dart
│ ├── product_service.dart
│ ├── cart_service.dart
│ ├── order_service.dart
│ ├── auth_service.dart
│ └── stripe_service.dart
├── skeletons
│ ├── category_list_skeleton.dart
│ ├── product_list_skeleton.dart
│ └── product_detail_skeleton.dart
├── application.properties
│ └── app_properties.dart
├── screens
│ ├── thank_you.dart
│ ├── order_history.dart
│ ├── single_order.dart
│ ├── product_detail.dart
│ ├── shipping.dart
│ ├── products_list.dart
│ └── payment_method.dart
└── main.dart
├── 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
│ │ │ │ ├── xml
│ │ │ │ │ └── my_backup_rules.xml
│ │ │ │ ├── drawable
│ │ │ │ │ └── launch_background.xml
│ │ │ │ └── values
│ │ │ │ │ └── styles.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── example
│ │ │ │ │ └── ecommerceapp
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── AndroidManifest.xml
│ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ └── profile
│ │ │ └── AndroidManifest.xml
│ └── build.gradle
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── .gitignore
├── settings.gradle
└── build.gradle
├── .metadata
├── .vscode
└── launch.json
├── test
└── widget_test.dart
├── .gitignore
├── README.md
├── pubspec.yaml
└── pubspec.lock
/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 |
--------------------------------------------------------------------------------
/assets/Images/cake.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KNehe/Ecommerce_app/HEAD/assets/Images/cake.jpg
--------------------------------------------------------------------------------
/assets/Images/fish.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KNehe/Ecommerce_app/HEAD/assets/Images/fish.jpg
--------------------------------------------------------------------------------
/assets/Images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KNehe/Ecommerce_app/HEAD/assets/Images/logo.png
--------------------------------------------------------------------------------
/lib/constants/button_text.dart:
--------------------------------------------------------------------------------
1 | const ADD_TO_CART_BUTTON_TEXT = 'ADD';
2 | const REMOVE_FROM_CART_BUTTON_TEXT = 'REMOVE';
3 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.enableR8=true
3 | android.useAndroidX=true
4 | android.enableJetifier=true
5 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KNehe/Ecommerce_app/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/KNehe/Ecommerce_app/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/KNehe/Ecommerce_app/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/KNehe/Ecommerce_app/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/KNehe/Ecommerce_app/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KNehe/Ecommerce_app/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KNehe/Ecommerce_app/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KNehe/Ecommerce_app/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/KNehe/Ecommerce_app/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/KNehe/Ecommerce_app/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/KNehe/Ecommerce_app/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/KNehe/Ecommerce_app/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/KNehe/Ecommerce_app/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/KNehe/Ecommerce_app/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/KNehe/Ecommerce_app/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/KNehe/Ecommerce_app/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/KNehe/Ecommerce_app/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/KNehe/Ecommerce_app/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/KNehe/Ecommerce_app/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/KNehe/Ecommerce_app/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/KNehe/Ecommerce_app/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/KNehe/Ecommerce_app/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/KNehe/Ecommerce_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/lib/constants/payment.dart:
--------------------------------------------------------------------------------
1 | const STRIPE_PAYMENT = 'Stripe';
2 | const PAY_PAL = 'Pay pal';
3 |
4 | const USER_TYPE_RESGISTERED = 'Registered user';
5 | const USER_TYPE_GUEST = 'Guest user';
6 |
--------------------------------------------------------------------------------
/android/app/src/main/res/xml/my_backup_rules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/example/ecommerceapp/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.ecommerceapp
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/controllers/activity_tracker_controller.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/foundation.dart';
2 |
3 | class ActivityTracker extends ChangeNotifier {
4 | String _currentTask = '';
5 |
6 | setTaskCurrentTask(String task) {
7 | _currentTask = task;
8 | }
9 |
10 | String get currentTask => _currentTask;
11 | }
12 |
--------------------------------------------------------------------------------
/lib/models/authdata.dart:
--------------------------------------------------------------------------------
1 | class AuthData {
2 | String name;
3 | String email;
4 | String password;
5 |
6 | AuthData({this.name, this.email, this.password});
7 |
8 | Map toJson() => {
9 | "name": name,
10 | "email": email,
11 | "password": password,
12 | };
13 | }
14 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.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 and should not be manually edited.
5 |
6 | version:
7 | revision: 2ae34518b87dd891355ed6c6ea8cb68c4d52bb9d
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/lib/constants/tasks.dart:
--------------------------------------------------------------------------------
1 | const VIEWING_ORDER_HISTORY = 'Viewing order history';
2 | const VIEWING_SINGLE_OLD_ORDER_HISTORY = 'Viewing single new order history';
3 | const VIEWING_SINGLE_NEW_ORDER_HISTORY = 'Viewing single all order history';
4 | const VIEWING_PROFILE = 'Viewing profile';
5 | const CHANGING_NAME = 'Changing name';
6 | const CHANGING_EMAIL = 'Changing email';
7 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/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.
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "ecommerce_app",
9 | "request": "launch",
10 | "type": "dart"
11 | }
12 | ]
13 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/lib/utils/validator.dart:
--------------------------------------------------------------------------------
1 | class Validator {
2 | static bool isPostalCodeValid(String postalCode) {
3 | return RegExp(r"^(^\d{5}$)|(^\d{5}-\d{4}$)$").hasMatch(postalCode);
4 | }
5 |
6 | static bool isPhoneNumberValid(String phoneNumber) {
7 | return RegExp(r"([\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6})")
8 | .hasMatch(phoneNumber);
9 | }
10 |
11 | static bool isEmailValid(String email) {
12 | return RegExp(r"\S+@\S+\.\S+").hasMatch(email);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | -
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/lib/constants/screen_ids.dart:
--------------------------------------------------------------------------------
1 | const ShoppingCart_Screen_Id = 'ShoppingCartScreen';
2 | const ProductDetail_Screen_Id = 'ProductDetailsScreen';
3 | const ProductList_Screen_Id = 'ProductListScreen';
4 | const Shipping_Screen_Id = 'ShippingScreen';
5 | const PaymentMethod_Screen_Id = 'PaymentMethodScreen';
6 | const ThankYou_Screen_Id = 'ThaankYouScreen';
7 | const SingleOrder_Screen_Id = 'SingleOrderScreen';
8 | const AuthScreen_Id = 'AuthScreen';
9 | const OrderHistoryScreen_Id = 'OrderHistoryScreen';
10 | const ProfileScreen_id = 'ProfileScreen';
11 |
--------------------------------------------------------------------------------
/lib/widgets/dialog.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:progress_dialog/progress_dialog.dart';
3 |
4 | class CDialog {
5 | ProgressDialog dialog;
6 |
7 | CDialog(BuildContext context) {
8 | ProgressDialog pr = ProgressDialog(context);
9 | pr = ProgressDialog(
10 | context,
11 | type: ProgressDialogType.Normal,
12 | isDismissible: false,
13 | showLogs: false,
14 | );
15 | pr.style(
16 | message: 'Please wait...',
17 | );
18 | dialog = pr;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/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/widgets/global_snackbar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | enum SnackBarType { Error, Success }
4 |
5 | class GlobalSnackBar {
6 | static showSnackbar(
7 | GlobalKey scaffoldKey, String message, SnackBarType type) {
8 | scaffoldKey.currentState.showSnackBar(SnackBar(
9 | backgroundColor:
10 | type == SnackBarType.Error ? Colors.red[900] : Colors.blue[700],
11 | content: Text(
12 | '$message',
13 | style: TextStyle(fontSize: 15),
14 | ),
15 | ));
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/services/category_service.dart:
--------------------------------------------------------------------------------
1 | import 'package:ecommerceapp/application.properties/app_properties.dart';
2 | import 'package:http/http.dart' as http;
3 |
4 | class CategoryService {
5 | static CategoryService _categoryService;
6 |
7 | CategoryService._internal() {
8 | _categoryService = this;
9 | }
10 |
11 | factory CategoryService() => _categoryService ?? CategoryService._internal();
12 |
13 | static var httpClient = http.Client();
14 |
15 | Future getCategories() async {
16 | return await httpClient.get(AppProperties.categoryUrl);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/lib/utils/date_util.dart:
--------------------------------------------------------------------------------
1 | import 'package:intl/intl.dart';
2 |
3 | class DateUtils {
4 | String getFormattedDate(String timestmapWithMillis) {
5 | var dateTime =
6 | new DateFormat("yyyy-MM-dd HH:mm:ss").parse(timestmapWithMillis);
7 |
8 | return dateTime.day.toString() +
9 | "-" +
10 | dateTime.month.toString() +
11 | "-" +
12 | dateTime.year.toString() +
13 | " " +
14 | dateTime.hour.toString() +
15 | ":" +
16 | dateTime.minute.toString() +
17 | ":" +
18 | dateTime.second.toString();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/lib/constants/screen_titles.dart:
--------------------------------------------------------------------------------
1 | const SignIn_Screen_Title = 'Welcome\n Back';
2 |
3 | const SignUp_Screen_Title = 'Create\nAccount';
4 |
5 | const ForgotPassword_Screen_Ttile = 'Forgot\nPasword';
6 | const OrderHistoryScreen_Title = 'Order History';
7 | const ProfileScreen_Title = 'Profile';
8 | const Payment_Screen_Title = 'Order summary & Payment method';
9 | const ProductList_Screen_Title = 'Store';
10 | const ShippingDetail_Screen_Title = 'Shipping details';
11 | const ShoppingCart_Screen_Title = 'Shopping cart';
12 | const SingleOrder_Screen_Title = 'Order';
13 | const Thanks_Screen_Title = 'Payment successfull';
14 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.50'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.5.0'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | jcenter()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/lib/widgets/underlined_text..dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class UnderlinedText extends StatelessWidget {
4 | final String text;
5 | final double decorationThickness;
6 | final FontWeight fontWeight;
7 | final double fontSize;
8 |
9 | const UnderlinedText(
10 | {Key key,
11 | @required this.text,
12 | this.decorationThickness,
13 | this.fontWeight,
14 | this.fontSize})
15 | : super(key: key);
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return Text(
20 | text,
21 | style: TextStyle(
22 | decoration: TextDecoration.underline,
23 | decorationThickness: decorationThickness,
24 | fontSize: fontSize,
25 | fontWeight: fontWeight,
26 | ),
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/models/cart_item.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:ecommerceapp/models/product.dart';
4 |
5 | String cartItemToJson(List data) =>
6 | json.encode(List.from(data.map((x) => x.toJson())));
7 |
8 | List cartItemFromJson(String str) =>
9 | List.from(json.decode(str).map((x) => CartItem.fromJson(x)));
10 |
11 | class CartItem {
12 | Product product;
13 | int quantity;
14 |
15 | CartItem({this.product, this.quantity, int});
16 |
17 | Map toJson() => {
18 | "product": product.toJson(),
19 | "quantity": quantity,
20 | };
21 |
22 | factory CartItem.fromJson(Map json) => CartItem(
23 | product: Product.fromJson(json["product"]),
24 | quantity: json["quantity"],
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/lib/widgets/round_cart_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class RoundCartButton extends StatelessWidget {
4 | final Function onTap;
5 | final IconData icon;
6 | final double width;
7 | const RoundCartButton({
8 | Key key,
9 | @required this.onTap,
10 | @required this.icon,
11 | @required this.width,
12 | }) : super(key: key);
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return GestureDetector(
17 | onTap: onTap,
18 | child: Container(
19 | width: width,
20 | decoration: BoxDecoration(
21 | color: Colors.orange[100],
22 | borderRadius: BorderRadius.circular(50),
23 | ),
24 | child: Icon(
25 | icon,
26 | color: Colors.red,
27 | ),
28 | ),
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/lib/controllers/shipping_controller.dart:
--------------------------------------------------------------------------------
1 | import 'package:ecommerceapp/models/shipping_details.dart';
2 | import 'package:flutter/foundation.dart';
3 |
4 | class ShippingController extends ChangeNotifier {
5 | var shippingDetails = ShippingDetails(
6 | name: '',
7 | phoneContact: '',
8 | city: '',
9 | addressLine: '',
10 | postalCode: '',
11 | country: '',
12 | );
13 |
14 | void setShippingDetails({@required ShippingDetails details}) {
15 | shippingDetails = details;
16 | notifyListeners();
17 | }
18 |
19 | ShippingDetails getShippingDetails() => shippingDetails;
20 |
21 | void reset() {
22 | shippingDetails = ShippingDetails(
23 | name: '',
24 | phoneContact: '',
25 | city: '',
26 | addressLine: '',
27 | postalCode: '',
28 | country: '',
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/lib/models/category.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | List categoryFromJson(String str) => List.from(
4 | json.decode(str).map((x) => CategoryModel.fromJson(x)));
5 |
6 | String categoryToJson(List data) =>
7 | json.encode(List.from(data.map((x) => x.toJson())));
8 |
9 | class CategoryModel {
10 | CategoryModel({
11 | this.id,
12 | this.category,
13 | this.v,
14 | });
15 |
16 | String id;
17 | String category;
18 | int v;
19 |
20 | factory CategoryModel.fromJson(Map json) => CategoryModel(
21 | id: json["_id"],
22 | category: json["category"],
23 | v: json["__v"],
24 | );
25 |
26 | Map toJson() => {
27 | "_id": id,
28 | "category": category,
29 | "__v": v,
30 | };
31 | }
32 |
--------------------------------------------------------------------------------
/test/widget_test.dart:
--------------------------------------------------------------------------------
1 |
2 | //import 'package:flutter/material.dart';
3 | //import 'package:flutter_test/flutter_test.dart';
4 | //
5 | //import 'package:ecommerceapp/main.dart';
6 | //
7 | //void main() {
8 | // testWidgets('Counter increments smoke test', (WidgetTester tester) async {
9 | // // Build our app and trigger a frame.
10 | // await tester.pumpWidget(EcommerceApp());
11 | //
12 | // // Verify that our counter starts at 0.
13 | // expect(find.text('0'), findsOneWidget);
14 | // expect(find.text('1'), findsNothing);
15 | //
16 | // // Tap the '+' icon and trigger a frame.
17 | // await tester.tap(find.byIcon(Icons.add));
18 | // await tester.pump();
19 | //
20 | // // Verify that our counter has incremented.
21 | // expect(find.text('0'), findsNothing);
22 | // expect(find.text('1'), findsOneWidget);
23 | // });
24 | //}
25 |
--------------------------------------------------------------------------------
/lib/widgets/cart_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class CartButton extends StatelessWidget {
4 | final String text;
5 | final double width;
6 |
7 | const CartButton({
8 | Key key,
9 | @required this.text,
10 | @required this.width,
11 | }) : super(key: key);
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return Container(
16 | width: width,
17 | decoration: BoxDecoration(
18 | color: Colors.orange,
19 | borderRadius: BorderRadius.circular(50),
20 | ),
21 | child: Padding(
22 | padding: const EdgeInsets.all(8.0),
23 | child: Center(
24 | child: Text(
25 | text,
26 | style: TextStyle(
27 | color: Colors.white,
28 | ),
29 | ),
30 | ),
31 | ),
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/services/paypal_service.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/services.dart';
2 | import 'package:flutter_braintree/flutter_braintree.dart';
3 |
4 | class PayPalService {
5 | static final String tokenizationKey = 'sandbox_9qqht4sx_2c34stzd8dh7rzxb';
6 |
7 | static Future processPayment(
8 | String amount,
9 | ) async {
10 | try {
11 | final request = BraintreePayPalRequest(amount: amount);
12 |
13 | BraintreePaymentMethodNonce result =
14 | await Braintree.requestPaypalNonce(tokenizationKey, request);
15 |
16 | if (result != null) {
17 | return result.nonce;
18 | } else {
19 | return null;
20 | }
21 | } on PlatformException catch (e) {
22 | print("Paltform excepion: ${e.toString()}");
23 | return null;
24 | } catch (e) {
25 | print("ERROR: ${e.toString()} ");
26 | return null;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 | 8.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 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
43 | # Exceptions to above rules.
44 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
45 |
--------------------------------------------------------------------------------
/lib/services/product_service.dart:
--------------------------------------------------------------------------------
1 | import 'package:ecommerceapp/application.properties/app_properties.dart';
2 | import 'package:http/http.dart' as http;
3 |
4 | class ProductService {
5 | static ProductService _productService;
6 |
7 | ProductService._internal() {
8 | _productService = this;
9 | }
10 |
11 | factory ProductService() => _productService ?? ProductService._internal();
12 |
13 | static var httpClient = http.Client();
14 |
15 | Future getAllProducts() async {
16 | return await http.get(AppProperties.productUrl);
17 | }
18 |
19 | Future getProductByCategoryOrName(String value) async {
20 | return await http.get('${AppProperties.searchByCategoryOrNameUrl}$value');
21 | }
22 |
23 | Future getProductByCategory(String value) async {
24 | return await http.get('${AppProperties.searchByCategoryUrl}$value');
25 | }
26 |
27 | Future getProductById(String id) async {
28 | return await http.get('${AppProperties.productUrl}$id');
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/widgets/no_order_history_content.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class NoOrderHistoryFoundContent extends StatelessWidget {
4 | const NoOrderHistoryFoundContent({
5 | Key key,
6 | }) : super(key: key);
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | return Column(
11 | children: [
12 | Text(
13 | 'No orders found',
14 | style: TextStyle(
15 | fontSize: 18,
16 | fontWeight: FontWeight.bold,
17 | ),
18 | ),
19 | SizedBox(height: 10),
20 | Text(
21 | 'This could be because you bought products as a guest',
22 | style: TextStyle(
23 | fontSize: 15,
24 | ),
25 | ),
26 | SizedBox(height: 10),
27 | Text(
28 | 'Or you haven\'t bought any item yet',
29 | style: TextStyle(
30 | fontSize: 15,
31 | ),
32 | ),
33 | ],
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/skeletons/category_list_skeleton.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class CategoryListSkeleton extends StatelessWidget {
4 | const CategoryListSkeleton({
5 | Key key,
6 | }) : super(key: key);
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | return ListView.builder(
11 | itemCount: 5,
12 | scrollDirection: Axis.horizontal,
13 | itemBuilder: (context, index) {
14 | return Padding(
15 | padding: EdgeInsets.all(4.0),
16 | child: Container(
17 | width: 100,
18 | decoration: BoxDecoration(
19 | color: Colors.grey[200],
20 | borderRadius: BorderRadius.circular(50),
21 | ),
22 | child: Text(
23 | '',
24 | style: TextStyle(
25 | color: Colors.black,
26 | fontSize: 5,
27 | ),
28 | ),
29 | ),
30 | );
31 | },
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/lib/models/shipping_details.dart:
--------------------------------------------------------------------------------
1 | class ShippingDetails {
2 | ShippingDetails({
3 | this.id,
4 | this.name,
5 | this.phoneContact,
6 | this.addressLine,
7 | this.city,
8 | this.postalCode,
9 | this.country,
10 | });
11 |
12 | String id;
13 | String name;
14 | String phoneContact;
15 | String addressLine;
16 | String city;
17 | String postalCode;
18 | String country;
19 |
20 | factory ShippingDetails.fromJson(Map json) =>
21 | ShippingDetails(
22 | id: json["_id"],
23 | name: json["name"],
24 | phoneContact: json["phoneContact"],
25 | addressLine: json["addressLine"],
26 | city: json["city"],
27 | postalCode: json["postalCode"],
28 | country: json["country"],
29 | );
30 |
31 | Map toJson() => {
32 | "_id": id,
33 | "name": name,
34 | "phoneContact": phoneContact,
35 | "addressLine": addressLine,
36 | "city": city,
37 | "postalCode": postalCode,
38 | "country": country,
39 | };
40 | }
41 |
--------------------------------------------------------------------------------
/lib/widgets/round_icon_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class RoundIconButton extends StatelessWidget {
4 | final double width;
5 | final double height;
6 | final Color backgroundColor;
7 | final Color iconColor;
8 | final IconData iconData;
9 | final Function onPressed;
10 |
11 | RoundIconButton({
12 | @required this.width,
13 | @required this.height,
14 | @required this.backgroundColor,
15 | @required this.iconColor,
16 | @required this.iconData,
17 | @required this.onPressed,
18 | });
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | return Container(
23 | width: width,
24 | height: height,
25 | child: Material(
26 | borderRadius: BorderRadius.circular(50),
27 | color: backgroundColor,
28 | child: Center(
29 | child: IconButton(
30 | icon: Icon(
31 | iconData,
32 | color: iconColor,
33 | ),
34 | onPressed: onPressed,
35 | ),
36 | ),
37 | ),
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/lib/models/product.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | List productFromJson(String str) =>
4 | List.from(json.decode(str).map((x) => Product.fromJson(x)));
5 |
6 | String productToJson(List data) =>
7 | json.encode(List.from(data.map((x) => x.toJson())));
8 |
9 | class Product {
10 | Product({
11 | this.id,
12 | this.name,
13 | this.price,
14 | this.imageUrl,
15 | this.category,
16 | this.details,
17 | this.v,
18 | });
19 |
20 | String id;
21 | String name;
22 | int price;
23 | String imageUrl;
24 | String category;
25 | String details;
26 | int v;
27 |
28 | factory Product.fromJson(Map json) => Product(
29 | id: json["_id"],
30 | name: json["name"],
31 | price: json["price"],
32 | imageUrl: json["imageUrl"],
33 | category: json["category"],
34 | details: json["details"],
35 | v: json["__v"],
36 | );
37 |
38 | Map toJson() => {
39 | "_id": id,
40 | "name": name,
41 | "price": price,
42 | "imageUrl": imageUrl,
43 | "category": category,
44 | "details": details,
45 | "__v": v,
46 | };
47 | }
48 |
--------------------------------------------------------------------------------
/lib/application.properties/app_properties.dart:
--------------------------------------------------------------------------------
1 | class AppProperties {
2 | //static String _baseApiUrl = 'http://10.0.2.2:3000/api/v1';
3 | static String _baseApiUrl = 'https://nehe-ecommerce-api.herokuapp.com/api/v1';
4 |
5 | static String productUrl = '$_baseApiUrl/products/';
6 |
7 | static String categoryUrl = '$_baseApiUrl/categories/';
8 |
9 | static String searchByCategoryOrNameUrl = '$_baseApiUrl/products/search/';
10 |
11 | static String searchByCategoryUrl = '$_baseApiUrl/products/search/category/';
12 |
13 | static String saveOrderUrl = '$_baseApiUrl/cart/flutter/stripepayment';
14 |
15 | static String payPalRequestUrl = '$_baseApiUrl/cart/braintree/paypalpayment/';
16 |
17 | static String signUpUrl = '$_baseApiUrl/users/signup';
18 |
19 | static String signInUrl = '$_baseApiUrl/users/signin';
20 |
21 | static String checkTokenExpiryUrl = '$_baseApiUrl/users/checktokenexpiry';
22 |
23 | static String cartUrl = '$_baseApiUrl/cart/';
24 |
25 | static String getOrdersUrl = '$_baseApiUrl/cart/orders/user/';
26 |
27 | static String changenameUrl = '$_baseApiUrl/users/updatename/';
28 |
29 | static String changeMailUrl = '$_baseApiUrl/users/updatemail/';
30 |
31 | static String forgotPasswordUrl = '$_baseApiUrl/users/forgotpassword';
32 | }
33 |
--------------------------------------------------------------------------------
/lib/skeletons/product_list_skeleton.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class ProductListSkeleton extends StatelessWidget {
4 | const ProductListSkeleton({
5 | Key key,
6 | }) : super(key: key);
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | return GridView.builder(
11 | gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
12 | crossAxisCount: 2,
13 | crossAxisSpacing: 10,
14 | mainAxisSpacing: 15,
15 | ),
16 | itemCount: 6,
17 | itemBuilder: (context, index) {
18 | return Column(children: [
19 | Container(
20 | height: 120,
21 | width: double.infinity,
22 | color: Colors.grey,
23 | child: Text(''),
24 | ),
25 | SizedBox(
26 | height: 5.0,
27 | ),
28 | Row(
29 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
30 | children: [
31 | Container(
32 | height: 10,
33 | width: 80,
34 | color: Colors.grey,
35 | child: Text(''),
36 | ),
37 | Container(
38 | height: 10,
39 | width: 50,
40 | color: Colors.grey,
41 | child: Text(''),
42 | ),
43 | ],
44 | ),
45 | ]);
46 | },
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/lib/widgets/category.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class Category extends StatelessWidget {
4 | final String category;
5 | final int categoryIndex;
6 | final int categorySelectedIndex;
7 | final Function onTapped;
8 | Category({
9 | @required this.category,
10 | @required this.categorySelectedIndex,
11 | @required this.categoryIndex,
12 | @required this.onTapped,
13 | });
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return GestureDetector(
18 | onTap: onTapped,
19 | child: Padding(
20 | padding: const EdgeInsets.all(4.0),
21 | child: Container(
22 | decoration: BoxDecoration(
23 | color: categoryIndex == categorySelectedIndex
24 | ? Colors.grey[700]
25 | : Colors.grey[300],
26 | borderRadius: BorderRadius.circular(50),
27 | ),
28 | child: Padding(
29 | padding: const EdgeInsets.all(8.0),
30 | child: Center(
31 | child: Text(
32 | '$category',
33 | style: TextStyle(
34 | color: categoryIndex == categorySelectedIndex
35 | ? Colors.white
36 | : Colors.black,
37 | fontSize: 15,
38 | ),
39 | ),
40 | ),
41 | ),
42 | ),
43 | ),
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/lib/services/cart_service.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:ecommerceapp/application.properties/app_properties.dart';
4 | import 'package:http/http.dart' as http;
5 |
6 | class CartService {
7 | static CartService _cartService;
8 |
9 | CartService._internal() {
10 | _cartService = this;
11 | }
12 |
13 | factory CartService() => _cartService ?? CartService._internal();
14 |
15 | static var httpClient = http.Client();
16 |
17 | static Map headers = {'Content-Type': 'application/json'};
18 |
19 | Future saveCart(
20 | String productId,
21 | String userId,
22 | String quantity,
23 | String jwtToken,
24 | ) async {
25 | var bodyObject = Map();
26 | bodyObject.putIfAbsent('productId', () => productId);
27 | bodyObject.putIfAbsent('userId', () => userId);
28 | bodyObject.putIfAbsent('quantity', () => quantity);
29 |
30 | headers.putIfAbsent('Authorization', () => 'Bearer $jwtToken');
31 |
32 | return await http.post(
33 | AppProperties.cartUrl,
34 | body: json.encode(bodyObject),
35 | headers: headers,
36 | );
37 | }
38 |
39 | Future getCart(
40 | String userId,
41 | String jwtToken,
42 | ) async {
43 | headers.putIfAbsent('Authorization', () => 'Bearer $jwtToken');
44 | return await http.get(
45 | '${AppProperties.cartUrl}/$userId',
46 | headers: headers,
47 | );
48 | }
49 |
50 | Future deleteCart(
51 | String userId,
52 | ) async {
53 | return await http.delete('${AppProperties.cartUrl}/$userId');
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/lib/services/order_service.dart:
--------------------------------------------------------------------------------
1 | import 'package:ecommerceapp/application.properties/app_properties.dart';
2 | import 'package:http/http.dart' as http;
3 |
4 | class OrderService {
5 | static OrderService _orderService;
6 |
7 | OrderService._internal() {
8 | _orderService = this;
9 | }
10 |
11 | factory OrderService() => _orderService ?? OrderService._internal();
12 |
13 | Map headers = {'Content-Type': 'application/json'};
14 |
15 | static var httpClient = http.Client();
16 |
17 | Future getShippingCost(String country) async {
18 | //can be used to fetch shipping cost for a particular place from API.
19 | return 0;
20 | }
21 |
22 | Future getTax(String country) async {
23 | //can be used to fetch tax for a particular place from API.
24 | return 100;
25 | }
26 |
27 | //used to save order details after making stripe payment
28 | Future saveOrder(String order) async {
29 | return await httpClient.post(AppProperties.saveOrderUrl,
30 | body: order, headers: headers);
31 | }
32 |
33 | //used to send order details along with paypal nonce to process payment and save the order
34 | Future sendPayPalRequest(String order, String nonce) async {
35 | return await httpClient.post('${AppProperties.payPalRequestUrl}$nonce',
36 | body: order, headers: headers);
37 | }
38 |
39 | Future getOrders(String userId, String jwtToken) async {
40 | headers.putIfAbsent('Authorization', () => 'Bearer $jwtToken');
41 | return await httpClient.get('${AppProperties.getOrdersUrl}$userId',
42 | headers: headers);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ecommerceapp
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/lib/skeletons/product_detail_skeleton.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class ProductDetailSkeleton extends StatelessWidget {
4 | const ProductDetailSkeleton({
5 | Key key,
6 | }) : super(key: key);
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | return Container(
11 | margin: EdgeInsets.only(left: 10, right: 10),
12 | child: Column(
13 | children: [
14 | Container(
15 | height: MediaQuery.of(context).size.height / 2,
16 | width: MediaQuery.of(context).size.width,
17 | color: Colors.grey,
18 | child: Text(''),
19 | ),
20 | SizedBox(
21 | height: 10,
22 | ),
23 | Row(
24 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
25 | children: [
26 | Container(
27 | height: MediaQuery.of(context).size.height / 18,
28 | width: MediaQuery.of(context).size.width / 3,
29 | color: Colors.grey,
30 | child: Text(''),
31 | ),
32 | Container(
33 | height: MediaQuery.of(context).size.height / 18,
34 | width: MediaQuery.of(context).size.width / 3,
35 | color: Colors.grey,
36 | child: Text(''),
37 | ),
38 | ],
39 | ),
40 | SizedBox(
41 | height: 10,
42 | ),
43 | Container(
44 | height: MediaQuery.of(context).size.height / 4,
45 | width: MediaQuery.of(context).size.width,
46 | color: Colors.grey,
47 | child: Text(''),
48 | ),
49 | ],
50 | ),
51 | );
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/lib/controllers/error_controller.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:ecommerceapp/widgets/global_snackbar.dart';
4 | import 'package:flutter/material.dart';
5 |
6 | class ErrorController {
7 | static showErrorFromApi(GlobalKey scaffoldKey, var response) {
8 | var resBody = json.decode(response.body);
9 | String message = ErrorController()._formatErrorFromApi(resBody['message']);
10 | ErrorController()._showErrorSnackbar(scaffoldKey, message);
11 | }
12 |
13 | static showNoInternetError(GlobalKey scaffoldKey) {
14 | ErrorController()._showErrorSnackbar(scaffoldKey, 'No internet connection');
15 | }
16 |
17 | static showNoServerError(GlobalKey scaffoldKey) {
18 | ErrorController()._showErrorSnackbar(scaffoldKey, 'Failed to reach server');
19 | }
20 |
21 | static showFormatExceptionError(
22 | GlobalKey scaffoldKey,
23 | ) {
24 | ErrorController()._showErrorSnackbar(scaffoldKey, 'Bad format error');
25 | }
26 |
27 | static showUnKownError(GlobalKey scaffoldKey) {
28 | ErrorController()._showErrorSnackbar(scaffoldKey, 'Unknown error');
29 | }
30 |
31 | static showCustomError(GlobalKey scaffoldKey, String message) {
32 | ErrorController()._showErrorSnackbar(scaffoldKey, message);
33 | }
34 |
35 | _showErrorSnackbar(GlobalKey scaffoldKey, String message) {
36 | GlobalSnackBar.showSnackbar(scaffoldKey, message, SnackBarType.Error);
37 | }
38 |
39 | _formatErrorFromApi(String message) {
40 | switch (message) {
41 | case 'Jwt token is invalid':
42 | return message = 'Authentication failed';
43 | break;
44 | default:
45 | return message;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/lib/widgets/order_history_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:ecommerceapp/constants/tasks.dart';
2 | import 'package:ecommerceapp/controllers/activity_tracker_controller.dart';
3 | import 'package:ecommerceapp/controllers/order_controller.dart';
4 | import 'package:ecommerceapp/models/order.dart';
5 | import 'package:ecommerceapp/screens/single_order.dart';
6 | import 'package:ecommerceapp/utils/date_util.dart';
7 | import 'package:flutter/material.dart';
8 | import 'package:provider/provider.dart';
9 |
10 | class OrderHistoryItem extends StatelessWidget {
11 | final Order order;
12 | const OrderHistoryItem({
13 | Key key,
14 | @required this.order,
15 | }) : super(key: key);
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return Padding(
20 | padding: const EdgeInsets.only(bottom: 8.0),
21 | child: ListTile(
22 | leading: Icon(Icons.info),
23 | trailing: Icon(Icons.info_outlined),
24 | title: Text(
25 | 'Order ${order.id}',
26 | style: TextStyle(
27 | fontSize: 18,
28 | ),
29 | ),
30 | subtitle: Text(
31 | 'Made on ${DateUtils().getFormattedDate(order.dateOrdered.toString())}',
32 | style: TextStyle(
33 | fontSize: 16,
34 | ),
35 | ),
36 | tileColor: Colors.grey[200],
37 | contentPadding: EdgeInsets.all(10.0),
38 | onTap: () {
39 | Provider.of(context, listen: false)
40 | .setTaskCurrentTask(VIEWING_SINGLE_OLD_ORDER_HISTORY);
41 |
42 | Provider.of(context, listen: false)
43 | .setSingleOrder(order);
44 |
45 | Navigator.pushNamed(context, SingleOrder.id);
46 | },
47 | ),
48 | );
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/lib/widgets/guest_user_drawer_widget.dart:
--------------------------------------------------------------------------------
1 | import 'package:ecommerceapp/controllers/activity_tracker_controller.dart';
2 | import 'package:ecommerceapp/screens/auth_screen.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:provider/provider.dart';
5 | import 'package:flutter/foundation.dart';
6 |
7 | class GuestUserDrawerWidget extends StatelessWidget {
8 | final String message;
9 | final String currentTask;
10 | const GuestUserDrawerWidget(
11 | {Key key, @required this.message, @required this.currentTask})
12 | : super(key: key);
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return Container(
17 | margin: EdgeInsets.only(top: MediaQuery.of(context).size.height * 0.3),
18 | child: Column(
19 | children: [
20 | Text(
21 | '$message',
22 | style: TextStyle(
23 | fontSize: 18,
24 | ),
25 | ),
26 | SizedBox(
27 | height: 50,
28 | ),
29 | ButtonTheme(
30 | minWidth: 150,
31 | child: RaisedButton(
32 | elevation: 0,
33 | shape: RoundedRectangleBorder(
34 | borderRadius: BorderRadius.circular(
35 | 10,
36 | ),
37 | ),
38 | color: Colors.orange,
39 | onPressed: () {
40 | Provider.of(context, listen: false)
41 | .setTaskCurrentTask(currentTask);
42 | Navigator.pushReplacementNamed(context, AuthScreen.id);
43 | },
44 | child: Text(
45 | 'SIGN IN',
46 | style: TextStyle(
47 | color: Colors.white,
48 | fontWeight: FontWeight.bold,
49 | ),
50 | ),
51 | ),
52 | ),
53 | ],
54 | ),
55 | );
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ecommerce App
2 |
3 | - An ecommerce app built using Flutter
4 |
5 | - To run open terminal and execute ```flutter run```
6 |
7 | ## Sample Screenshots
8 |
9 |
10 |
11 |  |
12 |  |
13 |
14 |
15 |
16 |
17 |
18 | ## Features
19 |
20 | - View products
21 | - View product details
22 | - Add to cart
23 | - Increase or decrease item's quantity
24 | - Save cart if user is logged in
25 | - Payment using paypal and stripe
26 | - Prompt user to continue as guest or sign in - if not logged in or when login session has expired
27 | - Ask user to login in if they want to view order history or profile
28 | and they haven't signed-in or login session has expired
29 | - Can then view single order history
30 | - Product list page reload with refresh indicator
31 | - Log out
32 | - Can view single order history after successful order
33 | - Checkout
34 | - Search by name or category
35 | - View products by category
36 |
37 | ## Note
38 |
39 | - To test payment using stripe(Credit card) You'll have to go to [stripe dashboard](https://dashboard.stripe.com/) and pick your
40 | ```client secret```.
41 | Open ``` lib/services/stripe.service.dart ```.
42 | Include it on line 16 ``` static String secret = 'your-secret'; ```.
43 | If you don't the payment process will always get cancelled. You don't have to do this for paypal as it is handled fully by the API.
44 | It has been tested and works fine with secret added. I removed mine for security reasons. I'm working on a way of including it from backend coupled with encryption so that you don't have to insert your on secret inorder to test the feature. As that is in the pipeline please follow the above mentioned steps.
45 |
46 | - This app has been developed on windows and tested on only android using ```Flutter 1.22.4 • channel stable``` and ``` Dart 2.10.4 ```
47 |
--------------------------------------------------------------------------------
/lib/controllers/category_controller.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:ecommerceapp/controllers/error_controller.dart';
4 | import 'package:ecommerceapp/models/category.dart';
5 | import 'package:ecommerceapp/services/category_service.dart';
6 | import 'dart:convert';
7 |
8 | import 'package:flutter/foundation.dart';
9 | import 'package:flutter/material.dart';
10 |
11 | class CategoryController extends ChangeNotifier {
12 | final _categoryService = CategoryService();
13 |
14 | var _isLoadingCategories = true;
15 |
16 | var _categoryList = List();
17 |
18 | List get categoryList => _categoryList;
19 |
20 | bool get isLoadingCategories => _isLoadingCategories;
21 |
22 | setIsLoadingCategories(bool value) {
23 | _isLoadingCategories = value;
24 | notifyListeners();
25 | }
26 |
27 | void getAllCategories(GlobalKey scaffoldKey) async {
28 | try {
29 | _isLoadingCategories = true;
30 | //important when refresh indicator is called
31 | //to avoid add same items
32 | _categoryList.clear();
33 |
34 | var response = await _categoryService.getCategories();
35 |
36 | if (response.statusCode == 200) {
37 | var jsonBody = json.decode(response.body);
38 | var jsonCategories = jsonBody['data']['categories'];
39 | _categoryList.addAll(categoryFromJson(json.encode(jsonCategories)));
40 | _isLoadingCategories = false;
41 | notifyListeners();
42 | } else {
43 | ErrorController.showErrorFromApi(scaffoldKey, response);
44 | }
45 | } on SocketException catch (_) {
46 | ErrorController.showNoInternetError(scaffoldKey);
47 | } on HttpException catch (_) {
48 | ErrorController.showNoServerError(scaffoldKey);
49 | } on FormatException catch (_) {
50 | ErrorController.showFormatExceptionError(scaffoldKey);
51 | } catch (e) {
52 | print("Error ${e.toString()}");
53 | ErrorController.showUnKownError(scaffoldKey);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion 28
30 |
31 | sourceSets {
32 | main.java.srcDirs += 'src/main/kotlin'
33 | }
34 |
35 | lintOptions {
36 | disable 'InvalidPackage'
37 | }
38 |
39 | defaultConfig {
40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
41 | applicationId "com.example.ecommerceapp"
42 | minSdkVersion 21
43 | targetSdkVersion 28
44 | versionCode flutterVersionCode.toInteger()
45 | versionName flutterVersionName
46 | }
47 |
48 | buildTypes {
49 | release {
50 | // TODO: Add your own signing config for the release build.
51 | // Signing with the debug keys for now, so `flutter run --release` works.
52 | signingConfig signingConfigs.debug
53 | }
54 | }
55 | }
56 |
57 | flutter {
58 | source '../..'
59 | }
60 |
61 | dependencies {
62 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
63 | implementation 'io.card:android-sdk:5.+'
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/lib/widgets/drawer.dart:
--------------------------------------------------------------------------------
1 | import 'package:ecommerceapp/screens/order_history.dart';
2 | import 'package:ecommerceapp/screens/profile.dart';
3 | import 'package:flutter/material.dart';
4 |
5 | class CDrawer extends StatefulWidget {
6 | CDrawer({Key key}) : super(key: key);
7 |
8 | @override
9 | _CDrawerState createState() => _CDrawerState();
10 | }
11 |
12 | class _CDrawerState extends State {
13 | @override
14 | Widget build(BuildContext context) {
15 | return Drawer(
16 | child: ListView(
17 | children: [
18 | DrawerHeader(
19 | child: Image(
20 | image: AssetImage("assets/images/logo.png"),
21 | fit: BoxFit.fill,
22 | ),
23 | ),
24 | SizedBox(height: 50),
25 | Padding(
26 | padding: const EdgeInsets.only(left: 15.0, right: 10.0),
27 | child: ListTile(
28 | tileColor: Colors.grey[200],
29 | leading: Icon(Icons.person),
30 | trailing: Icon(Icons.people),
31 | title: Text(
32 | 'Profile',
33 | style: TextStyle(
34 | fontSize: 15,
35 | ),
36 | ),
37 | onTap: () {
38 | Navigator.pop(context);
39 | Navigator.pushNamed(context, Profile.id);
40 | },
41 | ),
42 | ),
43 | SizedBox(height: 50),
44 | Padding(
45 | padding: const EdgeInsets.only(left: 15.0, right: 10.0),
46 | child: ListTile(
47 | tileColor: Colors.grey[200],
48 | leading: Icon(Icons.history),
49 | trailing: Icon(Icons.history_edu),
50 | title: Text(
51 | 'Order history',
52 | style: TextStyle(
53 | fontSize: 15,
54 | ),
55 | ),
56 | onTap: () {
57 | Navigator.pop(context);
58 | Navigator.pushNamed(context, OrderHistroy.id);
59 | },
60 | ),
61 | ),
62 | ],
63 | ),
64 | );
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/lib/models/order.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:ecommerceapp/models/cart_item.dart';
4 | import 'package:ecommerceapp/models/shipping_details.dart';
5 |
6 | Order orderFromJson(String str) => Order.fromJson(json.decode(str));
7 |
8 | List ordersFromJson(String str) =>
9 | List.from(json.decode(str).map((x) => Order.fromJson(x)));
10 |
11 | String orderToJson(Order data) => json.encode(data.toJson());
12 |
13 | class Order {
14 | Order({
15 | this.userId,
16 | this.dateOrdered,
17 | this.id,
18 | this.shippingDetails,
19 | this.shippingCost,
20 | this.tax,
21 | this.total,
22 | this.totalItemPrice,
23 | this.paymentMethod,
24 | this.userType,
25 | this.cartItems,
26 | this.v,
27 | });
28 |
29 | dynamic userId;
30 | DateTime dateOrdered;
31 | String id;
32 | ShippingDetails shippingDetails;
33 | String shippingCost;
34 | String tax;
35 | String total;
36 | String totalItemPrice;
37 | String paymentMethod;
38 | String userType;
39 | List cartItems;
40 | int v;
41 |
42 | factory Order.fromJson(Map jsonData) => Order(
43 | userId: jsonData["userId"],
44 | dateOrdered: DateTime.parse(jsonData["dateOrdered"]),
45 | id: jsonData["_id"],
46 | shippingDetails: ShippingDetails.fromJson(jsonData["shippingDetails"]),
47 | shippingCost: jsonData["shippingCost"],
48 | tax: jsonData["tax"],
49 | total: jsonData["total"],
50 | totalItemPrice: jsonData["totalItemPrice"],
51 | paymentMethod: jsonData["paymentMethod"],
52 | userType: jsonData["userType"],
53 | cartItems: cartItemFromJson(json.encode(jsonData["cartItems"])),
54 | v: jsonData["__v"],
55 | );
56 |
57 | Map toJson() => {
58 | "userId": userId,
59 | "dateOrdered": dateOrdered.toIso8601String(),
60 | "shippingDetails": shippingDetails.toJson(),
61 | "shippingCost": shippingCost,
62 | "tax": tax,
63 | "total": total,
64 | "totalItemPrice": totalItemPrice,
65 | "paymentMethod": paymentMethod,
66 | "userType": userType,
67 | "cartItems": json.decode(cartItemToJson(cartItems)),
68 | };
69 | }
70 |
--------------------------------------------------------------------------------
/lib/widgets/product_detail_bottomsheet_content.dart:
--------------------------------------------------------------------------------
1 | import 'package:ecommerceapp/screens/shopping_cart.dart';
2 | import 'package:ecommerceapp/widgets/cart_button.dart';
3 | import 'package:flutter/material.dart';
4 |
5 | class ProductDetailBottomSheetContent extends StatelessWidget {
6 | const ProductDetailBottomSheetContent({
7 | Key key,
8 | @required cartCtlr,
9 | }) : _cartCtlr = cartCtlr,
10 | super(key: key);
11 |
12 | final _cartCtlr;
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return Container(
17 | height: 120,
18 | color: Colors.grey[200],
19 | child: Column(
20 | children: [
21 | SizedBox(
22 | height: 10,
23 | ),
24 | Text(
25 | '${_cartCtlr.selectedItem.product.name} already in cart',
26 | style: TextStyle(
27 | fontSize: 20,
28 | fontWeight: FontWeight.bold,
29 | ),
30 | ),
31 | SizedBox(
32 | height: 10,
33 | ),
34 | Row(
35 | mainAxisAlignment: MainAxisAlignment.spaceAround,
36 | children: [
37 | RaisedButton(
38 | elevation: 0,
39 | shape: RoundedRectangleBorder(
40 | borderRadius: BorderRadius.circular(18.0),
41 | side: BorderSide(color: Colors.red)),
42 | color: Colors.red,
43 | onPressed: () {
44 | _cartCtlr.removeFromCart(_cartCtlr.selectedItem);
45 | Navigator.pop(context);
46 | },
47 | child: Text(
48 | 'REMOVE',
49 | style: TextStyle(
50 | color: Colors.white,
51 | ),
52 | ),
53 | ),
54 | Text('OR'),
55 | InkWell(
56 | child: CartButton(
57 | text: 'View cart',
58 | width: MediaQuery.of(context).size.width * 0.25,
59 | ),
60 | onTap: () {
61 | Navigator.pop(context);
62 | Navigator.pushNamed(context, ShoppingCart.id);
63 | },
64 | )
65 | ],
66 | ),
67 | ],
68 | ),
69 | );
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/lib/widgets/product_card.dart:
--------------------------------------------------------------------------------
1 | import 'package:ecommerceapp/models/product.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | class ProductCard extends StatelessWidget {
5 | final Product product;
6 | final Function onProductTapped;
7 |
8 | const ProductCard({
9 | Key key,
10 | @required this.product,
11 | @required this.onProductTapped,
12 | }) : super(key: key);
13 | @override
14 | Widget build(BuildContext context) {
15 | return GestureDetector(
16 | onTap: onProductTapped,
17 | child: Container(
18 | color: Colors.grey[200],
19 | child: Column(
20 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
21 | crossAxisAlignment: CrossAxisAlignment.stretch,
22 | children: [
23 | Expanded(
24 | child: Image.network(
25 | product.imageUrl,
26 | fit: BoxFit.fill,
27 | errorBuilder: (BuildContext context, Object exception,
28 | StackTrace stackTrace) {
29 | return Center(
30 | child: Icon(Icons.error),
31 | );
32 | },
33 | loadingBuilder: (BuildContext context, Widget child,
34 | ImageChunkEvent loadingProgress) {
35 | if (loadingProgress == null) return child;
36 | return Center(
37 | child: CircularProgressIndicator(
38 | strokeWidth: 2.0,
39 | ),
40 | );
41 | },
42 | ),
43 | ),
44 | Padding(
45 | padding: const EdgeInsets.only(
46 | top: 10.0,
47 | bottom: 8.0,
48 | left: 8.0,
49 | right: 8.0,
50 | ),
51 | child: Row(
52 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
53 | crossAxisAlignment: CrossAxisAlignment.end,
54 | children: [
55 | Text(
56 | product.name ?? '',
57 | style: TextStyle(fontWeight: FontWeight.bold),
58 | ),
59 | Text(product.price.toString() ?? ''),
60 | ],
61 | ),
62 | ),
63 | ],
64 | ),
65 | ),
66 | );
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/lib/widgets/shopping_cart_bottom_sheet.dart:
--------------------------------------------------------------------------------
1 | import 'package:ecommerceapp/screens/auth_screen.dart';
2 | import 'package:ecommerceapp/screens/shipping.dart';
3 | import 'package:flutter/material.dart';
4 |
5 | class ShoppingCartBottomSheet extends StatelessWidget {
6 | final String message;
7 | const ShoppingCartBottomSheet({
8 | this.message,
9 | Key key,
10 | }) : super(key: key);
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | return Container(
15 | height: 200,
16 | width: MediaQuery.of(context).size.width,
17 | color: Colors.grey[300],
18 | child: Column(
19 | children: [
20 | SizedBox(
21 | height: 20,
22 | ),
23 | Text(
24 | "${message != null ? message : ''}",
25 | style: TextStyle(
26 | fontWeight: FontWeight.bold,
27 | ),
28 | ),
29 | RaisedButton(
30 | elevation: 0,
31 | shape: RoundedRectangleBorder(
32 | borderRadius: BorderRadius.circular(18.0),
33 | ),
34 | color: Colors.red[900],
35 | onPressed: () {
36 | Navigator.pushNamed(context, AuthScreen.id);
37 | },
38 | child: Text(
39 | 'Sign In',
40 | style: TextStyle(
41 | color: Colors.white,
42 | fontSize: 18,
43 | ),
44 | ),
45 | ),
46 | SizedBox(
47 | height: 20,
48 | ),
49 | Text(
50 | 'OR',
51 | style: TextStyle(fontWeight: FontWeight.bold),
52 | ),
53 | SizedBox(
54 | height: 20,
55 | ),
56 | RaisedButton(
57 | elevation: 0,
58 | shape: RoundedRectangleBorder(
59 | borderRadius: BorderRadius.circular(18.0),
60 | ),
61 | color: Colors.orange[900],
62 | onPressed: () {
63 | Navigator.pop(context);
64 | Navigator.pushNamed(context, Shipping.id);
65 | },
66 | child: Text(
67 | 'Continue as Guest',
68 | style: TextStyle(
69 | color: Colors.white,
70 | fontSize: 18,
71 | ),
72 | ),
73 | ),
74 | ],
75 | ),
76 | );
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/lib/screens/thank_you.dart:
--------------------------------------------------------------------------------
1 | import 'package:ecommerceapp/constants/screen_ids.dart';
2 | import 'package:ecommerceapp/constants/screen_titles.dart';
3 | import 'package:ecommerceapp/screens/products_list.dart';
4 | import 'package:ecommerceapp/screens/single_order.dart';
5 | import 'package:flutter/material.dart';
6 |
7 | class Thanks extends StatelessWidget {
8 | const Thanks({Key key}) : super(key: key);
9 | static String id = ThankYou_Screen_Id;
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | Future _onBackPressed() {
14 | Navigator.pushNamedAndRemoveUntil(
15 | context, ProductList.id, (route) => false);
16 | return Future.value(true);
17 | }
18 |
19 | return WillPopScope(
20 | onWillPop: _onBackPressed,
21 | child: SafeArea(
22 | child: Scaffold(
23 | appBar: AppBar(
24 | title: Text(
25 | '$Thanks_Screen_Title',
26 | style: TextStyle(
27 | color: Colors.black,
28 | ),
29 | ),
30 | iconTheme: IconThemeData(color: Colors.black),
31 | elevation: 1,
32 | backgroundColor: Colors.white,
33 | ),
34 | body: Container(
35 | child: Center(
36 | child: Column(
37 | crossAxisAlignment: CrossAxisAlignment.center,
38 | mainAxisAlignment: MainAxisAlignment.center,
39 | children: [
40 | Icon(
41 | Icons.star,
42 | size: 40,
43 | ),
44 | SizedBox(
45 | height: 20,
46 | ),
47 | Text(
48 | 'Payment made successfully',
49 | style: TextStyle(
50 | fontSize: 20,
51 | ),
52 | ),
53 | SizedBox(
54 | height: 20,
55 | ),
56 | RaisedButton(
57 | onPressed: () {
58 | Navigator.pushNamed(context, SingleOrder.id);
59 | },
60 | child: Text(
61 | "VIEW ORDER",
62 | style: TextStyle(
63 | color: Colors.white,
64 | ),
65 | ),
66 | color: Colors.orange,
67 | shape: RoundedRectangleBorder(
68 | borderRadius: BorderRadius.circular(50),
69 | ),
70 | ),
71 | ],
72 | ),
73 | ),
74 | ),
75 | ),
76 | ),
77 | );
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/lib/services/auth_service.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:ecommerceapp/application.properties/app_properties.dart';
4 | import 'package:ecommerceapp/models/authdata.dart';
5 | import 'package:http/http.dart' as http;
6 |
7 | class AuthService {
8 | static AuthService _authService;
9 |
10 | AuthService._internal() {
11 | _authService = this;
12 | }
13 |
14 | factory AuthService() => _authService ?? AuthService._internal();
15 |
16 | static var httpClient = http.Client();
17 |
18 | Map headers = {'Content-Type': 'application/json'};
19 |
20 | Future emailNameAndPasswordSignUp(
21 | String name,
22 | String email,
23 | String password,
24 | ) async {
25 | var authData = AuthData(name: name, email: email, password: password);
26 |
27 | return await http.post(
28 | AppProperties.signUpUrl,
29 | body: json.encode(authData.toJson()),
30 | headers: headers,
31 | );
32 | }
33 |
34 | Future emailAndPasswordSignIn(
35 | String email,
36 | String password,
37 | ) async {
38 | var authData = AuthData(name: '', email: email, password: password);
39 |
40 | return await http.post(
41 | AppProperties.signInUrl,
42 | body: json.encode(authData.toJson()),
43 | headers: headers,
44 | );
45 | }
46 |
47 | Future checkTokenExpiry(String token) async {
48 | var tokenObject = Map();
49 | tokenObject.putIfAbsent('token', () => token);
50 |
51 | return await http.post(
52 | AppProperties.checkTokenExpiryUrl,
53 | body: json.encode(tokenObject),
54 | headers: headers,
55 | );
56 | }
57 |
58 | Future changeName(String name, String userId, String jwtToken) async {
59 | headers.putIfAbsent('Authorization', () => 'Bearer $jwtToken');
60 | var bodyObject = Map();
61 | bodyObject.putIfAbsent('name', () => name);
62 |
63 | return await http.patch(
64 | "${AppProperties.changenameUrl}$userId",
65 | headers: headers,
66 | body: json.encode(bodyObject),
67 | );
68 | }
69 |
70 | Future changeEmail(String email, String userId, String jwtToken) async {
71 | headers.putIfAbsent('Authorization', () => 'Bearer $jwtToken');
72 | var bodyObject = Map();
73 | bodyObject.putIfAbsent('email', () => email);
74 |
75 | return await http.patch(
76 | "${AppProperties.changeMailUrl}$userId",
77 | headers: headers,
78 | body: json.encode(bodyObject),
79 | );
80 | }
81 |
82 | Future forgotPassword(String email) async {
83 | var bodyObject = Map();
84 | bodyObject.putIfAbsent('email', () => email);
85 | return await http.post(
86 | "${AppProperties.forgotPasswordUrl}",
87 | headers: headers,
88 | body: json.encode(bodyObject),
89 | );
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:ecommerceapp/controllers/activity_tracker_controller.dart';
2 | import 'package:ecommerceapp/controllers/cart_controller.dart';
3 | import 'package:ecommerceapp/controllers/category_controller.dart';
4 | import 'package:ecommerceapp/controllers/order_controller.dart';
5 | import 'package:ecommerceapp/controllers/product_controller.dart';
6 | import 'package:ecommerceapp/controllers/shipping_controller.dart';
7 | import 'package:ecommerceapp/screens/auth_screen.dart';
8 | import 'package:ecommerceapp/screens/order_history.dart';
9 | import 'package:ecommerceapp/screens/payment_method.dart';
10 | import 'package:ecommerceapp/screens/product_detail.dart';
11 | import 'package:ecommerceapp/screens/products_list.dart';
12 | import 'package:ecommerceapp/screens/profile.dart';
13 | import 'package:ecommerceapp/screens/shipping.dart';
14 | import 'package:ecommerceapp/screens/shopping_cart.dart';
15 | import 'package:ecommerceapp/screens/single_order.dart';
16 | import 'package:ecommerceapp/screens/thank_you.dart';
17 | import 'package:flutter/material.dart';
18 | import 'package:flutter/services.dart';
19 | import 'package:provider/provider.dart';
20 |
21 | void main() {
22 | runApp(EcommerceApp());
23 | }
24 |
25 | class EcommerceApp extends StatelessWidget {
26 | @override
27 | Widget build(BuildContext context) {
28 | SystemChrome.setPreferredOrientations(
29 | [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
30 |
31 | SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
32 | statusBarColor: Colors.grey,
33 | statusBarIconBrightness: Brightness.dark,
34 | systemNavigationBarColor: Colors.grey,
35 | systemNavigationBarIconBrightness: Brightness.light,
36 | ));
37 |
38 | return MultiProvider(
39 | providers: [
40 | ChangeNotifierProvider(create: (context) => ProductController()),
41 | ChangeNotifierProvider(create: (context) => CategoryController()),
42 | ChangeNotifierProvider(create: (context) => CartController()),
43 | ChangeNotifierProvider(create: (context) => ShippingController()),
44 | ChangeNotifierProvider(create: (context) => OrderController()),
45 | ChangeNotifierProvider(create: (context) => ActivityTracker()),
46 | ],
47 | child: MaterialApp(
48 | debugShowCheckedModeBanner: false,
49 | title: 'Ecommerce app',
50 | theme: ThemeData(
51 | primarySwatch: Colors.grey,
52 | visualDensity: VisualDensity.adaptivePlatformDensity,
53 | ),
54 | initialRoute: ProductList.id,
55 | routes: {
56 | ProductList.id: (context) => ProductList(),
57 | ShoppingCart.id: (context) => ShoppingCart(),
58 | ProductDetail.id: (context) => ProductDetail(),
59 | Shipping.id: (context) => Shipping(),
60 | PaymentMethod.id: (context) => PaymentMethod(),
61 | Thanks.id: (context) => Thanks(),
62 | SingleOrder.id: (context) => SingleOrder(),
63 | AuthScreen.id: (context) => AuthScreen(),
64 | OrderHistroy.id: (context) => OrderHistroy(),
65 | Profile.id: (context) => Profile()
66 | },
67 | ),
68 | );
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/lib/services/stripe_service.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/services.dart';
4 | import 'package:stripe_payment/stripe_payment.dart';
5 | import 'package:http/http.dart' as http;
6 |
7 | class StripeTransactionResponse {
8 | String message;
9 | bool success;
10 | StripeTransactionResponse({this.message, this.success});
11 | }
12 |
13 | class StripeService {
14 | static String apiBase = 'https://api.stripe.com/v1';
15 | static String paymentApiUrl = '${StripeService.apiBase}/payment_intents';
16 | static String secret = '';
17 | static String publishableKey = 'pk_test_nGOzznmrg37WxxREAMw8jNHj00ukQ2wSgi';
18 | static Map headers = {
19 | 'Authorization': 'Bearer ${StripeService.secret}',
20 | 'Content-Type': 'application/x-www-form-urlencoded'
21 | };
22 |
23 | static init() {
24 | StripePayment.setOptions(
25 | StripeOptions(
26 | publishableKey: publishableKey,
27 | merchantId: "Test",
28 | androidPayMode: "test"),
29 | );
30 | }
31 |
32 | static Future processPayment(
33 | String amount, currency) async {
34 | try {
35 | var paymentMethod = await StripePayment.paymentRequestWithCardForm(
36 | CardFormPaymentRequest());
37 | var paymentIntent = await StripeService.createPaymentIntent(
38 | amount,
39 | currency,
40 | );
41 |
42 | var response = await StripePayment.confirmPaymentIntent(
43 | PaymentIntent(
44 | clientSecret: paymentIntent['client_secret'],
45 | paymentMethodId: paymentMethod.id,
46 | ),
47 | );
48 |
49 | if (response.status == 'succeeded') {
50 | return new StripeTransactionResponse(
51 | message: 'Transaction successful',
52 | success: true,
53 | );
54 | } else {
55 | return new StripeTransactionResponse(
56 | message: 'Transaction failed',
57 | success: false,
58 | );
59 | }
60 | } on PlatformException catch (err) {
61 | return StripeService.getPlatformExceptionErrorResult(err);
62 | } catch (e) {
63 | return new StripeTransactionResponse(
64 | message: 'Transaction failed: ${e.toString()}',
65 | success: false,
66 | );
67 | }
68 | }
69 |
70 | static getPlatformExceptionErrorResult(err) {
71 | String message = 'Something went wrong';
72 | if (err.code == 'cancelled') {
73 | message = 'Transaction cancelled';
74 | }
75 |
76 | return new StripeTransactionResponse(message: message, success: false);
77 | }
78 |
79 | static Future