wordpress/lib/view/check_out_screen.dart
2025-10-16 11:27:27 +05:30

725 lines
23 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:wordpress/providers/api_controller.dart';
import 'package:wordpress/viewmodel/cart_model.dart';
// ✅ Checkout Screen with similar design to Cart Screen
class CheckOutScreen extends ConsumerWidget {
const CheckOutScreen({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(
"Checkout",
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 _buildCheckoutItem(context, item);
},
),
const SizedBox(height: 24),
// Delivery Address Section
_buildAddressSection(context),
const SizedBox(height: 24),
// Payment Method Section
_buildPaymentSection(context),
const SizedBox(height: 24),
// Cart Summary
_buildCheckoutSummary(context, items),
// 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 order...",
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"),
),
],
),
),
),
),
// Bottom Checkout Button
bottomNavigationBar: cartAsync.when(
data: (cartResponse) {
if (cartResponse.cart.isEmpty) return const SizedBox();
final items = cartResponse.cart.values.toList();
final total = _calculateTotal(items);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, -4),
),
],
),
child: SafeArea(
child: SizedBox(
height: 56,
child: ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Processing payment..."),
backgroundColor: Colors.green,
),
);
},
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.payment),
const SizedBox(width: 8),
Text(
"Pay ₹${total.toStringAsFixed(2)}",
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
),
);
},
loading: () => const SizedBox(),
error: (_, __) => const SizedBox(),
),
);
}
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 checkout",
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 _buildCheckoutItem(BuildContext context, CartItem item) {
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
Text(
item.name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 8),
// Price and Quantity
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
Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 4,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.grey.shade300),
),
child: Text(
"Qty: ${item.quantity}",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.grey.shade700,
),
),
),
],
),
],
),
),
],
),
),
);
}
Widget _buildAddressSection(BuildContext context) {
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: 8,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Delivery Address",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.grey.shade800,
),
),
TextButton(
onPressed: () {},
child: Text(
"Change",
style: TextStyle(
color: Colors.orange.shade700,
fontWeight: FontWeight.w600,
),
),
),
],
),
const SizedBox(height: 12),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
Icons.location_on_outlined,
color: Colors.orange.shade600,
size: 20,
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Home",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.grey.shade800,
),
),
const SizedBox(height: 4),
Text(
"123 Main Street, Apartment 4B, City, State - 123456",
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
),
),
const SizedBox(height: 4),
Text(
"Phone: +91 9876543210",
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
),
),
],
),
),
],
),
],
),
);
}
Widget _buildPaymentSection(BuildContext context) {
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: 8,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Payment Method",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.grey.shade800,
),
),
TextButton(
onPressed: () {},
child: Text(
"Change",
style: TextStyle(
color: Colors.orange.shade700,
fontWeight: FontWeight.w600,
),
),
),
],
),
const SizedBox(height: 12),
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.credit_card,
color: Colors.blue.shade700,
size: 24,
),
),
const SizedBox(width: 12),
Text(
"Credit/Debit Card",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.grey.shade800,
),
),
const Spacer(),
Icon(Icons.check_circle, color: Colors.green.shade600),
],
),
],
),
);
}
Widget _buildCheckoutSummary(BuildContext context, List<CartItem> items) {
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,
),
),
],
),
],
),
);
}
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,
),
),
],
);
}
double _calculateTotal(List<CartItem> items) {
double subtotal = 0;
for (var item in items) {
subtotal += (double.tryParse(item.price) ?? 0) * item.quantity;
}
final shipping = subtotal > 500 ? 0.0 : 50.0;
final tax = subtotal * 0.18;
return subtotal + shipping + tax;
}
}