last 16-10-2025
This commit is contained in:
commit
5fdfdd9302
146 files changed
+10525
No files matched your search
@@ -0,0 +1,325 @@
|
||||
class AllProductModel {
|
||||
final int id;
|
||||
final String name;
|
||||
final String slug;
|
||||
final String permalink;
|
||||
final DateTime dateCreated;
|
||||
final DateTime dateCreatedGmt;
|
||||
final DateTime dateModified;
|
||||
final DateTime dateModifiedGmt;
|
||||
final String type;
|
||||
final String status;
|
||||
final bool featured;
|
||||
final String catalogVisibility;
|
||||
final String description;
|
||||
final String shortDescription;
|
||||
final String sku;
|
||||
final String price;
|
||||
final String? regularPrice;
|
||||
final String? salePrice;
|
||||
final bool onSale;
|
||||
final bool purchasable;
|
||||
final int totalSales;
|
||||
final bool virtual;
|
||||
final bool downloadable;
|
||||
final List<dynamic> downloads;
|
||||
final int downloadLimit;
|
||||
final int downloadExpiry;
|
||||
final String externalUrl;
|
||||
final String buttonText;
|
||||
final String taxStatus;
|
||||
final String taxClass;
|
||||
final bool manageStock;
|
||||
final int? stockQuantity;
|
||||
final String backorders;
|
||||
final bool backordersAllowed;
|
||||
final bool backordered;
|
||||
final int? lowStockAmount;
|
||||
final bool soldIndividually;
|
||||
final String weight;
|
||||
final Dimensions dimensions;
|
||||
final bool shippingRequired;
|
||||
final bool shippingTaxable;
|
||||
final String shippingClass;
|
||||
final int shippingClassId;
|
||||
final bool reviewsAllowed;
|
||||
final String averageRating;
|
||||
final int ratingCount;
|
||||
final List<int> upsellIds;
|
||||
final List<int> crossSellIds;
|
||||
final int parentId;
|
||||
final String purchaseNote;
|
||||
final List<Category> categories;
|
||||
final List<dynamic> brands;
|
||||
final List<Tag> tags;
|
||||
final List<ProductImage> images;
|
||||
final List<Attribute> attributes;
|
||||
final List<dynamic> defaultAttributes;
|
||||
final List<int> variations;
|
||||
final List<dynamic> groupedProducts;
|
||||
final int menuOrder;
|
||||
final String priceHtml;
|
||||
final List<int> relatedIds;
|
||||
final List<MetaData> metaData;
|
||||
final String stockStatus;
|
||||
final bool hasOptions;
|
||||
final String postPassword;
|
||||
final String globalUniqueId;
|
||||
|
||||
AllProductModel({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.slug,
|
||||
required this.permalink,
|
||||
required this.dateCreated,
|
||||
required this.dateCreatedGmt,
|
||||
required this.dateModified,
|
||||
required this.dateModifiedGmt,
|
||||
required this.type,
|
||||
required this.status,
|
||||
required this.featured,
|
||||
required this.catalogVisibility,
|
||||
required this.description,
|
||||
required this.shortDescription,
|
||||
required this.sku,
|
||||
required this.price,
|
||||
this.regularPrice,
|
||||
this.salePrice,
|
||||
required this.onSale,
|
||||
required this.purchasable,
|
||||
required this.totalSales,
|
||||
required this.virtual,
|
||||
required this.downloadable,
|
||||
required this.downloads,
|
||||
required this.downloadLimit,
|
||||
required this.downloadExpiry,
|
||||
required this.externalUrl,
|
||||
required this.buttonText,
|
||||
required this.taxStatus,
|
||||
required this.taxClass,
|
||||
required this.manageStock,
|
||||
this.stockQuantity,
|
||||
required this.backorders,
|
||||
required this.backordersAllowed,
|
||||
required this.backordered,
|
||||
this.lowStockAmount,
|
||||
required this.soldIndividually,
|
||||
required this.weight,
|
||||
required this.dimensions,
|
||||
required this.shippingRequired,
|
||||
required this.shippingTaxable,
|
||||
required this.shippingClass,
|
||||
required this.shippingClassId,
|
||||
required this.reviewsAllowed,
|
||||
required this.averageRating,
|
||||
required this.ratingCount,
|
||||
required this.upsellIds,
|
||||
required this.crossSellIds,
|
||||
required this.parentId,
|
||||
required this.purchaseNote,
|
||||
required this.categories,
|
||||
required this.brands,
|
||||
required this.tags,
|
||||
required this.images,
|
||||
required this.attributes,
|
||||
required this.defaultAttributes,
|
||||
required this.variations,
|
||||
required this.groupedProducts,
|
||||
required this.menuOrder,
|
||||
required this.priceHtml,
|
||||
required this.relatedIds,
|
||||
required this.metaData,
|
||||
required this.stockStatus,
|
||||
required this.hasOptions,
|
||||
required this.postPassword,
|
||||
required this.globalUniqueId,
|
||||
});
|
||||
|
||||
factory AllProductModel.fromJson(Map<String, dynamic> json) => AllProductModel(
|
||||
id: json['id'],
|
||||
name: json['name'],
|
||||
slug: json['slug'],
|
||||
permalink: json['permalink'],
|
||||
dateCreated: DateTime.parse(json['date_created']),
|
||||
dateCreatedGmt: DateTime.parse(json['date_created_gmt']),
|
||||
dateModified: DateTime.parse(json['date_modified']),
|
||||
dateModifiedGmt: DateTime.parse(json['date_modified_gmt']),
|
||||
type: json['type'],
|
||||
status: json['status'],
|
||||
featured: json['featured'],
|
||||
catalogVisibility: json['catalog_visibility'],
|
||||
description: json['description'],
|
||||
shortDescription: json['short_description'],
|
||||
sku: json['sku'],
|
||||
price: json['price'],
|
||||
regularPrice: json['regular_price'],
|
||||
salePrice: json['sale_price'],
|
||||
onSale: json['on_sale'],
|
||||
purchasable: json['purchasable'],
|
||||
totalSales: json['total_sales'],
|
||||
virtual: json['virtual'],
|
||||
downloadable: json['downloadable'],
|
||||
downloads: json['downloads'],
|
||||
downloadLimit: json['download_limit'],
|
||||
downloadExpiry: json['download_expiry'],
|
||||
externalUrl: json['external_url'],
|
||||
buttonText: json['button_text'],
|
||||
taxStatus: json['tax_status'],
|
||||
taxClass: json['tax_class'],
|
||||
manageStock: json['manage_stock'],
|
||||
stockQuantity: json['stock_quantity'],
|
||||
backorders: json['backorders'],
|
||||
backordersAllowed: json['backorders_allowed'],
|
||||
backordered: json['backordered'],
|
||||
lowStockAmount: json['low_stock_amount'],
|
||||
soldIndividually: json['sold_individually'],
|
||||
weight: json['weight'],
|
||||
dimensions: Dimensions.fromJson(json['dimensions']),
|
||||
shippingRequired: json['shipping_required'],
|
||||
shippingTaxable: json['shipping_taxable'],
|
||||
shippingClass: json['shipping_class'],
|
||||
shippingClassId: json['shipping_class_id'],
|
||||
reviewsAllowed: json['reviews_allowed'],
|
||||
averageRating: json['average_rating'],
|
||||
ratingCount: json['rating_count'],
|
||||
upsellIds: List<int>.from(json['upsell_ids']),
|
||||
crossSellIds: List<int>.from(json['cross_sell_ids']),
|
||||
parentId: json['parent_id'],
|
||||
purchaseNote: json['purchase_note'],
|
||||
categories: List<Category>.from(json['categories'].map((x) => Category.fromJson(x))),
|
||||
brands: json['brands'],
|
||||
tags: List<Tag>.from(json['tags'].map((x) => Tag.fromJson(x))),
|
||||
images: List<ProductImage>.from(json['images'].map((x) => ProductImage.fromJson(x))),
|
||||
attributes: List<Attribute>.from(json['attributes'].map((x) => Attribute.fromJson(x))),
|
||||
defaultAttributes: json['default_attributes'],
|
||||
variations: List<int>.from(json['variations']),
|
||||
groupedProducts: json['grouped_products'],
|
||||
menuOrder: json['menu_order'],
|
||||
priceHtml: json['price_html'],
|
||||
relatedIds: List<int>.from(json['related_ids']),
|
||||
metaData: List<MetaData>.from(json['meta_data'].map((x) => MetaData.fromJson(x))),
|
||||
stockStatus: json['stock_status'],
|
||||
hasOptions: json['has_options'],
|
||||
postPassword: json['post_password'],
|
||||
globalUniqueId: json['global_unique_id'],
|
||||
);
|
||||
}
|
||||
|
||||
class Dimensions {
|
||||
final String length;
|
||||
final String width;
|
||||
final String height;
|
||||
|
||||
Dimensions({required this.length, required this.width, required this.height});
|
||||
|
||||
factory Dimensions.fromJson(Map<String, dynamic> json) => Dimensions(
|
||||
length: json['length'],
|
||||
width: json['width'],
|
||||
height: json['height'],
|
||||
);
|
||||
}
|
||||
|
||||
class Category {
|
||||
final int id;
|
||||
final String name;
|
||||
final String slug;
|
||||
|
||||
Category({required this.id, required this.name, required this.slug});
|
||||
|
||||
factory Category.fromJson(Map<String, dynamic> json) => Category(
|
||||
id: json['id'],
|
||||
name: json['name'],
|
||||
slug: json['slug'],
|
||||
);
|
||||
}
|
||||
|
||||
class Tag {
|
||||
final int id;
|
||||
final String name;
|
||||
final String slug;
|
||||
|
||||
Tag({required this.id, required this.name, required this.slug});
|
||||
|
||||
factory Tag.fromJson(Map<String, dynamic> json) => Tag(
|
||||
id: json['id'],
|
||||
name: json['name'],
|
||||
slug: json['slug'],
|
||||
);
|
||||
}
|
||||
|
||||
class ProductImage {
|
||||
final int id;
|
||||
final DateTime dateCreated;
|
||||
final DateTime dateCreatedGmt;
|
||||
final DateTime dateModified;
|
||||
final DateTime dateModifiedGmt;
|
||||
final String src;
|
||||
final String name;
|
||||
final String alt;
|
||||
|
||||
ProductImage({
|
||||
required this.id,
|
||||
required this.dateCreated,
|
||||
required this.dateCreatedGmt,
|
||||
required this.dateModified,
|
||||
required this.dateModifiedGmt,
|
||||
required this.src,
|
||||
required this.name,
|
||||
required this.alt,
|
||||
});
|
||||
|
||||
factory ProductImage.fromJson(Map<String, dynamic> json) => ProductImage(
|
||||
id: json['id'],
|
||||
dateCreated: DateTime.parse(json['date_created']),
|
||||
dateCreatedGmt: DateTime.parse(json['date_created_gmt']),
|
||||
dateModified: DateTime.parse(json['date_modified']),
|
||||
dateModifiedGmt: DateTime.parse(json['date_modified_gmt']),
|
||||
src: json['src'],
|
||||
name: json['name'],
|
||||
alt: json['alt'],
|
||||
);
|
||||
}
|
||||
|
||||
class Attribute {
|
||||
final int id;
|
||||
final String name;
|
||||
final String slug;
|
||||
final int position;
|
||||
final bool visible;
|
||||
final bool variation;
|
||||
final List<String> options;
|
||||
|
||||
Attribute({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.slug,
|
||||
required this.position,
|
||||
required this.visible,
|
||||
required this.variation,
|
||||
required this.options,
|
||||
});
|
||||
|
||||
factory Attribute.fromJson(Map<String, dynamic> json) => Attribute(
|
||||
id: json['id'],
|
||||
name: json['name'],
|
||||
slug: json['slug'],
|
||||
position: json['position'],
|
||||
visible: json['visible'],
|
||||
variation: json['variation'],
|
||||
options: List<String>.from(json['options']),
|
||||
);
|
||||
}
|
||||
|
||||
class MetaData {
|
||||
final int id;
|
||||
final String key;
|
||||
final dynamic value;
|
||||
|
||||
MetaData({required this.id, required this.key, required this.value});
|
||||
|
||||
factory MetaData.fromJson(Map<String, dynamic> json) => MetaData(
|
||||
id: json['id'],
|
||||
key: json['key'],
|
||||
value: json['value'],
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
class CartResponse {
|
||||
final bool success;
|
||||
final Map<String, CartItem> cart;
|
||||
|
||||
CartResponse({required this.success, required this.cart});
|
||||
|
||||
factory CartResponse.fromJson(Map<String, dynamic> json) {
|
||||
final cartMap = <String, CartItem>{};
|
||||
|
||||
if (json['cart'] != null) {
|
||||
if (json['cart'] is Map<String, dynamic>) {
|
||||
// Case 1: cart is a Map
|
||||
(json['cart'] as Map<String, dynamic>).forEach((key, value) {
|
||||
cartMap[key] = CartItem.fromJson(value);
|
||||
});
|
||||
} else if (json['cart'] is List) {
|
||||
// Case 2: cart is a List
|
||||
for (var i = 0; i < (json['cart'] as List).length; i++) {
|
||||
final item = (json['cart'] as List)[i];
|
||||
if (item is Map<String, dynamic>) {
|
||||
cartMap[i.toString()] = CartItem.fromJson(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CartResponse(success: json['success'] ?? false, cart: cartMap);
|
||||
}
|
||||
}
|
||||
|
||||
class CartItem {
|
||||
final int productId;
|
||||
final int quantity;
|
||||
final String name;
|
||||
final String price;
|
||||
|
||||
CartItem({
|
||||
required this.productId,
|
||||
required this.quantity,
|
||||
required this.name,
|
||||
required this.price,
|
||||
});
|
||||
|
||||
factory CartItem.fromJson(Map<String, dynamic> json) {
|
||||
return CartItem(
|
||||
productId: int.tryParse(json['product_id']?.toString() ?? '0') ?? 0,
|
||||
quantity: int.tryParse(json['quantity']?.toString() ?? '0') ?? 0,
|
||||
name: json['name']?.toString() ?? '',
|
||||
price: json['price']?.toString() ?? '0',
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'product_id': productId,
|
||||
'quantity': quantity,
|
||||
'name': name,
|
||||
'price': price,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,661 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:wordpress/providers/api_controller.dart';
|
||||
import 'package:wordpress/providers/api_repositoryprovider.dart';
|
||||
import 'package:wordpress/utils/ftaf.dart';
|
||||
import 'package:wordpress/view/check_out_screen.dart';
|
||||
import 'package:wordpress/viewmodel/cart_model.dart';
|
||||
|
||||
class CartScreen extends ConsumerWidget {
|
||||
const CartScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final cartAsync = ref.watch(cartProvider);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.grey.shade50,
|
||||
appBar: AppBar(
|
||||
title: const Text(
|
||||
"My Cart",
|
||||
style: TextStyle(
|
||||
color: Colors.black87,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
shadowColor: Colors.black12,
|
||||
iconTheme: const IconThemeData(color: Colors.black87),
|
||||
systemOverlayStyle: SystemUiOverlayStyle.dark,
|
||||
actions: [
|
||||
cartAsync.when(
|
||||
data: (cartResponse) => cartResponse.cart.isNotEmpty
|
||||
? Container(
|
||||
margin: const EdgeInsets.only(right: 16),
|
||||
child: Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.orange.shade100,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Text(
|
||||
"${cartResponse.cart.length} items",
|
||||
style: TextStyle(
|
||||
color: Colors.orange.shade800,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
loading: () => const SizedBox(),
|
||||
error: (_, __) => const SizedBox(),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: cartAsync.when(
|
||||
data: (cartResponse) {
|
||||
if (cartResponse.cart.isEmpty) {
|
||||
return _buildEmptyCart(context);
|
||||
}
|
||||
|
||||
final items = cartResponse.cart.values.toList();
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
// Header with item count
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
vertical: 16,
|
||||
),
|
||||
color: Colors.white,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Review your order",
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.grey.shade800,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.orange.shade100,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Text(
|
||||
"${items.length} items",
|
||||
style: TextStyle(
|
||||
color: Colors.orange.shade800,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Cart Items
|
||||
ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: items.length,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
separatorBuilder: (context, index) =>
|
||||
const SizedBox(height: 12),
|
||||
itemBuilder: (context, index) {
|
||||
final item = items[index];
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: _buildCartItem(context, item, ref),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Cart Summary
|
||||
_buildCartSummary(context, items, ref),
|
||||
|
||||
// Add extra space for bottom navigation
|
||||
const SizedBox(height: 100),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
loading: () => Container(
|
||||
color: Colors.white,
|
||||
child: const Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
CircularProgressIndicator(
|
||||
valueColor: AlwaysStoppedAnimation<Color>(Colors.orange),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Text(
|
||||
"Loading your cart...",
|
||||
style: TextStyle(fontSize: 16, color: Colors.grey),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
error: (err, stack) => Container(
|
||||
color: Colors.white,
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.error_outline, size: 64, color: Colors.red.shade300),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
"Something went wrong",
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.grey.shade800,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
"Please try again later",
|
||||
style: TextStyle(fontSize: 14, color: Colors.grey.shade600),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
ElevatedButton(
|
||||
onPressed: () => ref.invalidate(cartProvider),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.orange,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: const Text("Retry"),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEmptyCart(BuildContext context) {
|
||||
return Container(
|
||||
color: Colors.white,
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(24),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.orange.shade50,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.shopping_cart_outlined,
|
||||
size: 80,
|
||||
color: Colors.orange.shade300,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
"Your cart is empty",
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.grey.shade800,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
"Add items to get started",
|
||||
style: TextStyle(fontSize: 16, color: Colors.grey.shade600),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
icon: const Icon(Icons.shopping_bag_outlined),
|
||||
label: const Text("Continue Shopping"),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.orange,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
),
|
||||
elevation: 2,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCartItem(BuildContext context, CartItem item, WidgetRef ref) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade50,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: Colors.grey.shade200),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
children: [
|
||||
// Product Image/Icon
|
||||
Container(
|
||||
width: 70,
|
||||
height: 70,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [Colors.orange.shade100, Colors.orange.shade50],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.shopping_bag_outlined,
|
||||
size: 32,
|
||||
color: Colors.orange.shade600,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
|
||||
// Product Info
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Product Name and Delete Button
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
item.name,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black87,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
// Show confirmation dialog
|
||||
final confirm = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
title: const Text("Remove Item"),
|
||||
content: Text("Remove ${item.name} from cart?"),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(context).pop(false),
|
||||
child: const Text("Cancel"),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(context).pop(true),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
child: const Text("Remove"),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (confirm == true) {
|
||||
try {
|
||||
final apiRepo = ref.read(apiRepositoryProvider);
|
||||
await apiRepo.removeItem(
|
||||
productId: item.productId,
|
||||
quantity: item.quantity,
|
||||
);
|
||||
ref.invalidate(cartProvider);
|
||||
} catch (e) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text("Failed to remove item: $e"),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(6),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red.shade50,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.delete_outline,
|
||||
size: 20,
|
||||
color: Colors.red.shade400,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Price and Quantity Row
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
// Price
|
||||
Text(
|
||||
"₹${((double.tryParse(item.price) ?? 0) * item.quantity).toStringAsFixed(2)}",
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.orange.shade700,
|
||||
),
|
||||
),
|
||||
|
||||
// Quantity Controls
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
border: Border.all(color: Colors.grey.shade300),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildQuantityButton(
|
||||
icon: Icons.remove,
|
||||
onTap: () async {
|
||||
try {
|
||||
final apiRepo = ref.read(
|
||||
apiRepositoryProvider,
|
||||
);
|
||||
if (item.quantity > 1) {
|
||||
await apiRepo.removeItem(
|
||||
productId: item.productId,
|
||||
quantity: 1,
|
||||
);
|
||||
} else {
|
||||
await apiRepo.removeItem(
|
||||
productId: item.productId,
|
||||
quantity: item.quantity,
|
||||
);
|
||||
}
|
||||
ref.invalidate(cartProvider);
|
||||
} catch (e) {
|
||||
print("Error decreasing quantity: $e");
|
||||
}
|
||||
},
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
child: Text(
|
||||
"${item.quantity}",
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
_buildQuantityButton(
|
||||
icon: Icons.add,
|
||||
onTap: () async {
|
||||
try {
|
||||
await ref
|
||||
.read(apiRepositoryProvider)
|
||||
.addItem(
|
||||
productId: item.productId,
|
||||
quantity: 1,
|
||||
);
|
||||
ref.invalidate(cartProvider);
|
||||
} catch (e) {
|
||||
print("Error increasing quantity: $e");
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildQuantityButton({
|
||||
required IconData icon,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade50,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Icon(icon, size: 18, color: Colors.grey.shade700),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCartSummary(
|
||||
BuildContext context,
|
||||
List<CartItem> items,
|
||||
WidgetRef ref,
|
||||
) {
|
||||
double subtotal = 0;
|
||||
int totalItems = 0;
|
||||
|
||||
for (var item in items) {
|
||||
subtotal += (double.tryParse(item.price) ?? 0) * item.quantity;
|
||||
totalItems += item.quantity;
|
||||
}
|
||||
|
||||
final shipping = subtotal > 500 ? 0.0 : 50.0;
|
||||
final tax = subtotal * 0.18; // 18% GST
|
||||
final total = subtotal + shipping + tax;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: Colors.grey.shade200),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Summary Header
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(
|
||||
"Order Summary",
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"$totalItems items",
|
||||
style: TextStyle(fontSize: 14, color: Colors.grey.shade600),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Price Breakdown
|
||||
_buildPriceRow("Subtotal", subtotal),
|
||||
const SizedBox(height: 8),
|
||||
_buildPriceRow(
|
||||
"Shipping",
|
||||
shipping,
|
||||
subtitle: shipping == 0 ? "Free shipping!" : null,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildPriceRow("GST (18%)", tax),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
const Divider(thickness: 1),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Total
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(
|
||||
"Total",
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"₹${total.toStringAsFixed(2)}",
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.orange.shade700,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Checkout Button
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 56,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text("Proceeding to checkout..."),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
Get.to(CheckOutScreen());
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.orange,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
elevation: 3,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.shopping_bag),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
"Proceed to Checkout",
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 30),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPriceRow(String label, double amount, {String? subtitle}) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(fontSize: 14, color: Colors.grey.shade700),
|
||||
),
|
||||
if (subtitle != null)
|
||||
Text(
|
||||
subtitle,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.green.shade600,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
amount == 0 ? "FREE" : "₹${amount.toStringAsFixed(2)}",
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: amount == 0 ? Colors.green.shade600 : Colors.black87,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,427 @@
|
||||
import 'dart:convert';
|
||||
|
||||
DetailModel detailModelFromJson(String str) =>
|
||||
DetailModel.fromJson(json.decode(str));
|
||||
|
||||
String detailModelToJson(DetailModel data) => json.encode(data.toJson());
|
||||
|
||||
class DetailModel {
|
||||
final int? id;
|
||||
final String? name;
|
||||
final String? slug;
|
||||
final String? permalink;
|
||||
final String? dateCreated;
|
||||
final String? dateCreatedGmt;
|
||||
final String? dateModified;
|
||||
final String? dateModifiedGmt;
|
||||
final String? type;
|
||||
final String? status;
|
||||
final bool? featured;
|
||||
final String? catalogVisibility;
|
||||
final String? description;
|
||||
final String? shortDescription;
|
||||
final String? sku;
|
||||
final String? price;
|
||||
final String? regularPrice;
|
||||
final String? salePrice;
|
||||
final bool? onSale;
|
||||
final bool? purchasable;
|
||||
final int? totalSales;
|
||||
final bool? virtual;
|
||||
final bool? downloadable;
|
||||
final int? downloadLimit;
|
||||
final int? downloadExpiry;
|
||||
final String? externalUrl;
|
||||
final String? buttonText;
|
||||
final String? taxStatus;
|
||||
final String? taxClass;
|
||||
final bool? manageStock;
|
||||
final int? stockQuantity;
|
||||
final String? backorders;
|
||||
final bool? backordersAllowed;
|
||||
final bool? backordered;
|
||||
final bool? soldIndividually;
|
||||
final String? weight;
|
||||
final Dimensions? dimensions;
|
||||
final bool? shippingRequired;
|
||||
final bool? shippingTaxable;
|
||||
final String? shippingClass;
|
||||
final int? shippingClassId;
|
||||
final bool? reviewsAllowed;
|
||||
final String? averageRating;
|
||||
final int? ratingCount;
|
||||
final int? parentId;
|
||||
final String? purchaseNote;
|
||||
final List<Category>? categories;
|
||||
final List<dynamic>? brands;
|
||||
final List<Tag>? tags;
|
||||
final List<ImageModel>? images;
|
||||
final List<Attribute>? attributes;
|
||||
final List<dynamic>? defaultAttributes;
|
||||
final List<int>? variations;
|
||||
final List<dynamic>? groupedProducts;
|
||||
final int? menuOrder;
|
||||
final String? priceHtml;
|
||||
final List<int>? relatedIds;
|
||||
final List<MetaData>? metaData;
|
||||
final String? stockStatus;
|
||||
final bool? hasOptions;
|
||||
final String? postPassword;
|
||||
final String? globalUniqueId;
|
||||
|
||||
DetailModel({
|
||||
this.id,
|
||||
this.name,
|
||||
this.slug,
|
||||
this.permalink,
|
||||
this.dateCreated,
|
||||
this.dateCreatedGmt,
|
||||
this.dateModified,
|
||||
this.dateModifiedGmt,
|
||||
this.type,
|
||||
this.status,
|
||||
this.featured,
|
||||
this.catalogVisibility,
|
||||
this.description,
|
||||
this.shortDescription,
|
||||
this.sku,
|
||||
this.price,
|
||||
this.regularPrice,
|
||||
this.salePrice,
|
||||
this.onSale,
|
||||
this.purchasable,
|
||||
this.totalSales,
|
||||
this.virtual,
|
||||
this.downloadable,
|
||||
this.downloadLimit,
|
||||
this.downloadExpiry,
|
||||
this.externalUrl,
|
||||
this.buttonText,
|
||||
this.taxStatus,
|
||||
this.taxClass,
|
||||
this.manageStock,
|
||||
this.stockQuantity,
|
||||
this.backorders,
|
||||
this.backordersAllowed,
|
||||
this.backordered,
|
||||
this.soldIndividually,
|
||||
this.weight,
|
||||
this.dimensions,
|
||||
this.shippingRequired,
|
||||
this.shippingTaxable,
|
||||
this.shippingClass,
|
||||
this.shippingClassId,
|
||||
this.reviewsAllowed,
|
||||
this.averageRating,
|
||||
this.ratingCount,
|
||||
this.parentId,
|
||||
this.purchaseNote,
|
||||
this.categories,
|
||||
this.brands,
|
||||
this.tags,
|
||||
this.images,
|
||||
this.attributes,
|
||||
this.defaultAttributes,
|
||||
this.variations,
|
||||
this.groupedProducts,
|
||||
this.menuOrder,
|
||||
this.priceHtml,
|
||||
this.relatedIds,
|
||||
this.metaData,
|
||||
this.stockStatus,
|
||||
this.hasOptions,
|
||||
this.postPassword,
|
||||
this.globalUniqueId,
|
||||
});
|
||||
|
||||
factory DetailModel.fromJson(Map<String, dynamic> json) => DetailModel(
|
||||
id: json["id"],
|
||||
name: json["name"],
|
||||
slug: json["slug"],
|
||||
permalink: json["permalink"],
|
||||
dateCreated: json["date_created"],
|
||||
dateCreatedGmt: json["date_created_gmt"],
|
||||
dateModified: json["date_modified"],
|
||||
dateModifiedGmt: json["date_modified_gmt"],
|
||||
type: json["type"],
|
||||
status: json["status"],
|
||||
featured: json["featured"],
|
||||
catalogVisibility: json["catalog_visibility"],
|
||||
description: json["description"],
|
||||
shortDescription: json["short_description"],
|
||||
sku: json["sku"],
|
||||
price: json["price"],
|
||||
regularPrice: json["regular_price"],
|
||||
salePrice: json["sale_price"],
|
||||
onSale: json["on_sale"],
|
||||
purchasable: json["purchasable"],
|
||||
totalSales: json["total_sales"],
|
||||
virtual: json["virtual"],
|
||||
downloadable: json["downloadable"],
|
||||
downloadLimit: json["download_limit"],
|
||||
downloadExpiry: json["download_expiry"],
|
||||
externalUrl: json["external_url"],
|
||||
buttonText: json["button_text"],
|
||||
taxStatus: json["tax_status"],
|
||||
taxClass: json["tax_class"],
|
||||
manageStock: json["manage_stock"],
|
||||
stockQuantity: json["stock_quantity"],
|
||||
backorders: json["backorders"],
|
||||
backordersAllowed: json["backorders_allowed"],
|
||||
backordered: json["backordered"],
|
||||
soldIndividually: json["sold_individually"],
|
||||
weight: json["weight"],
|
||||
dimensions: json["dimensions"] != null
|
||||
? Dimensions.fromJson(json["dimensions"])
|
||||
: null,
|
||||
shippingRequired: json["shipping_required"],
|
||||
shippingTaxable: json["shipping_taxable"],
|
||||
shippingClass: json["shipping_class"],
|
||||
shippingClassId: json["shipping_class_id"],
|
||||
reviewsAllowed: json["reviews_allowed"],
|
||||
averageRating: json["average_rating"],
|
||||
ratingCount: json["rating_count"],
|
||||
parentId: json["parent_id"],
|
||||
purchaseNote: json["purchase_note"],
|
||||
categories: json["categories"] == null
|
||||
? []
|
||||
: List<Category>.from(
|
||||
json["categories"].map((x) => Category.fromJson(x)),
|
||||
),
|
||||
brands: json["brands"] ?? [],
|
||||
tags: json["tags"] == null
|
||||
? []
|
||||
: List<Tag>.from(json["tags"].map((x) => Tag.fromJson(x))),
|
||||
images: json["images"] == null
|
||||
? []
|
||||
: List<ImageModel>.from(
|
||||
json["images"].map((x) => ImageModel.fromJson(x)),
|
||||
),
|
||||
attributes: json["attributes"] == null
|
||||
? []
|
||||
: List<Attribute>.from(
|
||||
json["attributes"].map((x) => Attribute.fromJson(x)),
|
||||
),
|
||||
defaultAttributes: json["default_attributes"] ?? [],
|
||||
variations: json["variations"] == null
|
||||
? []
|
||||
: List<int>.from(json["variations"].map((x) => x)),
|
||||
groupedProducts: json["grouped_products"] ?? [],
|
||||
menuOrder: json["menu_order"],
|
||||
priceHtml: json["price_html"],
|
||||
relatedIds: json["related_ids"] == null
|
||||
? []
|
||||
: List<int>.from(json["related_ids"].map((x) => x)),
|
||||
metaData: json["meta_data"] == null
|
||||
? []
|
||||
: List<MetaData>.from(
|
||||
json["meta_data"].map((x) => MetaData.fromJson(x)),
|
||||
),
|
||||
stockStatus: json["stock_status"],
|
||||
hasOptions: json["has_options"],
|
||||
postPassword: json["post_password"],
|
||||
globalUniqueId: json["global_unique_id"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"id": id,
|
||||
"name": name,
|
||||
"slug": slug,
|
||||
"permalink": permalink,
|
||||
"date_created": dateCreated,
|
||||
"date_created_gmt": dateCreatedGmt,
|
||||
"date_modified": dateModified,
|
||||
"date_modified_gmt": dateModifiedGmt,
|
||||
"type": type,
|
||||
"status": status,
|
||||
"featured": featured,
|
||||
"catalog_visibility": catalogVisibility,
|
||||
"description": description,
|
||||
"short_description": shortDescription,
|
||||
"sku": sku,
|
||||
"price": price,
|
||||
"regular_price": regularPrice,
|
||||
"sale_price": salePrice,
|
||||
"on_sale": onSale,
|
||||
"purchasable": purchasable,
|
||||
"total_sales": totalSales,
|
||||
"virtual": virtual,
|
||||
"downloadable": downloadable,
|
||||
"download_limit": downloadLimit,
|
||||
"download_expiry": downloadExpiry,
|
||||
"external_url": externalUrl,
|
||||
"button_text": buttonText,
|
||||
"tax_status": taxStatus,
|
||||
"tax_class": taxClass,
|
||||
"manage_stock": manageStock,
|
||||
"stock_quantity": stockQuantity,
|
||||
"backorders": backorders,
|
||||
"backorders_allowed": backordersAllowed,
|
||||
"backordered": backordered,
|
||||
"sold_individually": soldIndividually,
|
||||
"weight": weight,
|
||||
"dimensions": dimensions?.toJson(),
|
||||
"shipping_required": shippingRequired,
|
||||
"shipping_taxable": shippingTaxable,
|
||||
"shipping_class": shippingClass,
|
||||
"shipping_class_id": shippingClassId,
|
||||
"reviews_allowed": reviewsAllowed,
|
||||
"average_rating": averageRating,
|
||||
"rating_count": ratingCount,
|
||||
"parent_id": parentId,
|
||||
"purchase_note": purchaseNote,
|
||||
"categories": categories?.map((x) => x.toJson()).toList(),
|
||||
"brands": brands,
|
||||
"tags": tags?.map((x) => x.toJson()).toList(),
|
||||
"images": images?.map((x) => x.toJson()).toList(),
|
||||
"attributes": attributes?.map((x) => x.toJson()).toList(),
|
||||
"default_attributes": defaultAttributes,
|
||||
"variations": variations,
|
||||
"grouped_products": groupedProducts,
|
||||
"menu_order": menuOrder,
|
||||
"price_html": priceHtml,
|
||||
"related_ids": relatedIds,
|
||||
"meta_data": metaData?.map((x) => x.toJson()).toList(),
|
||||
"stock_status": stockStatus,
|
||||
"has_options": hasOptions,
|
||||
"post_password": postPassword,
|
||||
"global_unique_id": globalUniqueId,
|
||||
};
|
||||
}
|
||||
|
||||
class Dimensions {
|
||||
final String? length;
|
||||
final String? width;
|
||||
final String? height;
|
||||
|
||||
Dimensions({this.length, this.width, this.height});
|
||||
|
||||
factory Dimensions.fromJson(Map<String, dynamic> json) => Dimensions(
|
||||
length: json["length"],
|
||||
width: json["width"],
|
||||
height: json["height"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"length": length,
|
||||
"width": width,
|
||||
"height": height,
|
||||
};
|
||||
}
|
||||
|
||||
class Category {
|
||||
final int? id;
|
||||
final String? name;
|
||||
final String? slug;
|
||||
|
||||
Category({this.id, this.name, this.slug});
|
||||
|
||||
factory Category.fromJson(Map<String, dynamic> json) =>
|
||||
Category(id: json["id"], name: json["name"], slug: json["slug"]);
|
||||
|
||||
Map<String, dynamic> toJson() => {"id": id, "name": name, "slug": slug};
|
||||
}
|
||||
|
||||
class Tag {
|
||||
final int? id;
|
||||
final String? name;
|
||||
final String? slug;
|
||||
|
||||
Tag({this.id, this.name, this.slug});
|
||||
|
||||
factory Tag.fromJson(Map<String, dynamic> json) =>
|
||||
Tag(id: json["id"], name: json["name"], slug: json["slug"]);
|
||||
|
||||
Map<String, dynamic> toJson() => {"id": id, "name": name, "slug": slug};
|
||||
}
|
||||
|
||||
class ImageModel {
|
||||
final int? id;
|
||||
final String? dateCreated;
|
||||
final String? dateModified;
|
||||
final String? src;
|
||||
final String? name;
|
||||
final String? alt;
|
||||
|
||||
ImageModel({
|
||||
this.id,
|
||||
this.dateCreated,
|
||||
this.dateModified,
|
||||
this.src,
|
||||
this.name,
|
||||
this.alt,
|
||||
});
|
||||
|
||||
factory ImageModel.fromJson(Map<String, dynamic> json) => ImageModel(
|
||||
id: json["id"],
|
||||
dateCreated: json["date_created"],
|
||||
dateModified: json["date_modified"],
|
||||
src: json["src"],
|
||||
name: json["name"],
|
||||
alt: json["alt"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"id": id,
|
||||
"date_created": dateCreated,
|
||||
"date_modified": dateModified,
|
||||
"src": src,
|
||||
"name": name,
|
||||
"alt": alt,
|
||||
};
|
||||
}
|
||||
|
||||
class Attribute {
|
||||
final int? id;
|
||||
final String? name;
|
||||
final String? slug;
|
||||
final int? position;
|
||||
final bool? visible;
|
||||
final bool? variation;
|
||||
final List<String>? options;
|
||||
|
||||
Attribute({
|
||||
this.id,
|
||||
this.name,
|
||||
this.slug,
|
||||
this.position,
|
||||
this.visible,
|
||||
this.variation,
|
||||
this.options,
|
||||
});
|
||||
|
||||
factory Attribute.fromJson(Map<String, dynamic> json) => Attribute(
|
||||
id: json["id"],
|
||||
name: json["name"],
|
||||
slug: json["slug"],
|
||||
position: json["position"],
|
||||
visible: json["visible"],
|
||||
variation: json["variation"],
|
||||
options: json["options"] == null
|
||||
? []
|
||||
: List<String>.from(json["options"].map((x) => x)),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"id": id,
|
||||
"name": name,
|
||||
"slug": slug,
|
||||
"position": position,
|
||||
"visible": visible,
|
||||
"variation": variation,
|
||||
"options": options,
|
||||
};
|
||||
}
|
||||
|
||||
class MetaData {
|
||||
final int? id;
|
||||
final String? key;
|
||||
final dynamic value;
|
||||
|
||||
MetaData({this.id, this.key, this.value});
|
||||
|
||||
factory MetaData.fromJson(Map<String, dynamic> json) =>
|
||||
MetaData(id: json["id"], key: json["key"], value: json["value"]);
|
||||
|
||||
Map<String, dynamic> toJson() => {"id": id, "key": key, "value": value};
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
class ProductHomePageModel {
|
||||
final List<BannerModel> banners;
|
||||
final List<CategoryModel> categories;
|
||||
final List<ProductModel> featured;
|
||||
final List<ProductModel> latest;
|
||||
|
||||
ProductHomePageModel({
|
||||
required this.banners,
|
||||
required this.categories,
|
||||
required this.featured,
|
||||
required this.latest,
|
||||
});
|
||||
|
||||
factory ProductHomePageModel.fromJson(Map<String, dynamic> json) {
|
||||
return ProductHomePageModel(
|
||||
banners: (json['banners'] is List)
|
||||
? (json['banners'] as List)
|
||||
.map((e) => BannerModel.fromJson(e))
|
||||
.toList()
|
||||
: [],
|
||||
categories: (json['categories'] is List)
|
||||
? (json['categories'] as List)
|
||||
.map((e) => CategoryModel.fromJson(e))
|
||||
.toList()
|
||||
: [],
|
||||
featured: (json['featured_products'] is List)
|
||||
? (json['featured_products'] as List)
|
||||
.map((e) => ProductModel.fromJson(e))
|
||||
.toList()
|
||||
: [],
|
||||
latest: (json['latest_products'] is List)
|
||||
? (json['latest_products'] as List)
|
||||
.map((e) => ProductModel.fromJson(e))
|
||||
.toList()
|
||||
: [],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'banners': banners.map((e) => e.toJson()).toList(),
|
||||
'categories': categories.map((e) => e.toJson()).toList(),
|
||||
'featured_products': featured.map((e) => e.toJson()).toList(),
|
||||
'latest_products': latest.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
|
||||
/// Banner Model
|
||||
class BannerModel {
|
||||
final int id;
|
||||
final String image;
|
||||
|
||||
BannerModel({required this.id, required this.image});
|
||||
|
||||
factory BannerModel.fromJson(Map<String, dynamic> json) {
|
||||
return BannerModel(
|
||||
id: json['id'] ?? 0,
|
||||
image: (json['image'] is String) ? json['image'] : '',
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {'id': id, 'image': image};
|
||||
}
|
||||
|
||||
/// Category Model
|
||||
class CategoryModel {
|
||||
final int id;
|
||||
final String name;
|
||||
final String slug;
|
||||
final String? image; // can be null/false
|
||||
|
||||
CategoryModel({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.slug,
|
||||
this.image,
|
||||
});
|
||||
|
||||
factory CategoryModel.fromJson(Map<String, dynamic> json) {
|
||||
return CategoryModel(
|
||||
id: json['id'] ?? 0,
|
||||
name: (json['name'] is String) ? json['name'] : '',
|
||||
slug: (json['slug'] is String) ? json['slug'] : '',
|
||||
image: (json['image'] is String) ? json['image'] : null,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'slug': slug,
|
||||
'image': image,
|
||||
};
|
||||
}
|
||||
|
||||
/// Product Model (for featured & latest)
|
||||
class ProductModel {
|
||||
final int id;
|
||||
final String name;
|
||||
final String price;
|
||||
final String image;
|
||||
|
||||
ProductModel({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.price,
|
||||
required this.image,
|
||||
});
|
||||
|
||||
factory ProductModel.fromJson(Map<String, dynamic> json) {
|
||||
return ProductModel(
|
||||
id: json['id'] ?? 0,
|
||||
name: (json['name'] is String) ? json['name'] : '',
|
||||
price: (json['price'] is String) ? json['price'] : '',
|
||||
image: (json['image'] is String) ? json['image'] : '',
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'price': price,
|
||||
'image': image,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user