diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies new file mode 100644 index 00000000..2e95ca9b --- /dev/null +++ b/.flutter-plugins-dependencies @@ -0,0 +1 @@ +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"path_provider_ios","path":"D:\\\\Flutter\\\\flutter_windows_1.22.5-stable\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_ios-2.0.9\\\\","dependencies":[]},{"name":"webview_flutter_wkwebview","path":"D:\\\\Flutter\\\\flutter_windows_1.22.5-stable\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\webview_flutter_wkwebview-2.7.5\\\\","dependencies":[]}],"android":[{"name":"path_provider_android","path":"D:\\\\Flutter\\\\flutter_windows_1.22.5-stable\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_android-2.0.14\\\\","dependencies":[]},{"name":"webview_flutter_android","path":"D:\\\\Flutter\\\\flutter_windows_1.22.5-stable\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\webview_flutter_android-2.8.10\\\\","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"D:\\\\Flutter\\\\flutter_windows_1.22.5-stable\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_macos-2.0.6\\\\","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"D:\\\\Flutter\\\\flutter_windows_1.22.5-stable\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_linux-2.1.6\\\\","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"D:\\\\Flutter\\\\flutter_windows_1.22.5-stable\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_windows-2.0.6\\\\","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"path_provider","dependencies":["path_provider_android","path_provider_ios","path_provider_linux","path_provider_macos","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_ios","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"webview_flutter","dependencies":["webview_flutter_android","webview_flutter_wkwebview"]},{"name":"webview_flutter_android","dependencies":[]},{"name":"webview_flutter_wkwebview","dependencies":[]}],"date_created":"2022-06-23 15:27:58.328689","version":"2.10.4"} \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index 8ef25fd0..65a2a263 100755 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -32,7 +32,7 @@ if (keystorePropertiesFile.exists()) { } android { - compileSdkVersion 28 + compileSdkVersion 31 sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -45,8 +45,8 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.int2.ecommerce_int2" - minSdkVersion 16 - targetSdkVersion 28 + minSdkVersion 19 + targetSdkVersion 31 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index fcdfbdc1..7945117d 100755 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -21,13 +21,14 @@ until Flutter renders its first frame. It can be removed if there is no splash screen (such as the default splash screen defined in @style/LaunchTheme). --> - + + diff --git a/android/build.gradle b/android/build.gradle index 3100ad2d..714549c2 100755 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.3.50' + ext.kotlin_version = '1.6.10' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:4.1.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 556d288e..a9282f15 100755 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/ios/Flutter/Flutter.podspec b/ios/Flutter/Flutter.podspec deleted file mode 100755 index 5ca30416..00000000 --- a/ios/Flutter/Flutter.podspec +++ /dev/null @@ -1,18 +0,0 @@ -# -# NOTE: This podspec is NOT to be published. It is only used as a local source! -# - -Pod::Spec.new do |s| - s.name = 'Flutter' - s.version = '1.0.0' - s.summary = 'High-performance, high-fidelity mobile apps.' - s.description = <<-DESC -Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS. - DESC - s.homepage = 'https://flutter.io' - s.license = { :type => 'MIT' } - s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } - s.ios.deployment_target = '8.0' - s.vendored_frameworks = 'Flutter.framework' -end diff --git a/ios/Flutter/flutter_export_environment.sh b/ios/Flutter/flutter_export_environment.sh index 7f60bd78..c9bda9d4 100755 --- a/ios/Flutter/flutter_export_environment.sh +++ b/ios/Flutter/flutter_export_environment.sh @@ -1,14 +1,13 @@ #!/bin/sh # This is a generated file; do not edit or check into version control. -export "FLUTTER_ROOT=/Users/macbooki9/Documents/flutter" -export "FLUTTER_APPLICATION_PATH=/Users/macbooki9/Documents/personal/flutter_ecommerce_template" +export "FLUTTER_ROOT=D:\Flutter\flutter_windows_1.22.5-stable\flutter" +export "FLUTTER_APPLICATION_PATH=D:\Workspace\Flutter\do-an\flutter_ecommerce_template" export "COCOAPODS_PARALLEL_CODE_SIGN=true" -export "FLUTTER_TARGET=/Users/macbooki9/Documents/personal/flutter_ecommerce_template/lib/main.dart" +export "FLUTTER_TARGET=lib\main.dart" export "FLUTTER_BUILD_DIR=build" export "FLUTTER_BUILD_NAME=1.0.1" export "FLUTTER_BUILD_NUMBER=1.0.1" -export "DART_DEFINES=Zmx1dHRlci5pbnNwZWN0b3Iuc3RydWN0dXJlZEVycm9ycz10cnVl,RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==" export "DART_OBFUSCATION=false" -export "TRACK_WIDGET_CREATION=true" +export "TRACK_WIDGET_CREATION=false" export "TREE_SHAKE_ICONS=false" -export "PACKAGE_CONFIG=/Users/macbooki9/Documents/personal/flutter_ecommerce_template/.dart_tool/package_config.json" +export "PACKAGE_CONFIG=.packages" diff --git a/lib/data/models/address.model.dart b/lib/data/models/address.model.dart new file mode 100644 index 00000000..a3430b88 --- /dev/null +++ b/lib/data/models/address.model.dart @@ -0,0 +1,36 @@ +class Address { + String? id; + String? address; + String? city; + String? country; + String? zipCode; + bool? isDefault; + + Address( + {this.id, + this.address, + this.city, + this.country = "Viet Name", + this.zipCode = "+84", + this.isDefault = false}); + + Address.fromJson(Map json) { + id = json['_id']; + address = json['address']; + city = json['city']; + country = json['country']; + zipCode = json['zipCode']; + isDefault = json['isDefault'] ?? false; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['address'] = this.address; + data['city'] = this.city; + data['country'] = this.country; + data['zipCode'] = this.zipCode; + data['isDefault'] = this.isDefault; + return data; + } +} diff --git a/lib/data/models/cart.model.dart b/lib/data/models/cart.model.dart new file mode 100644 index 00000000..369d5383 --- /dev/null +++ b/lib/data/models/cart.model.dart @@ -0,0 +1,83 @@ +import 'package:ecommerce_int2/data/models/product.model.dart'; + +class CartResponse { + bool? success; + String? message; + List? data; + + CartResponse({this.success, this.message, this.data}); + + CartResponse.fromJson(Map json) { + success = json['success']; + message = json['message']; + if (json['data'] != null) { + data = []; + json['data'].forEach((v) { + data!.add(new CartModel.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + data['success'] = this.success; + data['message'] = this.message; + if (this.data != null) { + data['data'] = this.data!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class CartModel { + String? sId; + List? products; + String? user; + String? merchant; + int? total; + bool? isOrdered; + String? created; + int? iV; + + CartModel( + {this.sId, + this.products, + this.user, + this.merchant, + this.total, + this.isOrdered, + this.created, + this.iV}); + + CartModel.fromJson(Map json) { + sId = json['_id']; + if (json['products'] != null) { + products = []; + json['products'].forEach((v) { + products!.add(new Product.fromJson(v)); + }); + } + user = json['user']; + merchant = json['merchant'] != null ? + (json['merchant'] is String? ? json['merchant'] : json['merchant']['_id']) : ''; + total = json['total']; + isOrdered = json['isOrdered']; + created = json['created']; + iV = json['__v']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.sId; + if (this.products != null) { + data['products'] = this.products!.map((v) => v.toJson()).toList(); + } + data['user'] = this.user; + data['merchant'] = this.merchant; + data['total'] = this.total; + data['isOrdered'] = this.isOrdered; + data['created'] = this.created; + data['__v'] = this.iV; + return data; + } +} diff --git a/lib/data/models/category.model.dart b/lib/data/models/category.model.dart new file mode 100644 index 00000000..3b1816f6 --- /dev/null +++ b/lib/data/models/category.model.dart @@ -0,0 +1,100 @@ +class CategoryList { + List? data; + + CategoryList({this.data}); + + CategoryList.fromJson(Map json) { + if (json['data'] != null) { + data = []; + json['data'].forEach((v) { + data!.add(new Category.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + if (this.data != null) { + data['data'] = this.data!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class Category { + String? id; + String? name; + String? description; + List? subcategories; + + Category({this.id, this.name, this.description, this.subcategories}); + + Category.fromJson(Map json) { + id = json['_id']; + name = json['name']; + description = json['description']; + if (json['subcategories'] != null) { + subcategories = []; + json['subcategories'].forEach((v) { + subcategories!.add(new SubCategory.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.id; + data['name'] = this.name; + data['description'] = this.description; + if (this.subcategories != null) { + data['subcategories'] = + this.subcategories!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class SubCategory { + String? sId; + String? name; + String? description; + bool? isActive; + String? category; + String? created; + String? slug; + int? iV; + + SubCategory( + {this.sId, + this.name, + this.description, + this.isActive, + this.category, + this.created, + this.slug, + this.iV}); + + SubCategory.fromJson(Map json) { + sId = json['_id']; + name = json['name']; + description = json['description']; + isActive = json['isActive']; + category = json['category']; + created = json['created']; + slug = json['slug']; + iV = json['__v']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.sId; + data['name'] = this.name; + data['description'] = this.description; + data['isActive'] = this.isActive; + data['category'] = this.category; + data['created'] = this.created; + data['slug'] = this.slug; + data['__v'] = this.iV; + return data; + } +} diff --git a/lib/data/models/login.result.dart b/lib/data/models/login.result.dart new file mode 100644 index 00000000..523f9122 --- /dev/null +++ b/lib/data/models/login.result.dart @@ -0,0 +1,18 @@ +import 'package:ecommerce_int2/data/models/user.model.dart'; +import 'package:json_annotation/json_annotation.dart'; +part 'login.result.g.dart'; + +@JsonSerializable(explicitToJson: true) +class LoginResult { + final String token; + final bool success; + final UserModel data; + + LoginResult(this.token, this.success, this.data); + + + factory LoginResult.fromJson(Map json) => + _$LoginResultFromJson(json); + + Map toJson() => _$LoginResultToJson(this); +} \ No newline at end of file diff --git a/lib/data/models/login.result.g.dart b/lib/data/models/login.result.g.dart new file mode 100644 index 00000000..5b149d6a --- /dev/null +++ b/lib/data/models/login.result.g.dart @@ -0,0 +1,22 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'login.result.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +LoginResult _$LoginResultFromJson(Map json) { + return LoginResult( + json['token'] as String, + json['success'] as bool, + UserModel.fromJson(json['data'] as Map), + ); +} + +Map _$LoginResultToJson(LoginResult instance) => + { + 'token': instance.token, + 'success': instance.success, + 'data': instance.data.toJson(), + }; diff --git a/lib/data/models/merchant.model.dart b/lib/data/models/merchant.model.dart new file mode 100644 index 00000000..05f68179 --- /dev/null +++ b/lib/data/models/merchant.model.dart @@ -0,0 +1,157 @@ +import 'package:ecommerce_int2/data/models/product.model.dart'; + +class MerchantResponse { + bool? success; + MerchantData? data; + + MerchantResponse({this.success, this.data}); + + MerchantResponse.fromJson(Map json) { + success = json['success']; + data = json['data'] != null ? new MerchantData.fromJson(json['data']) : null; + } + + Map toJson() { + final Map data = new Map(); + data['success'] = this.success; + if (this.data != null) { + data['data'] = this.data!.toJson(); + } + return data; + } +} + +class MerchantData { + Merchant? merchant; + List? products; + int? page; + int? pages; + int? totalProducts; + + MerchantData( + {this.merchant, + this.products, + this.page, + this.pages, + this.totalProducts}); + + MerchantData.fromJson(Map json) { + merchant = json['merchant'] != null + ? new Merchant.fromJson(json['merchant']) + : null; + if (json['products'] != null) { + products = []; + json['products'].forEach((v) { + products!.add(new Product.fromJson(v)); + }); + } + page = json['page']; + pages = json['pages']; + totalProducts = json['totalProducts']; + } + + Map toJson() { + final Map data = new Map(); + if (this.merchant != null) { + data['merchant'] = this.merchant!.toJson(); + } + if (this.products != null) { + data['products'] = this.products!.map((v) => v.toJson()).toList(); + } + data['page'] = this.page; + data['pages'] = this.pages; + data['totalProducts'] = this.totalProducts; + return data; + } +} + +class Merchant { + String? sId; + String? name; + String? email; + String? phoneNumber; + List? categories; + String? business; + bool? isActive; + String? status; + String? created; + String? slug; + int? iV; + + Merchant( + {this.sId, + this.name, + this.email, + this.phoneNumber, + this.categories, + this.business, + this.isActive, + this.status, + this.created, + this.slug, + this.iV}); + + Merchant.fromJson(Map json) { + sId = json['_id']; + name = json['name']; + email = json['email']; + phoneNumber = json['phoneNumber']; + if (json['categories'] != null) { + categories = []; + json['categories'].forEach((v) { + categories!.add(new Categories.fromJson(v)); + }); + } + business = json['business']; + isActive = json['isActive']; + status = json['status']; + created = json['created']; + slug = json['slug']; + iV = json['__v']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.sId; + data['name'] = this.name; + data['email'] = this.email; + data['phoneNumber'] = this.phoneNumber; + if (this.categories != null) { + data['categories'] = this.categories!.map((v) => v.toJson()).toList(); + } + data['business'] = this.business; + data['isActive'] = this.isActive; + data['status'] = this.status; + data['created'] = this.created; + data['slug'] = this.slug; + data['__v'] = this.iV; + return data; + } +} + +class Categories { + String? sId; + String? name; + String? slug; + String? image; + + Categories({this.sId, this.name, this.slug, this.image}); + + Categories.fromJson(Map json) { + sId = json['_id']; + name = json['name']; + slug = json['slug']; + image = json['image']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.sId; + data['name'] = this.name; + data['slug'] = this.slug; + data['image'] = this.image; + return data; + } +} + + diff --git a/lib/data/models/order.model.dart b/lib/data/models/order.model.dart new file mode 100644 index 00000000..9a80233c --- /dev/null +++ b/lib/data/models/order.model.dart @@ -0,0 +1,517 @@ +import 'package:ecommerce_int2/data/models/cart.model.dart'; +import 'package:intl/intl.dart'; + +class OrderResponse { + bool? success; + String? message; + Order? order; + + OrderResponse({this.success, this.message, this.order}); + + OrderResponse.fromJson(Map json) { + success = json['success']; + message = json['message']; + order = json['data'] != null ? new Order.fromJson(json['data']) : null; + } + + Map toJson() { + final Map data = new Map(); + data['success'] = this.success; + data['message'] = this.message; + if (this.order != null) { + data['data'] = this.order!.toJson(); + } + return data; + } +} + +class Order { + String? cart; + String? user; + String? merchant; + String? payment; + String? otherAddress; + String? phoneNumber; + String? status; + String? paymentStatus; + String? sId; + String? created; + int? iV; + + String get createdDate { + if (created == null) return ''; + final date = DateTime.tryParse(created!); + if (date == null) return ''; + final res = DateFormat("yyyy-MM-dd HH:mm:ss aa").format(date.toLocal()); + return res; + } + + Order( + {this.cart, + this.user, + this.merchant, + this.payment, + this.otherAddress, + this.phoneNumber, + this.status, + this.paymentStatus, + this.sId, + this.created, + this.iV}); + + Order.fromJson(Map json) { + cart = json['cart'] is String ? json['cart']: json['cart']['_id']; + user = json['user']; + merchant = json['merchant']; + payment = json['payment']; + otherAddress = json['otherAddress']; + phoneNumber = json['phoneNumber']; + status = json['status']; + paymentStatus = json['paymentStatus']; + sId = json['_id']; + created = json['created']; + iV = json['__v']; + } + + Map toJson() { + final Map data = new Map(); + data['cart'] = this.cart; + data['user'] = this.user; + data['merchant'] = this.merchant; + data['payment'] = this.payment; + data['otherAddress'] = this.otherAddress; + data['phoneNumber'] = this.phoneNumber; + data['status'] = this.status; + data['paymentStatus'] = this.paymentStatus; + data['_id'] = this.sId; + data['created'] = this.created; + data['__v'] = this.iV; + return data; + } +} + +class CompleteOrderParam { + String? address; + String? otherAddress; + String? phoneNumber; + String? payment; + + CompleteOrderParam( + {this.address, this.otherAddress, this.phoneNumber, this.payment}); + + CompleteOrderParam.fromJson(Map json) { + address = json['address']; + otherAddress = json['otherAddress']; + phoneNumber = json['phoneNumber']; + payment = json['payment']; + } + + Map toJson() { + final Map data = new Map(); + data['address'] = this.address; + data['otherAddress'] = this.otherAddress; + data['phoneNumber'] = this.phoneNumber; + data['payment'] = this.payment; + return data; + } +} + +class CompleteOrderResponse { + bool? success; + String? message; + OrderDoc? orderDoc; + + CompleteOrderResponse({this.success, this.message, this.orderDoc}); + + CompleteOrderResponse.fromJson(Map json) { + success = json['success']; + message = json['message']; + orderDoc = + json['data'] != null ? new OrderDoc.fromJson(json['data']) : null; + } + + Map toJson() { + final Map data = new Map(); + data['success'] = this.success; + data['message'] = this.message; + if (this.orderDoc != null) { + data['data'] = this.orderDoc!.toJson(); + } + return data; + } +} + +class OrderDoc { + String? sId; + String? cart; + String? user; + String? merchant; + String? payment; + String? otherAddress; + String? phoneNumber; + String? status; + String? paymentStatus; + String? created; + int? iV; + + OrderDoc( + {this.sId, + this.cart, + this.user, + this.merchant, + this.payment, + this.otherAddress, + this.phoneNumber, + this.status, + this.paymentStatus, + this.created, + this.iV}); + + OrderDoc.fromJson(Map json) { + sId = json['_id']; + cart = json['cart']; + user = json['user']; + merchant = json['merchant']; + payment = json['payment']; + otherAddress = json['otherAddress']; + phoneNumber = json['phoneNumber']; + status = json['status']; + paymentStatus = json['paymentStatus']; + created = json['created']; + iV = json['__v']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.sId; + data['cart'] = this.cart; + data['user'] = this.user; + data['merchant'] = this.merchant; + data['payment'] = this.payment; + data['otherAddress'] = this.otherAddress; + data['phoneNumber'] = this.phoneNumber; + data['status'] = this.status; + data['paymentStatus'] = this.paymentStatus; + data['created'] = this.created; + data['__v'] = this.iV; + return data; + } +} + +class AllOrderResponse { + bool? success; + List? orders; + + AllOrderResponse({this.success, this.orders}); + + AllOrderResponse.fromJson(Map json) { + success = json['success']; + if (json['data'] != null) { + orders = []; + json['data'].forEach((v) { + orders!.add(new Order.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + data['success'] = this.success; + if (this.orders != null) { + data['data'] = this.orders!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class DetailOrderResponse { + bool? success; + OrderDocDetail? orderDoc; + + DetailOrderResponse({this.success, this.orderDoc}); + + DetailOrderResponse.fromJson(Map json) { + success = json['success']; + orderDoc = + json['data'] != null ? new OrderDocDetail.fromJson(json['data']) : null; + } + + Map toJson() { + final Map data = new Map(); + data['success'] = this.success; + if (this.orderDoc != null) { + data['data'] = this.orderDoc!.toJson(); + } + return data; + } +} + +class OrderDocDetail { + String? sId; + CartModel? cart; + String? user; + String? merchant; + String? payment; + String? phoneNumber; + String? status; + String? paymentStatus; + String? created; + int? iV; + String? address; + + String get createdDate { + if (created == null) return ''; + final date = DateTime.tryParse(created!); + if (date == null) return ''; + final res = DateFormat("yyyy-MM-dd HH:mm:ss aa").format(date); + return res; + } + + bool get isPayWithCash => payment == 'CASH' && canCancel; + bool get canConfirmReceive => status == 'DELIVERING'; + + bool get canCancel => !(status == 'CANCELLED' || status == 'RECEIVED') ; + + OrderDocDetail( + {this.sId, + this.cart, + this.user, + this.merchant, + this.payment, + this.phoneNumber, + this.status, + this.paymentStatus, + this.created, + this.iV, + this.address}); + + OrderDocDetail.fromJson(Map json) { + sId = json['_id']; + cart = json['cart'] != null ? new CartModel.fromJson(json['cart']) : null; + user = json['user']['_id']; + merchant = json['merchant']; + payment = json['payment']; + phoneNumber = json['phoneNumber']; + status = json['status']; + paymentStatus = json['paymentStatus']; + created = json['created']; + iV = json['__v']; + address = json['address']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.sId; + if (this.cart != null) { + data['cart'] = this.cart!.toJson(); + } + data['user'] = this.user; + data['merchant'] = this.merchant; + data['payment'] = this.payment; + data['phoneNumber'] = this.phoneNumber; + data['status'] = this.status; + data['paymentStatus'] = this.paymentStatus; + data['created'] = this.created; + data['__v'] = this.iV; + data['address'] = this.address; + return data; + } +} + +class OrderProceed { + bool? success; + Data? data; + + OrderProceed({this.success, this.data}); + + OrderProceed.fromJson(Map json) { + success = json['success']; + data = json['data'] != null ? new Data.fromJson(json['data']) : null; + } + + Map toJson() { + final Map data = new Map(); + data['success'] = this.success; + if (this.data != null) { + data['data'] = this.data!.toJson(); + } + return data; + } +} + +class Data { + Order? order; + Payment? payment; + + Data({this.order, this.payment}); + + Data.fromJson(Map json) { + order = json['order'] != null ? new Order.fromJson(json['order']) : null; + payment = + json['payment'] != null ? new Payment.fromJson(json['payment']) : null; + } + + Map toJson() { + final Map data = new Map(); + if (this.order != null) { + data['order'] = this.order!.toJson(); + } + if (this.payment != null) { + data['payment'] = this.payment!.toJson(); + } + return data; + } +} + + +class Payment { + String? id; + String? intent; + String? state; + Payer? payer; + List? transactions; + String? createTime; + List? links; + int? httpStatusCode; + + Payment( + {this.id, + this.intent, + this.state, + this.payer, + this.transactions, + this.createTime, + this.links, + this.httpStatusCode}); + + Payment.fromJson(Map json) { + id = json['id']; + intent = json['intent']; + state = json['state']; + payer = json['payer'] != null ? new Payer.fromJson(json['payer']) : null; + if (json['transactions'] != null) { + transactions = []; + json['transactions'].forEach((v) { + transactions!.add(new Transactions.fromJson(v)); + }); + } + createTime = json['create_time']; + if (json['links'] != null) { + links = []; + json['links'].forEach((v) { + links!.add(new Links.fromJson(v)); + }); + } + httpStatusCode = json['httpStatusCode']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['intent'] = this.intent; + data['state'] = this.state; + if (this.payer != null) { + data['payer'] = this.payer!.toJson(); + } + if (this.transactions != null) { + data['transactions'] = this.transactions!.map((v) => v.toJson()).toList(); + } + data['create_time'] = this.createTime; + if (this.links != null) { + data['links'] = this.links!.map((v) => v.toJson()).toList(); + } + data['httpStatusCode'] = this.httpStatusCode; + return data; + } +} + +class Payer { + String? paymentMethod; + + Payer({this.paymentMethod}); + + Payer.fromJson(Map json) { + paymentMethod = json['payment_method']; + } + + Map toJson() { + final Map data = new Map(); + data['payment_method'] = this.paymentMethod; + return data; + } +} + +class Transactions { + Amount? amount; + String? description; + List? relatedResources; + + Transactions({this.amount, this.description, this.relatedResources}); + + Transactions.fromJson(Map json) { + amount = + json['amount'] != null ? new Amount.fromJson(json['amount']) : null; + description = json['description']; + if (json['related_resources'] != null) { + relatedResources = []; + json['related_resources'].forEach((v) { + relatedResources!.add(v); + }); + } + } + + Map toJson() { + final Map data = new Map(); + if (this.amount != null) { + data['amount'] = this.amount!.toJson(); + } + data['description'] = this.description; + if (this.relatedResources != null) { + data['related_resources'] = + this.relatedResources!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class Amount { + String? total; + String? currency; + + Amount({this.total, this.currency}); + + Amount.fromJson(Map json) { + total = json['total']; + currency = json['currency']; + } + + Map toJson() { + final Map data = new Map(); + data['total'] = this.total; + data['currency'] = this.currency; + return data; + } +} + +class Links { + String? href; + String? rel; + String? method; + + Links({this.href, this.rel, this.method}); + + Links.fromJson(Map json) { + href = json['href']; + rel = json['rel']; + method = json['method']; + } + + Map toJson() { + final Map data = new Map(); + data['href'] = this.href; + data['rel'] = this.rel; + data['method'] = this.method; + return data; + } +} + diff --git a/lib/data/models/payment.model.dart b/lib/data/models/payment.model.dart new file mode 100644 index 00000000..b4cf7ac8 --- /dev/null +++ b/lib/data/models/payment.model.dart @@ -0,0 +1,163 @@ +class PaymentResponse { + bool? success; + String? message; + Data? data; + + PaymentResponse({this.success, this.message, this.data}); + + PaymentResponse.fromJson(Map json) { + success = json['success']; + message = json['message']; + data = json['data'] != null ? new Data.fromJson(json['data']) : null; + } + + Map toJson() { + final Map data = new Map(); + data['success'] = this.success; + data['message'] = this.message; + if (this.data != null) { + data['data'] = this.data!.toJson(); + } + return data; + } +} + +class Data { + String? id; + String? intent; + String? state; + Payer? payer; + List? transactions; + String? createTime; + List? links; + int? httpStatusCode; + + Data( + {this.id, + this.intent, + this.state, + this.payer, + this.transactions, + this.createTime, + this.links, + this.httpStatusCode}); + + Data.fromJson(Map json) { + id = json['id']; + intent = json['intent']; + state = json['state']; + payer = json['payer'] != null ? new Payer.fromJson(json['payer']) : null; + if (json['transactions'] != null) { + transactions = []; + json['transactions'].forEach((v) { + transactions!.add(new Transactions.fromJson(v)); + }); + } + createTime = json['create_time']; + if (json['links'] != null) { + links = []; + json['links'].forEach((v) { + links!.add(new Links.fromJson(v)); + }); + } + httpStatusCode = json['httpStatusCode']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['intent'] = this.intent; + data['state'] = this.state; + if (this.payer != null) { + data['payer'] = this.payer!.toJson(); + } + if (this.transactions != null) { + data['transactions'] = this.transactions!.map((v) => v.toJson()).toList(); + } + data['create_time'] = this.createTime; + if (this.links != null) { + data['links'] = this.links!.map((v) => v.toJson()).toList(); + } + data['httpStatusCode'] = this.httpStatusCode; + return data; + } +} + +class Payer { + String? paymentMethod; + + Payer({this.paymentMethod}); + + Payer.fromJson(Map json) { + paymentMethod = json['payment_method']; + } + + Map toJson() { + final Map data = new Map(); + data['payment_method'] = this.paymentMethod; + return data; + } +} + +class Transactions { + Amount? amount; + String? description; + + Transactions({this.amount, this.description}); + + Transactions.fromJson(Map json) { + amount = + json['amount'] != null ? new Amount.fromJson(json['amount']) : null; + description = json['description']; + } + + Map toJson() { + final Map data = new Map(); + if (this.amount != null) { + data['amount'] = this.amount!.toJson(); + } + data['description'] = this.description; + return data; + } +} + +class Amount { + String? total; + String? currency; + + Amount({this.total, this.currency}); + + Amount.fromJson(Map json) { + total = json['total']; + currency = json['currency']; + } + + Map toJson() { + final Map data = new Map(); + data['total'] = this.total; + data['currency'] = this.currency; + return data; + } +} + +class Links { + String? href; + String? rel; + String? method; + + Links({this.href, this.rel, this.method}); + + Links.fromJson(Map json) { + href = json['href']; + rel = json['rel']; + method = json['method']; + } + + Map toJson() { + final Map data = new Map(); + data['href'] = this.href; + data['rel'] = this.rel; + data['method'] = this.method; + return data; + } +} diff --git a/lib/data/models/product.model.dart b/lib/data/models/product.model.dart new file mode 100644 index 00000000..e62f33d2 --- /dev/null +++ b/lib/data/models/product.model.dart @@ -0,0 +1,236 @@ +class ProductList { + List? products; + int? page; + int? pages; + int? totalProducts; + + List get random => [products!.first, products!.last, products![1]]; + + ProductList({this.products, this.page, this.pages, this.totalProducts}); + + ProductList.fromJson(Map json) { + if (json['products'] != null) { + products = []; + json['products'].forEach((v) { + products!.add(new Product.fromJson(v)); + }); + } + page = json['page']; + pages = json['pages']; + totalProducts = json['totalProducts']; + } + + Map toJson() { + final Map data = new Map(); + if (this.products != null) { + data['products'] = this.products!.map((v) => v.toJson()).toList(); + } + data['page'] = this.page; + data['pages'] = this.pages; + data['totalProducts'] = this.totalProducts; + return data; + } +} + +class Product { + String? sId; + String? name; + String? imageUrl; + String? imageKey; + String? description; + int? quantity; + int? price; + int? purchasePrice; + int? totalPrice; + bool? isActive; + String? merchant; + String? category; + String? subcategory; + String? created; + String? slug; + int? iV; + int? totalRatings; + int? totalReviews; + int? averageRating; + + Product( + {this.sId, + this.name, + this.imageUrl, + this.imageKey, + this.description, + this.quantity, + this.price, + this.purchasePrice, + this.totalPrice, + this.isActive, + this.merchant, + this.category, + this.subcategory, + this.created, + this.slug, + this.iV, + this.totalRatings, + this.totalReviews, + this.averageRating}); + + Product.fromJson(Map json) { + sId = json['_id'] ?? json['product']['_id']; + name = json['name'] ?? json['product']['name']; + imageUrl = json['imageUrl'] ?? json['product']['imageUrl']; + imageKey = json['imageKey']; + description = json['description']; + quantity = json['quantity']; + price = json['price']; + purchasePrice = json['purchasePrice']; + totalPrice = json['totalPrice']; + isActive = json['isActive']; + merchant = json['merchant']; + category = json['category']; + subcategory = json['subcategory']; + created = json['created']; + slug = json['slug']; + iV = json['__v']; + totalRatings = json['totalRatings']; + totalReviews = json['totalReviews']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.sId; + data['name'] = this.name; + data['imageUrl'] = this.imageUrl; + data['imageKey'] = this.imageKey; + data['description'] = this.description; + data['quantity'] = this.quantity; + data['price'] = this.price; + data['purchasePrice'] = this.purchasePrice; + data['totalPrice'] = this.totalPrice; + data['isActive'] = this.isActive; + data['merchant'] = this.merchant; + data['category'] = this.category; + data['subcategory'] = this.subcategory; + data['created'] = this.created; + data['slug'] = this.slug; + data['__v'] = this.iV; + data['totalRatings'] = this.totalRatings; + data['totalReviews'] = this.totalReviews; + data['averageRating'] = this.averageRating; + return data; + } +} + +class ProductFilter { + SortOrder? sortOrder; + int? rating; + int? max; + int? min; + String? subcategory; + String? name; + String? merchant; + int? order; + int? pageNumber; + + ProductFilter( + {this.sortOrder, + this.rating, + this.max, + this.min, + this.subcategory, + this.name, + this.merchant, + this.order, + this.pageNumber}); + + ProductFilter.fromJson(Map json) { + sortOrder = json['sortOrder'] != null + ? new SortOrder.fromJson(json['sortOrder']) + : null; + rating = json['rating']; + max = json['max']; + min = json['min']; + subcategory = json['subcategory']; + order = json['order']; + pageNumber = json['pageNumber']; + } + + Map toJson() { + final Map data = new Map(); + if (this.sortOrder != null) { + data['sortOrder'] = this.sortOrder!.toJson(); + } + data['rating'] = this.rating; + data['max'] = this.max; + data['min'] = this.min; + data['subcategory'] = this.subcategory; + data['name'] = this.name ?? ''; + data['merchant'] = this.merchant; + data['order'] = this.order; + data['pageNumber'] = this.pageNumber; + return data; + } +} + +class SortOrder { + int? price; + + SortOrder({this.price}); + + SortOrder.fromJson(Map json) { + price = json['_id']; + } + + Map toJson() { + final Map data = new Map(); + data['price'] = this.price; + return data; + } +} + +class AddProductParam { + List? products; + + AddProductParam({this.products}); + + AddProductParam.fromJson(Map json) { + if (json['products'] != null) { + products = []; + json['products'].forEach((v) { + products!.add(new ProductParam.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + if (this.products != null) { + data['products'] = this.products!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class ProductParam { + String? product; + int? quantity; + int? price; + String? merchant; + + ProductParam({this.product, this.quantity, this.price, this.merchant}); + + ProductParam.fromJson(Map json) { + product = json['product']; + quantity = json['quantity']; + price = json['price']; + merchant = json['merchant']; + } + + Map toJson() { + final Map data = new Map(); + data['product'] = this.product; + data['quantity'] = this.quantity; + data['price'] = this.price; + data['merchant'] = this.merchant; + return data; + } +} diff --git a/lib/data/models/review.dart b/lib/data/models/review.dart new file mode 100644 index 00000000..689181ec --- /dev/null +++ b/lib/data/models/review.dart @@ -0,0 +1,120 @@ +import 'package:intl/intl.dart'; + +class ReviewResponse { + bool? success; + List? data; + + ReviewResponse({this.success, this.data}); + + ReviewResponse.fromJson(Map json) { + success = json['success']; + if (json['data'] != null) { + data = []; + json['data'].forEach((v) { + data!.add(new Review.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + data['success'] = this.success; + if (this.data != null) { + data['data'] = this.data!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class Review { + String? sId; + String? product; + User? user; + int? rating; + String? review; + String? created; + int? iV; + + String get createdDate { + if (created == null) return ''; + final date = DateTime.tryParse(created!); + if (date == null) return ''; + final res = DateFormat("yyyy-MM-dd HH:mm:ss aa").format(date); + return res; + } + + Review( + {this.sId, + this.product, + this.user, + this.rating, + this.review, + this.created, + this.iV}); + + Review.fromJson(Map json) { + sId = json['_id']; + product = json['product']; + user = json['user'] != null ? new User.fromJson(json['user']) : null; + rating = json['rating']; + review = json['review']; + created = json['created']; + iV = json['__v']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.sId; + data['product'] = this.product; + if (this.user != null) { + data['user'] = this.user!.toJson(); + } + data['rating'] = this.rating; + data['review'] = this.review; + data['created'] = this.created; + data['__v'] = this.iV; + return data; + } +} + +class User { + String? sId; + String? firstName; + + User({this.sId, this.firstName}); + + User.fromJson(Map json) { + sId = json['_id']; + firstName = json['firstName']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.sId; + data['firstName'] = this.firstName; + return data; + } +} + +class ReviewParam { + String? product; + int? rating; + String? review; + + ReviewParam({this.product, this.rating, this.review}); + + ReviewParam.fromJson(Map json) { + product = json['product']; + rating = json['rating']; + review = json['review']; + } + + Map toJson() { + final Map data = new Map(); + data['product'] = this.product; + data['rating'] = this.rating; + data['review'] = this.review; + return data; + } +} + diff --git a/lib/data/models/user.model.dart b/lib/data/models/user.model.dart new file mode 100644 index 00000000..d3d3b3b3 --- /dev/null +++ b/lib/data/models/user.model.dart @@ -0,0 +1,21 @@ +import 'package:json_annotation/json_annotation.dart'; +part 'user.model.g.dart'; + +@JsonSerializable() +class UserModel { + final String? id; + final String? firstName; + final String? lastName; + final String? email; + final String? role; + final String? resetToken; + + String get name => (firstName ?? '') + ' ' + (lastName ?? ''); + + UserModel(this.id, this.firstName, this.lastName, this.email, this.role, this.resetToken); + + factory UserModel.fromJson(Map json) => + _$UserModelFromJson(json); + + Map toJson() => _$UserModelToJson(this); +} diff --git a/lib/data/models/user.model.g.dart b/lib/data/models/user.model.g.dart new file mode 100644 index 00000000..641500ba --- /dev/null +++ b/lib/data/models/user.model.g.dart @@ -0,0 +1,27 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'user.model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +UserModel _$UserModelFromJson(Map json) { + return UserModel( + json['id'] as String?, + json['firstName'] as String?, + json['lastName'] as String?, + json['email'] as String?, + json['role'] as String?, + json['resetPasswordToken'] as String?, + ); +} + +Map _$UserModelToJson(UserModel instance) => { + 'id': instance.id, + 'firstName': instance.firstName, + 'lastName': instance.lastName, + 'email': instance.email, + 'role': instance.role, + 'resetToken': instance.resetToken, + }; diff --git a/lib/data/provider/address.provider.dart b/lib/data/provider/address.provider.dart new file mode 100644 index 00000000..250cc698 --- /dev/null +++ b/lib/data/provider/address.provider.dart @@ -0,0 +1,32 @@ +import 'package:ecommerce_int2/data/models/address.model.dart'; +import 'package:ecommerce_int2/services/network.service.dart'; +import 'package:get_storage/get_storage.dart'; + +class AddressProvider { + final NetWorkService networkService; + + final GetStorage storage = GetStorage(); + + AddressProvider(this.networkService); + + final String getAddressUrl = + 'https://ecommerce-api-dut.herokuapp.com/api/address'; + final String addAddressUrl = + 'https://ecommerce-api-dut.herokuapp.com/api/address/add'; + + + Future getAllAddress() { + return networkService + .get(getAddressUrl); + } + + Future addAddress( + Address address) { + return networkService.post(addAddressUrl, data: address.toJson()); + } + + Future updateAddress( + Address address) { + return networkService.post('$addAddressUrl/${address.id}', data: address.toJson()); + } +} diff --git a/lib/data/provider/auth.provider.dart b/lib/data/provider/auth.provider.dart new file mode 100644 index 00000000..b793d8eb --- /dev/null +++ b/lib/data/provider/auth.provider.dart @@ -0,0 +1,52 @@ +import 'package:ecommerce_int2/utils/app_properties.dart'; +import 'package:ecommerce_int2/data/models/user.model.dart'; +import 'package:ecommerce_int2/services/network.service.dart'; +import 'package:get_storage/get_storage.dart'; + +class AuthProvider { + final NetWorkService networkService; + + final GetStorage storage = GetStorage(); + + AuthProvider(this.networkService); + + final String loginUrl = + 'https://ecommerce-api-dut.herokuapp.com/api/auth/login'; + final String registerUrl = + 'https://ecommerce-api-dut.herokuapp.com/api/auth/register'; + final String forgotUrl = + 'https://ecommerce-api-dut.herokuapp.com/api/auth/forgot'; + final String reset = 'https://ecommerce-api-dut.herokuapp.com/api/auth/reset'; + final String userInfoUrl = 'https://ecommerce-api-dut.herokuapp.com/api/user'; + + Future login(String username, String password) { + return networkService + .post(loginUrl, data: {"email": username, "password": password}); + } + + Future getUserInfo() { + return networkService.get(userInfoUrl); + } + + Future register( + String username, String password, String firstName, String lastName) { + return networkService.post(registerUrl, data: { + "email": username, + "password": password, + "firstName": firstName, + "lastName": lastName + }); + } + + Future forgotPassword(String email) { + return networkService.post(forgotUrl, data: {"email": email}); + } + + Future resetPassword(String password) { + final json = storage.read(user); + final u = UserModel.fromJson(json); + if (u.resetToken == null) throw Exception('Reset token is not available'); + return networkService + .post('$reset/${u.resetToken}', data: {"password": password}); + } +} diff --git a/lib/data/provider/cart.provider.dart b/lib/data/provider/cart.provider.dart new file mode 100644 index 00000000..c9167365 --- /dev/null +++ b/lib/data/provider/cart.provider.dart @@ -0,0 +1,50 @@ +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/services/network.service.dart'; +import 'package:get_storage/get_storage.dart'; + +class CartProvider { + final NetWorkService networkService; + + final GetStorage storage = GetStorage(); + + CartProvider(this.networkService); + + final String getAllCartUrl = + 'https://ecommerce-api-dut.herokuapp.com/api/cart'; + + final String addProductToCartUrl = + 'https://ecommerce-api-dut.herokuapp.com/api/cart/add'; + + final String modifyProductToCartUrl = + 'https://ecommerce-api-dut.herokuapp.com/api/cart/modify'; + + final String deleteProductFromCartUrl = + 'https://ecommerce-api-dut.herokuapp.com/api/cart/delete'; + + final String checkOutCartUrl = + 'https://ecommerce-api-dut.herokuapp.com/api/order/proceed'; + + Future getCarts() { + return networkService.get(getAllCartUrl); + } + + Future addProduct(AddProductParam param) { + return networkService.post(addProductToCartUrl, data: param.toJson()); + } + + Future modifyProduct( + String cartId, String productId, int before, int after) { + return networkService.put('$addProductToCartUrl/$cartId/$productId', + data: {"previousQuantity": before, "currentQuantity": after}); + } + + Future deleteProduct(String cartId, String productId) { + return networkService + .delete('$deleteProductFromCartUrl/$cartId/$productId'); + } + + Future checkoutCart(String cartId, String merchant) { + return networkService + .post(checkOutCartUrl, data: {"cart": cartId, "merchant": merchant}); + } +} diff --git a/lib/data/provider/category.provider.dart b/lib/data/provider/category.provider.dart new file mode 100644 index 00000000..7bdd9d4d --- /dev/null +++ b/lib/data/provider/category.provider.dart @@ -0,0 +1,17 @@ +import 'package:ecommerce_int2/services/network.service.dart'; +import 'package:get_storage/get_storage.dart'; + +class CategoryProvider { + final NetWorkService networkService; + + final GetStorage storage = GetStorage(); + + CategoryProvider(this.networkService); + + final String getAllCaegories = + 'https://ecommerce-api-dut.herokuapp.com/api/category'; + + Future getCategories() { + return networkService.get(getAllCaegories); + } +} diff --git a/lib/data/provider/merchant.provider.dart b/lib/data/provider/merchant.provider.dart new file mode 100644 index 00000000..3b2b426b --- /dev/null +++ b/lib/data/provider/merchant.provider.dart @@ -0,0 +1,20 @@ +import 'package:ecommerce_int2/services/network.service.dart'; +import 'package:get_storage/get_storage.dart'; + +class MerchantProvider { + final NetWorkService networkService; + + final GetStorage storage = GetStorage(); + + MerchantProvider(this.networkService); + + final String getMerchantInfoUrl = + 'https://ecommerce-api-dut.herokuapp.com/api/merchant'; + + Future getMerchantInfo( + String keyword) { + return networkService.get("$getMerchantInfoUrl/$keyword"); + } + + +} diff --git a/lib/data/provider/order.provider.dart b/lib/data/provider/order.provider.dart new file mode 100644 index 00000000..730f5897 --- /dev/null +++ b/lib/data/provider/order.provider.dart @@ -0,0 +1,56 @@ +import 'package:ecommerce_int2/data/models/order.model.dart'; +import 'package:ecommerce_int2/services/network.service.dart'; +import 'package:get_storage/get_storage.dart'; + +class OrderProvider { + final NetWorkService networkService; + + final GetStorage storage = GetStorage(); + + OrderProvider(this.networkService); + + final String orderUrl = 'https://ecommerce-api-dut.herokuapp.com/api/order'; + + final String completeOrderUrl = + 'https://ecommerce-api-dut.herokuapp.com/api/order/'; + + final String checkOutCartUrl = + 'https://ecommerce-api-dut.herokuapp.com/api/order/proceed'; + + final String cancleOrderUrl = + 'https://ecommerce-api-dut.herokuapp.com/api/order'; + + final String confirmReceivedUrl = + 'https://ecommerce-api-dut.herokuapp.com/api/order'; + + Future getAllOrders() { + return networkService.get(orderUrl); + } + + Future completeOrder(String orderId, CompleteOrderParam param) { + return networkService.put('$completeOrderUrl/$orderId/', + data: param.toJson()); + } + + Future makePayment(String orderId) { + return networkService.post('$completeOrderUrl/$orderId/pay'); + } + + Future cancleOrder(String orderId) { + return networkService.delete('$cancleOrderUrl/$orderId/cancel'); + } + + Future getOrderDetail(String orderId) { + return networkService.get('$orderUrl/$orderId'); + } + + Future confirmReceived(String orderId) { + return networkService.put('$confirmReceivedUrl/$orderId/status', + data: {"status": "RECEIVED"}); + } + + Future payOrder(String orderId) { + return networkService.put('$confirmReceivedUrl/$orderId/status', + data: {"paymentStatus": "PAID"}); + } +} diff --git a/lib/data/provider/product.provider.dart b/lib/data/provider/product.provider.dart new file mode 100644 index 00000000..e3d1b707 --- /dev/null +++ b/lib/data/provider/product.provider.dart @@ -0,0 +1,30 @@ +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/services/network.service.dart'; +import 'package:get_storage/get_storage.dart'; + +class ProductProvider { + final NetWorkService networkService; + + final GetStorage storage = GetStorage(); + + ProductProvider(this.networkService); + + final String getAllProductsUrl = + 'https://ecommerce-api-dut.herokuapp.com/api/product/list'; + + final String searchProductUrl = + 'https://ecommerce-api-dut.herokuapp.com/api/product/list/search/'; + + + Future getProducts( + ProductFilter filter) { + return networkService.post(getAllProductsUrl, data: filter.toJson()); + } + + Future searchProducts( + String keyword) { + return networkService.get("$searchProductUrl/$keyword"); + } + + +} diff --git a/lib/data/provider/review.provider.dart b/lib/data/provider/review.provider.dart new file mode 100644 index 00000000..2cf5f019 --- /dev/null +++ b/lib/data/provider/review.provider.dart @@ -0,0 +1,30 @@ +import 'package:ecommerce_int2/data/models/review.dart'; +import 'package:ecommerce_int2/services/network.service.dart'; +import 'package:get_storage/get_storage.dart'; + +class ReviewProvider { + final NetWorkService networkService; + + final GetStorage storage = GetStorage(); + + ReviewProvider(this.networkService); + + final String addReviewUrl = + 'https://ecommerce-api-dut.herokuapp.com/api/review/add'; + + final String getRevAboutProductUrl = + 'https://ecommerce-api-dut.herokuapp.com/api/review'; + + + Future addReview( + ReviewParam filter) { + return networkService.post(addReviewUrl, data: filter.toJson()); + } + + Future getReviewProduct( + String keyword) { + return networkService.get("$getRevAboutProductUrl/$keyword"); + } + + +} diff --git a/lib/data/repository/address.repository.dart b/lib/data/repository/address.repository.dart new file mode 100644 index 00000000..d4284fd2 --- /dev/null +++ b/lib/data/repository/address.repository.dart @@ -0,0 +1,35 @@ +import 'package:ecommerce_int2/data/models/address.model.dart'; +import 'package:ecommerce_int2/data/provider/address.provider.dart'; + +class AddressRepository { + final AddressProvider provider; + + AddressRepository(this.provider); + + Future> getAllAddress() async { + final response = await provider.getAllAddress(); + if (response.statusCode != 200) { + throw Exception("Get address failed"); + } + final result = (response.body['data'] as List).map((i) => + Address.fromJson(i)).toList(); + return result; + } + + + Future addAddress(Address address) async { + final response = + await provider.addAddress(address); + if (response.statusCode != 200) { + throw Exception("Add address failed"); + } + } + + Future updateAddress(Address address) async { + final response = + await provider.updateAddress(address); + if (response.statusCode != 200) { + throw Exception("Update address failed"); + } + } +} diff --git a/lib/data/repository/auth.repository.dart b/lib/data/repository/auth.repository.dart new file mode 100644 index 00000000..c6158c56 --- /dev/null +++ b/lib/data/repository/auth.repository.dart @@ -0,0 +1,52 @@ +import 'package:ecommerce_int2/data/models/login.result.dart'; +import 'package:ecommerce_int2/data/models/user.model.dart'; +import 'package:ecommerce_int2/data/provider/auth.provider.dart'; + +class AuthRepository { + final AuthProvider provider; + + AuthRepository(this.provider); + + Future login(String username, password) async { + final response = await provider.login(username, password); + if (response.statusCode != 200) { + throw Exception("Login failed"); + } + final result = LoginResult.fromJson(response.body); + return result; + } + + Future getUserInfo() async { + final response = await provider.getUserInfo(); + if (response.statusCode != 200) { + throw Exception("Login failed"); + } + final result = UserModel.fromJson(response.body['user']); + return result; + } + + Future register(String username, String password, String firstName, + String lastName) async { + final response = + await provider.register(username, password, firstName, lastName); + if (response.statusCode != 200) { + throw Exception("Register failed"); + } + } + + Future forgotPassword(String email,) async { + final response = + await provider.forgotPassword(email); + if (response.statusCode != 200) { + throw Exception("Wrong email"); + } + } + + Future resetPassword(String password) async { + final response = + await provider.forgotPassword(password); + if (response.statusCode != 200) { + throw Exception("Wrong email"); + } + } +} diff --git a/lib/data/repository/cart.repository.dart b/lib/data/repository/cart.repository.dart new file mode 100644 index 00000000..f5de1288 --- /dev/null +++ b/lib/data/repository/cart.repository.dart @@ -0,0 +1,53 @@ +import 'package:ecommerce_int2/data/models/cart.model.dart'; +import 'package:ecommerce_int2/data/models/order.model.dart'; +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/data/provider/cart.provider.dart'; + +class CartRepository { + final CartProvider provider; + + CartRepository(this.provider); + + Future> getCarts() async { + final response = await provider.getCarts(); + if (response.statusCode != 200) { + throw Exception("Get cart failed"); + } + final result = CartResponse.fromJson(response.body); + return result.data ?? []; + } + + Future addProduct(AddProductParam param) async { + final response = await provider.addProduct(param); + if (response.statusCode != 200) { + throw Exception("Add product failed"); + } + return response.body['_id'] ?? ''; + } + + Future deleteProduct(String cartId, String productID) async { + final response = await provider.deleteProduct(cartId, productID); + if (response.statusCode != 200) { + throw Exception("Delete product failed"); + } + } + + Future modifyProduct( + String cartId, String productId, int before, int after) async { + final response = + await provider.modifyProduct(cartId, productId, before, after); + if (response.statusCode != 200) { + throw Exception("Update product failed"); + } + } + + Future> checkOutCart(List carts) async { + List orderIds = []; + for(CartModel cart in carts) { + final response = await provider.checkoutCart(cart.sId!, cart.merchant!); + OrderProceed order = OrderProceed.fromJson(response.body); + orderIds.add(order); + } + return orderIds; + } +} diff --git a/lib/data/repository/category.repository.dart b/lib/data/repository/category.repository.dart new file mode 100644 index 00000000..15cdd9b0 --- /dev/null +++ b/lib/data/repository/category.repository.dart @@ -0,0 +1,18 @@ +import 'package:ecommerce_int2/data/models/category.model.dart'; +import 'package:ecommerce_int2/data/provider/category.provider.dart'; + +class CategoryRepository { + final CategoryProvider provider; + + CategoryRepository(this.provider); + + Future getAllCategories() async { + final response = await provider.getCategories(); + if (response.statusCode != 200) { + throw Exception("Get category failed"); + } + final result = CategoryList.fromJson(response.body); + return result; + } + +} diff --git a/lib/data/repository/merchant.repository.dart b/lib/data/repository/merchant.repository.dart new file mode 100644 index 00000000..4270d6a1 --- /dev/null +++ b/lib/data/repository/merchant.repository.dart @@ -0,0 +1,17 @@ +import 'package:ecommerce_int2/data/models/merchant.model.dart'; +import 'package:ecommerce_int2/data/provider/merchant.provider.dart'; + +class MerchantRepository { + final MerchantProvider provider; + + MerchantRepository(this.provider); + + Future getMerchantInfo(String merchant) async { + final response = await provider.getMerchantInfo(merchant); + if (response.statusCode != 200) { + throw Exception("Get merchant failed"); + } + final result = MerchantResponse.fromJson(response.body); + return result.data!; + } +} diff --git a/lib/data/repository/order.repository.dart b/lib/data/repository/order.repository.dart new file mode 100644 index 00000000..333c8302 --- /dev/null +++ b/lib/data/repository/order.repository.dart @@ -0,0 +1,67 @@ +import 'package:ecommerce_int2/data/models/order.model.dart'; +import 'package:ecommerce_int2/data/models/payment.model.dart'; +import 'package:ecommerce_int2/data/provider/order.provider.dart'; + +class OrderRepository { + final OrderProvider provider; + + OrderRepository(this.provider); + + Future> getAllOrders() async { + final response = await provider.getAllOrders(); + if (response.statusCode != 200) { + throw Exception("Complete order failed"); + } + final result = AllOrderResponse.fromJson(response.body); + return result.orders ?? []; + } + + Future completeOrder(String orderId, CompleteOrderParam param) async { + final response = await provider.completeOrder(orderId, param); + if (response.statusCode != 200) { + throw Exception("Complete order failed"); + } + final result = CompleteOrderResponse.fromJson(response.body); + return result.orderDoc!.sId!; + } + + Future makePayment(String orderId) async { + final response = await provider.makePayment(orderId); + if (response.statusCode != 200) { + throw Exception("Make payment failed"); + } + final result = PaymentResponse.fromJson(response.body); + return result.data!.links?.firstWhere((element) => element.rel == 'approval_url').href ?? ''; + } + + Future cancleOrder(String orderId) async { + final response = await provider.cancleOrder(orderId); + if (response.statusCode != 200) { + throw Exception("Cancel order failed"); + } + return; + } + + Future getOrderDetail(String orderId) async { + final response = await provider.getOrderDetail(orderId); + if (response.statusCode != 200) { + throw Exception("Cancel order failed"); + } + final res = DetailOrderResponse.fromJson(response.body); + return res.orderDoc!; + } + + Future confirmReceived(String orderId) async { + final response = await provider.confirmReceived(orderId); + if (response.statusCode != 200) { + throw Exception("Confirm order failed"); + } + } + + Future payOrder(String orderId) async { + final response = await provider.payOrder(orderId); + if (response.statusCode != 200) { + throw Exception("Confirm order failed"); + } + } +} diff --git a/lib/data/repository/product.repository.dart b/lib/data/repository/product.repository.dart new file mode 100644 index 00000000..46ea2a82 --- /dev/null +++ b/lib/data/repository/product.repository.dart @@ -0,0 +1,27 @@ +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/data/provider/product.provider.dart'; + +class ProductRepository { + final ProductProvider provider; + + ProductRepository(this.provider); + + Future getProducts(ProductFilter filter) async { + final response = await provider.getProducts(filter); + if (response.statusCode != 200) { + throw Exception("Get products failed"); + } + final result = ProductList.fromJson(response.body['data']); + return result; + } + + Future searchProduct(String keyword) async { + final response = await provider.searchProducts(keyword); + if (response.statusCode != 200) { + throw Exception("Get products failed"); + } + final result = ProductList.fromJson(response.body); + return result; + } + +} diff --git a/lib/data/repository/review.repository.dart b/lib/data/repository/review.repository.dart new file mode 100644 index 00000000..1ec726d4 --- /dev/null +++ b/lib/data/repository/review.repository.dart @@ -0,0 +1,25 @@ +import 'package:ecommerce_int2/data/models/review.dart'; +import 'package:ecommerce_int2/data/provider/review.provider.dart'; + +class ReviewRepository { + final ReviewProvider provider; + + ReviewRepository(this.provider); + + Future addReview(ReviewParam filter) async { + final response = await provider.addReview(filter); + if (response.statusCode != 200) { + throw Exception("Add review failed"); + } + return; + } + + Future> getReviewProduct(String keyword) async { + final response = await provider.getReviewProduct(keyword); + if (response.statusCode != 200) { + throw Exception("Get products failed"); + } + final result = ReviewResponse.fromJson(response.body); + return result.data ?? []; + } +} diff --git a/lib/interceptor/dio_interceptor.dart b/lib/interceptor/dio_interceptor.dart new file mode 100644 index 00000000..641f5a2b --- /dev/null +++ b/lib/interceptor/dio_interceptor.dart @@ -0,0 +1,34 @@ +import 'dart:io'; +import 'package:dio/dio.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; +import 'package:ecommerce_int2/services/auth.service.dart'; +import 'package:get/get.dart'; +import 'package:get_storage/get_storage.dart'; + +class DioInterceptors extends InterceptorsWrapper { + + final _store = GetStorage(); + + DioInterceptors(); + + @override + Future onRequest( + RequestOptions options, RequestInterceptorHandler handler) async { + String? token = _store.read(accessToken); + if (token != null && token.isNotEmpty) { + options.headers.addAll({ + HttpHeaders.authorizationHeader: 'Bearer $token', + }); + } + + handler.next(options); + } + + @override + void onError(DioError err, ErrorInterceptorHandler handler) { + if (err.response?.statusCode == 401) { + Get.find().logout(); + } + super.onError(err, handler); + } +} diff --git a/lib/main.dart b/lib/main.dart index 42e4cfed..1f72b393 100755 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,25 +1,41 @@ - +import 'package:ecommerce_int2/utils/dependencies.dart'; import 'package:ecommerce_int2/screens/splash_page.dart'; import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:get_storage/get_storage.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; - - -void main() => runApp(MyApp()); +void main() { + GetStorage.init(); + Dependency.init(); + runApp(MyApp()); +} class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { - return MaterialApp( - title: 'eCommerce int2', - debugShowCheckedModeBanner: false, - theme: ThemeData( - brightness: Brightness.light, - canvasColor: Colors.transparent, - primarySwatch: Colors.blue, - fontFamily: "Montserrat", + return RefreshConfiguration( + headerBuilder: () => WaterDropHeader(), // Configure the default header indicator. If you have the same header indicator for each page, you need to set this + footerBuilder: () => ClassicFooter(), // Configure default bottom indicator + headerTriggerDistance: 80.0, // header trigger refresh trigger distance + maxOverScrollExtent :100, //The maximum dragging range of the head. Set this property if a rush out of the view area occurs + maxUnderScrollExtent:100, // Maximum dragging range at the bottom + enableScrollWhenRefreshCompleted: true, //This property is incompatible with PageView and TabBarView. If you need TabBarView to slide left and right, you need to set it to true. + enableLoadingWhenFailed : true, //In the case of load failure, users can still trigger more loads by gesture pull-up. + hideFooterWhenNotFull: false, // Disable pull-up to load more functionality when Viewport is less than one screen + enableBallisticLoad: true, + child: GetMaterialApp( + title: 'E-commerce', + debugShowCheckedModeBanner: false, + theme: ThemeData( + brightness: Brightness.light, + canvasColor: Colors.transparent, + primarySwatch: Colors.blue, + fontFamily: "Montserrat", + ), + home: SplashScreen(), ), - home: SplashScreen(), ); } } diff --git a/lib/models/product.dart b/lib/models/product.dart index 4fa8c7df..30f05406 100755 --- a/lib/models/product.dart +++ b/lib/models/product.dart @@ -1,8 +1,8 @@ -class Product{ - String image; - String name; - String description; - double price; +// class Product{ +// String image; +// String name; +// String description; +// double price; - Product(this.image, this.name, this.description, this.price); -} \ No newline at end of file +// Product(this.image, this.name, this.description, this.price); +// } \ No newline at end of file diff --git a/lib/screens/address/add_address_page.dart b/lib/screens/address/add_address_page.dart index bd8760c0..e1b8be3f 100755 --- a/lib/screens/address/add_address_page.dart +++ b/lib/screens/address/add_address_page.dart @@ -1,167 +1,162 @@ -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:card_swiper/card_swiper.dart'; +import 'package:ecommerce_int2/data/repository/order.repository.dart'; +import 'package:ecommerce_int2/screens/shop/components/credit_card.dart'; +import 'package:ecommerce_int2/services/auth.service.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; +import 'package:ecommerce_int2/data/repository/address.repository.dart'; import 'package:ecommerce_int2/screens/address/address_form.dart'; -import 'package:ecommerce_int2/screens/select_card_page.dart'; +import 'package:ecommerce_int2/screens/address/address.controller.dart'; import 'package:flutter/material.dart'; +import 'package:get/get.dart'; class AddAddressPage extends StatelessWidget { - @override - Widget build(BuildContext context) { - Widget finishButton = InkWell( - onTap:()=> Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => SelectCardPage())), - child: Container( - height: 80, - width: MediaQuery.of(context).size.width / 1.5, - decoration: BoxDecoration( - gradient: mainButton, - boxShadow: [ - BoxShadow( - color: Color.fromRGBO(0, 0, 0, 0.16), - offset: Offset(0, 5), - blurRadius: 10.0, - ) - ], - borderRadius: BorderRadius.circular(9.0)), - child: Center( - child: Text("Finish", - style: const TextStyle( - color: const Color(0xfffefefe), - fontWeight: FontWeight.w600, - fontStyle: FontStyle.normal, - fontSize: 20.0)), + + + Widget buildFinishButton(AddressController controller) => InkWell( + onTap: () { + controller.updateCurrentAddress(); + controller.submitAddress(); + }, + child: Container( + height: 80, + width: Get.width / 1.5, + decoration: BoxDecoration( + gradient: mainButton, + boxShadow: [ + BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.16), + offset: Offset(0, 5), + blurRadius: 10.0, + ) + ], + borderRadius: BorderRadius.circular(9.0)), + child: Center( + child: Text("Finish", + style: const TextStyle( + color: const Color(0xfffefefe), + fontWeight: FontWeight.w600, + fontStyle: FontStyle.normal, + fontSize: 20.0)), + ), ), - ), - ); + ); - return Scaffold( - backgroundColor: Colors.grey[100], - appBar: AppBar( - backgroundColor: Colors.transparent, - elevation: 0.0, - iconTheme: IconThemeData(color: darkGrey), - title: Text( - 'Add Address', - style: const TextStyle( - color: darkGrey, - fontWeight: FontWeight.w500, - fontFamily: "Montserrat", - fontSize: 18.0), + @override + Widget build(BuildContext context) { + return GetBuilder( + init: AddressController( + Get.find(), Get.find(), Get.find()), + builder: (controller) => Scaffold( + backgroundColor: Colors.grey[100], + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0.0, + iconTheme: IconThemeData(color: darkGrey), + title: Text( + 'Add Address', + style: const TextStyle( + color: darkGrey, + fontWeight: FontWeight.w500, + fontFamily: "Montserrat", + fontSize: 18.0), + ), ), - ), - body: LayoutBuilder( - builder: (_, viewportConstraints) => SingleChildScrollView( - child: ConstrainedBox( - constraints: - BoxConstraints(minHeight: viewportConstraints.maxHeight), - child: Container( - padding: EdgeInsets.only( - left: 16.0, - right: 16.0, - bottom: MediaQuery.of(context).padding.bottom == 0 - ? 20 - : MediaQuery.of(context).padding.bottom), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceAround, - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Card( - margin: EdgeInsets.symmetric(vertical: 8.0), - color: Colors.white, - elevation: 3, - child: SizedBox( - height: 100, - width: 80, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.all(4.0), - child: Image.asset( - 'assets/icons/address_home.png'), - ), - Text( - 'Add New Address', - style: TextStyle( - fontSize: 8, - color: darkGrey, - ), - textAlign: TextAlign.center, - ) - ], - ), - ))), - Card( - margin: EdgeInsets.symmetric(vertical: 8.0), - color: yellow, - elevation: 3, - child: SizedBox( - height: 80, - width: 100, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.all(4.0), - child: Image.asset( - 'assets/icons/address_home.png', - color: Colors.white, - height: 20, - ), - ), - Text( - 'Simon Philip,\nCity Oscarlad', - style: TextStyle( - fontSize: 8, - color: Colors.white, - ), - textAlign: TextAlign.center, - ) - ], - ), - ))), - Card( - margin: EdgeInsets.symmetric(vertical: 8.0), - color: yellow, - elevation: 3, - child: SizedBox( - height: 80, - width: 100, + body: LayoutBuilder( + builder: (_, viewportConstraints) => SingleChildScrollView( + child: ConstrainedBox( + constraints: + BoxConstraints(minHeight: viewportConstraints.maxHeight), + child: Container( + padding: EdgeInsets.only( + left: 16.0, + right: 16.0, + bottom: MediaQuery.of(context).padding.bottom == 0 + ? 20 + : MediaQuery.of(context).padding.bottom), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + height: 100, + child: ListView.builder( + scrollDirection: Axis.horizontal, + shrinkWrap: false, + itemCount: controller.addresses.length, + itemBuilder: (BuildContext context, int index) { + final e = controller.addresses[index]; + return GestureDetector( + onTap: () => controller.updateIndex(index), child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.all(4.0), - child: Image.asset( - 'assets/icons/address_work.png', - color: Colors.white, - height: 20), - ), - Text( - 'Workplace', - style: TextStyle( - fontSize: 8, - color: Colors.white, - ), - textAlign: TextAlign.center, - ) - ], - ), - ))) - ], - ), - AddAddressForm(), - Center(child: finishButton) - ], + padding: const EdgeInsets.only(right: 8.0), + child: Card( + margin: EdgeInsets.symmetric(vertical: 8.0), + color: index == controller.selectIndex + ? yellow + : Colors.white, + elevation: 3, + child: SizedBox( + height: 100, + width: 80, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Padding( + padding: + const EdgeInsets.all(4.0), + child: Image.asset( + 'assets/icons/address_home.png'), + ), + Text( + e.address ?? 'Add New Address', + style: TextStyle( + fontSize: 8, + color: darkGrey, + ), + textAlign: TextAlign.center, + ) + ], + ), + ))), + ), + ); + }), + ), + const SizedBox(height: 16), + AddAddressForm( + address: controller.selectedAddress, + addr: controller.address, + phone: controller.phone, + city: controller.city), + const SizedBox(height: 16), + if(controller.orderIds != null) + Padding( + padding: const EdgeInsets.all(16.0), + child: SizedBox( + height: 50, + child: Swiper( + itemCount: 2, + itemBuilder: (_, index) { + return CreditCard( + text: index == 0 ? 'CASH' : 'PAYPAL', + ); + }, + scale: 0.8, + controller: controller.swiperController, + onIndexChanged: (value) { + controller.swiperIndex = value; + }, + viewportFraction: 0.6, + loop: false, + fade: 0.7, + ), + ), + ), + Center(child: buildFinishButton(controller)) + ], + ), ), ), ), diff --git a/lib/screens/address/address.controller.dart b/lib/screens/address/address.controller.dart new file mode 100644 index 00000000..de980396 --- /dev/null +++ b/lib/screens/address/address.controller.dart @@ -0,0 +1,121 @@ +import 'package:card_swiper/card_swiper.dart'; +import 'package:ecommerce_int2/data/models/address.model.dart'; +import 'package:ecommerce_int2/data/models/order.model.dart'; +import 'package:ecommerce_int2/data/repository/address.repository.dart'; +import 'package:ecommerce_int2/data/repository/order.repository.dart'; +import 'package:ecommerce_int2/screens/payment/payment_page.dart'; +import 'package:ecommerce_int2/screens/tracking/tracking_page.dart'; +import 'package:ecommerce_int2/services/auth.service.dart'; +import 'package:ecommerce_int2/utils/message_dialog.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class AddressController extends GetxController { + final AddressRepository repository; + final OrderRepository orderRepository; + final AuthService authService; + + final TextEditingController address = TextEditingController(); + final TextEditingController city = TextEditingController(); + final TextEditingController phone = TextEditingController(); + + final SwiperController swiperController = SwiperController(); + + AddressController(this.repository, this.orderRepository, this.authService); + + List? orderIds; + + List
addresses =
[Address()]; + + int selectIndex = 0; + + int swiperIndex = 0; + + Address get selectedAddress => addresses[selectIndex]; + + @override + void onInit() { + getArgument(); + super.onInit(); + } + + @override + void onReady() { + getAllAddress(); + + super.onReady(); + } + + void getArgument() { + final arg = Get.arguments; + if (arg != null && arg is List) { + orderIds = arg; + } + } + + void getAllAddress() async { + MessageDialog.showLoading(); + final List
res = await repository.getAllAddress(); + addresses.assignAll(res); + addresses.insert(0, Address()); + int index = addresses.indexWhere((element) => element.isDefault!); + if (index != -1) selectIndex = index; + address.text = selectedAddress.address ?? ""; + city.text = selectedAddress.city ?? ""; + phone.text = authService.userModel!.email ?? ""; + MessageDialog.hideLoading(); + update(); + } + + void updateIndex(int index) { + selectIndex = index; + address.text = selectedAddress.address ?? ""; + city.text = selectedAddress.city ?? ""; + update(); + } + + void updateCurrentAddress() { + if (address.text.isNotEmpty) selectedAddress.address = address.text; + if (city.text.isNotEmpty) selectedAddress.city = city.text; + } + + void submitAddress() { + if (selectedAddress.id == null) { + repository + .addAddress(selectedAddress) + .then((value) => null, onError: (e) => null); + } else { + repository + .updateAddress(selectedAddress) + .then((value) => null, onError: (e) => null); + } + checkOut(); + } + + void checkOut() async { + if (orderIds == null) { + Get.back(); + return; + } + if (swiperIndex == 1) { + final done = await Get.to(() => PaymentPage(), arguments: orderIds); + if (!done) return; + } + try { + MessageDialog.showLoading(); + for (OrderProceed order in orderIds!) { + await orderRepository.completeOrder( + order.data!.order!.sId!, + CompleteOrderParam( + address: '${selectedAddress.address}, ${selectedAddress.city}', + phoneNumber: authService.userModel!.email, + payment: swiperIndex == 0 ? 'CASH' : 'PAYPAL')); + } + MessageDialog.hideLoading(); + Get.offAll(() => TrackingPage(), arguments: true); + } on Exception catch (e) { + MessageDialog.hideLoading(); + print(e); + } + } +} diff --git a/lib/screens/address/address_form.dart b/lib/screens/address/address_form.dart index 0268760d..c980c5bb 100755 --- a/lib/screens/address/address_form.dart +++ b/lib/screens/address/address_form.dart @@ -1,105 +1,75 @@ -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:ecommerce_int2/data/models/address.model.dart'; import 'package:flutter/material.dart'; class AddAddressForm extends StatelessWidget { + final Address address; + + final TextEditingController addr; + final TextEditingController city; + final TextEditingController phone; + + const AddAddressForm( + {Key? key, + required this.address, + required this.addr, + required this.phone, + required this.city}) + : super(key: key); @override Widget build(BuildContext context) { - return SizedBox( - height: 500, + return Flexible( child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.start, children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text("Address"), + ), Container( - padding: EdgeInsets.only(left: 16.0, top: 4.0, bottom: 4.0), + padding: EdgeInsets.only(left: 16.0, top: 8.0, bottom: 4.0), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(5)), color: Colors.white, ), child: TextField( + controller: addr, decoration: InputDecoration( - border: InputBorder.none, hintText: 'Flat Number/House Number'), + border: InputBorder.none, hintText: address.address), ), ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text("City"), + ), Container( - padding: EdgeInsets.only(left: 16.0, top: 4.0, bottom: 4.0), + padding: EdgeInsets.only(left: 16.0, top: 8.0, bottom: 4.0), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(5)), color: Colors.white, ), child: TextField( - decoration: - InputDecoration(border: InputBorder.none, hintText: 'Street'), + controller: city, + decoration: InputDecoration( + border: InputBorder.none, hintText: address.city), ), ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.all(16.0), - child: Text( - 'Area', - style: TextStyle(fontSize: 12, color: darkGrey), - ), - ), - ClipRRect( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(5), topRight: Radius.circular(5)), - child: Container( - padding: EdgeInsets.only(left: 16.0, top: 4.0, bottom: 4.0), - decoration: BoxDecoration( - border: Border( - bottom: BorderSide(color: Colors.orange, width: 2)), - color: Colors.orange[100], - ), - child: TextField( - style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), - decoration: InputDecoration( - border: InputBorder.none, - hintText: 'Name on card', - hintStyle: - TextStyle(fontSize: 24, fontWeight: FontWeight.bold), - ), - ), - ), - ), - ], + Padding( + padding: const EdgeInsets.all(8.0), + child: Text("Phone number"), ), Container( - padding: EdgeInsets.only(left: 16.0, top: 4.0, bottom: 4.0), + padding: EdgeInsets.only(left: 16.0, top: 8.0, bottom: 4.0), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(5)), color: Colors.white, ), child: TextField( + controller: phone, decoration: InputDecoration( - border: InputBorder.none, hintText: 'Name on card'), - ), - ), - ClipRRect( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(5), topRight: Radius.circular(5)), - child: Container( - padding: EdgeInsets.only(left: 16.0, top: 4.0, bottom: 4.0), - decoration: BoxDecoration( - border: Border(bottom: BorderSide(color: Colors.red, width: 1)), - color: Colors.white, - ), - child: TextField( - decoration: InputDecoration( - border: InputBorder.none, hintText: 'Postal code'), - ), + border: InputBorder.none, hintText: address.city), ), ), - Row( - children: [ - Checkbox( - value: true, - onChanged: (_) {}, - ), - Text('Add this to address bookmark') - ], - ) ], ), ); diff --git a/lib/screens/auth/confirm_otp_page.dart b/lib/screens/auth/confirm_otp_page.dart deleted file mode 100755 index ded6a0bc..00000000 --- a/lib/screens/auth/confirm_otp_page.dart +++ /dev/null @@ -1,215 +0,0 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/screens/intro_page.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:pin_code_text_field/pin_code_text_field.dart'; - -class ConfirmOtpPage extends StatefulWidget { - @override - _ConfirmOtpPageState createState() => _ConfirmOtpPageState(); -} - -class _ConfirmOtpPageState extends State { - TextEditingController otp1 = TextEditingController(text: '1'); - TextEditingController otp2 = TextEditingController(text: '2'); - TextEditingController otp3 = TextEditingController(text: '3'); - TextEditingController otp4 = TextEditingController(text: '4'); - TextEditingController otp5 = TextEditingController(text: '5'); - - Widget otpBox(TextEditingController otpController) { - return Container( - height: 48, - width: 48, - decoration: BoxDecoration( - color: Color.fromRGBO(255, 255, 255, 0.8), - borderRadius: BorderRadius.all(Radius.circular(10))), - child: Center( - child: SizedBox( - width: 9, - child: Padding( - padding: const EdgeInsets.only(top: 8.0), - child: TextField( - controller: otpController, - decoration: InputDecoration( - border: InputBorder.none, contentPadding: EdgeInsets.zero), - style: TextStyle(fontSize: 16.0), - keyboardType: TextInputType.phone, - ), - ), - ), - ), - ); - } - - @override - Widget build(BuildContext context) { - Widget title = Text( - 'Confirm your OTP', - style: TextStyle( - color: Colors.white, - fontSize: 34.0, - fontWeight: FontWeight.bold, - shadows: [ - BoxShadow( - color: Color.fromRGBO(0, 0, 0, 0.15), - offset: Offset(0, 5), - blurRadius: 10.0, - ) - ]), - ); - - Widget subTitle = Padding( - padding: const EdgeInsets.only(right: 56.0), - child: Text( - 'Please wait, we are confirming your OTP', - style: TextStyle( - color: Colors.white, - fontSize: 16.0, - ), - )); - - Widget verifyButton = Center( - child: InkWell( - onTap: () { - Navigator.of(context) - .push(MaterialPageRoute(builder: (_) => IntroPage())); - }, - child: Container( - width: MediaQuery.of(context).size.width / 2, - height: 80, - child: Center( - child: new Text("Verify", - style: const TextStyle( - color: const Color(0xfffefefe), - fontWeight: FontWeight.w600, - fontStyle: FontStyle.normal, - fontSize: 20.0))), - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - Color.fromRGBO(236, 60, 3, 1), - Color.fromRGBO(234, 60, 3, 1), - Color.fromRGBO(216, 78, 16, 1), - ], - begin: FractionalOffset.topCenter, - end: FractionalOffset.bottomCenter), - boxShadow: [ - BoxShadow( - color: Color.fromRGBO(0, 0, 0, 0.16), - offset: Offset(0, 5), - blurRadius: 10.0, - ) - ], - borderRadius: BorderRadius.circular(9.0)), - ), - ), - ); - - Widget otpCode = Container( - padding: const EdgeInsets.only(right: 28.0), - height: 190, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - otpBox(otp1), - otpBox(otp2), - otpBox(otp3), - otpBox(otp4), - otpBox(otp5) - ], - ), - ); - - Widget resendText = Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - "Resend again after ", - style: TextStyle( - fontStyle: FontStyle.italic, - color: Color.fromRGBO(255, 255, 255, 0.5), - fontSize: 14.0, - ), - ), - InkWell( - onTap: () {}, - child: Text( - '0:39', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 14.0, - ), - ), - ), - ], - ); - - return GestureDetector( - onTap: () => FocusScope.of(context).requestFocus(new FocusNode()), - child: Container( - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage('assets/background.jpg'), fit: BoxFit.cover)), - child: Container( - decoration: BoxDecoration(color: transparentYellow), - child: Scaffold( - appBar: AppBar( - backgroundColor: Colors.transparent, - elevation: 0.0, - ), - body: Stack( - children: [ - Padding( - padding: const EdgeInsets.only(left: 28.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Spacer(flex: 3), - title, - Spacer(), - subTitle, - Spacer(flex: 1), - Padding( - padding: const EdgeInsets.only(right: 28.0), - child: Center( - child: PinCodeTextField( - controller: new TextEditingController(), - highlightColor: Colors.white, - highlightAnimation: true, - highlightAnimationBeginColor: Colors.white, - highlightAnimationEndColor: Theme.of(context).primaryColor, - pinTextAnimatedSwitcherDuration: Duration(milliseconds: 500), - wrapAlignment: WrapAlignment.center, - hasTextBorderColor: Colors.transparent, - highlightPinBoxColor: Colors.white, - autofocus: true, - pinBoxHeight: 60, - pinBoxWidth: 60, - pinBoxRadius: 5, - defaultBorderColor: Colors.transparent, - pinBoxColor: Color.fromRGBO(255, 255, 255, 0.8), - maxLength: 4, - ), - ), - ), - Spacer(flex: 1), -// otpCode, - Padding( - padding: const EdgeInsets.only(right: 28.0), - child: verifyButton, - ), - Spacer(flex: 2), - resendText, - Spacer() - ], - ), - ) - ], - ), - ), - ), - ), - ); - } -} diff --git a/lib/screens/auth/forgot_password_page.dart b/lib/screens/auth/forgot_password_page.dart index da7f6a47..adef55b9 100755 --- a/lib/screens/auth/forgot_password_page.dart +++ b/lib/screens/auth/forgot_password_page.dart @@ -1,11 +1,10 @@ - -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; +import 'package:ecommerce_int2/services/auth.service.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:country_code_picker/country_code_picker.dart'; +import 'package:get/get.dart'; -import 'confirm_otp_page.dart'; class ForgotPasswordPage extends StatefulWidget { @override @@ -13,7 +12,7 @@ class ForgotPasswordPage extends StatefulWidget { } class _ForgotPasswordPageState extends State { - TextEditingController phoneNumber = TextEditingController(text: '46834683'); + TextEditingController email = TextEditingController(text: 'Your email'); GlobalKey prefixKey = GlobalKey(); double prefixWidth = 0; @@ -35,14 +34,7 @@ class _ForgotPasswordPageState extends State { @override Widget build(BuildContext context) { - Widget background = Container( - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage('assets/background.jpg'), fit: BoxFit.cover), - ), - foregroundDecoration: BoxDecoration(color: transparentYellow), - ); - + final controller = Get.find(); Widget title = Text( 'Forgot your Password?', style: TextStyle( @@ -61,7 +53,7 @@ class _ForgotPasswordPageState extends State { Widget subTitle = Padding( padding: const EdgeInsets.only(right: 56.0), child: Text( - 'Enter your registered mobile number to get the OTP', + 'Enter your registered email', style: TextStyle( color: Colors.white, fontSize: 16.0, @@ -73,13 +65,13 @@ class _ForgotPasswordPageState extends State { bottom: 40, child: InkWell( onTap: () { - Navigator.of(context).push(MaterialPageRoute(builder:(_)=>ConfirmOtpPage())); + controller.forgetPassword(email.text.trim()); }, child: Container( width: MediaQuery.of(context).size.width / 2, height: 80, child: Center( - child: new Text("Send OTP", + child: new Text("Send Email", style: const TextStyle( color: const Color(0xfffefefe), fontWeight: FontWeight.w600, @@ -122,12 +114,11 @@ class _ForgotPasswordPageState extends State { child: Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - prefix(), Flexible( child: Padding( padding: const EdgeInsets.only(top: 8.0), child: TextField( - controller: phoneNumber, + controller: email, style: TextStyle(fontSize: 16.0), keyboardType: TextInputType.phone, ), diff --git a/lib/screens/auth/register_page.dart b/lib/screens/auth/register_page.dart index 8eb312fa..e2de9dc2 100755 --- a/lib/screens/auth/register_page.dart +++ b/lib/screens/auth/register_page.dart @@ -1,8 +1,7 @@ - -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; +import 'package:ecommerce_int2/services/auth.service.dart'; import 'package:flutter/material.dart'; - -import 'forgot_password_page.dart'; +import 'package:get/get.dart'; class RegisterPage extends StatefulWidget { @override @@ -15,11 +14,13 @@ class _RegisterPageState extends State { TextEditingController password = TextEditingController(text: '12345678'); - TextEditingController cmfPassword = TextEditingController(text: '12345678'); + TextEditingController firstName = TextEditingController(text: 'First name'); + TextEditingController lastName = TextEditingController(text: 'Last name'); @override Widget build(BuildContext context) { + final controller = Get.find(); Widget title = Text( 'Glad To Meet You', style: TextStyle( @@ -50,8 +51,7 @@ class _RegisterPageState extends State { bottom: 40, child: InkWell( onTap: () { - Navigator.of(context) - .push(MaterialPageRoute(builder: (_) => ForgotPasswordPage())); + controller.register(email.text, password.text, "first name", "last Name"); }, child: Container( width: MediaQuery.of(context).size.width / 2, @@ -78,7 +78,7 @@ class _RegisterPageState extends State { ); Widget registerForm = Container( - height: 300, + height: 350, child: Stack( children: [ Container( @@ -103,7 +103,7 @@ class _RegisterPageState extends State { Padding( padding: const EdgeInsets.only(top: 8.0), child: TextField( - controller: password, + controller: firstName, style: TextStyle(fontSize: 16.0), obscureText: true, ), @@ -111,7 +111,15 @@ class _RegisterPageState extends State { Padding( padding: const EdgeInsets.only(top: 8.0), child: TextField( - controller: cmfPassword, + controller: lastName, + style: TextStyle(fontSize: 16.0), + obscureText: true, + ), + ), + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: TextField( + controller: password, style: TextStyle(fontSize: 16.0), obscureText: true, ), diff --git a/lib/screens/auth/welcome_back_page.dart b/lib/screens/auth/welcome_back_page.dart index 44ff3166..9a2839be 100755 --- a/lib/screens/auth/welcome_back_page.dart +++ b/lib/screens/auth/welcome_back_page.dart @@ -1,24 +1,26 @@ - -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; +import 'package:ecommerce_int2/screens/auth/forgot_password_page.dart'; +import 'package:ecommerce_int2/services/auth.service.dart'; import 'package:flutter/material.dart'; +import 'package:get/get.dart'; import 'register_page.dart'; -class WelcomeBackPage extends StatefulWidget { +class WelcomeBackPage extends StatefulWidget { @override _WelcomeBackPageState createState() => _WelcomeBackPageState(); } class _WelcomeBackPageState extends State { - TextEditingController email = - TextEditingController(text: 'example@email.com'); + TextEditingController email = TextEditingController(); - TextEditingController password = TextEditingController(text: '12345678'); + TextEditingController password = TextEditingController(); @override Widget build(BuildContext context) { + final controller = Get.find(); Widget welcomeBack = Text( - 'Welcome Back Roberto,', + 'Welcome Back', style: TextStyle( color: Colors.white, fontSize: 34.0, @@ -47,8 +49,7 @@ class _WelcomeBackPageState extends State { bottom: 40, child: InkWell( onTap: () { - Navigator.of(context) - .push(MaterialPageRoute(builder: (_) => RegisterPage())); + controller.login(email.text.trim(), password.text.trim()); }, child: Container( width: MediaQuery.of(context).size.width / 2, @@ -134,7 +135,9 @@ class _WelcomeBackPageState extends State { ), ), InkWell( - onTap: () {}, + onTap: () { + Get.to(() => ForgotPasswordPage()); + }, child: Text( 'Reset password', style: TextStyle( @@ -148,21 +151,36 @@ class _WelcomeBackPageState extends State { ), ); - return Scaffold( + + Widget register = Padding( + padding: const EdgeInsets.only(bottom: 20), + child: InkWell( + onTap: () { + Get.to(() => RegisterPage()); + }, + child: Text( + 'Register new account', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 14.0, + ), + ), + ), + ); + return Scaffold( body: Stack( children: [ - Container( decoration: BoxDecoration( - image: DecorationImage(image: AssetImage('assets/background.jpg'), - fit: BoxFit.cover) - ), + image: DecorationImage( + image: AssetImage('assets/background.jpg'), + fit: BoxFit.cover)), ), Container( decoration: BoxDecoration( - color: transparentYellow, - + color: transparentYellow, ), ), Padding( @@ -177,7 +195,9 @@ class _WelcomeBackPageState extends State { Spacer(flex: 2), loginForm, Spacer(flex: 2), - forgotPassword + register, + Spacer(flex: 1), + forgotPassword, ], ), ) diff --git a/lib/screens/category/category.controller.dart b/lib/screens/category/category.controller.dart new file mode 100644 index 00000000..2403d34b --- /dev/null +++ b/lib/screens/category/category.controller.dart @@ -0,0 +1,61 @@ +import 'package:ecommerce_int2/data/models/category.model.dart'; +import 'package:ecommerce_int2/data/repository/category.repository.dart'; +import 'package:ecommerce_int2/utils/message_dialog.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class CategoryController extends GetxController { + final CategoryRepository repository; + + CategoryController(this.repository); + + CategoryList categoryList = CategoryList(); + + TextEditingController searchController = TextEditingController(); + + List get list => categoryList.data ?? []; + + List tempList = []; + + @override + void onInit() { + // getCategoryList(); + super.onInit(); + } + + @override + void onReady() { + getCategoryList(); + super.onReady(); + } + + void getCategoryList() async { + MessageDialog.showLoading(); + try { + final value = await repository.getAllCategories(); + categoryList = value; + final temp = CategoryList.fromJson(value.toJson()); + tempList = temp.data!; + update(); + } on Exception catch (e) { + print(e); + } finally { + MessageDialog.hideLoading(); + } + } + + void onChanged(String value) { + if (value.isNotEmpty) { + tempList.clear(); + list.forEach((category) { + if (category.name!.toLowerCase().contains(value)) { + tempList.add(category); + } + }); + } else { + tempList.clear(); + tempList.addAll(list); + } + update(); + } +} diff --git a/lib/screens/category/category_list_page.dart b/lib/screens/category/category_list_page.dart index 140698a6..a15bacbf 100755 --- a/lib/screens/category/category_list_page.dart +++ b/lib/screens/category/category_list_page.dart @@ -1,142 +1,80 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/models/category.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; +import 'package:ecommerce_int2/data/repository/category.repository.dart'; +import 'package:ecommerce_int2/screens/category/category.controller.dart'; +import 'package:ecommerce_int2/screens/category/sub_category_list_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get/get.dart'; import 'components/staggered_category_card.dart'; -class CategoryListPage extends StatefulWidget { - @override - _CategoryListPageState createState() => _CategoryListPageState(); -} - -class _CategoryListPageState extends State { - List categories = [ - Category( - Color(0xffFCE183), - Color(0xffF68D7F), - 'Gadgets', - 'assets/jeans_5.png', - ), - Category( - Color(0xffF749A2), - Color(0xffFF7375), - 'Clothes', - 'assets/jeans_5.png', - ), - Category( - Color(0xff00E9DA), - Color(0xff5189EA), - 'Fashion', - 'assets/jeans_5.png', - ), - Category( - Color(0xffAF2D68), - Color(0xff632376), - 'Home', - 'assets/jeans_5.png', - ), - Category( - Color(0xff36E892), - Color(0xff33B2B9), - 'Beauty', - 'assets/jeans_5.png', - ), - Category( - Color(0xffF123C4), - Color(0xff668CEA), - 'Appliances', - 'assets/jeans_5.png', - ), - ]; - - List searchResults = []; - TextEditingController searchController = TextEditingController(); - - @override - void initState() { - super.initState(); - searchResults = categories; - } - +class CategoryListPage extends StatelessWidget { @override Widget build(BuildContext context) { return Material( color: Color(0xffF9F9F9), child: Container( - margin: const EdgeInsets.only(top: kToolbarHeight), padding: EdgeInsets.symmetric(horizontal: 16.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Align( - alignment: Alignment(-1, 0), - child: Padding( - padding: EdgeInsets.symmetric(vertical: 16.0), - child: Text( - 'Category List', - style: TextStyle( - color: darkGrey, - fontSize: 22, - fontWeight: FontWeight.bold, + child: GetBuilder( + init: CategoryController(Get.find()), + builder: (controller) => SafeArea( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Align( + alignment: Alignment(-1, 0), + child: Padding( + padding: EdgeInsets.symmetric(vertical: 16.0), + child: Text( + 'Category List', + style: TextStyle( + color: darkGrey, + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), ), ), - ), - ), - Container( - padding: EdgeInsets.only(left: 16.0), - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(5)), - color: Colors.white, - ), - child: TextField( - controller: searchController, - decoration: InputDecoration( - border: InputBorder.none, - hintText: 'Search', - prefixIcon: SvgPicture.asset( - 'assets/icons/search_icon.svg', - fit: BoxFit.scaleDown, - )), - onChanged: (value) { - if (value.isNotEmpty) { - List tempList = []; - categories.forEach((category) { - if (category.category.toLowerCase().contains(value)) { - tempList.add(category); - } - }); - setState(() { - searchResults.clear(); - searchResults.addAll(tempList); - }); - return; - } else { - setState(() { - searchResults.clear(); - searchResults.addAll(categories); - }); - } - }, - ), - ), - Flexible( - child: ListView.builder( - itemCount: searchResults.length, - itemBuilder: (_, index) => Padding( - padding: EdgeInsets.symmetric( - vertical: 16.0, + Container( + padding: EdgeInsets.only(left: 16.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(5)), + color: Colors.white, ), - child: StaggeredCardCard( - begin: searchResults[index].begin, - end: searchResults[index].end, - categoryName: searchResults[index].category, - assetPath: searchResults[index].image, + child: TextField( + controller: controller.searchController, + decoration: InputDecoration( + border: InputBorder.none, + hintText: 'Search', + prefixIcon: SvgPicture.asset( + 'assets/icons/search_icon.svg', + fit: BoxFit.scaleDown, + )), + onChanged: (value) => controller.onChanged(value), ), ), - ), - ) - ], + Flexible( + child: ListView.builder( + itemCount: controller.tempList.length, + itemBuilder: (_, index) => Padding( + padding: EdgeInsets.symmetric( + vertical: 16.0, + ), + child: StaggeredCardCard( + begin: const Color(0xffFCE183), + end: const Color(0xffF68D7F), + categoryName: + controller.tempList[index].name ?? "", + assetPath: 'assets/jeans_5.png', + onViewMore: () => Get.to(() => SubCategoryListPage( + category: controller.tempList[index])), + ), + ), + ), + ) + ], + ), + ), ), ), ); diff --git a/lib/screens/category/components/staggered_category_card.dart b/lib/screens/category/components/staggered_category_card.dart index 6deb245f..0515060f 100755 --- a/lib/screens/category/components/staggered_category_card.dart +++ b/lib/screens/category/components/staggered_category_card.dart @@ -5,6 +5,7 @@ class CategoryCard extends StatelessWidget { final Color end; final String categoryName; final String assetPath; + final Function onViewMore; CategoryCard({ required this.controller, @@ -12,6 +13,7 @@ class CategoryCard extends StatelessWidget { required this.end, required this.categoryName, required this.assetPath, + required this.onViewMore, }) : height = Tween(begin: 150, end: 250.0).animate( CurvedAnimation( parent: controller, @@ -56,12 +58,15 @@ class CategoryCard extends StatelessWidget { children: [ Align( alignment: Alignment(-1, 0), - child: Text( - categoryName, - style: TextStyle( - fontSize: 22, - color: Colors.white, - fontWeight: FontWeight.bold), + child: Container( + width: 150, + child: Text( + categoryName, + style: TextStyle( + fontSize: 22, + color: Colors.white, + fontWeight: FontWeight.bold), + ), )), Column( mainAxisAlignment: MainAxisAlignment.center, @@ -75,15 +80,18 @@ class CategoryCard extends StatelessWidget { assetPath, ), ), - Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.all(Radius.circular(24))), - padding: - const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), - child: Text( - 'View more', - style: TextStyle(color: end, fontWeight: FontWeight.bold), + GestureDetector( + onTap: () => onViewMore(), + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(24))), + padding: + const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + child: Text( + 'View more', + style: TextStyle(color: end, fontWeight: FontWeight.bold), + ), ), ) ], @@ -107,12 +115,14 @@ class StaggeredCardCard extends StatefulWidget { final Color end; final String categoryName; final String assetPath; + final Function onViewMore; const StaggeredCardCard({ required this.begin, required this.end, required this.categoryName, required this.assetPath, + required this.onViewMore, }); @override @@ -150,7 +160,6 @@ class _StaggeredCardCardState extends State @override Widget build(BuildContext context) { - var timeDilation = 10.0; // 1.0 is normal animation speed. return GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { @@ -168,6 +177,7 @@ class _StaggeredCardCardState extends State begin: widget.begin, end: widget.end, assetPath: widget.assetPath, + onViewMore: widget.onViewMore, ), ); } diff --git a/lib/screens/category/sub_category.controller.dart b/lib/screens/category/sub_category.controller.dart new file mode 100644 index 00000000..c2a14a98 --- /dev/null +++ b/lib/screens/category/sub_category.controller.dart @@ -0,0 +1,45 @@ +import 'package:ecommerce_int2/data/models/category.model.dart'; +import 'package:ecommerce_int2/data/repository/category.repository.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class SubCategoryController extends GetxController { + + final CategoryRepository repository; + final Category category; + + SubCategoryController(this.repository, this.category); + + TextEditingController searchController = TextEditingController(); + + List get list => category.subcategories ?? []; + + List tempList = []; + + @override + void onInit() { + super.onInit(); + } + + @override + void onReady() { + final temp = Category.fromJson(category.toJson()); + tempList = temp.subcategories!; + update(); + super.onReady(); + } + + void onChanged(String value) { + tempList.clear(); + if (value.isNotEmpty) { + list.forEach((subCategory) { + if (subCategory.name!.toLowerCase().contains(value)) { + tempList.add(subCategory); + } + }); + } else { + tempList.addAll(list); + } + update(); + } +} diff --git a/lib/screens/category/sub_category_list_page.dart b/lib/screens/category/sub_category_list_page.dart new file mode 100644 index 00000000..1e32a69e --- /dev/null +++ b/lib/screens/category/sub_category_list_page.dart @@ -0,0 +1,93 @@ +import 'package:ecommerce_int2/utils/app_properties.dart'; +import 'package:ecommerce_int2/data/models/category.model.dart'; +import 'package:ecommerce_int2/data/repository/category.repository.dart'; +import 'package:ecommerce_int2/screens/category/sub_category.controller.dart'; +import 'package:ecommerce_int2/screens/search_products/search_page.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get/get.dart'; + +import 'components/staggered_category_card.dart'; + +class SubCategoryListPage extends StatelessWidget { + final Category category; + + const SubCategoryListPage({Key? key, required this.category}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Material( + color: Color(0xffF9F9F9), + child: Container( + padding: EdgeInsets.symmetric(horizontal: 16.0), + child: GetBuilder( + init: SubCategoryController(Get.find(), category), + builder: (controller) => SafeArea( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + BackButton( + onPressed: () => Get.back(), + ), + Padding( + padding: EdgeInsets.symmetric(vertical: 16.0), + child: Text( + 'Sub Category List', + style: TextStyle( + color: darkGrey, + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + Container( + padding: EdgeInsets.only(left: 16.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(5)), + color: Colors.white, + ), + child: TextField( + controller: controller.searchController, + decoration: InputDecoration( + border: InputBorder.none, + hintText: 'Search', + prefixIcon: SvgPicture.asset( + 'assets/icons/search_icon.svg', + fit: BoxFit.scaleDown, + )), + onChanged: (value) => controller.onChanged(value), + ), + ), + Flexible( + child: ListView.builder( + itemCount: controller.tempList.length, + itemBuilder: (_, index) => Padding( + padding: EdgeInsets.symmetric( + vertical: 16.0, + ), + child: StaggeredCardCard( + begin: const Color(0xffFCE183), + end: const Color(0xffF68D7F), + categoryName: controller.tempList[index].name ?? "", + assetPath: 'assets/jeans_5.png', + onViewMore: () => Get.to( + () => SearchPage( + subCategory: controller.tempList[index].slug), + ), + ), + ), + ), + ) + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/screens/faq_page.dart b/lib/screens/faq_page.dart index e62b6662..1ef1e78f 100755 --- a/lib/screens/faq_page.dart +++ b/lib/screens/faq_page.dart @@ -1,5 +1,4 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:flutter/cupertino.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:flutter/material.dart'; class FaqPage extends StatefulWidget { @@ -43,7 +42,6 @@ class _FaqPageState extends State { iconTheme: IconThemeData( color: Colors.black, ), - brightness: Brightness.light, backgroundColor: Colors.transparent, title: Text( 'Settings', diff --git a/lib/screens/intro_page.dart b/lib/screens/intro_page.dart deleted file mode 100755 index 5ab956af..00000000 --- a/lib/screens/intro_page.dart +++ /dev/null @@ -1,229 +0,0 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/screens/main/main_page.dart'; -import 'package:flutter/material.dart'; - -class IntroPage extends StatefulWidget { - @override - _IntroPageState createState() => _IntroPageState(); -} - -class _IntroPageState extends State { - PageController controller = PageController(); - int pageIndex = 0; - - @override - Widget build(BuildContext context) { - return Material( - child: Container( -// width: MediaQuery.of(context).size.width, - decoration: BoxDecoration( - color: Colors.grey[100], - image: DecorationImage(image: AssetImage('assets/background.png'))), - child: Stack( - children: [ - PageView( - onPageChanged: (value) { - setState(() { - pageIndex = value; - }); - }, - controller: controller, - children: [ - Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Center( - child: Image.asset( - 'assets/firstScreen.png', - height: 200, - width: 200, - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 32), - child: Text( - 'Get Any Thing Online', - textAlign: TextAlign.right, - style: TextStyle( - fontWeight: FontWeight.bold, fontSize: 16), - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 32, vertical: 16.0), - child: Text( - 'You can buy anything ranging from digital products to hardware within few clicks.', - textAlign: TextAlign.right, - style: TextStyle(color: Colors.grey, fontSize: 12.0), - ), - ), - ], - ), - Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Center( - child: Image.asset( - 'assets/secondScreen.png', - height: 200, - width: 200, - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 32), - child: Text( - 'Shipping to anywhere ', - textAlign: TextAlign.right, - style: TextStyle( - fontWeight: FontWeight.bold, fontSize: 16), - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 32, vertical: 16.0), - child: Text( - 'We will ship to anywhere in the world, With 30 day 100% money back policy.', - textAlign: TextAlign.right, - style: TextStyle(color: Colors.grey, fontSize: 12.0), - ), - ), - ], - ), - Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Center( - child: Image.asset( - 'assets/thirdScreen.png', - height: 200, - width: 200, - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 32), - child: Text( - 'On-time delivery', - textAlign: TextAlign.right, - style: TextStyle( - fontWeight: FontWeight.bold, fontSize: 16), - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 32, vertical: 16.0), - child: Text( - 'You can track your product with our powerful tracking service.', - textAlign: TextAlign.right, - style: TextStyle(color: Colors.grey, fontSize: 12.0), - ), - ), - ], - ), - ], - ), - Positioned( - bottom: 16.0, - child: SizedBox( - width: MediaQuery.of(context).size.width, - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - margin: EdgeInsets.all(8.0), - height: 12, - width: 12, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all(color: Colors.black, width: 2), - color: pageIndex == 0 ? yellow : Colors.white), - ), - Container( - margin: EdgeInsets.all(8.0), - height: 12, - width: 12, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all(color: Colors.black, width: 2), - color: pageIndex == 1 ? yellow : Colors.white), - ), - Container( - margin: EdgeInsets.all(8.0), - height: 12, - width: 12, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all(color: Colors.black, width: 2), - color: pageIndex == 2 ? yellow : Colors.white), - ) - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Opacity( - opacity: pageIndex != 2 ? 1.0 : 0.0, - child: FlatButton( - splashColor: Colors.transparent, - child: Text( - 'SKIP', - style: TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 16), - ), - onPressed: () { - Navigator.of(context).pushReplacement( - MaterialPageRoute( - builder: (context) => MainPage())); - }, - ), - ), - pageIndex != 2 - ? FlatButton( - splashColor: Colors.transparent, - child: Text( - 'NEXT', - style: TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 16), - ), - onPressed: () { - if (!(controller.page == 2.0)) - controller.nextPage( - duration: Duration(milliseconds: 200), - curve: Curves.linear); - }, - ) - : FlatButton( - splashColor: Colors.transparent, - child: Text( - 'FINISH', - style: TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 16), - ), - onPressed: () { - Navigator.of(context).pushReplacement( - MaterialPageRoute( - builder: (context) => MainPage())); - }, - ) - ], - ), - ], - ), - ), - ) - ], - ), - ), - ); - } -} diff --git a/lib/screens/main/components/product_list.dart b/lib/screens/main/components/product_list.dart index cab0dc23..b6e91393 100755 --- a/lib/screens/main/components/product_list.dart +++ b/lib/screens/main/components/product_list.dart @@ -1,15 +1,18 @@ import 'package:card_swiper/card_swiper.dart'; -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/models/product.dart'; +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/screens/rating/rating_dialog.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:ecommerce_int2/screens/product/product_page.dart'; import 'package:flutter/material.dart'; +import 'package:get/get.dart'; -class ProductList extends StatelessWidget { +// ignore: must_be_immutable +class ProductListView extends StatelessWidget { List products; final SwiperController swiperController = SwiperController(); - ProductList({required this.products}); + ProductListView({required this.products}); @override Widget build(BuildContext context) { @@ -97,11 +100,13 @@ class ProductCard extends StatelessWidget { final Product product; final double height; final double width; + final bool hasReceived; const ProductCard({ required this.product, required this.height, required this.width, + this.hasReceived = false, }); @override @@ -109,74 +114,79 @@ class ProductCard extends StatelessWidget { return InkWell( onTap: () => Navigator.of(context).push( MaterialPageRoute(builder: (_) => ProductPage(product: product))), - child: Stack( - children: [ - Container( - margin: const EdgeInsets.only(left: 30), - height: height, - width: width, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(24)), - color: mediumYellow, + child: Container( + margin: const EdgeInsets.symmetric(vertical: 8), + height: height, + width: Get.width, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(8)), + color: mediumYellow, + ), + child: Row(children: [ + Hero( + tag: product.sId!, + child: Image.network( + product.imageUrl!, + height: 130, + width: 130, + fit: BoxFit.contain, ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - IconButton( - icon: Icon(Icons.favorite_border), - onPressed: () {}, - color: Colors.white, - ), - Column( - children: [ - Align( - alignment: Alignment.topLeft, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - product.name, - style: - TextStyle(color: Colors.white, fontSize: 16.0), - ), - )), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + product.name ?? "", + style: TextStyle(color: Colors.white, fontSize: 16.0), + ), + Container( + padding: const EdgeInsets.fromLTRB(8.0, 4.0, 12.0, 4.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + bottomLeft: Radius.circular(10)), + color: Color.fromRGBO(224, 69, 10, 1), + ), + child: Text( + '\$${product.price ?? product.purchasePrice ?? 0}', + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.bold), + ), + ), + if (hasReceived) Align( - alignment: Alignment.topRight, - child: Container( - margin: const EdgeInsets.only(bottom: 12.0), - padding: const EdgeInsets.fromLTRB(8.0, 4.0, 12.0, 4.0), - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10), - bottomLeft: Radius.circular(10)), - color: Color.fromRGBO(224, 69, 10, 1), - ), - child: Text( - '\$${product.price}', - style: TextStyle( - color: Colors.white, - fontSize: 18, - fontWeight: FontWeight.bold), - ), + alignment: Alignment.centerRight, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: GestureDetector( + child: Text('Add review', style: TextStyle(color: Colors.white),), + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return Dialog( + shape: BeveledRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(10))), + child: RatingDialog(product: product), + ); + }, + ); + }), ), - ), - ], - ) - ], - ), - ), - Positioned( - child: Hero( - tag: product.image, - child: Image.asset( - product.image, - height: height / 1.7, - width: width / 1.4, - fit: BoxFit.contain, + ) + ], ), ), ), - ], + ]), ), ); } diff --git a/lib/screens/main/components/recommended_list.dart b/lib/screens/main/components/recommended_list.dart index 939f2c9f..1b65cec2 100755 --- a/lib/screens/main/components/recommended_list.dart +++ b/lib/screens/main/components/recommended_list.dart @@ -1,22 +1,14 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/models/product.dart'; +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:ecommerce_int2/screens/product/product_page.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:flutter/material.dart'; class RecommendedList extends StatelessWidget { - List products = [ - Product('assets/bag_1.png', 'Bag', 'Beautiful bag', 2.33), - Product('assets/cap_5.png', 'Cap', 'Cap with beautiful design', 10), - Product('assets/jeans_1.png', 'Jeans', 'Jeans for you', 20), - Product('assets/womanshoe_3.png', 'Woman Shoes', - 'Shoes with special discount', 30), - Product('assets/bag_10.png', 'Bag Express', 'Bag for your shops', 40), - Product('assets/jeans_3.png', 'Jeans', 'Beautiful Jeans', 102.33), - Product('assets/ring_1.png', 'Silver Ring', 'Description', 52.33), - Product('assets/shoeman_7.png', 'Shoes', 'Description', 62.33), - Product('assets/headphone_9.png', 'Headphones', 'Description', 72.33), - ]; + final List products; + + const RecommendedList({Key? key,required this.products}) : super(key: key); + @override Widget build(BuildContext context) { @@ -71,8 +63,8 @@ class RecommendedList extends StatelessWidget { focalRadius: 0.1), ), child: Hero( - tag: products[index].image, - child: Image.asset(products[index].image))), + tag: UniqueKey(), + child: Image.network(products[index].imageUrl!))), ), ), staggeredTileBuilder: (int index) => diff --git a/lib/screens/main/components/tab_view.dart b/lib/screens/main/components/tab_view.dart index c9a2446e..5bc928b2 100755 --- a/lib/screens/main/components/tab_view.dart +++ b/lib/screens/main/components/tab_view.dart @@ -1,106 +1,103 @@ -import 'package:ecommerce_int2/models/category.dart'; -import 'package:flutter/material.dart'; -import 'category_card.dart'; -import 'recommended_list.dart'; +// import 'package:ecommerce_int2/models/category.dart'; +// import 'package:flutter/material.dart'; +// import 'category_card.dart'; +// import 'recommended_list.dart'; -class TabView extends StatelessWidget { - List categories = [ - Category( - Color(0xffFCE183), - Color(0xffF68D7F), - 'Gadgets', - 'assets/jeans_5.png', - ), - Category( - Color(0xffF749A2), - Color(0xffFF7375), - 'Clothes', - 'assets/jeans_5.png', - ), - Category( - Color(0xff00E9DA), - Color(0xff5189EA), - 'Fashion', - 'assets/jeans_5.png', - ), - Category( - Color(0xffAF2D68), - Color(0xff632376), - 'Home', - 'assets/jeans_5.png', - ), - Category( - Color(0xff36E892), - Color(0xff33B2B9), - 'Beauty', - 'assets/jeans_5.png', - ), - Category( - Color(0xffF123C4), - Color(0xff668CEA), - 'Appliances', - 'assets/jeans_5.png', - ), - ]; +// class TabView extends StatelessWidget { +// List categories = [ +// Category( +// Color(0xffFCE183), +// Color(0xffF68D7F), +// 'Gadgets', +// 'assets/jeans_5.png', +// ), +// Category( +// Color(0xffF749A2), +// Color(0xffFF7375), +// 'Clothes', +// 'assets/jeans_5.png', +// ), +// Category( +// Color(0xff00E9DA), +// Color(0xff5189EA), +// 'Fashion', +// 'assets/jeans_5.png', +// ), +// Category( +// Color(0xffAF2D68), +// Color(0xff632376), +// 'Home', +// 'assets/jeans_5.png', +// ), +// Category( +// Color(0xff36E892), +// Color(0xff33B2B9), +// 'Beauty', +// 'assets/jeans_5.png', +// ), +// Category( +// Color(0xffF123C4), +// Color(0xff668CEA), +// 'Appliances', +// 'assets/jeans_5.png', +// ), +// ]; - final TabController tabController; +// final TabController tabController; - TabView({ - required this.tabController, - }); +// TabView({ +// required this.tabController, +// }); - @override - Widget build(BuildContext context) { - print(MediaQuery.of(context).size.height / 9); - return TabBarView( - physics: NeverScrollableScrollPhysics(), - controller: tabController, - children: [ - Container( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - margin: EdgeInsets.all(8.0), - height: MediaQuery.of(context).size.height / 9, - width: MediaQuery.of(context).size.width, - child: ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: categories.length, - itemBuilder: (_, index) => CategoryCard( - category: categories[index], - ))), - SizedBox( - height: 16.0, - ), - Flexible(child: RecommendedList()), - ], - ), - ), - Column(children: [ - SizedBox( - height: 16.0, - ), - Flexible(child: RecommendedList()) - ]), - Column(children: [ - SizedBox( - height: 16.0, - ), - Flexible(child: RecommendedList()) - ]), - Column(children: [ - SizedBox( - height: 16.0, - ), - Flexible(child: RecommendedList()) - ]), - Column(children: [ - SizedBox( - height: 16.0, - ), - Flexible(child: RecommendedList()) - ]), - ]); - } -} +// @override +// Widget build(BuildContext context) { +// return TabBarView( +// physics: NeverScrollableScrollPhysics(), +// controller: tabController, +// children: [ +// Column( +// mainAxisSize: MainAxisSize.min, +// children: [ +// Container( +// margin: EdgeInsets.all(8.0), +// height: MediaQuery.of(context).size.height / 9, +// width: MediaQuery.of(context).size.width, +// child: ListView.builder( +// scrollDirection: Axis.horizontal, +// itemCount: categories.length, +// itemBuilder: (_, index) => CategoryCard( +// category: categories[index], +// ))), +// SizedBox( +// height: 16.0, +// ), +// Flexible(child: RecommendedList()), +// ], +// ), +// Column(children: [ +// SizedBox( +// height: 16.0, +// ), +// Flexible(child: RecommendedList()) +// ]), +// Column(children: [ +// SizedBox( +// height: 16.0, +// ), +// Flexible(child: RecommendedList()) +// ]), +// Column(children: [ +// SizedBox( +// height: 16.0, +// ), +// Flexible(child: RecommendedList()) +// ]), +// Column(children: [ +// SizedBox( +// height: 16.0, +// ), +// Flexible(child: RecommendedList()) +// ]), +// ]); +// } +// } diff --git a/lib/screens/main/home.controller.dart b/lib/screens/main/home.controller.dart new file mode 100644 index 00000000..18d276ea --- /dev/null +++ b/lib/screens/main/home.controller.dart @@ -0,0 +1,78 @@ +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/data/repository/product.repository.dart'; +import 'package:ecommerce_int2/utils/debounce.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; + +class HomeController extends GetxController { + final ProductRepository repository; + + HomeController(this.repository); + + TextEditingController searchController = TextEditingController(); + + RefreshController refreshController = + RefreshController(initialRefresh: false); + int currentPage = 1; + + int total = 0; + + ProductFilter filter = ProductFilter( + sortOrder: SortOrder(price: 1), + pageNumber: 1, + rating: 0, + min: 1, + max: 500000, + // subcategory: '', + // name: '', + // merchant: '', + order: 2); + + final Debounce debounce = Debounce(milliseconds: 2000); + + List list = []; + + @override + void onInit() { + getProduct(); + super.onInit(); + } + + Future getProduct() async { + try { + final res = await repository.getProducts(filter); + total = res.totalProducts ?? 0; + list.addAll(res.products!); + update(); + } on Exception catch (e) { + print(e); + } + } + + void onRefresh() async { + list.clear(); + currentPage = 1; + filter.pageNumber = 1; + await getProduct(); + refreshController.refreshCompleted(); + } + + void onLoadMore() async { + if(list.length == total) return; + ++currentPage; + filter.pageNumber = currentPage; + await getProduct(); + refreshController.loadComplete(); + } + + void onChanged(String value) { + debounce.run(() { + if (value.isNotEmpty) { + filter.name = value; + getProduct(); + update(); + } + }); + } +} diff --git a/lib/screens/main/main_page.dart b/lib/screens/main/main_page.dart index 1d58201c..22ee4099 100755 --- a/lib/screens/main/main_page.dart +++ b/lib/screens/main/main_page.dart @@ -1,16 +1,17 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/custom_background.dart'; -import 'package:ecommerce_int2/models/product.dart'; +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/screens/main/components/product_list.dart'; +import 'package:ecommerce_int2/screens/main/components/recommended_list.dart'; +import 'package:ecommerce_int2/screens/main/home.controller.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; +import 'package:ecommerce_int2/utils/custom_background.dart'; import 'package:ecommerce_int2/screens/category/category_list_page.dart'; -import 'package:ecommerce_int2/screens/notifications_page.dart'; import 'package:ecommerce_int2/screens/profile_page.dart'; -import 'package:ecommerce_int2/screens/search_page.dart'; +import 'package:ecommerce_int2/screens/search_products/search_page.dart'; import 'package:ecommerce_int2/screens/shop/check_out_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:get/get.dart'; import 'components/custom_bottom_bar.dart'; -import 'components/product_list.dart'; -import 'components/tab_view.dart'; class MainPage extends StatefulWidget { @override @@ -21,21 +22,21 @@ List timelines = ['Weekly featured', 'Best of June', 'Best of 2018']; String selectedTimeline = 'Weekly featured'; List products = [ - Product( - 'assets/headphones_2.png', - 'Skullcandy headset L325', - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', - 102.99), - Product( - 'assets/headphones_3.png', - 'Skullcandy headset X25', - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', - 55.99), - Product( - 'assets/headphones.png', - 'Blackzy PRO hedphones M003', - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', - 152.99), + // Product( + // 'assets/headphones_2.png', + // 'Skullcandy headset L325', + // 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', + // 102.99), + // Product( + // 'assets/headphones_3.png', + // 'Skullcandy headset X25', + // 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', + // 55.99), + // Product( + // 'assets/headphones.png', + // 'Blackzy PRO hedphones M003', + // 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', + // 152.99), ]; class _MainPageState extends State @@ -55,12 +56,8 @@ class _MainPageState extends State Widget appBar = Container( height: kToolbarHeight + MediaQuery.of(context).padding.top, child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: MainAxisAlignment.end, children: [ - IconButton( - onPressed: () => Navigator.of(context) - .push(MaterialPageRoute(builder: (_) => NotificationsPage())), - icon: Icon(Icons.notifications)), IconButton( onPressed: () => Navigator.of(context) .push(MaterialPageRoute(builder: (_) => SearchPage())), @@ -71,163 +68,80 @@ class _MainPageState extends State Widget topHeader = Padding( padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 4.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Flexible( - child: InkWell( - onTap: () { - setState(() { - selectedTimeline = timelines[0]; - products = [ - Product( - 'assets/headphones_2.png', - 'Skullcandy headset L325', - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', - 102.99), - Product( - 'assets/headphones_3.png', - 'Skullcandy headset X25', - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', - 55.99), - Product( - 'assets/headphones.png', - 'Blackzy PRO hedphones M003', - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', - 152.99), - ]; - }); - }, - child: Text( - timelines[0], - style: TextStyle( - fontSize: timelines[0] == selectedTimeline ? 20 : 14, - color: darkGrey), - ), - ), + child: Flexible( + child: InkWell( + onTap: () { + setState(() { + selectedTimeline = timelines[0]; + products = [ + // Product( + // 'assets/headphones_2.png', + // 'Skullcandy headset L325', + // 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', + // 102.99), + // Product( + // 'assets/headphones_3.png', + // 'Skullcandy headset X25', + // 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', + // 55.99), + // Product( + // 'assets/headphones.png', + // 'Blackzy PRO hedphones M003', + // 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', + // 152.99), + ]; + }); + }, + child: Text( + timelines[0], + style: TextStyle( + fontSize: timelines[0] == selectedTimeline ? 20 : 14, + color: darkGrey), ), - Flexible( - child: InkWell( - onTap: () { - setState(() { - selectedTimeline = timelines[1]; - products = [ - Product( - 'assets/bag_5.png', - 'Skullcandy headset L325', - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', - 102.99), - Product( - 'assets/bag_6.png', - 'Skullcandy headset X25', - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', - 55.99), - Product( - 'assets/bag_3.png', - 'Blackzy PRO hedphones M003', - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', - 152.99), - ]; - }); - }, - child: Text(timelines[1], - textAlign: TextAlign.center, - style: TextStyle( - fontSize: timelines[1] == selectedTimeline ? 20 : 14, - color: darkGrey)), - ), - ), - Flexible( - child: InkWell( - onTap: () { - setState(() { - selectedTimeline = timelines[2]; - products = [ - Product( - 'assets/headphone_13.png', - 'Skullcandy headset L325', - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', - 102.99), - Product( - 'assets/jeans_4.png', - 'Skullcandy headset X25', - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', - 55.99), - Product( - 'assets/ring_7.png', - 'Blackzy PRO hedphones M003', - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', - 152.99), - ]; - }); - }, - child: Text(timelines[2], - textAlign: TextAlign.right, - style: TextStyle( - fontSize: timelines[2] == selectedTimeline ? 20 : 14, - color: darkGrey)), - ), - ), - ], + ), )); - Widget tabBar = TabBar( - tabs: [ - Tab(text: 'Trending'), - Tab(text: 'Sports'), - Tab(text: 'Headsets'), - Tab(text: 'Wireless'), - Tab(text: 'Bluetooth'), - ], - labelStyle: TextStyle(fontSize: 16.0), - unselectedLabelStyle: TextStyle( - fontSize: 14.0, - ), - labelColor: darkGrey, - unselectedLabelColor: Color.fromRGBO(0, 0, 0, 0.5), - isScrollable: true, - controller: tabController, - ); - return Scaffold( bottomNavigationBar: CustomBottomBar(controller: bottomTabController), - body: CustomPaint( - painter: MainBackground(), - child: TabBarView( - controller: bottomTabController, - physics: NeverScrollableScrollPhysics(), - children: [ - SafeArea( - child: NestedScrollView( - headerSliverBuilder: - (BuildContext context, bool innerBoxIsScrolled) { - // These are the slivers that show up in the "outer" scroll view. - return [ - SliverToBoxAdapter( - child: appBar, - ), - SliverToBoxAdapter( - child: topHeader, + body: GetBuilder( + init: HomeController(Get.find()), + builder: (controller) => CustomPaint( + painter: MainBackground(), + child: controller.list.isEmpty + ? Center( + child: CircularProgressIndicator.adaptive(), + ) + : TabBarView( + controller: bottomTabController, + physics: NeverScrollableScrollPhysics(), + children: [ + SafeArea( + child: NestedScrollView( + headerSliverBuilder: + (BuildContext context, bool innerBoxIsScrolled) { + // These are the slivers that show up in the "outer" scroll view. + return [ + SliverToBoxAdapter( + child: appBar, + ), + SliverToBoxAdapter( + child: topHeader, + ), + SliverToBoxAdapter( + child: ProductListView( + products: + controller.list.getRange(0, 3).toList(), + ), + ), + ]; + }, + body: RecommendedList(products: controller.list)), ), - SliverToBoxAdapter( - child: ProductList( - products: products, - ), - ), - SliverToBoxAdapter( - child: tabBar, - ) - ]; - }, - body: TabView( - tabController: tabController, + CategoryListPage(), + CheckOutPage(), + ProfilePage() + ], ), - ), - ), - CategoryListPage(), - CheckOutPage(), - ProfilePage() - ], ), ), ); diff --git a/lib/screens/merchant/components/credit_card.dart b/lib/screens/merchant/components/credit_card.dart new file mode 100644 index 00000000..47ca4ec2 --- /dev/null +++ b/lib/screens/merchant/components/credit_card.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +class CreditCard extends StatelessWidget { + final String text; + + const CreditCard({Key? key,required this.text}) : super(key: key); + @override + Widget build(BuildContext context) { + return Container( + height: 50, + width: 250, + padding: EdgeInsets.all(16.0), + decoration: BoxDecoration( + color: Colors.deepPurple[700], + borderRadius: BorderRadius.all(Radius.circular(10))), + child: Center( + child: Text( + text, + style: TextStyle(color: Colors.white), + ), + ), + ); + } +} diff --git a/lib/screens/merchant/components/shop_item_list.dart b/lib/screens/merchant/components/shop_item_list.dart new file mode 100644 index 00000000..fbb420dc --- /dev/null +++ b/lib/screens/merchant/components/shop_item_list.dart @@ -0,0 +1,121 @@ +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; +import 'package:ecommerce_int2/screens/product/components/color_list.dart'; +import 'package:ecommerce_int2/screens/product/components/shop_product.dart'; +import 'package:flutter/material.dart'; +import 'package:numberpicker/numberpicker.dart'; + +class ShopItemList extends StatefulWidget { + final Product product; + final VoidCallback onRemove; + + ShopItemList(this.product, {required this.onRemove}); + + @override + _ShopItemListState createState() => _ShopItemListState(); +} + +class _ShopItemListState extends State { + int quantity = 1; + + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.symmetric(vertical: 10), + height: 130, + child: Stack( + children: [ + Align( + alignment: Alignment(0, 0.8), + child: Container( + height: 100, + margin: EdgeInsets.symmetric(horizontal: 16.0), + decoration: BoxDecoration( + color: Colors.white, + boxShadow: shadow, + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(10), + bottomRight: Radius.circular(10))), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Container( + padding: EdgeInsets.only(top: 12.0, right: 12.0), + width: 200, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Align( + alignment: Alignment.centerRight, + child: Container( + width: 160, + padding: const EdgeInsets.only( + left: 32.0, top: 8.0, bottom: 8.0), + child: Column(children: [ + Text( + widget.product.name ?? 'Product', + textAlign: TextAlign.right, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, + color: darkGrey, + ), + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + ColorOption(Colors.red), + Text( + '\$${widget.product.price ?? widget.product.purchasePrice ?? 0}', + textAlign: TextAlign.center, + style: TextStyle( + color: darkGrey, + fontWeight: FontWeight.bold, + fontSize: 18.0), + ) + ], + ), + ]), + ), + ) + ], + ), + ), + Theme( + data: ThemeData( + textTheme: TextTheme( + headline6: TextStyle( + fontFamily: 'Montserrat', + fontSize: 14, + color: Colors.black, + fontWeight: FontWeight.bold), + bodyText1: TextStyle( + fontFamily: 'Montserrat', + fontSize: 12, + color: Colors.grey[400], + ), + )), + child: NumberPicker( + value: quantity, + minValue: 1, + maxValue: 10, + onChanged: (value) { + setState(() { + quantity = value; + }); + }, + )) + ])), + ), + Positioned( + top: 5, + child: ShopProductDisplay( + widget.product, + onPressed: widget.onRemove, + )), + ], + ), + ); + } +} diff --git a/lib/screens/merchant/merchant.controller.dart b/lib/screens/merchant/merchant.controller.dart new file mode 100644 index 00000000..d1d62557 --- /dev/null +++ b/lib/screens/merchant/merchant.controller.dart @@ -0,0 +1,68 @@ +import 'package:card_swiper/card_swiper.dart'; +import 'package:ecommerce_int2/data/models/merchant.model.dart'; +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/data/repository/merchant.repository.dart'; +import 'package:ecommerce_int2/utils/message_dialog.dart'; +import 'package:get/get.dart'; + +class MerchantController extends GetxController { + final MerchantRepository merchantRepository; + + final SwiperController swiperController = SwiperController(); + + MerchantController(this.merchantRepository); + + List get products { + return data?.products ?? []; + } + + String? merchantId; + MerchantData? data; + + String get merchant => data?.merchant?.name ?? 'Merchant'; + String get des => + data?.merchant?.business ?? 'A shop sellling high quality assets'; + String get email => data?.merchant?.email ?? ''; + String get phone => data?.merchant?.phoneNumber ?? ''; + + @override + onInit() { + getArg(); + super.onInit(); + } + + @override + void onReady() { + getInfo(); + super.onReady(); + } + + void getArg() { + final arg = Get.arguments; + if (arg != null && arg is String) { + merchantId = arg; + } + } + + void getInfo() async { + if (merchantId == null) { + MessageDialog.showConfirmDialog( + content: "Error loading", + ); + return; + } + + try { + MessageDialog.showLoading(); + final res = await merchantRepository.getMerchantInfo(merchantId!); + data = res; + update(); + MessageDialog.hideLoading(); + } on Exception catch (e) { + MessageDialog.hideLoading(); + MessageDialog.showConfirmDialog( + content: "Error loading", + ); + } + } +} diff --git a/lib/screens/merchant/merchant.view.dart b/lib/screens/merchant/merchant.view.dart new file mode 100644 index 00000000..7466ea69 --- /dev/null +++ b/lib/screens/merchant/merchant.view.dart @@ -0,0 +1,154 @@ +import 'package:ecommerce_int2/data/repository/merchant.repository.dart'; +import 'package:ecommerce_int2/screens/merchant/merchant.controller.dart'; +import 'package:ecommerce_int2/screens/product/product_page.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; +import 'package:get/get.dart'; + +class MerchantPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0.0, + iconTheme: IconThemeData(color: darkGrey), + title: Text( + 'Merchant', + style: TextStyle( + color: darkGrey, fontWeight: FontWeight.w500, fontSize: 18.0), + ), + ), + body: GetBuilder( + init: MerchantController(Get.find()), + builder: (controller) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + color: yellow, + width: Get.width, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + controller.merchant, + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + controller.des, + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + controller.email, + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + controller.phone, + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16), + ), + ), + ], + ), + ), + Expanded( + child: StaggeredGridView.countBuilder( + padding: EdgeInsets.zero, + crossAxisCount: 4, + itemCount: controller.products.length, + itemBuilder: (BuildContext context, int index) => new ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(5.0)), + child: InkWell( + onTap: () => Navigator.of(context).push(MaterialPageRoute( + builder: (_) => + ProductPage(product: controller.products[index]))), + child: Container( + decoration: BoxDecoration( + gradient: RadialGradient( + colors: [ + Colors.grey.withOpacity(0.3), + Colors.grey.withOpacity(0.7), + ], + center: Alignment(0, 0), + radius: 0.6, + focal: Alignment(0, 0), + focalRadius: 0.1), + ), + child: Hero( + tag: controller.products[index].sId!, + child: Image.network( + controller.products[index].imageUrl!))), + ), + ), + staggeredTileBuilder: (int index) => + StaggeredTile.count(2, index.isEven ? 3 : 2), + mainAxisSpacing: 4.0, + crossAxisSpacing: 4.0, + ), + ), + SizedBox(height: 24), + ], + ), + ), + ); + } +} + +class Scroll extends CustomPainter { + @override + void paint(Canvas canvas, Size size) { + LinearGradient grT = LinearGradient( + colors: [Colors.transparent, Colors.black26], + begin: Alignment.topCenter, + end: Alignment.bottomCenter); + LinearGradient grB = LinearGradient( + colors: [Colors.transparent, Colors.black26], + begin: Alignment.bottomCenter, + end: Alignment.topCenter); + + canvas.drawRect( + Rect.fromLTRB(0, 0, size.width, 30), + Paint() + ..shader = grT.createShader(Rect.fromLTRB(0, 0, size.width, 30))); + + canvas.drawRect(Rect.fromLTRB(0, 30, size.width, size.height - 40), + Paint()..color = Color.fromRGBO(50, 50, 50, 0.4)); + + canvas.drawRect( + Rect.fromLTRB(0, size.height - 40, size.width, size.height), + Paint() + ..shader = grB.createShader( + Rect.fromLTRB(0, size.height - 40, size.width, size.height))); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return false; + } +} diff --git a/lib/screens/notifications_page.dart b/lib/screens/notifications_page.dart index 0f5a3f8e..a3da004a 100755 --- a/lib/screens/notifications_page.dart +++ b/lib/screens/notifications_page.dart @@ -1,6 +1,5 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/screens/rating/rating_page.dart'; -import 'package:ecommerce_int2/screens/tracking_page.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; +import 'package:ecommerce_int2/screens/tracking/tracking_page.dart'; import 'package:flutter/material.dart'; class NotificationsPage extends StatelessWidget { @@ -262,27 +261,6 @@ class NotificationsPage extends StatelessWidget { ) ]), ), - InkWell( - onTap: () => Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => RatingPage())), - child: Container( - padding: const EdgeInsets.all(14.0), - decoration: BoxDecoration( - color: yellow, - borderRadius: BorderRadius.only( - bottomRight: Radius.circular(5.0), - bottomLeft: Radius.circular(5.0))), - child: Align( - alignment: Alignment.centerRight, - child: Text( - 'Share your feedback', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 10), - ))), - ) ], ), ), diff --git a/lib/screens/payment/payment.controller.dart b/lib/screens/payment/payment.controller.dart new file mode 100644 index 00000000..8affcf36 --- /dev/null +++ b/lib/screens/payment/payment.controller.dart @@ -0,0 +1,53 @@ +import 'package:ecommerce_int2/data/models/order.model.dart'; +import 'package:ecommerce_int2/data/repository/order.repository.dart'; +import 'package:ecommerce_int2/utils/message_dialog.dart'; +import 'package:ecommerce_int2/utils/webview.dart'; +import 'package:get/get.dart'; + +class PaymentController extends GetxController { + final OrderRepository repository; + + List? orders; + + PaymentController(this.repository); + + OrderDocDetail? detail; + + @override + void onInit() { + getArg(); + super.onInit(); + } + + void getArg() { + final arg = Get.arguments; + if (arg != null && arg is List) { + orders = arg; + } + } + + void makePayment(OrderProceed order) async { + MessageDialog.showLoading(); + try { + repository.payOrder(order.data!.order!.sId!); + await Get.to(() => WebViewExample( + url: order.data!.payment!.links! + .firstWhere( + (element) => element.method!.toUpperCase() == 'REDIRECT') + .href!)); + final res = await repository.getOrderDetail(order.data!.order!.sId!); + if (res.paymentStatus == "PAID") orders!.remove(order); + update(); + } on Exception catch (e) { + MessageDialog.showToast(e.toString()); + } + MessageDialog.hideLoading(); + if (orders!.isEmpty) { + onBack(); + } + } + + void onBack() { + Get.back(result: orders!.isEmpty); + } +} diff --git a/lib/screens/payment/payment_page.dart b/lib/screens/payment/payment_page.dart index 49c30642..4dee5cf3 100755 --- a/lib/screens/payment/payment_page.dart +++ b/lib/screens/payment/payment_page.dart @@ -1,331 +1,79 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/screens/product/components/color_list.dart'; +import 'package:ecommerce_int2/data/repository/order.repository.dart'; +import 'package:ecommerce_int2/screens/payment/payment.controller.dart'; +import 'package:ecommerce_int2/screens/tracking/row_text.widget.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'dart:math' as math; - -class PaymentPage extends StatefulWidget { - @override - _PaymentPageState createState() => _PaymentPageState(); -} - -class _PaymentPageState extends State { - Color active = Colors.red; - TextEditingController cardNumber = TextEditingController(); - TextEditingController year = TextEditingController(); - TextEditingController month = TextEditingController(); - TextEditingController cvc = TextEditingController(); - TextEditingController cardHolder = TextEditingController(); - - ScrollController scrollController = ScrollController(); - - @override - void initState() { - super.initState(); - scrollController.addListener(() { - if (scrollController.position.userScrollDirection.index == 1) { - FocusScope.of(context).requestFocus(FocusNode()); - } - }); - } - - String convertCardNumber(String src, String divider) { - String newStr = ''; - int step = 4; - for (int i = 0; i < src.length; i += step) { - newStr += src.substring(i, math.min(i + step, src.length)); - if (i + step < src.length) newStr += divider; - } - return newStr; - } - - String convertMonthYear(String month, String year) { - if (month.isNotEmpty) - return month + '/' + year; - else - return ''; - } +import 'package:get/get.dart'; +class PaymentPage extends StatelessWidget { @override Widget build(BuildContext context) { - Widget addThisCard = InkWell( -// onTap: () => Navigator.of(context) -// .push(MaterialPageRoute(builder: (_) => ViewProductPage())), - child: Container( - height: 80, - width: MediaQuery.of(context).size.width / 1.5, + return GetBuilder( + init: PaymentController(Get.find()), + builder: (controller) => Container( decoration: BoxDecoration( - gradient: mainButton, - boxShadow: [ - BoxShadow( - color: Color.fromRGBO(0, 0, 0, 0.16), - offset: Offset(0, 5), - blurRadius: 10.0, - ) - ], - borderRadius: BorderRadius.circular(9.0)), - child: Center( - child: Text("Add This Card", - style: const TextStyle( - color: const Color(0xfffefefe), - fontWeight: FontWeight.w600, - fontStyle: FontStyle.normal, - fontSize: 20.0)), - ), - ), - ); - - return Scaffold( - backgroundColor: Colors.white, - body: LayoutBuilder( - builder: (_, constraints) => GestureDetector( - onPanDown: (val) {}, - behavior: HitTestBehavior.opaque, - child: SingleChildScrollView( - controller: scrollController, - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: constraints.maxHeight, + color: Colors.grey[100], + image: DecorationImage( + image: AssetImage('assets/Group 444.png'), + fit: BoxFit.contain)), + child: Container( + color: Colors.white54, + child: Scaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0.0, + iconTheme: IconThemeData(color: Colors.grey), + title: Text( + 'Your payment', + style: TextStyle( + color: darkGrey, + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + leading: BackButton(onPressed: () => controller.onBack()), ), - child: Container( - margin: const EdgeInsets.only(top: kToolbarHeight), - padding: EdgeInsets.symmetric(horizontal: 16.0), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Payment', - style: TextStyle( - color: darkGrey, - fontSize: 22, - fontWeight: FontWeight.bold, - ), - ), - CloseButton() - ], - ), - Container( - height: 200, - width: MediaQuery.of(context).size.width, - padding: EdgeInsets.all(32.0), - decoration: BoxDecoration( - color: active, - borderRadius: BorderRadius.all(Radius.circular(10))), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'CREDIT CARD', - style: TextStyle(color: Colors.white), - ), - SizedBox(height: 16.0), - Row( - children: [ - Container( - height: 25, - width: 40, - color: Colors.white, - ), - Flexible( - child: Center( - child: Text( - convertCardNumber( - cardNumber.text, '-'), - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 18.0)))), - ], - ), - Spacer(), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(convertMonthYear(month.text, year.text), - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold)), - Text(cvc.text, - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold)) - ], - ), - Spacer(), - Text(cardHolder.text, - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 18.0)) - ], - ), - ), - Padding( - padding: const EdgeInsets.all(16.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Colors.red, - Colors.blue, - Colors.purple[700], - Colors.green[700], - Colors.lightBlueAccent - ] - .map((c) => InkWell( - onTap: () { - setState(() { - active = c ?? Colors.red; - }); - }, - child: Transform.scale( + body: SafeArea( + child: LayoutBuilder( + builder: (_, constraints) => SingleChildScrollView( + child: Column( + children: [ + ...controller.orders!.reversed + .map((order) => GestureDetector( + onTap: (() => controller.makePayment(order)), + child: Container( + width: Get.width, + child: Card( child: Padding( padding: const EdgeInsets.all(8.0), - child: ColorOption(c ?? Colors.red), + child: Column( + children: [ + RowTextWidget( + text1: 'ID', + text2: + '${order.data!.order!.sId}'), + RowTextWidget( + text1: 'Total', + text2: + ' ${order.data!.payment!.transactions!.fold(0, (previousValue, element) => (previousValue as int) + (int.tryParse(element.amount!.total!) ?? 0))}'), + RowTextWidget( + text1: 'Merchant', + text2: + '${order.data?.order?.merchant ?? ''}'), + ], + crossAxisAlignment: + CrossAxisAlignment.start, + ), ), - scale: active == c ? 1.2 : 1), - )) - .toList(), - ), - ), - Container( - padding: EdgeInsets.all(16.0), - height: 250, - width: MediaQuery.of(context).size.width, - decoration: BoxDecoration( - color: Colors.white, - boxShadow: shadow, - borderRadius: BorderRadius.only( - bottomRight: Radius.circular(10), - bottomLeft: Radius.circular(10))), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Container( - padding: EdgeInsets.only(left: 16.0), - decoration: BoxDecoration( - borderRadius: - BorderRadius.all(Radius.circular(5)), - color: Colors.grey[200], - ), - child: TextField( - inputFormatters: [ - LengthLimitingTextInputFormatter(16) - ], - controller: cardNumber, - onChanged: (val) { - setState(() {}); - }, - decoration: InputDecoration( - border: InputBorder.none, - hintText: 'Card Number'), - ), - ), - Row( - children: [ - Flexible( - child: Container( - padding: EdgeInsets.only(left: 16.0), - decoration: BoxDecoration( - borderRadius: - BorderRadius.all(Radius.circular(5)), - color: Colors.grey[200], - ), - child: TextField( - inputFormatters: [ - LengthLimitingTextInputFormatter(2) - ], - controller: month, - decoration: InputDecoration( - border: InputBorder.none, - hintText: 'Month'), - onChanged: (val) { - setState(() {}); - }, + ), ), - ), - ), - SizedBox( - width: 8.0, - ), - Flexible( - child: Container( - padding: EdgeInsets.only(left: 16.0), - decoration: BoxDecoration( - borderRadius: - BorderRadius.all(Radius.circular(5)), - color: Colors.grey[200], - ), - child: TextField( - inputFormatters: [ - LengthLimitingTextInputFormatter(2) - ], - controller: year, - decoration: InputDecoration( - border: InputBorder.none, - hintText: 'Year'), - onChanged: (val) { - setState(() {}); - }, - ), - ), - ), - SizedBox( - width: 8.0, - ), - Flexible( - child: Container( - padding: EdgeInsets.only(left: 16.0), - decoration: BoxDecoration( - borderRadius: - BorderRadius.all(Radius.circular(5)), - color: Colors.grey[200], - ), - child: TextField( - controller: cvc, - decoration: InputDecoration( - border: InputBorder.none, - hintText: 'CVC'), - onChanged: (val) { - setState(() {}); - }, - ), - ), - ) - ], - ), - Container( - padding: EdgeInsets.only(left: 16.0), - decoration: BoxDecoration( - borderRadius: - BorderRadius.all(Radius.circular(5)), - color: Colors.grey[200], - ), - child: TextField( - controller: cardHolder, - decoration: InputDecoration( - border: InputBorder.none, - hintText: 'Name on card'), - onChanged: (val) { - setState(() {}); - }, - ), - ), - ], - ), + )) + .toList() + ], ), - SizedBox(height: 24.0), - Center( - child: Padding( - padding: EdgeInsets.only(bottom: 20), - child: addThisCard, - )) - ], + ), ), - ), - ), - ), + )), ), ), ); diff --git a/lib/screens/payment/promo_item.dart b/lib/screens/payment/promo_item.dart index 631243cc..a6615f4f 100755 --- a/lib/screens/payment/promo_item.dart +++ b/lib/screens/payment/promo_item.dart @@ -1,5 +1,5 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/models/product.dart'; +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:ecommerce_int2/screens/product/components/color_list.dart'; import 'package:ecommerce_int2/screens/product/components/shop_product.dart'; import 'package:flutter/material.dart'; @@ -131,10 +131,10 @@ class PromoItem extends StatelessWidget { top: 5, child: ShopProductDisplay( Product( - 'assets/headphones.png', - 'Boat roackerz 400 On-Ear Bluetooth Headphones', - 'description', - 45.3), + imageUrl: 'assets/headphones.png', + name: 'Boat roackerz 400 On-Ear Bluetooth Headphones', + description: 'description', + price: 45000), onPressed: () {}, ), ), diff --git a/lib/screens/payment/unpaid_page.dart b/lib/screens/payment/unpaid_page.dart index 74141a9f..014c3b6c 100755 --- a/lib/screens/payment/unpaid_page.dart +++ b/lib/screens/payment/unpaid_page.dart @@ -1,4 +1,4 @@ -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:ecommerce_int2/screens/payment/promo_item.dart'; import 'package:flutter/material.dart'; diff --git a/lib/screens/payment_history_page.dart b/lib/screens/payment_history_page.dart index 5d28b846..c0d2d886 100755 --- a/lib/screens/payment_history_page.dart +++ b/lib/screens/payment_history_page.dart @@ -1,4 +1,4 @@ -import 'package:ecommerce_int2/api_service.dart'; +import 'package:ecommerce_int2/utils/api_service.dart'; import 'package:ecommerce_int2/models/user.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; diff --git a/lib/screens/product/components/color_list.dart b/lib/screens/product/components/color_list.dart index 699cd4b8..6f6e9e1c 100755 --- a/lib/screens/product/components/color_list.dart +++ b/lib/screens/product/components/color_list.dart @@ -1,4 +1,4 @@ -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:flutter/material.dart'; class ColorList extends StatefulWidget { diff --git a/lib/screens/product/components/more_products.dart b/lib/screens/product/components/more_products.dart index 5a6cb17c..7dc6d7fa 100755 --- a/lib/screens/product/components/more_products.dart +++ b/lib/screens/product/components/more_products.dart @@ -1,25 +1,26 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/models/product.dart'; +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/screens/product/product_page.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:ecommerce_int2/screens/product/components/product_card.dart'; import 'package:flutter/material.dart'; class MoreProducts extends StatelessWidget { final List products = [ - Product( - 'assets/headphones_2.png', - 'Skullcandy headset L325', - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', - 102.99), - Product( - 'assets/headphones_3.png', - 'Skullcandy headset X25', - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', - 55.99), - Product( - 'assets/headphones.png', - 'Blackzy PRO hedphones M003', - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', - 152.99), + // Product( + // 'assets/headphones_2.png', + // 'Skullcandy headset L325', + // 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', + // 102.99), + // Product( + // 'assets/headphones_3.png', + // 'Skullcandy headset X25', + // 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', + // 55.99), + // Product( + // 'assets/headphones.png', + // 'Blackzy PRO hedphones M003', + // 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', + // 152.99), ]; @override @@ -49,7 +50,10 @@ class MoreProducts extends StatelessWidget { : index == 4 ? EdgeInsets.only(right: 24.0, left: 8.0) : EdgeInsets.symmetric(horizontal: 8.0), - child: ProductCard(products[index])); + child: ProductCard(products[index], () => Navigator.of(context).push(MaterialPageRoute( + builder: (_) => ProductPage( + product: products[index], + ))),), ); }, scrollDirection: Axis.horizontal, ), diff --git a/lib/screens/product/components/product_card.dart b/lib/screens/product/components/product_card.dart index e4d0e1c3..3fb54709 100755 --- a/lib/screens/product/components/product_card.dart +++ b/lib/screens/product/components/product_card.dart @@ -1,57 +1,95 @@ -import 'package:ecommerce_int2/models/product.dart'; +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; class ProductCard extends StatelessWidget { final Product product; + final Function onTap; - ProductCard(this.product); + ProductCard(this.product, this.onTap); @override Widget build(BuildContext context) { - return InkWell( - onTap: null, - child: Container( - height: 250, - width: MediaQuery.of(context).size.width / 2 - 29, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(10)), - color: Color(0xfffbd085).withOpacity(0.46)), - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Align( - alignment: Alignment.topCenter, - child: Container( - padding: EdgeInsets.all(16.0), - width: MediaQuery.of(context).size.width / 2 - 64, - height: MediaQuery.of(context).size.width / 2 - 64, - child: Image.asset( - product.image, + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Card( + child: InkWell( + onTap: () => onTap(), + child: Container( + width: MediaQuery.of(context).size.width / 2 - 29, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(10)), + color: Color(0xfffbd085).withOpacity(0.46)), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: EdgeInsets.all(16.0), + child: Image.network( + product.imageUrl!, + width: 200, + height: 200, + ), ), - ), - ), - Flexible( - child: Align( - alignment: Alignment(1, 0.5), - child: Container( - margin: const EdgeInsets.only(left: 16.0), - padding: const EdgeInsets.all(8.0), - decoration: BoxDecoration( - color: Color(0xffe0450a).withOpacity(0.51), - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10), - bottomLeft: Radius.circular(10))), - child: Text( - product.name, - textAlign: TextAlign.right, - style: TextStyle( - fontSize: 12.0, - color: Colors.white, - ), - )), - ), - ) - ], - ))); + Expanded( + child: Container( + margin: const EdgeInsets.only(left: 16.0, top: 16), + padding: const EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Color(0xffe0450a).withOpacity(0.51), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + bottomLeft: Radius.circular(10))), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + product.name ?? "Product", + textAlign: TextAlign.right, + style: TextStyle( + fontSize: 22.0, + color: Colors.white, + ), + ), + const SizedBox(height: 16), + Text( + '\$ ' + + (product.purchasePrice ?? + product.price ?? + 0) + .toString(), + textAlign: TextAlign.right, + style: TextStyle( + fontSize: 18.0, + color: Colors.white, + ), + ), + Divider(), + const SizedBox(height: 8), + Text( + '${product.totalRatings ?? 0} ratings', + textAlign: TextAlign.right, + style: TextStyle( + fontSize: 13.0, + color: Colors.white, + ), + ), + const SizedBox(height: 8), + Text( + '${product.totalReviews ?? 0} reviews', + textAlign: TextAlign.right, + style: TextStyle( + fontSize: 13.0, + color: Colors.white, + ), + ), + ], + )), + ) + ], + ))), + ), + ); } } diff --git a/lib/screens/product/components/product_display.dart b/lib/screens/product/components/product_display.dart index 2bb9492b..02d1d91b 100755 --- a/lib/screens/product/components/product_display.dart +++ b/lib/screens/product/components/product_display.dart @@ -1,6 +1,5 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/models/product.dart'; -import 'package:ecommerce_int2/screens/rating/rating_page.dart'; +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:flutter/material.dart'; class ProductDisplay extends StatelessWidget { @@ -11,11 +10,39 @@ class ProductDisplay extends StatelessWidget { }); @override Widget build(BuildContext context) { - return Stack( + return Column( children: [ - Positioned( - top: 30.0, - right: 0, + Align( + alignment: Alignment(-1, 0), + child: Padding( + padding: const EdgeInsets.only(right: 20.0, left: 20.0), + child: Container( + height: screenAwareSize(220, context), + child: Stack( + children: [ + Padding( + padding: const EdgeInsets.only( + bottom: 18.0, + ), + child: Container( + child: Hero( + tag: product.imageUrl!, + child: Image.network( + product.imageUrl!, + fit: BoxFit.contain, + height: 230, + width: 230, + ), + ), + ), + ) + ], + ), + ), + ), + ), + Align( + alignment: Alignment.centerRight, child: Container( width: MediaQuery.of(context).size.width / 1.5, height: 85, @@ -43,7 +70,7 @@ class ProductDisplay extends StatelessWidget { fontFamily: "Montserrat", fontSize: 36.0)), TextSpan( - text: '.58', + text: 'VND', style: const TextStyle( color: const Color(0xFFFFFFFF), fontWeight: FontWeight.w400, @@ -51,49 +78,6 @@ class ProductDisplay extends StatelessWidget { fontSize: 18.0)) ])), ))), - Align( - alignment: Alignment(-1, 0), - child: Padding( - padding: const EdgeInsets.only(right: 20.0, left: 20.0), - child: Container( - height: screenAwareSize(220, context), - child: Stack( - children: [ - Padding( - padding: const EdgeInsets.only( - bottom: 18.0, - ), - child: Container( - child: Hero( - tag: product.image, - child: Image.asset( - product.image, - fit: BoxFit.contain, - height: 230, - width: 230, - ), - ), - ), - ) - ], - ), - ), - ), - ), - Positioned( - left: 20.0, - bottom: 0.0, - child: RawMaterialButton( - onPressed: () => Navigator.of(context) - .push(MaterialPageRoute(builder: (_) => RatingPage())), - constraints: const BoxConstraints(minWidth: 45, minHeight: 45), - child: - Icon(Icons.favorite, color: Color.fromRGBO(255, 137, 147, 1)), - elevation: 0.0, - shape: CircleBorder(), - fillColor: Color.fromRGBO(255, 255, 255, 0.4), - ), - ) ], ); } diff --git a/lib/screens/product/components/product_options.dart b/lib/screens/product/components/product_options.dart index a1ac9570..06bf3137 100755 --- a/lib/screens/product/components/product_options.dart +++ b/lib/screens/product/components/product_options.dart @@ -1,5 +1,5 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/models/product.dart'; +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:ecommerce_int2/screens/shop/check_out_page.dart'; import 'package:flutter/material.dart'; @@ -22,7 +22,7 @@ class ProductOption extends StatelessWidget { Positioned( left: 16.0, child: Image.asset( - product.image, + product.imageUrl!, height: 200, width: 200, ), @@ -38,7 +38,7 @@ class ProductOption extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 24.0), - child: Text(product.name, + child: Text(product.name ?? '', textAlign: TextAlign.right, style: TextStyle( fontWeight: FontWeight.bold, diff --git a/lib/screens/product/components/rating_bottomSheet.dart b/lib/screens/product/components/rating_bottomSheet.dart index 3a66cf54..6d5824ae 100755 --- a/lib/screens/product/components/rating_bottomSheet.dart +++ b/lib/screens/product/components/rating_bottomSheet.dart @@ -1,4 +1,4 @@ -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:flutter/material.dart'; import 'package:flutter_rating_bar/flutter_rating_bar.dart'; diff --git a/lib/screens/product/components/shop_bottomSheet.dart b/lib/screens/product/components/shop_bottomSheet.dart index ba8fcd04..7d0792b1 100755 --- a/lib/screens/product/components/shop_bottomSheet.dart +++ b/lib/screens/product/components/shop_bottomSheet.dart @@ -1,5 +1,5 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/models/product.dart'; +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:ecommerce_int2/screens/shop/check_out_page.dart'; import 'package:flutter/material.dart'; @@ -12,21 +12,21 @@ class ShopBottomSheet extends StatefulWidget { class _ShopBottomSheetState extends State { List products = [ - Product( - 'assets/headphones.png', - 'Boat roackerz 400 On-Ear Bluetooth Headphones', - 'description', - 45.3), - Product( - 'assets/headphones_2.png', - 'Boat roackerz 100 On-Ear Bluetooth Headphones', - 'description', - 22.3), - Product( - 'assets/headphones_3.png', - 'Boat roackerz 300 On-Ear Bluetooth Headphones', - 'description', - 58.3) + // Product( + // 'assets/headphones.png', + // 'Boat roackerz 400 On-Ear Bluetooth Headphones', + // 'description', + // 45.3), + // Product( + // 'assets/headphones_2.png', + // 'Boat roackerz 100 On-Ear Bluetooth Headphones', + // 'description', + // 22.3), + // Product( + // 'assets/headphones_3.png', + // 'Boat roackerz 300 On-Ear Bluetooth Headphones', + // 'description', + // 58.3) ]; @override diff --git a/lib/screens/product/components/shop_product.dart b/lib/screens/product/components/shop_product.dart index ae0c95ac..c8b82eaa 100755 --- a/lib/screens/product/components/shop_product.dart +++ b/lib/screens/product/components/shop_product.dart @@ -1,5 +1,5 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/models/product.dart'; +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:flutter/material.dart'; class ShopProduct extends StatelessWidget { @@ -25,7 +25,7 @@ class ShopProduct extends StatelessWidget { Padding( padding: const EdgeInsets.all(8.0), child: Text( - product.name, + product.name ?? '', textAlign: TextAlign.center, style: TextStyle( color: darkGrey, @@ -72,8 +72,8 @@ class ShopProductDisplay extends StatelessWidget { child: SizedBox( height: 80, width: 80, - child: Image.asset( - '${product.image}', + child: Image.network( + product.imageUrl ?? '', fit: BoxFit.contain, )), ), diff --git a/lib/screens/product/product_page.dart b/lib/screens/product/product_page.dart index 193c7e54..3249d617 100755 --- a/lib/screens/product/product_page.dart +++ b/lib/screens/product/product_page.dart @@ -1,38 +1,31 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/models/product.dart'; -import 'package:ecommerce_int2/screens/search_page.dart'; +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/screens/main/components/product_list.dart'; +import 'package:ecommerce_int2/screens/merchant/merchant.view.dart'; +import 'package:ecommerce_int2/screens/rating/rating_page.dart'; +import 'package:ecommerce_int2/screens/search_products/search_controller.dart'; +import 'package:ecommerce_int2/screens/shop/cart.controller.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; +import 'package:ecommerce_int2/utils/cart_button.widget.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get/get.dart'; import 'components/product_display.dart'; -import 'view_product_page.dart'; -class ProductPage extends StatefulWidget { +class ProductPage extends StatelessWidget { final Product product; ProductPage({required this.product}); - @override - _ProductPageState createState() => _ProductPageState(product); -} - -class _ProductPageState extends State { - final Product product; - - _ProductPageState(this.product); - @override Widget build(BuildContext context) { + final controller = Get.find(); double width = MediaQuery.of(context).size.width; double bottomPadding = MediaQuery.of(context).padding.bottom; Widget viewProductButton = InkWell( - onTap: () => Navigator.of(context).push(MaterialPageRoute( - builder: (_) => ViewProductPage( - product: product, - ))), + onTap: () => controller.addProduct(product, quantity: 1), child: Container( - height: 80, + height: 60, width: width / 1.5, decoration: BoxDecoration( gradient: mainButton, @@ -45,7 +38,7 @@ class _ProductPageState extends State { ], borderRadius: BorderRadius.circular(9.0)), child: Center( - child: Text("View Product", + child: Text('Add to Cart', style: const TextStyle( color: const Color(0xfffefefe), fontWeight: FontWeight.w600, @@ -61,111 +54,83 @@ class _ProductPageState extends State { backgroundColor: Colors.transparent, elevation: 0.0, iconTheme: IconThemeData(color: darkGrey), - actions: [ - IconButton( - icon: new SvgPicture.asset( - 'assets/icons/search_icon.svg', - fit: BoxFit.scaleDown, - ), - onPressed: () => Navigator.of(context) - .push(MaterialPageRoute(builder: (_) => SearchPage())), - ) - ], title: Text( - 'Headphones', + product.name ?? 'Product', style: const TextStyle( color: darkGrey, fontWeight: FontWeight.w500, fontSize: 18.0), ), + actions: [CartButton()], ), - body: Stack( - children: [ - SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - height: 80.0, - ), - ProductDisplay( - product: product, - ), - SizedBox( - height: 16.0, - ), - Padding( - padding: const EdgeInsets.only(left: 20.0, right: 16.0), - child: Text( - product.name, + body: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ProductDisplay( + product: product, + ), + SizedBox( + height: 16.0, + ), + Padding( + padding: const EdgeInsets.only(left: 20.0, right: 16.0), + child: Text( + product.name ?? "", + style: const TextStyle( + color: const Color(0xFFFEFEFE), + fontWeight: FontWeight.w600, + fontSize: 20.0), + ), + ), + const SizedBox( + height: 24.0 + ), + Padding( + padding: EdgeInsets.only(left: 20.0, right: 40.0, bottom: 20), + child: new Text(product.description ?? "", style: const TextStyle( - color: const Color(0xFFFEFEFE), - fontWeight: FontWeight.w600, - fontSize: 20.0), - ), - ), - SizedBox( - height: 24.0, - ), - Padding( - padding: const EdgeInsets.only(left: 20.0), - child: Row( - children: [ - Container( - width: 90, - height: 40, - decoration: BoxDecoration( - color: Color.fromRGBO(253, 192, 84, 1), - borderRadius: BorderRadius.circular(4.0), - border: - Border.all(color: Color(0xFFFFFFFF), width: 0.5), - ), - child: Center( - child: new Text("Details", - style: const TextStyle( - color: const Color(0xeefefefe), - fontWeight: FontWeight.w300, - fontStyle: FontStyle.normal, - fontSize: 12.0)), - ), - ) + color: const Color(0xfefefefe), + fontWeight: FontWeight.w800, + fontFamily: "NunitoSans", + fontStyle: FontStyle.normal, + fontSize: 16.0))), + Align( + alignment: Alignment.bottomCenter, + child: Container( + padding: EdgeInsets.only( + top: 8.0, bottom: bottomPadding != 20 ? 20 : bottomPadding), + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Color.fromRGBO(255, 255, 255, 0), + Color.fromRGBO(253, 192, 84, 0.5), + Color.fromRGBO(253, 192, 84, 1), ], - ), - ), - SizedBox( - height: 16.0, - ), - Padding( - padding: - EdgeInsets.only(left: 20.0, right: 40.0, bottom: 130), - child: new Text(product.description, - style: const TextStyle( - color: const Color(0xfefefefe), - fontWeight: FontWeight.w800, - fontFamily: "NunitoSans", - fontStyle: FontStyle.normal, - fontSize: 16.0))) - ], + begin: FractionalOffset.topCenter, + end: FractionalOffset.bottomCenter)), + width: width, + height: 120, + child: Center(child: viewProductButton), + ), ), - ), - Align( - alignment: Alignment.bottomCenter, - child: Container( - padding: EdgeInsets.only( - top: 8.0, bottom: bottomPadding != 20 ? 20 : bottomPadding), - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - Color.fromRGBO(255, 255, 255, 0), - Color.fromRGBO(253, 192, 84, 0.5), - Color.fromRGBO(253, 192, 84, 1), - ], - begin: FractionalOffset.topCenter, - end: FractionalOffset.bottomCenter)), - width: width, - height: 120, - child: Center(child: viewProductButton), + Ratings(product: product), + GestureDetector( + onTap: () => Get.to(() => MerchantPage(), arguments: product.merchant), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Text( + 'More product', + style: TextStyle(fontSize: 18), + ), + ), ), - ), - ], + const SizedBox(height: 8), + ProductListView( + products: Get.find() + .list + .where((element) => element.subcategory == product.subcategory) + .toList()) + ], + ), ), ); } diff --git a/lib/screens/product/view_product_page.dart b/lib/screens/product/view_product_page.dart index 4bbdd652..c57eb8db 100755 --- a/lib/screens/product/view_product_page.dart +++ b/lib/screens/product/view_product_page.dart @@ -1,13 +1,11 @@ -import 'package:ecommerce_int2/models/product.dart'; -import 'package:ecommerce_int2/screens/product/components/rating_bottomSheet.dart'; -import 'package:ecommerce_int2/screens/search_page.dart'; +import 'package:ecommerce_int2/screens/search_products/search_page.dart'; +import 'package:ecommerce_int2/utils/cart_button.widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import '../../app_properties.dart'; -import 'components/color_list.dart'; +import '../../data/models/product.model.dart'; +import '../../utils/app_properties.dart'; import 'components/more_products.dart'; -import 'components/product_options.dart'; class ViewProductPage extends StatefulWidget { final Product product; @@ -59,7 +57,7 @@ class _ViewProductPageState extends State { Widget description = Padding( padding: const EdgeInsets.all(24.0), child: Text( - widget.product.description, + widget.product.description ?? 'description', maxLines: 5, semanticsLabel: '...', overflow: TextOverflow.ellipsis, @@ -82,10 +80,11 @@ class _ViewProductPageState extends State { ), onPressed: () => Navigator.of(context) .push(MaterialPageRoute(builder: (_) => SearchPage())), - ) + ), + CartButton() ], title: Text( - 'Headphones', + widget.product.name ?? 'Product', style: const TextStyle( color: darkGrey, fontWeight: FontWeight.w500, @@ -98,44 +97,11 @@ class _ViewProductPageState extends State { width: MediaQuery.of(context).size.width, child: Column( children: [ - ProductOption( - _scaffoldKey, - product: widget.product, - ), + // ProductOption( + // _scaffoldKey, + // product: widget.product, + // ), description, - Padding( - padding: const EdgeInsets.all(24.0), - child: Row(mainAxisSize: MainAxisSize.min, children: [ - Flexible( - child: ColorList([ - Colors.red, - Colors.blue, - Colors.purple, - Colors.green, - Colors.yellow - ]), - ), - RawMaterialButton( - onPressed: () { - showModalBottomSheet( - context: context, - builder: (context) { - return RatingBottomSheet(); - }, - //elevation: 0, - //backgroundColor: Colors.transparent - ); - }, - constraints: - const BoxConstraints(minWidth: 45, minHeight: 45), - child: Icon(Icons.favorite, - color: Color.fromRGBO(255, 137, 147, 1)), - elevation: 0.0, - shape: CircleBorder(), - fillColor: Color.fromRGBO(255, 255, 255, 0.4), - ), - ]), - ), MoreProducts() ], ), diff --git a/lib/screens/profile_page.dart b/lib/screens/profile_page.dart index ddea54bc..f515d700 100755 --- a/lib/screens/profile_page.dart +++ b/lib/screens/profile_page.dart @@ -1,14 +1,15 @@ -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:ecommerce_int2/screens/faq_page.dart'; -import 'package:ecommerce_int2/screens/payment/payment_page.dart'; import 'package:ecommerce_int2/screens/settings/settings_page.dart'; -import 'package:ecommerce_int2/screens/tracking_page.dart'; -import 'package:ecommerce_int2/screens/wallet/wallet_page.dart'; +import 'package:ecommerce_int2/screens/tracking/tracking_page.dart'; +import 'package:ecommerce_int2/services/auth.service.dart'; import 'package:flutter/material.dart'; +import 'package:get/get.dart'; class ProfilePage extends StatelessWidget { @override Widget build(BuildContext context) { + final controller = Get.find(); return Scaffold( backgroundColor: Color(0xffF9F9F9), body: SafeArea( @@ -26,7 +27,7 @@ class ProfilePage extends StatelessWidget { Padding( padding: const EdgeInsets.all(8.0), child: Text( - 'Rose Helbert', + controller.userModel!.name, style: TextStyle(fontWeight: FontWeight.bold), ), ), @@ -51,50 +52,49 @@ class ProfilePage extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - IconButton( - icon: Image.asset('assets/icons/wallet.png'), - onPressed:()=> Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => WalletPage())), - ), - Text( - 'Wallet', - style: TextStyle(fontWeight: FontWeight.bold), - ) - ], - ), + // Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // IconButton( + // icon: Image.asset('assets/icons/wallet.png'), + // onPressed:()=> Navigator.of(context).push( + // MaterialPageRoute( + // builder: (_) => WalletPage())), + // ), + // Text( + // 'Wallet', + // style: TextStyle(fontWeight: FontWeight.bold), + // ) + // ], + // ), Column( mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( icon: Image.asset('assets/icons/truck.png'), - onPressed: () => Navigator.of(context).push( - MaterialPageRoute(builder: (_) => TrackingPage())), - ), - Text( - 'Shipped', - style: TextStyle(fontWeight: FontWeight.bold), - ) - ], - ), - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - IconButton( - icon: Image.asset('assets/icons/card.png'), - onPressed:()=> Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => PaymentPage())), + onPressed: () => Get.to(() => TrackingPage()), ), Text( - 'Payment', + 'Orders', style: TextStyle(fontWeight: FontWeight.bold), ) ], ), + // Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // IconButton( + // icon: Image.asset('assets/icons/card.png'), + // onPressed:()=> Navigator.of(context).push( + // MaterialPageRoute( + // builder: (_) => PaymentPage())), + // ), + // Text( + // 'Payment', + // style: TextStyle(fontWeight: FontWeight.bold), + // ) + // ], + // ), Column( mainAxisAlignment: MainAxisAlignment.center, children: [ diff --git a/lib/screens/rating/rating_dialog.dart b/lib/screens/rating/rating_dialog.dart index d0eab80f..87a4284b 100755 --- a/lib/screens/rating/rating_dialog.dart +++ b/lib/screens/rating/rating_dialog.dart @@ -1,107 +1,114 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/screens/shop/check_out_page.dart'; +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/data/repository/review.repository.dart'; +import 'package:ecommerce_int2/screens/rating/review.controller.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:flutter/material.dart'; import 'package:flutter_rating_bar/flutter_rating_bar.dart'; +import 'package:get/get.dart'; class RatingDialog extends StatelessWidget { + final Product product; + + const RatingDialog({Key? key, required this.product}) : super(key: key); @override Widget build(BuildContext context) { - double width = MediaQuery.of(context).size.width; - - Widget payNow = InkWell( - onTap: () async { - Navigator.of(context).pop(); - Navigator.of(context) - .push(MaterialPageRoute(builder: (_) => CheckOutPage())); - }, - child: Container( - height: 60, - width: width / 1.5, - decoration: BoxDecoration( - gradient: mainButton, - boxShadow: [ - BoxShadow( - color: Color.fromRGBO(0, 0, 0, 0.16), - offset: Offset(0, 5), - blurRadius: 10.0, - ) - ], - borderRadius: BorderRadius.circular(9.0)), - child: Center( - child: Text("Pay Now", - style: const TextStyle( - color: const Color(0xfffefefe), - fontWeight: FontWeight.w600, - fontStyle: FontStyle.normal, - fontSize: 20.0)), - ), - ), - ); - - return SingleChildScrollView( - physics: ClampingScrollPhysics(), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(10)), - color: Colors.grey[50]), - padding: EdgeInsets.all(24.0), - child: Column(mainAxisSize: MainAxisSize.min, children: [ - Text( - 'Thank You!', - style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0), - child: RichText( - text: TextSpan( - style: - TextStyle(fontFamily: 'Montserrat', color: Colors.grey), - children: [ - TextSpan( - text: 'You rated ', - ), - TextSpan( - text: 'Boat Rockerz 350 On-Ear Bluetooth Headphones', - style: TextStyle( - fontWeight: FontWeight.bold, - color: Colors.grey[600])) - ]), + return GetBuilder( + init: ReviewController(Get.find(), product.sId!), + tag: product.sId!, + global: false, + builder: (controller) => SingleChildScrollView( + physics: ClampingScrollPhysics(), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(10)), + color: Colors.grey[50]), + padding: EdgeInsets.all(24.0), + child: Column(mainAxisSize: MainAxisSize.min, children: [ + Text( + 'Thank You!', + style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), ), - ), - RatingBar( - itemSize: 32, - allowHalfRating: false, - initialRating: 1, - itemPadding: EdgeInsets.symmetric(horizontal: 4.0), - onRatingUpdate: (value) {}, - ratingWidget: RatingWidget( - empty: Icon(Icons.favorite_border, - color: Color(0xffFF8993), size: 20), - full: Icon( - Icons.favorite, - color: Color(0xffFF8993), - size: 20, + Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: RichText( + text: TextSpan( + style: TextStyle( + fontFamily: 'Montserrat', color: Colors.grey), + children: [ + TextSpan( + text: 'You rated ', + ), + TextSpan( + text: product.name, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.grey[600])) + ]), ), - half: SizedBox(), ), - ), - Container( - margin: EdgeInsets.symmetric(vertical: 16.0), - padding: EdgeInsets.all(16.0), - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: BorderRadius.all(Radius.circular(5))), - child: TextField( - controller: TextEditingController(), - decoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.zero, - hintText: 'Say something about the product.'), - style: TextStyle(fontSize: 12, color: Colors.grey[600]), - maxLength: 200, - )), - payNow - ])), + RatingBar( + itemSize: 32, + allowHalfRating: false, + initialRating: 1, + itemPadding: EdgeInsets.symmetric(horizontal: 4.0), + onRatingUpdate: (value) { + controller.updateRating(value.toInt()); + }, + ratingWidget: RatingWidget( + empty: Icon(Icons.favorite_border, + color: Color(0xffFF8993), size: 20), + full: Icon( + Icons.favorite, + color: Color(0xffFF8993), + size: 20, + ), + half: SizedBox(), + ), + ), + Container( + margin: EdgeInsets.symmetric(vertical: 16.0), + padding: EdgeInsets.all(16.0), + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: BorderRadius.all(Radius.circular(5))), + child: TextField( + controller: controller.textEditingController, + decoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.zero, + hintText: 'Say something about the product.'), + style: TextStyle(fontSize: 12, color: Colors.grey[600]), + maxLength: 200, + )), + InkWell( + onTap: () async { + controller.addReview(); + }, + child: Container( + height: 60, + width: Get.width / 1.5, + decoration: BoxDecoration( + gradient: mainButton, + boxShadow: [ + BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.16), + offset: Offset(0, 5), + blurRadius: 10.0, + ) + ], + borderRadius: BorderRadius.circular(9.0)), + child: Center( + child: Text("Add review", + style: const TextStyle( + color: const Color(0xfffefefe), + fontWeight: FontWeight.w600, + fontStyle: FontStyle.normal, + fontSize: 20.0)), + ), + ), + ) + ])), + ), ); } } diff --git a/lib/screens/rating/rating_page.dart b/lib/screens/rating/rating_page.dart index 08b6ef19..de29ce5e 100755 --- a/lib/screens/rating/rating_page.dart +++ b/lib/screens/rating/rating_page.dart @@ -1,262 +1,119 @@ -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/data/repository/review.repository.dart'; +import 'package:ecommerce_int2/screens/rating/review.controller.dart'; import 'package:flutter/material.dart'; import 'package:flutter_rating_bar/flutter_rating_bar.dart'; -import 'package:ecommerce_int2/screens/rating/rating_dialog.dart'; +import 'package:get/get.dart'; -class RatingPage extends StatefulWidget { - @override - _RatingPageState createState() => _RatingPageState(); -} - -class _RatingPageState extends State { - double rating = 0.0; - List ratings = [2, 1, 5, 2, 4, 3]; +class Ratings extends StatelessWidget { + const Ratings({Key? key, required this.product}) : super(key: key); + final Product product; @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.grey[100], - appBar: AppBar( - backgroundColor: Colors.transparent, - iconTheme: IconThemeData(color: Colors.black), - elevation: 0.0, - actions: [ - IconButton( - icon: Image.asset('assets/icons/comment.png'), - onPressed: () { - showDialog( - context: context, - builder: (BuildContext context) { - return Dialog( - shape: BeveledRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - child: RatingDialog(), - ); - }, - ); - }, - color: Colors.black, - ), - ], - ), - body: SafeArea( - child: LayoutBuilder( - builder: (b, constraints) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints(minHeight: constraints.maxHeight), - child: Column( - children: [ - Column( - children: [ - Container( + return GetBuilder( + init: ReviewController(Get.find(), product.sId!), + tag: product.sId!, + global: false, + builder: (controller) => controller.reviewList.isEmpty + ? SizedBox.shrink() + : Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + "Reviews:", + style: TextStyle(fontSize: 18), + ), + ), + ...controller.reviewList + .map((val) => Container( + margin: const EdgeInsets.symmetric(vertical: 4.0), padding: const EdgeInsets.all(8.0), - height: 92, - width: 92, decoration: BoxDecoration( - color: yellow, - shape: BoxShape.circle, - boxShadow: shadow, - border: Border.all( - width: 8.0, color: Colors.white)), - child: Image.asset('assets/headphones.png'), - ), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 72.0, vertical: 16.0), - child: Text( - 'Boat Rockerz 350 On-Ear Bluetooth Headphones', - style: TextStyle(fontWeight: FontWeight.bold), - textAlign: TextAlign.center, - ), - ) - ], - ), - Divider(), - Padding( - padding: const EdgeInsets.symmetric(vertical: 40), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.only(right: 16.0), - child: Text( - '4.8', - style: TextStyle(fontSize: 48), - ), - ), - Column( + color: Colors.white, + borderRadius: + BorderRadius.all(Radius.circular(5.0))), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - RatingBar( -// borderColor: Color(0xffFF8993), -// fillColor: Color(0xffFF8993), - ignoreGestures: true, - itemSize: 20, - allowHalfRating: true, - initialRating: 1, - itemPadding: - EdgeInsets.symmetric(horizontal: 4.0), - ratingWidget: RatingWidget( - empty: Icon(Icons.favorite_border, - color: Color(0xffFF8993), size: 20), - full: Icon( - Icons.favorite, - color: Color(0xffFF8993), - size: 20, - ), - half: SizedBox(), + Padding( + padding: const EdgeInsets.only(right: 16.0), + child: CircleAvatar( + maxRadius: 14, + backgroundImage: + AssetImage('assets/background.jpg'), ), - onRatingUpdate: (value) { - setState(() { - rating = value; - }); - print(value); - }, ), - Padding( - padding: const EdgeInsets.only(top: 4.0), - child: Text('from 25 people'), - ) - ], - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 16.0), - child: Align( - alignment: Alignment(-1, 0), - child: Text('Recent Reviews')), - ), - Column( - children: [ - ...ratings - .map((val) => Container( - margin: - const EdgeInsets.symmetric(vertical: 4.0), - padding: const EdgeInsets.all(8.0), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.all( - Radius.circular(5.0))), - child: Row( + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + val.user?.firstName ?? "Anonymous", + style: TextStyle( + fontWeight: FontWeight.bold), + ), + Text( + val.createdDate, + style: TextStyle( + color: Colors.grey, + fontSize: 10.0), + ) + ], + ), Padding( - padding: - const EdgeInsets.only(right: 16.0), - child: CircleAvatar( - maxRadius: 14, - backgroundImage: AssetImage( - 'assets/background.jpg'), + padding: const EdgeInsets.symmetric( + vertical: 8.0), + child: RatingBar( + ignoreGestures: true, + itemSize: 20, + allowHalfRating: true, + initialRating: + (val.rating ?? 5).toDouble(), + itemPadding: EdgeInsets.symmetric( + horizontal: 4.0), + ratingWidget: RatingWidget( + empty: Icon(Icons.favorite_border, + color: Color(0xffFF8993), + size: 20), + full: Icon( + Icons.favorite, + color: Color(0xffFF8993), + size: 20, + ), + half: SizedBox(), + ), + onRatingUpdate: (value) { + print(value); + }, ), ), - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: - MainAxisAlignment - .spaceBetween, - children: [ - Text( - 'Billy Holand', - style: TextStyle( - fontWeight: - FontWeight.bold), - ), - Text( - '10 am, Via iOS', - style: TextStyle( - color: Colors.grey, - fontSize: 10.0), - ) - ], - ), - Padding( - padding: - const EdgeInsets.symmetric( - vertical: 8.0), - child: RatingBar( - ignoreGestures: true, - itemSize: 20, - allowHalfRating: true, - initialRating: val.toDouble(), - itemPadding: - EdgeInsets.symmetric( - horizontal: 4.0), - ratingWidget: RatingWidget( - empty: Icon( - Icons.favorite_border, - color: Color(0xffFF8993), - size: 20), - full: Icon( - Icons.favorite, - color: Color(0xffFF8993), - size: 20, - ), - half: SizedBox(), - ), - onRatingUpdate: (value) { - setState(() { - rating = value; - }); - print(value); - }, - ), - ), - Text( - 'Not as I expected! ... I`m really sad', - style: TextStyle( - color: Colors.grey, - ), - ), - Padding( - padding: - const EdgeInsets.symmetric( - vertical: 16.0), - child: Row( - mainAxisAlignment: - MainAxisAlignment - .spaceBetween, - children: [ - Text( - '21 likes', - style: TextStyle( - color: Colors.grey[400], - fontSize: 10.0), - ), - Text( - '1 Comment', - style: TextStyle( - color: Colors.blue, - fontWeight: - FontWeight.bold, - fontSize: 10.0), - ) - ], - ), - ) - ], + Text( + val.review ?? "Review is hidden", + style: TextStyle( + color: Colors.grey, ), - ) + ), + const SizedBox(height: 12) ], - ))) - .toList() - ], - ) - ], - ), + ), + ) + ], + ))) + .toList() + ], ), - ), - ), - ), - )); + )); } } diff --git a/lib/screens/rating/review.controller.dart b/lib/screens/rating/review.controller.dart new file mode 100644 index 00000000..239ec337 --- /dev/null +++ b/lib/screens/rating/review.controller.dart @@ -0,0 +1,48 @@ +import 'package:ecommerce_int2/data/models/review.dart'; +import 'package:ecommerce_int2/data/repository/review.repository.dart'; +import 'package:ecommerce_int2/utils/message_dialog.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:get/get.dart'; + +class ReviewController extends GetxController { + final ReviewRepository repository; + final String productId; + + final TextEditingController textEditingController = TextEditingController(); + + ReviewController(this.repository, this.productId); + + ReviewParam param = ReviewParam(rating: 5); + + @override + onInit() { + getReview(); + super.onInit(); + } + + void updateRating(int value) { + param.rating = value; + } + + void addReview() async { + try { + ReviewParam param = ReviewParam( + product: productId, review: textEditingController.text.trim()); + await repository.addReview(param); + MessageDialog.showToast("Thank you for your review"); + Get.back(); + } on Exception catch (e) { + print(e); + } + } + + List? reviews; + + List get reviewList => reviews ?? []; + + void getReview() async { + final res = await repository.getReviewProduct(productId); + reviews = res; + update(); + } +} diff --git a/lib/screens/request_money/receive_page.dart b/lib/screens/request_money/receive_page.dart index 3b5458ad..690c107b 100755 --- a/lib/screens/request_money/receive_page.dart +++ b/lib/screens/request_money/receive_page.dart @@ -1,4 +1,4 @@ -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:ecommerce_int2/models/user.dart'; import 'package:flutter/material.dart'; import 'dart:math' as math; @@ -16,7 +16,6 @@ class ReceivePaymentPage extends StatelessWidget { iconTheme: IconThemeData( color: Colors.black, ), - brightness: Brightness.light, backgroundColor: Colors.transparent, title: Text( 'Receive Payment', diff --git a/lib/screens/request_money/request_amount_page.dart b/lib/screens/request_money/request_amount_page.dart index 2210a2b6..1ada1ea5 100755 --- a/lib/screens/request_money/request_amount_page.dart +++ b/lib/screens/request_money/request_amount_page.dart @@ -1,4 +1,4 @@ -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:ecommerce_int2/models/user.dart'; import 'package:ecommerce_int2/screens/request_money/receive_page.dart'; import 'package:flutter/material.dart'; @@ -10,7 +10,6 @@ class RequestAmountPage extends StatelessWidget { @override Widget build(BuildContext context) { - double width = MediaQuery.of(context).size.width; Widget viewProductButton = InkWell( onTap: () {}, child: Container( @@ -72,7 +71,6 @@ class RequestAmountPage extends StatelessWidget { iconTheme: IconThemeData( color: Colors.black, ), - brightness: Brightness.light, backgroundColor: Colors.transparent, title: Text( 'Request Amount', diff --git a/lib/screens/request_money/request_page.dart b/lib/screens/request_money/request_page.dart index 0ef51e57..7c2f1751 100755 --- a/lib/screens/request_money/request_page.dart +++ b/lib/screens/request_money/request_page.dart @@ -1,11 +1,11 @@ -import 'package:ecommerce_int2/api_service.dart'; +import 'package:ecommerce_int2/utils/api_service.dart'; import 'package:ecommerce_int2/models/user.dart'; import 'package:ecommerce_int2/screens/request_money/request_amount_page.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import '../../app_properties.dart'; +import '../../utils/app_properties.dart'; class RequestPage extends StatefulWidget { @override @@ -45,7 +45,6 @@ class _RequestPageState extends State { iconTheme: IconThemeData( color: Colors.black, ), - brightness: Brightness.light, backgroundColor: Colors.transparent, title: Text( 'Request Amount', diff --git a/lib/screens/search_page.dart b/lib/screens/search_page.dart deleted file mode 100755 index ae34f571..00000000 --- a/lib/screens/search_page.dart +++ /dev/null @@ -1,346 +0,0 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/models/product.dart'; -import 'package:ecommerce_int2/screens/product/view_product_page.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:rubber/rubber.dart'; - -class SearchPage extends StatefulWidget { - @override - _SearchPageState createState() => _SearchPageState(); -} - -class _SearchPageState extends State - with SingleTickerProviderStateMixin { - String selectedPeriod = ""; - String selectedCategory = ""; - String selectedPrice = ""; - - List products = [ - Product( - 'assets/headphones_2.png', - 'Skullcandy headset L325', - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', - 102.99), - Product( - 'assets/headphones_3.png', - 'Skullcandy headset X25', - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', - 55.99), - Product( - 'assets/headphones.png', - 'Blackzy PRO hedphones M003', - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ut labore et dolore magna aliqua. Nec nam aliquam sem et tortor consequat id porta nibh. Orci porta non pulvinar neque laoreet suspendisse. Id nibh tortor id aliquet. Dui sapien eget mi proin. Viverra vitae congue eu consequat ac felis donec. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Vulputate mi sit amet mauris commodo quis imperdiet. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Sit amet cursus sit amet dictum sit amet justo. Mattis pellentesque id nibh tortor. Sed blandit libero volutpat sed cras ornare arcu dui. Fermentum et sollicitudin ac orci phasellus. Ipsum nunc aliquet bibendum enim facilisis gravida. Viverra suspendisse potenti nullam ac tortor. Dapibus ultrices in iaculis nunc sed. Nisi porta lorem mollis aliquam ut porttitor leo a. Phasellus egestas tellus rutrum tellus pellentesque. Et malesuada fames ac turpis egestas maecenas pharetra convallis. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Eros in cursus turpis massa. Eget mauris pharetra et ultrices neque.', - 152.99), - ]; - - List timeFilter = [ - 'Brand', - 'New', - 'Latest', - 'Trending', - 'Discount', - ]; - - List categoryFilter = [ - 'Skull Candy', - 'Boat', - 'JBL', - 'Micromax', - 'Seg', - ]; - - List priceFilter = [ - '\$50-200', - '\$200-400', - '\$400-800', - '\$800-1000', - ]; - - List searchResults = []; - - TextEditingController searchController = TextEditingController(); - - late RubberAnimationController _controller; - - @override - void initState() { - _controller = RubberAnimationController( - vsync: this, - halfBoundValue: AnimationControllerValue(percentage: 0.4), - upperBoundValue: AnimationControllerValue(percentage: 0.4), - lowerBoundValue: AnimationControllerValue(pixel: 50), - duration: Duration(milliseconds: 200)); - super.initState(); - } - - @override - void dispose() { - super.dispose(); - } - - void _expand() { - _controller.expand(); - } - - Widget _getLowerLayer() { - return Container( - margin: const EdgeInsets.only(top: kToolbarHeight), - child: Column( - children: [ - Padding( - padding: EdgeInsets.symmetric(horizontal: 16.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Search', - style: TextStyle( - color: darkGrey, - fontSize: 22, - fontWeight: FontWeight.bold, - ), - ), - CloseButton() - ], - ), - ), - Container( - margin: EdgeInsets.symmetric(horizontal: 16.0), - decoration: BoxDecoration( - border: - Border(bottom: BorderSide(color: Colors.orange, width: 1))), - child: TextField( - controller: searchController, - onChanged: (value) { - if (value.isNotEmpty) { - List tempList = []; - products.forEach((product) { - if (product.name.toLowerCase().contains(value)) { - tempList.add(product); - } - }); - setState(() { - searchResults.clear(); - searchResults.addAll(tempList); - }); - return; - } else { - setState(() { - searchResults.clear(); - searchResults.addAll(products); - }); - } - }, - cursorColor: darkGrey, - decoration: InputDecoration( - contentPadding: EdgeInsets.zero, - border: InputBorder.none, - prefixIcon: SvgPicture.asset( - 'assets/icons/search_icon.svg', - fit: BoxFit.scaleDown, - ), - suffix: FlatButton( - onPressed: () { - searchController.clear(); - searchResults.clear(); - }, - child: Text( - 'Clear', - style: TextStyle(color: Colors.red), - ), - ), - ), - ), - ), - Flexible( - child: Container( - color: Colors.orange[50], - child: ListView.builder( - itemCount: searchResults.length, - itemBuilder: (_, index) => Padding( - padding: EdgeInsets.symmetric(horizontal: 16.0), - child: ListTile( - onTap: () => - Navigator.of(context).push(MaterialPageRoute( - builder: (_) => ViewProductPage( - product: searchResults[index], - ))), - title: Text(searchResults[index].name), - ))), - ), - ) - ], - ), - ); - } - - Widget _getUpperLayer() { - return Container( - decoration: BoxDecoration( - boxShadow: [ - BoxShadow( - color: Color.fromRGBO(0, 0, 0, 0.05), - offset: Offset(0, -3), - blurRadius: 10) - ], - borderRadius: BorderRadius.only( - topRight: Radius.circular(24), topLeft: Radius.circular(24)), - color: Colors.white), - child: ListView( - physics: NeverScrollableScrollPhysics(), -// controller: _scrollController, - children: [ - Align( - alignment: Alignment.center, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - 'Filters', - style: TextStyle(color: Colors.grey[300]), - ), - ), - ), - Align( - alignment: Alignment.centerLeft, - child: Padding( - padding: - const EdgeInsets.only(left: 32.0, top: 16.0, bottom: 16.0), - child: Text( - 'Sort By', - style: TextStyle(fontWeight: FontWeight.bold), - ), - ), - ), - Container( - height: 50, - child: ListView.builder( - itemBuilder: (_, index) => Center( - child: Padding( - padding: EdgeInsets.symmetric( - horizontal: 8.0, - ), - child: InkWell( - onTap: () { - setState(() { - selectedPeriod = timeFilter[index]; - }); - }, - child: Container( - padding: EdgeInsets.symmetric( - vertical: 4.0, horizontal: 20.0), - decoration: selectedPeriod == timeFilter[index] - ? BoxDecoration( - color: Color(0xffFDB846), - borderRadius: - BorderRadius.all(Radius.circular(45))) - : BoxDecoration(), - child: Text( - timeFilter[index], - style: TextStyle(fontSize: 16.0), - ))), - )), - itemCount: timeFilter.length, - scrollDirection: Axis.horizontal, - ), - ), - Container( - height: 50, - child: ListView.builder( - itemBuilder: (_, index) => Center( - child: Padding( - padding: EdgeInsets.symmetric( - horizontal: 8.0, - ), - child: InkWell( - onTap: () { - setState(() { - selectedCategory = categoryFilter[index]; - }); - }, - child: Container( - padding: EdgeInsets.symmetric( - vertical: 4.0, horizontal: 20.0), - decoration: selectedCategory == categoryFilter[index] - ? BoxDecoration( - color: Color(0xffFDB846), - borderRadius: - BorderRadius.all(Radius.circular(45))) - : BoxDecoration(), - child: Text( - categoryFilter[index], - style: TextStyle(fontSize: 16.0), - ))), - )), - itemCount: categoryFilter.length, - scrollDirection: Axis.horizontal, - ), - ), - Container( - height: 50, - child: ListView.builder( - itemBuilder: (_, index) => Center( - child: Padding( - padding: EdgeInsets.symmetric( - horizontal: 8.0, - ), - child: InkWell( - onTap: () { - setState(() { - selectedPrice = priceFilter[index]; - }); - }, - child: Container( - padding: EdgeInsets.symmetric( - vertical: 4.0, horizontal: 20.0), - decoration: selectedPrice == priceFilter[index] - ? BoxDecoration( - color: Color(0xffFDB846), - borderRadius: - BorderRadius.all(Radius.circular(45))) - : BoxDecoration(), - child: Text( - priceFilter[index], - style: TextStyle(fontSize: 16.0), - ))), - )), - itemCount: priceFilter.length, - scrollDirection: Axis.horizontal, - ), - ) - ], - ), - ); - } - - @override - Widget build(BuildContext context) { - return Material( - color: Colors.white, - child: SafeArea( - top: true, - bottom: false, - child: Scaffold( -// bottomSheet: ClipRRect( -// borderRadius: BorderRadius.only( -// topRight: Radius.circular(25), topLeft: Radius.circular(25)), -// child: BottomSheet( -// onClosing: () {}, -// builder: (_) => Container( -// padding: EdgeInsets.all(16.0), -// child: Row( -// mainAxisAlignment: MainAxisAlignment.center, -// children: [Text('Filters')]), -// color: Colors.white, -// width: MediaQuery.of(context).size.height, -// )), -// ), - body: RubberBottomSheet( - lowerLayer: _getLowerLayer(), // The underlying page (Widget) - upperLayer: _getUpperLayer(), // The bottomsheet content (Widget) - animationController: _controller, // The one we created earlier - )), - ), - ); - } -} diff --git a/lib/screens/search_products/search_controller.dart b/lib/screens/search_products/search_controller.dart new file mode 100644 index 00000000..571cb3ef --- /dev/null +++ b/lib/screens/search_products/search_controller.dart @@ -0,0 +1,105 @@ +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/data/repository/product.repository.dart'; +import 'package:ecommerce_int2/utils/debounce.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; + +class SearchProductController extends GetxController { + final ProductRepository repository; + + SearchProductController(this.repository); + + TextEditingController searchController = TextEditingController(); + + RefreshController refreshController = + RefreshController(initialRefresh: false); + int currentPage = 1; + + int total = 0; + + ProductFilter filter = ProductFilter( + sortOrder: SortOrder(price: 1), + pageNumber: 1, + rating: 0, + min: 1, + max: 500000, + // subcategory: '', + // name: '', + // merchant: '', + order: 2); + + final Debounce debounce = Debounce(milliseconds: 2000); + + List list = []; + + ScrollController scrollController = ScrollController(); + + @override + void onInit() { + getArgument(); + getProduct(); + super.onInit(); + } + + void onClear() { + searchController.clear(); + onRefresh(); + update(); + } + + @override + void onReady() { + scrollController.addListener(() { + if (scrollController.position.pixels < + scrollController.position.maxScrollExtent - 100) { + // debounce.run(() { + // onLoadMore(); + // }); + } + }); + super.onReady(); + } + + void getArgument() { + final dynamic subCategory = Get.arguments; + if (subCategory != null && subCategory is String) { + filter.subcategory = subCategory; + } + } + + Future getProduct() async { + try { + final res = await repository.getProducts(filter); + total = res.totalProducts ?? 0; + list.addAll(res.products!); + update(); + } on Exception catch (e) { + print(e); + } + } + + void onRefresh() async { + list.clear(); + currentPage = 1; + filter.pageNumber = 1; + await getProduct(); + refreshController.refreshCompleted(); + } + + void onLoadMore() async { + if (list.length == total) return; + ++currentPage; + filter.pageNumber = currentPage; + await getProduct(); + refreshController.loadComplete(); + } + + void onChanged(String value) { + debounce.run(() { + filter.name = value; + onRefresh(); + update(); + }); + } +} diff --git a/lib/screens/search_products/search_page.dart b/lib/screens/search_products/search_page.dart new file mode 100644 index 00000000..5a9575f7 --- /dev/null +++ b/lib/screens/search_products/search_page.dart @@ -0,0 +1,121 @@ +// ignore_for_file: deprecated_member_use + +import 'package:ecommerce_int2/screens/product/components/product_card.dart'; +import 'package:ecommerce_int2/screens/product/product_page.dart'; +import 'package:ecommerce_int2/screens/search_products/search_controller.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; +import 'package:ecommerce_int2/utils/cart_button.widget.dart'; + +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get/get.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; + +// ignore: must_be_immutable +class SearchPage extends GetView { + String? subCategory; + + SearchPage({this.subCategory}); + + Widget _getLowerLayer(SearchProductController controller) { + return Container( + margin: const EdgeInsets.only(top: 16), + child: Column( + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: 16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + BackButton(), + Text( + 'Search', + style: TextStyle( + color: darkGrey, + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + Spacer(), + CartButton(), + ], + ), + ), + Container( + margin: EdgeInsets.symmetric(horizontal: 16.0), + decoration: BoxDecoration( + border: + Border(bottom: BorderSide(color: Colors.orange, width: 1))), + child: TextField( + controller: controller.searchController, + onChanged: (value) => controller.onChanged(value), + cursorColor: darkGrey, + decoration: InputDecoration( + contentPadding: EdgeInsets.zero, + border: InputBorder.none, + prefixIcon: SvgPicture.asset( + 'assets/icons/search_icon.svg', + fit: BoxFit.scaleDown, + ), + suffix: FlatButton( + onPressed: () { + controller.onClear(); + }, + child: Text( + 'Clear', + style: TextStyle(color: Colors.red), + ), + ), + ), + ), + ), + Expanded( + child: Container( + color: Colors.orange[50], + child: SmartRefresher( + enablePullDown: true, + enablePullUp: true, + controller: controller.refreshController, + onLoading: controller.onLoadMore, + onRefresh: controller.onRefresh, + header: WaterDropHeader(), + child: ListView.builder( + controller: controller.scrollController, + itemCount: controller.list.length, + itemBuilder: (_, index) => Padding( + padding: EdgeInsets.symmetric(horizontal: 16.0), + child: ProductCard( + controller.list[index], + () => Get.to(() => ProductPage( + product: controller.list[index], + ))), + )), + ), + ), + ) + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return Material( + color: Colors.white, + child: SafeArea( + top: true, + bottom: false, + child: Scaffold( + body: GetBuilder( + init: controller, + initState: (state) { + controller.filter.subcategory = subCategory; + controller.filter.name = ''; + controller.onRefresh(); + }, + builder: (controller) => _getLowerLayer(controller), + )), + ), + ); + } +} diff --git a/lib/screens/select_card_page.dart b/lib/screens/select_card_page.dart index 0216b4f2..65f7f66c 100755 --- a/lib/screens/select_card_page.dart +++ b/lib/screens/select_card_page.dart @@ -1,4 +1,4 @@ -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:flutter/material.dart'; import 'dart:math' as math; diff --git a/lib/screens/send_money/quick_send_amount_page.dart b/lib/screens/send_money/quick_send_amount_page.dart index b58ce663..fef3c26e 100755 --- a/lib/screens/send_money/quick_send_amount_page.dart +++ b/lib/screens/send_money/quick_send_amount_page.dart @@ -1,4 +1,4 @@ -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:ecommerce_int2/models/user.dart'; import 'package:ecommerce_int2/screens/request_money/receive_page.dart'; import 'package:flutter/material.dart'; diff --git a/lib/screens/send_money/send_page.dart b/lib/screens/send_money/send_page.dart index e153cb13..abd23393 100755 --- a/lib/screens/send_money/send_page.dart +++ b/lib/screens/send_money/send_page.dart @@ -1,11 +1,11 @@ -import 'package:ecommerce_int2/api_service.dart'; +import 'package:ecommerce_int2/utils/api_service.dart'; import 'package:ecommerce_int2/models/user.dart'; import 'package:ecommerce_int2/screens/send_money/quick_send_amount_page.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import '../../app_properties.dart'; +import '../../utils/app_properties.dart'; class SendPage extends StatefulWidget { @override diff --git a/lib/screens/settings/change_country.dart b/lib/screens/settings/change_country.dart index f5c73dc5..ab26033d 100755 --- a/lib/screens/settings/change_country.dart +++ b/lib/screens/settings/change_country.dart @@ -1,4 +1,4 @@ -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:flutter/material.dart'; class ChangeCountryPage extends StatefulWidget { diff --git a/lib/screens/settings/change_language_page.dart b/lib/screens/settings/change_language_page.dart index 1ae518e4..1fc4cd05 100755 --- a/lib/screens/settings/change_language_page.dart +++ b/lib/screens/settings/change_language_page.dart @@ -1,4 +1,4 @@ -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:flutter/material.dart'; class ChangeLanguagePage extends StatefulWidget { diff --git a/lib/screens/settings/change_password_page.dart b/lib/screens/settings/change_password_page.dart index 420841d4..7da4b017 100755 --- a/lib/screens/settings/change_password_page.dart +++ b/lib/screens/settings/change_password_page.dart @@ -1,6 +1,7 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:flutter/cupertino.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; +import 'package:ecommerce_int2/services/auth.service.dart'; import 'package:flutter/material.dart'; +import 'package:get/get.dart'; class ChangePasswordPage extends StatefulWidget { @override @@ -8,13 +9,15 @@ class ChangePasswordPage extends StatefulWidget { } class _ChangePasswordPageState extends State { + TextEditingController password = TextEditingController(); @override Widget build(BuildContext context) { + final controller = Get.find(); double width = MediaQuery.of(context).size.width; double bottomPadding = MediaQuery.of(context).padding.bottom; Widget changePasswordButton = InkWell( - onTap: () {}, + onTap: () {controller.resetPassword(password.text.trim());}, child: Container( height: 80, width: width / 1.5, @@ -45,7 +48,6 @@ class _ChangePasswordPageState extends State { iconTheme: IconThemeData( color: Colors.black, ), - brightness: Brightness.light, backgroundColor: Colors.transparent, title: Text( 'Settings', @@ -113,6 +115,7 @@ class _ChangePasswordPageState extends State { borderRadius: BorderRadius.all(Radius.circular(5))), child: TextField( + controller: password, decoration: InputDecoration( border: InputBorder.none, hintText: 'New Password', diff --git a/lib/screens/settings/legal_about_page.dart b/lib/screens/settings/legal_about_page.dart index 9b2bf0c7..a9667abe 100755 --- a/lib/screens/settings/legal_about_page.dart +++ b/lib/screens/settings/legal_about_page.dart @@ -1,7 +1,4 @@ -import 'dart:io'; - -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:flutter/cupertino.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:flutter/material.dart'; class LegalAboutPage extends StatefulWidget { diff --git a/lib/screens/settings/notifications_settings_page.dart b/lib/screens/settings/notifications_settings_page.dart index 3fd668aa..ddc6386a 100755 --- a/lib/screens/settings/notifications_settings_page.dart +++ b/lib/screens/settings/notifications_settings_page.dart @@ -1,6 +1,6 @@ import 'dart:io'; -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; diff --git a/lib/screens/settings/settings_page.dart b/lib/screens/settings/settings_page.dart index 0105377f..db3f0210 100755 --- a/lib/screens/settings/settings_page.dart +++ b/lib/screens/settings/settings_page.dart @@ -1,11 +1,12 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/custom_background.dart'; -import 'package:ecommerce_int2/screens/auth/welcome_back_page.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; +import 'package:ecommerce_int2/utils/custom_background.dart'; import 'package:ecommerce_int2/screens/settings/change_country.dart'; import 'package:ecommerce_int2/screens/settings/change_password_page.dart'; import 'package:ecommerce_int2/screens/settings/legal_about_page.dart'; import 'package:ecommerce_int2/screens/settings/notifications_settings_page.dart'; +import 'package:ecommerce_int2/services/auth.service.dart'; import 'package:flutter/material.dart'; +import 'package:get/get.dart'; import 'change_language_page.dart'; @@ -19,7 +20,6 @@ class SettingsPage extends StatelessWidget { iconTheme: IconThemeData( color: Colors.black, ), - brightness: Brightness.light, backgroundColor: Colors.transparent, title: Text( 'Settings', @@ -96,8 +96,7 @@ class SettingsPage extends StatelessWidget { ListTile( title: Text('Sign out'), leading: Image.asset('assets/icons/sign_out.png'), - onTap: () => Navigator.of(context).push( - MaterialPageRoute(builder: (_) => WelcomeBackPage())), + onTap: () => Get.find().logout(), ), ], diff --git a/lib/screens/shop/cart.controller.dart b/lib/screens/shop/cart.controller.dart new file mode 100644 index 00000000..cd231e50 --- /dev/null +++ b/lib/screens/shop/cart.controller.dart @@ -0,0 +1,103 @@ +import 'package:ecommerce_int2/data/models/cart.model.dart'; +import 'package:ecommerce_int2/data/models/order.model.dart'; +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/data/repository/cart.repository.dart'; +import 'package:ecommerce_int2/screens/address/add_address_page.dart'; +import 'package:ecommerce_int2/utils/message_dialog.dart'; +import 'package:get/get.dart'; + +class CartController extends GetxController { + final CartRepository repository; + + CartController(this.repository); + + bool isLoadingCart = false; + + List get products { + if (carts == null) return []; + List products = []; + for (CartModel cart in carts!) { + products.addAll(cart.products!); + } + return products; + } + + List? carts; + + @override + onInit() { + super.onInit(); + } + + String get total { + int result = 0; + products.forEach((element) { + result += (element.price ?? element.purchasePrice ?? 0) * + (element.quantity ?? 1); + }); + return '$result VND'; + } + + Future getCart() async { + isLoadingCart = true; + update(); + repository.getCarts().then((value) { + carts = value; + isLoadingCart = false; + update(); + }); + } + + void remove(Product product) { + CartModel? cart = carts! + .firstWhereOrNull((element) => element.products!.contains(product)); + if (cart == null) { + MessageDialog.showToast("There is something wrong!!"); + return; + } + repository.deleteProduct(cart.sId!, product.sId!); + cart.products!.remove(product); + update(); + } + + void addProduct(Product product, {int? quantity}) async { + try { + AddProductParam param = AddProductParam(products: [ + ProductParam( + product: product.sId, + price: product.price, + merchant: product.merchant, + quantity: quantity) + ]); + await repository.addProduct(param); + MessageDialog.showToast("Added product to cart"); + update(); + } on Exception catch (e) { + print(e); + } + } + + void checkOut() async { + MessageDialog.showLoading(); + final List orderIds = await repository.checkOutCart(carts!); + MessageDialog.hideLoading(); + Get.to(() => AddAddressPage(), arguments: orderIds); + } + + void onChangeQuantity(String id, int quantity) { + try { + final product = products.firstWhere((element) => element.sId == id); + repository.modifyProduct( + carts! + .firstWhere((element) => element.products!.contains(product)) + .sId!, + product.sId!, + product.quantity!, + quantity); + product.quantity = quantity; + update(); + } on Exception catch (e) { + print(e.toString()); + } + } +} diff --git a/lib/screens/shop/check_out_page.dart b/lib/screens/shop/check_out_page.dart index 9a2d4d28..85a452d2 100755 --- a/lib/screens/shop/check_out_page.dart +++ b/lib/screens/shop/check_out_page.dart @@ -1,58 +1,39 @@ -import 'package:card_swiper/card_swiper.dart'; -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/models/product.dart'; -import 'package:ecommerce_int2/screens/address/add_address_page.dart'; -import 'package:ecommerce_int2/screens/payment/unpaid_page.dart'; +import 'package:ecommerce_int2/data/repository/cart.repository.dart'; +import 'package:ecommerce_int2/screens/shop/cart.controller.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:flutter/material.dart'; +import 'package:get/get.dart'; -import 'components/credit_card.dart'; import 'components/shop_item_list.dart'; -class CheckOutPage extends StatefulWidget { - @override - _CheckOutPageState createState() => _CheckOutPageState(); -} - -class _CheckOutPageState extends State { - SwiperController swiperController = SwiperController(); - - List products = [ - Product('assets/headphones.png', - 'Boat roackerz 400 On-Ear Bluetooth Headphones', 'description', 45.3), - Product('assets/headphones_2.png', - 'Boat roackerz 100 On-Ear Bluetooth Headphones', 'description', 22.3), - Product('assets/headphones_3.png', - 'Boat roackerz 300 On-Ear Bluetooth Headphones', 'description', 58.3) - ]; - +class CheckOutPage extends StatelessWidget { @override Widget build(BuildContext context) { - Widget checkOutButton = InkWell( - onTap: () => Navigator.of(context) - .push(MaterialPageRoute(builder: (_) => AddAddressPage())), - child: Container( - height: 80, - width: MediaQuery.of(context).size.width / 1.5, - decoration: BoxDecoration( - gradient: mainButton, - boxShadow: [ - BoxShadow( - color: Color.fromRGBO(0, 0, 0, 0.16), - offset: Offset(0, 5), - blurRadius: 10.0, - ) - ], - borderRadius: BorderRadius.circular(9.0)), - child: Center( - child: Text("Check Out", - style: const TextStyle( - color: const Color(0xfffefefe), - fontWeight: FontWeight.w600, - fontStyle: FontStyle.normal, - fontSize: 20.0)), - ), - ), - ); + Widget checkOutButton(CartController controller) => InkWell( + onTap: () => controller.checkOut(), + child: Container( + height: 80, + width: MediaQuery.of(context).size.width / 1.5, + decoration: BoxDecoration( + gradient: mainButton, + boxShadow: [ + BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.16), + offset: Offset(0, 5), + blurRadius: 10.0, + ) + ], + borderRadius: BorderRadius.circular(9.0)), + child: Center( + child: Text("Check Out", + style: const TextStyle( + color: const Color(0xfffefefe), + fontWeight: FontWeight.w600, + fontStyle: FontStyle.normal, + fontSize: 20.0)), + ), + ), + ); return Scaffold( backgroundColor: Colors.white, @@ -60,13 +41,6 @@ class _CheckOutPageState extends State { backgroundColor: Colors.transparent, elevation: 0.0, iconTheme: IconThemeData(color: darkGrey), - actions: [ - IconButton( - icon: Image.asset('assets/icons/denied_wallet.png'), - onPressed: () => Navigator.of(context) - .push(MaterialPageRoute(builder: (_) => UnpaidPage())), - ) - ], title: Text( 'Checkout', style: TextStyle( @@ -78,83 +52,87 @@ class _CheckOutPageState extends State { physics: ClampingScrollPhysics(), child: ConstrainedBox( constraints: BoxConstraints(minHeight: constraints.maxHeight), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - padding: EdgeInsets.symmetric(horizontal: 32.0), - height: 48.0, - color: yellow, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Subtotal', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16), - ), - Text( - products.length.toString() + ' items', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16), - ) - ], + child: GetBuilder( + init: CartController(Get.find()), + initState: (state) { + Get.find().getCart(); + }, + builder: (controller) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: EdgeInsets.symmetric(horizontal: 32.0), + height: 48.0, + color: yellow, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Subtotal', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16), + ), + Text( + controller.products.length.toString() + ' items', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16), + ) + ], + ), ), - ), - SizedBox( - height: 300, - child: Scrollbar( - child: ListView.builder( - itemBuilder: (_, index) => ShopItemList( - products[index], - onRemove: () { - setState(() { - products.remove(products[index]); - }); - }, + if (controller.isLoadingCart) + Container( + height: 100, + child: + Center(child: CircularProgressIndicator.adaptive()), + ), + if (!controller.isLoadingCart) + ...controller.products.map( + (product) => ShopItemList( + product, + onChange: (a, b) => controller.onChangeQuantity(a, b), + onRemove: () => controller.remove(product), ), - itemCount: products.length, + ), + Container( + padding: EdgeInsets.symmetric(horizontal: 32.0), + height: 48.0, + color: yellow, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Total', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16), + ), + Text( + controller.total, + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16), + ) + ], ), ), - ), - Padding( - padding: const EdgeInsets.all(16.0), - child: Text( - 'Payment', - style: TextStyle( - fontSize: 20, - color: darkGrey, - fontWeight: FontWeight.bold), - ), - ), - SizedBox( - height: 250, - child: Swiper( - itemCount: 2, - itemBuilder: (_, index) { - return CreditCard(); - }, - scale: 0.8, - controller: swiperController, - viewportFraction: 0.6, - loop: false, - fade: 0.7, - ), - ), - SizedBox(height: 24), - Center( - child: Padding( - padding: EdgeInsets.only( - bottom: MediaQuery.of(context).padding.bottom == 0 - ? 20 - : MediaQuery.of(context).padding.bottom), - child: checkOutButton, - )) - ], + SizedBox(height: 24), + Center( + child: Padding( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom == 0 + ? 20 + : MediaQuery.of(context).padding.bottom), + child: checkOutButton(controller), + )) + ], + ), ), ), ), diff --git a/lib/screens/shop/components/credit_card.dart b/lib/screens/shop/components/credit_card.dart index 0ac1e6cd..47ca4ec2 100755 --- a/lib/screens/shop/components/credit_card.dart +++ b/lib/screens/shop/components/credit_card.dart @@ -1,47 +1,23 @@ import 'package:flutter/material.dart'; class CreditCard extends StatelessWidget { + final String text; + + const CreditCard({Key? key,required this.text}) : super(key: key); @override Widget build(BuildContext context) { return Container( - height: 200, + height: 50, width: 250, padding: EdgeInsets.all(16.0), decoration: BoxDecoration( color: Colors.deepPurple[700], borderRadius: BorderRadius.all(Radius.circular(10))), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Text( - 'CREDIT CARD', - style: TextStyle(color: Colors.white), - ), - Container( - height: 25, - width: 40, - color: Colors.white, - ), - Text( - 'xxxx - xxxx - xxxx - 4951', - style: TextStyle(color: Colors.white), - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Name', - style: TextStyle(color: Colors.grey), - ), - Text( - 'GEORGE W BUSH', - style: TextStyle(color: Colors.white), - ), - ], - ) - ], + child: Center( + child: Text( + text, + style: TextStyle(color: Colors.white), + ), ), ); } diff --git a/lib/screens/shop/components/shop_item_list.dart b/lib/screens/shop/components/shop_item_list.dart index 2358eb22..7c00cbb6 100755 --- a/lib/screens/shop/components/shop_item_list.dart +++ b/lib/screens/shop/components/shop_item_list.dart @@ -1,5 +1,5 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:ecommerce_int2/models/product.dart'; +import 'package:ecommerce_int2/data/models/product.model.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:ecommerce_int2/screens/product/components/color_list.dart'; import 'package:ecommerce_int2/screens/product/components/shop_product.dart'; import 'package:flutter/material.dart'; @@ -8,27 +8,26 @@ import 'package:numberpicker/numberpicker.dart'; class ShopItemList extends StatefulWidget { final Product product; final VoidCallback onRemove; + final Function(String, int) onChange; - ShopItemList(this.product, {required this.onRemove}); + ShopItemList(this.product, {required this.onRemove,required this.onChange}); @override _ShopItemListState createState() => _ShopItemListState(); } class _ShopItemListState extends State { - int quantity = 1; - @override Widget build(BuildContext context) { return Container( - margin: EdgeInsets.only(top: 20), + margin: EdgeInsets.symmetric(vertical: 10), height: 130, child: Stack( children: [ Align( alignment: Alignment(0, 0.8), child: Container( - height: 100, + height: 120, margin: EdgeInsets.symmetric(horizontal: 16.0), decoration: BoxDecoration( color: Colors.white, @@ -45,36 +44,38 @@ class _ShopItemListState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - widget.product.name, - textAlign: TextAlign.right, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 12, - color: darkGrey, - ), - ), Align( alignment: Alignment.centerRight, child: Container( width: 160, padding: const EdgeInsets.only( left: 32.0, top: 8.0, bottom: 8.0), - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - ColorOption(Colors.red), - Text( - '\$${widget.product.price}', - textAlign: TextAlign.center, - style: TextStyle( - color: darkGrey, - fontWeight: FontWeight.bold, - fontSize: 18.0), - ) - ], - ), + child: Column(children: [ + Text( + widget.product.name ?? 'Product', + textAlign: TextAlign.right, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, + color: darkGrey, + ), + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + ColorOption(Colors.red), + Text( + '\$${widget.product.price ?? widget.product.purchasePrice ?? 0}', + textAlign: TextAlign.center, + style: TextStyle( + color: darkGrey, + fontWeight: FontWeight.bold, + fontSize: 18.0), + ) + ], + ), + ]), ), ) ], @@ -82,7 +83,6 @@ class _ShopItemListState extends State { ), Theme( data: ThemeData( - accentColor: Colors.black, textTheme: TextTheme( headline6: TextStyle( fontFamily: 'Montserrat', @@ -96,13 +96,11 @@ class _ShopItemListState extends State { ), )), child: NumberPicker( - value: quantity, + value: widget.product.quantity ?? 1, minValue: 1, maxValue: 10, onChanged: (value) { - setState(() { - quantity = value; - }); + widget.onChange( widget.product.sId!, value); }, )) ])), diff --git a/lib/screens/splash_page.dart b/lib/screens/splash_page.dart index 4420ceb6..dde8322b 100755 --- a/lib/screens/splash_page.dart +++ b/lib/screens/splash_page.dart @@ -1,4 +1,4 @@ -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:ecommerce_int2/screens/auth/welcome_back_page.dart'; import 'package:flutter/material.dart'; @@ -53,19 +53,6 @@ class _SplashScreenState extends State opacity: opacity.value, child: new Image.asset('assets/logo.png')), ), - Padding( - padding: const EdgeInsets.all(8.0), - child: RichText( - text: TextSpan( - style: TextStyle(color: Colors.black), - children: [ - TextSpan(text: 'Powered by '), - TextSpan( - text: 'int2.io', - style: TextStyle(fontWeight: FontWeight.bold)) - ]), - ), - ) ], ), ), diff --git a/lib/screens/tracking/order.controller.dart b/lib/screens/tracking/order.controller.dart new file mode 100644 index 00000000..b3c9f171 --- /dev/null +++ b/lib/screens/tracking/order.controller.dart @@ -0,0 +1,57 @@ +import 'package:ecommerce_int2/data/models/order.model.dart'; +import 'package:ecommerce_int2/data/repository/order.repository.dart'; +import 'package:ecommerce_int2/screens/main/main_page.dart'; +import 'package:ecommerce_int2/screens/tracking/order_detail.view.dart'; +import 'package:ecommerce_int2/utils/message_dialog.dart'; +import 'package:get/get.dart'; + +class OrderController extends GetxController { + final OrderRepository repository; + + OrderController(this.repository); + + List orders = []; + + List get notProcessList => + orders.where((element) => element.status == 'NOT_PROCESSED').toList(); + List get processingList => + orders.where((element) => element.status == 'PROCESSING').toList(); + List get cancelledList => + orders.where((element) => element.status == 'CANCELLED').toList(); + List get deliveringList => + orders.where((element) => element.status == 'DELIVERING').toList(); + List get receivedList => + orders.where((element) => element.status == 'RECEIVED').toList(); + + @override + void onReady() { + getAllOrders(); + super.onReady(); + } + + void onBack() { + if (Get.arguments != null && Get.arguments == true) { + Get.offAll(() => MainPage()); + } else { + Get.back(); + } + } + + void getAllOrders() { + MessageDialog.showLoading(); + repository.getAllOrders().then((value) { + orders = value; + update(); + MessageDialog.hideLoading(); + }); + } + + void getOrderDetail(Order order) { + Get.to(() => OrderDetail(orderId: order.sId!)); + } + + void remove(String orderId) { + orders.removeWhere((element) => element.sId == orderId); + update(); + } +} diff --git a/lib/screens/tracking/order_detail.controller.dart b/lib/screens/tracking/order_detail.controller.dart new file mode 100644 index 00000000..cbdb0586 --- /dev/null +++ b/lib/screens/tracking/order_detail.controller.dart @@ -0,0 +1,68 @@ +import 'package:ecommerce_int2/data/models/order.model.dart'; +import 'package:ecommerce_int2/data/repository/order.repository.dart'; +import 'package:ecommerce_int2/screens/tracking/order.controller.dart'; +import 'package:ecommerce_int2/utils/message_dialog.dart'; +import 'package:ecommerce_int2/utils/webview.dart'; +import 'package:get/get.dart'; + +class OrderDetailController extends GetxController { + final OrderRepository repository; + + final OrderController orderController; + + final String orderDetail; + + OrderDetailController( + this.repository, this.orderDetail, this.orderController); + + OrderDocDetail? detail; + + @override + void onReady() { + getOrder(); + super.onReady(); + } + + void getOrder() { + MessageDialog.showLoading(); + repository.getOrderDetail(orderDetail).then((value) { + detail = value; + MessageDialog.hideLoading(); + update(); + }); + } + + void remove(String orderId) { + repository.cancleOrder(orderId); + orderController.remove(orderId); + Get.back(); + } + + void makePayment(String orderId) async { + MessageDialog.showLoading(); + try { + final String href = await repository.makePayment(orderId); + await Get.to(() => WebViewExample(url: href)); + await repository + .getOrderDetail(orderDetail) + .then((value) => detail = value); + orderController.getAllOrders(); + } on Exception catch (e) { + MessageDialog.showToast(e.toString()); + } + MessageDialog.hideLoading(); + } + + void confirmOrder(String orderId) async { + MessageDialog.showLoading(); + try { + await repository.confirmReceived(orderId); + orderController.getAllOrders(); + MessageDialog.hideLoading(); + Get.back(); + } on Exception catch (e) { + MessageDialog.hideLoading(); + MessageDialog.showToast(e.toString()); + } + } +} diff --git a/lib/screens/tracking/order_detail.view.dart b/lib/screens/tracking/order_detail.view.dart new file mode 100644 index 00000000..728bc3ca --- /dev/null +++ b/lib/screens/tracking/order_detail.view.dart @@ -0,0 +1,169 @@ +import 'package:ecommerce_int2/data/repository/order.repository.dart'; +import 'package:ecommerce_int2/screens/main/components/product_list.dart'; +import 'package:ecommerce_int2/screens/tracking/order.controller.dart'; +import 'package:ecommerce_int2/screens/tracking/order_detail.controller.dart'; +import 'package:ecommerce_int2/screens/tracking/row_text.widget.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; + +class OrderDetail extends StatelessWidget { + final String orderId; + + const OrderDetail({Key? key, required this.orderId}) : super(key: key); + + @override + Widget build(BuildContext context) { + return GetBuilder( + init: OrderDetailController( + Get.find(), orderId, Get.find()), + builder: (controller) => Container( + decoration: BoxDecoration( + color: Colors.grey[100], + image: DecorationImage( + image: AssetImage('assets/Group 444.png'), + fit: BoxFit.contain)), + child: Container( + color: Colors.white54, + child: Scaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0.0, + iconTheme: IconThemeData(color: Colors.grey), + title: Text( + orderId, + style: TextStyle( + color: darkGrey, + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + leading: BackButton(), + ), + body: SafeArea( + child: LayoutBuilder( + builder: (_, constraints) => SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + maxHeight: constraints.maxHeight - 48, + ), + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: controller.detail != null + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 24), + RowTextWidget( + text1: 'Deliver status', + text2: + ' ${controller.detail?.status ?? ''}'), + RowTextWidget( + text1: 'Payment status', + text2: + '${controller.detail?.paymentStatus ?? ''}'), + RowTextWidget( + text1: 'Merchant', + text2: + '${controller.detail?.merchant ?? ''}'), + RowTextWidget( + text1: 'Date', + text2: + '${controller.detail?.createdDate ?? ''}'), + const SizedBox(height: 16), + ...controller.detail!.cart!.products! + .map((product) => ProductCard( + product: product, + height: 130, + hasReceived: controller.detail?.status == 'RECEIVED', + width: Get.width)) + .toList(), + const SizedBox(height: 16), + if (controller.detail!.canCancel) + Button( + title: 'Cancel Order', + onTap: () => controller.remove(orderId), + ), + if (controller.detail!.canConfirmReceive) + Button( + title: 'Confirm Received', + onTap: () => + controller.confirmOrder(orderId), + ), + ], + ) + : SizedBox.shrink(), + ), + ), + ), + ), + ), + )), + ), + ), + ); + } +} + +class Button extends StatelessWidget { + const Button({Key? key, required this.onTap, required this.title}) + : super(key: key); + + final String title; + final Function onTap; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: GestureDetector( + onTap: (() => onTap()), + child: Center( + child: Container( + height: 40, + width: MediaQuery.of(context).size.width / 1.5, + decoration: BoxDecoration( + gradient: mainButton, + boxShadow: [ + BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.16), + offset: Offset(0, 5), + blurRadius: 10.0, + ) + ], + borderRadius: BorderRadius.circular(9.0)), + child: Center( + child: Text(title, + style: const TextStyle( + color: const Color(0xfffefefe), + fontWeight: FontWeight.w600, + fontStyle: FontStyle.normal, + fontSize: 20.0)), + ), + ), + ), + ), + ); + } +} + +class Location { + String city; + DateTime date; + bool showHour; + bool isHere; + bool passed; + + Location(this.city, this.date, + {this.showHour = false, this.isHere = false, this.passed = false}); + + String getDate() { + if (showHour) { + return DateFormat("K:mm aaa, d MMMM y").format(date); + } else { + return DateFormat('d MMMM y').format(date); + } + } +} diff --git a/lib/screens/tracking/row_text.widget.dart b/lib/screens/tracking/row_text.widget.dart new file mode 100644 index 00000000..3b6afed0 --- /dev/null +++ b/lib/screens/tracking/row_text.widget.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +// ignore: must_be_immutable +class RowTextWidget extends StatelessWidget { + RowTextWidget( + {required this.text1, + required this.text2, + Key? key, + this.isBold = false, + this.isHighlight = false}) + : super(key: key); + final String text1; + final String text2; + bool isBold; + final bool isHighlight; + + @override + Widget build(BuildContext context) { + TextStyle subHeader = TextStyle(fontSize: 14); + final style = !isBold + ? subHeader.copyWith(color: const Color(0xFF292B30)) + : subHeader.copyWith( + color: const Color(0xFF292B30), fontWeight: FontWeight.w700); + var textStyle = style; + if (isHighlight) { + textStyle = style.copyWith(fontSize: 16); + } + return SizedBox( + height: 56, + child: Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(text1, style: textStyle), + const SizedBox(width: 12), + Container( + constraints: BoxConstraints(maxWidth: Get.width * 0.5), + child: Text( + text2, + style: textStyle, + overflow: TextOverflow.ellipsis, + ), + ) + ], + ), + ), + ); + } +} diff --git a/lib/screens/tracking/tracking_page.dart b/lib/screens/tracking/tracking_page.dart new file mode 100644 index 00000000..de2537d2 --- /dev/null +++ b/lib/screens/tracking/tracking_page.dart @@ -0,0 +1,125 @@ +import 'package:ecommerce_int2/data/models/order.model.dart'; +import 'package:ecommerce_int2/data/repository/order.repository.dart'; +import 'package:ecommerce_int2/screens/tracking/order.controller.dart'; +import 'package:ecommerce_int2/screens/tracking/row_text.widget.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class TrackingPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return GetBuilder( + init: OrderController(Get.find()), + builder: (controller) => Container( + decoration: BoxDecoration( + color: Colors.grey[100], + image: DecorationImage( + image: AssetImage('assets/Group 444.png'), + fit: BoxFit.contain)), + child: Container( + color: Colors.white54, + child: MaterialApp( + home: DefaultTabController( + length: 5, + child: Scaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0.0, + iconTheme: IconThemeData(color: Colors.grey), + title: Text( + 'Your orders', + style: TextStyle( + color: darkGrey, + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + leading: + BackButton(onPressed: () => controller.onBack()), + bottom: TabBar( + labelColor: Colors.black, + isScrollable: true, + tabs: [ + Tab(child: Text('Not processing')), + Tab(child: Text('Processing')), + Tab(child: Text('Delivering')), + Tab(child: Text('Cancel')), + Tab(child: Text('Received')), + ], + ), + ), + body: TabBarView( + children: [ + Body(orders: controller.notProcessList), + Body(orders: controller.processingList), + Body(orders: controller.deliveringList), + Body(orders: controller.cancelledList), + Body(orders: controller.receivedList), + ], + ), + ), + ), + ), + ), + )); + } +} + +class Body extends StatelessWidget { + const Body({Key? key, required this.orders}) : super(key: key); + + final List orders; + @override + Widget build(BuildContext context) { + return GetBuilder( + builder: (controller) => SafeArea( + child: LayoutBuilder( + builder: (_, constraints) => SingleChildScrollView( + child: orders.isEmpty + ? Padding( + padding: const EdgeInsets.all(18.0), + child: Center(child: Text('Empty')), + ) + : Column( + children: [ + ...orders + .map((order) => GestureDetector( + onTap: (() => controller.getOrderDetail(order)), + child: Container( + width: Get.width, + child: Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + RowTextWidget( + text1: 'Date', + text2: '${order.createdDate}'), + RowTextWidget( + text1: 'Deliver status', + text2: ' ${order.status ?? ''}'), + RowTextWidget( + text1: 'Payment status', + text2: + '${order.paymentStatus ?? ''}'), + RowTextWidget( + text1: 'Merchant', + text2: '${order.merchant ?? ''}'), + ], + crossAxisAlignment: + CrossAxisAlignment.start, + ), + ), + ), + ), + )) + .toList() + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/screens/tracking_page.dart b/lib/screens/tracking_page.dart deleted file mode 100755 index 9d12c96e..00000000 --- a/lib/screens/tracking_page.dart +++ /dev/null @@ -1,169 +0,0 @@ -import 'package:ecommerce_int2/app_properties.dart'; -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; - -class TrackingPage extends StatefulWidget { - @override - _TrackingPageState createState() => _TrackingPageState(); -} - -class _TrackingPageState extends State { - final List locations = [ - Location('Kolkata Facility', DateTime(2019, 6, 5, 5, 23, 4), - showHour: false, isHere: false, passed: true), - Location('Hyderabad Facility', DateTime(2019, 6, 6, 5, 23, 4), - showHour: false, isHere: false, passed: true), - Location( - 'Chennai Facility', - DateTime(2019, 6, 9, 5, 23, 4), - showHour: false, - isHere: true, - ), - Location( - 'Kerala Facility', - DateTime(2019, 6, 10, 5, 23, 4), - showHour: true, - isHere: false, - ), - ]; - - String selectedProduct = 'Boat Headphones Bass boost 100v'; - - @override - Widget build(BuildContext context) { - return Container( - decoration: BoxDecoration( - color: Colors.grey[100], - image: DecorationImage( - image: AssetImage('assets/Group 444.png'), fit: BoxFit.contain)), - child: Container( - color: Colors.white54, - child: Scaffold( - appBar: AppBar( - backgroundColor: Colors.transparent, - elevation: 0.0, - brightness: Brightness.light, - iconTheme: IconThemeData(color: Colors.grey), - title: Text( - 'Shipped', - style: TextStyle( - color: darkGrey, - fontSize: 22, - fontWeight: FontWeight.bold, - ), - ), - leading: SizedBox(), - actions: [CloseButton()], - ), - body: SafeArea( - child: LayoutBuilder( - builder: (_, constraints) => Column( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - margin: const EdgeInsets.symmetric(horizontal: 16.0), - padding: const EdgeInsets.symmetric(horizontal: 16.0), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.all(Radius.circular(5))), - child: DropdownButtonHideUnderline( - child: DropdownButton( - items: [ - 'Boat Headphones Bass boost 100v', - 'Boat Headphones Bass boost 200v', - 'Boat Headphones Bass boost 300v', - 'Boat Headphones Bass boost 400v', - 'Boat Headphones Bass boost 500v', - 'Boat Headphones Bass double boosting 600v' - ].map((val) { - return DropdownMenuItem( - value: val, - child: Container( - color: Colors.white, - child: Align( - alignment: Alignment.centerLeft, - child: Text( - val, - maxLines: 2, - semanticsLabel: '...', - overflow: TextOverflow.ellipsis, - ))), - ); - }).toList(), - onChanged: (newValue) { - setState(() { - selectedProduct = newValue as String; - }); - }, - value: selectedProduct, - isExpanded: true, - icon: Icon(Icons.keyboard_arrow_down), - elevation: 0, - ), - ), - ), - SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints( - maxHeight: constraints.maxHeight - 48, - ), - child: Theme( - data: ThemeData( - primaryColor: yellow, fontFamily: 'Montserrat'), - child: Stepper( -// physics: NeverScrollableScrollPhysics(), - steps: [ - ...locations - .map( - (location) => Step( - isActive: - location.isHere || location.passed, - title: Text(location.city), - subtitle: Text(location.getDate()), - content: Align( - child: Image.asset( - 'assets/icons/truck.png'), - alignment: Alignment.centerLeft, - ), - state: location.passed - ? StepState.complete - : location.isHere - ? StepState.editing - : StepState.indexed, - ), - ) - .toList() - ], - currentStep: locations.indexOf( - locations.firstWhere((loc) => loc.isHere)), - ), - ), - ), - ), - ], - ), - ), - )), - ), - ); - } -} - -class Location { - String city; - DateTime date; - bool showHour; - bool isHere; - bool passed; - - Location(this.city, this.date, - {this.showHour = false, this.isHere = false, this.passed = false}); - - String getDate() { - if (showHour) { - return DateFormat("K:mm aaa, d MMMM y").format(date); - } else { - return DateFormat('d MMMM y').format(date); - } - } -} diff --git a/lib/screens/wallet/wallet_page.dart b/lib/screens/wallet/wallet_page.dart index b776669c..31db5476 100755 --- a/lib/screens/wallet/wallet_page.dart +++ b/lib/screens/wallet/wallet_page.dart @@ -1,5 +1,5 @@ -import 'package:ecommerce_int2/api_service.dart'; -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:ecommerce_int2/utils/api_service.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:ecommerce_int2/models/user.dart'; import 'package:ecommerce_int2/screens/payment_history_page.dart'; import 'package:ecommerce_int2/screens/request_money/request_amount_page.dart'; diff --git a/lib/services/auth.service.dart b/lib/services/auth.service.dart new file mode 100644 index 00000000..3bc0a296 --- /dev/null +++ b/lib/services/auth.service.dart @@ -0,0 +1,95 @@ +import 'package:ecommerce_int2/utils/app_properties.dart'; +import 'package:ecommerce_int2/data/models/user.model.dart'; +import 'package:ecommerce_int2/data/repository/auth.repository.dart'; +import 'package:ecommerce_int2/screens/auth/welcome_back_page.dart'; +import 'package:ecommerce_int2/screens/main/main_page.dart'; +import 'package:ecommerce_int2/utils/message_dialog.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:get_storage/get_storage.dart'; + +class AuthService extends GetxService { + final AuthRepository repository; + + final store = GetStorage(); + + UserModel? userModel; + + AuthService(this.repository); + + @override + void onInit() { + // login('manmaihuu@gmail.com', '123456A'); + getUserInfo(); + super.onInit(); + } + + + void getUserInfo() async { + try { + final res = await repository.getUserInfo(); + userModel = res; + store.write(user, userModel!.toJson()); + Navigator.of(Get.context!) + .pushReplacement(MaterialPageRoute(builder: (_) => MainPage())); + } catch (e) { + Navigator.of(Get.context!).pushReplacement( + MaterialPageRoute(builder: (_) => WelcomeBackPage())); + } + } + + Future login(String username, String password) async { + try { + MessageDialog.showLoading(); + final res = await repository.login(username, password); + MessageDialog.hideLoading(); + await setAccessToken(res.token); + getUserInfo(); + Navigator.of(Get.context!) + .pushReplacement(MaterialPageRoute(builder: (_) => MainPage())); + } catch (e) { + print(e); + } + } + + Future setAccessToken(String token) async { + await store.write(accessToken, token); + } + + String getAccessToken() { + final String? token = store.read(accessToken); + if (token == null) throw Exception("Access token not available"); + return token; + } + + Future register(String username, String password, String firstName, + String lastName) async { + try { + repository.register(username, password, firstName, lastName); + } on Exception catch (e) { + Navigator.of(Get.context!).pushReplacement( + MaterialPageRoute(builder: (_) => WelcomeBackPage())); + } + } + + Future forgetPassword(String email) async { + try { + repository.forgotPassword(email); + } finally { + Navigator.of(Get.context!).pushReplacement( + MaterialPageRoute(builder: (_) => WelcomeBackPage())); + } + } + + void logout() { + store.remove(accessToken); + Navigator.of(Get.context!) + .pushReplacement(MaterialPageRoute(builder: (_) => WelcomeBackPage())); + } + + void resetPassword(String password) { + try { + repository.resetPassword(password); + } catch (e) {} + } +} diff --git a/lib/services/network.service.dart b/lib/services/network.service.dart new file mode 100644 index 00000000..a52b2d43 --- /dev/null +++ b/lib/services/network.service.dart @@ -0,0 +1,198 @@ +import 'package:dio/dio.dart'; +import 'package:ecommerce_int2/interceptor/dio_interceptor.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:path_provider/path_provider.dart'; + +class HttpResponse { + HttpResponse({ + this.body, + this.headers, + this.request, + this.statusCode, + this.statusMessage, + this.extra, + }); + + T? body; + Headers? headers; + RequestOptions? request; + int? statusCode; + String? statusMessage; + Map? extra; +} + +class NetWorkService { + Dio _dio = Dio(); + + NetWorkService() { + _dio.interceptors.add(DioInterceptors()); + } + + Dio getDio() { + return _dio; + } + + String? getUriQueryParam(Uri uri, String key) { + final queryParams = uri.queryParametersAll.entries.toList(); + if (queryParams.any((_) => _.key == key)) { + return queryParams.firstWhere((_) => _.key == key).value.first; + } + return null; + } + + FormData mapToFormData(Map map) { + final formData = FormData.fromMap(map); + final FormData newData = FormData(); + + /// + formData.fields.forEach((element) { + newData.fields.add(MapEntry(element.key, element.value)); + }); + + /// + formData.files.forEach((element) { + if (element.key.contains('[') || element.key.contains(']')) { + final newKey = + element.key.replaceAllMapped(RegExp('(\[+[a-zA-Z]+\])'), (m) { + return '${m[0]}'.replaceAll('[', '.').replaceAll(']', ''); + }); + final newValue = element.value; + final newEntry = MapEntry(newKey, newValue); + newData.files.add(newEntry); + } else { + newData.files.add(element); + } + }); + return newData; + } + + Future get(String url, + {Map? queryParameters, Options? options}) async { + final Dio dio = getDio(); + debugPrint("--[GET]--: $url"); + final Response response = await dio.get( + url, + queryParameters: queryParameters, + options: options, + ); + return HttpResponse( + body: response.data, + headers: response.headers, + request: response.requestOptions, + statusCode: response.statusCode, + statusMessage: response.statusMessage, + extra: response.extra, + ); + } + + /// + /// Return the stored File path in the TemporaryDirectory + /// + Future downloadFile(String url, + {ProgressCallback? onReceiveProgress, + Map? queryParameters, + Options? options}) async { + final Dio dio = getDio(); + debugPrint("--[DOWNLOAD]--: $url"); + final tempDir = await getTemporaryDirectory(); + String filename = ''; + return await dio.download( + url, + (Headers headers) { + List tokens = headers.value('content-disposition')!.split(";"); + for (var i = 0; i < tokens.length; i++) { + if (tokens[i].contains('filename')) { + filename = tokens[i] + .substring(tokens[i].indexOf("=") + 1, tokens[i].length); + } + } + filename = Uri.decodeFull(filename).replaceAll("UTF-8''", ''); + return '${tempDir.path}/$filename'; + }, + onReceiveProgress: onReceiveProgress, + queryParameters: queryParameters, + options: options, + ).then((response) { + return '${tempDir.path}/$filename'; + }, onError: (e) { + throw e; + }); + } + + Future post(String url, + {data, + Map? queryParameters, + Options? options, + Map? formData}) async { + final Dio dio = getDio(); + debugPrint("--[POST]--: $url"); + if (formData != null) { + data = mapToFormData(formData); + } + final Response response = await dio.post( + url, + data: data, + queryParameters: queryParameters, + options: options, + ); + return HttpResponse( + body: response.data, + headers: response.headers, + request: response.requestOptions, + statusCode: response.statusCode, + statusMessage: response.statusMessage, + extra: response.extra, + ); + } + + Future put(String url, + {data, + Map? queryParameters, + Options? options, + Map? formData}) async { + final Dio dio = getDio(); + if (formData != null) { + data = mapToFormData(formData); + } + + debugPrint("--[PUT]--: $url"); + final Response response = await dio.put( + url, + data: data, + queryParameters: queryParameters, + options: options, + ); + return HttpResponse( + body: response.data, + headers: response.headers, + request: response.requestOptions, + statusCode: response.statusCode, + statusMessage: response.statusMessage, + extra: response.extra, + ); + } + + Future delete( + String url, { + data, + Map? queryParameters, + Options? options, + }) async { + final Dio dio = getDio(); + debugPrint("--[DELETE]--: $url"); + final Response response = await dio.delete( + url, + data: data, + queryParameters: queryParameters, + options: options, + ); + return HttpResponse( + body: response.data, + headers: response.headers, + request: response.requestOptions, + statusCode: response.statusCode, + statusMessage: response.statusMessage, + extra: response.extra, + ); + } +} diff --git a/lib/api_service.dart b/lib/utils/api_service.dart old mode 100755 new mode 100644 similarity index 96% rename from lib/api_service.dart rename to lib/utils/api_service.dart index ba39f20a..4311f8b9 --- a/lib/api_service.dart +++ b/lib/utils/api_service.dart @@ -2,7 +2,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import 'models/user.dart'; +import '../models/user.dart'; class ApiService { static String url(int nrResults) { diff --git a/lib/app_properties.dart b/lib/utils/app_properties.dart old mode 100755 new mode 100644 similarity index 91% rename from lib/app_properties.dart rename to lib/utils/app_properties.dart index 01cfbbf9..bbaa741b --- a/lib/app_properties.dart +++ b/lib/utils/app_properties.dart @@ -19,4 +19,7 @@ const List shadow = [ screenAwareSize(int size, BuildContext context) { double baseHeight = 640.0; return size * MediaQuery.of(context).size.height / baseHeight; -} \ No newline at end of file +} + +const String accessToken = "accessToken"; +const String user = "user"; \ No newline at end of file diff --git a/lib/utils/cart_button.widget.dart b/lib/utils/cart_button.widget.dart new file mode 100644 index 00000000..15986870 --- /dev/null +++ b/lib/utils/cart_button.widget.dart @@ -0,0 +1,18 @@ +import 'package:ecommerce_int2/screens/shop/check_out_page.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get/get.dart'; + +class CartButton extends StatelessWidget { + const CartButton({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return IconButton( + icon: SvgPicture.asset('assets/icons/cart_icon.svg'), + onPressed: () { + Get.to(() => CheckOutPage()); + }, + ); + } +} \ No newline at end of file diff --git a/lib/custom_background.dart b/lib/utils/custom_background.dart old mode 100755 new mode 100644 similarity index 90% rename from lib/custom_background.dart rename to lib/utils/custom_background.dart index 1bf1b50b..00934f7b --- a/lib/custom_background.dart +++ b/lib/utils/custom_background.dart @@ -1,4 +1,4 @@ -import 'package:ecommerce_int2/app_properties.dart'; +import 'package:ecommerce_int2/utils/app_properties.dart'; import 'package:flutter/material.dart'; class MainBackground extends CustomPainter { diff --git a/lib/utils/debounce.dart b/lib/utils/debounce.dart new file mode 100644 index 00000000..d0fa2276 --- /dev/null +++ b/lib/utils/debounce.dart @@ -0,0 +1,17 @@ +import 'package:flutter/foundation.dart'; +import 'dart:async'; + +class Debounce { + final int milliseconds; + Timer? _timer; + + Debounce({required this.milliseconds}); + + run(VoidCallback action) { + if (_timer != null) { + _timer!.cancel(); + } + + _timer = Timer(Duration(milliseconds: milliseconds), action); + } +} diff --git a/lib/utils/dependencies.dart b/lib/utils/dependencies.dart new file mode 100644 index 00000000..89662d76 --- /dev/null +++ b/lib/utils/dependencies.dart @@ -0,0 +1,48 @@ +import 'package:ecommerce_int2/data/provider/address.provider.dart'; +import 'package:ecommerce_int2/data/provider/auth.provider.dart'; +import 'package:ecommerce_int2/data/provider/cart.provider.dart'; +import 'package:ecommerce_int2/data/provider/category.provider.dart'; +import 'package:ecommerce_int2/data/provider/merchant.provider.dart'; +import 'package:ecommerce_int2/data/provider/order.provider.dart'; +import 'package:ecommerce_int2/data/provider/product.provider.dart'; +import 'package:ecommerce_int2/data/provider/review.provider.dart'; +import 'package:ecommerce_int2/data/repository/address.repository.dart'; +import 'package:ecommerce_int2/data/repository/auth.repository.dart'; +import 'package:ecommerce_int2/data/repository/cart.repository.dart'; +import 'package:ecommerce_int2/data/repository/category.repository.dart'; +import 'package:ecommerce_int2/data/repository/merchant.repository.dart'; +import 'package:ecommerce_int2/data/repository/order.repository.dart'; +import 'package:ecommerce_int2/data/repository/product.repository.dart'; +import 'package:ecommerce_int2/data/repository/review.repository.dart'; +import 'package:ecommerce_int2/screens/search_products/search_controller.dart'; +import 'package:ecommerce_int2/screens/shop/cart.controller.dart'; +import 'package:ecommerce_int2/services/auth.service.dart'; +import 'package:ecommerce_int2/services/network.service.dart'; +import 'package:get/get.dart'; + +class Dependency { + static void init() { + Get + ..put(NetWorkService()) + ..put(AuthProvider(Get.find())) + ..put(AuthRepository(Get.find())) + ..put(AuthService(Get.find())) + ..put(AddressProvider(Get.find())) + ..put(AddressRepository(Get.find())) + ..put(CategoryProvider(Get.find())) + ..put(CategoryRepository(Get.find())) + ..put(ProductProvider(Get.find())) + ..put(ProductRepository(Get.find())) + ..put(CartProvider(Get.find())) + ..put(CartRepository(Get.find())) + ..put(CartController(Get.find()), permanent: true) + ..put(OrderProvider(Get.find())) + ..put(OrderRepository(Get.find())) + ..put(MerchantProvider(Get.find())) + ..put(MerchantRepository(Get.find())) + ..put(ReviewProvider(Get.find())) + ..put(ReviewRepository(Get.find())) + ..put(SearchProductController(Get.find())) + ; + } +} diff --git a/lib/utils/message_dialog.dart b/lib/utils/message_dialog.dart new file mode 100644 index 00000000..14ace041 --- /dev/null +++ b/lib/utils/message_dialog.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class MessageDialog { + static void hideLoading() { + if (Get.isDialogOpen!) { + Get.back(); + } + } + + static void showToast(String text) { + Get.showSnackbar(GetSnackBar( + backgroundColor: Colors.white, + duration: Duration(seconds: 1), + messageText: Text(text), + )); + } + + static void showConfirmDialog({ + required String content, + }) { + Get.generalDialog( + barrierDismissible: false, + barrierColor: const Color.fromRGBO(0, 0, 0, 0.3), + transitionDuration: Duration.zero, + pageBuilder: (context, _, __) { + return AlertDialog( + backgroundColor: Colors.white, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(10), + ), + ), + content: SizedBox( + height: 65, + width: Get.width, + child: Center( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(width: 16), + Text( + content, + ), + ], + ), + ), + ), + buttonPadding: EdgeInsets.zero, + ); + }, + ); + } + + static void showLoading() { + Get.generalDialog( + barrierDismissible: false, + barrierColor: const Color.fromRGBO(0, 0, 0, 0.3), + transitionDuration: Duration.zero, + pageBuilder: (context, _, __) { + return AlertDialog( + backgroundColor: Colors.white, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(10), + ), + ), + content: SizedBox( + height: 65, + width: Get.width, + child: Center( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + //todo: Change the loading indicator + const CircularProgressIndicator(), + const SizedBox(width: 16), + Text( + 'Loading...', + ), + ], + ), + ), + ), + buttonPadding: EdgeInsets.zero, + ); + }, + ); + } +} diff --git a/lib/utils/webview.dart b/lib/utils/webview.dart new file mode 100644 index 00000000..ceacfebf --- /dev/null +++ b/lib/utils/webview.dart @@ -0,0 +1,37 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:webview_flutter/webview_flutter.dart'; + +class WebViewExample extends StatefulWidget { + final String url; + + const WebViewExample({Key? key, required this.url}) : super(key: key); + @override + WebViewExampleState createState() => WebViewExampleState(); +} + +class WebViewExampleState extends State { + @override + void initState() { + super.initState(); + // Enable virtual display. + if (Platform.isAndroid) WebView.platform = AndroidWebView(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Paypal"), + leading: BackButton(), + ), + body: SafeArea( + child: WebView( + javascriptMode: JavascriptMode.unrestricted, + initialUrl: widget.url, + ), + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 6f43fd5b..e00dce4f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -42,7 +42,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.8.1" + version: "2.8.2" boolean_selector: dependency: transitive description: @@ -119,7 +119,7 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" charcode: dependency: transitive description: @@ -197,6 +197,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.1" + dio: + dependency: "direct main" + description: + name: dio + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.6" fake_async: dependency: transitive description: @@ -204,6 +211,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" file: dependency: transitive description: @@ -284,6 +298,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.2" + get: + dependency: "direct main" + description: + name: get + url: "https://pub.dartlang.org" + source: hosted + version: "4.6.1" + get_storage: + dependency: "direct main" + description: + name: get_storage + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" glob: dependency: transitive description: @@ -381,7 +409,14 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10" + version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" meta: dependency: transitive description: @@ -438,6 +473,55 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.2.1" + path_provider: + dependency: "direct main" + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.10" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.14" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.9" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.6" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" pedantic: dependency: transitive description: @@ -459,6 +543,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" pool: dependency: transitive description: @@ -466,6 +564,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.5.0" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.4" pub_semver: dependency: transitive description: @@ -480,6 +585,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" + pull_to_refresh: + dependency: "direct main" + description: + name: pull_to_refresh + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" rubber: dependency: "direct main" description: @@ -561,7 +673,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.2" + version: "0.4.8" timing: dependency: transitive description: @@ -589,7 +701,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" watcher: dependency: transitive description: @@ -604,6 +716,48 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + webview_flutter: + dependency: "direct main" + description: + name: webview_flutter + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.4" + webview_flutter_android: + dependency: transitive + description: + name: webview_flutter_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.8.10" + webview_flutter_platform_interface: + dependency: transitive + description: + name: webview_flutter_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.1" + webview_flutter_wkwebview: + dependency: transitive + description: + name: webview_flutter_wkwebview + url: "https://pub.dartlang.org" + source: hosted + version: "2.7.5" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "2.5.2" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0+1" xml: dependency: transitive description: @@ -619,5 +773,5 @@ packages: source: hosted version: "3.1.0" sdks: - dart: ">=2.14.0 <3.0.0" - flutter: ">=1.24.0-7.0" + dart: ">=2.15.0 <3.0.0" + flutter: ">=2.8.1" diff --git a/pubspec.yaml b/pubspec.yaml index 9f6e6bb9..f3dfb46e 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,6 +34,12 @@ dependencies: numberpicker: ^2.1.1 flare_splash_screen: ^4.0.0 flutter_svg: ^0.23.0+1 + get: 4.6.1 + dio: ^4.0.6 + get_storage: ^2.0.3 + path_provider: ^2.0.10 + pull_to_refresh: ^2.0.0 + webview_flutter: ^3.0.4 dev_dependencies: