login-page/lib/checkout_page.dart
2025-05-17 11:13:16 +03:00

170 lines
7.4 KiB
Dart

import 'package:flutter/material.dart';
import 'services/cart_service.dart';
class CheckoutPage extends StatefulWidget {
const CheckoutPage({super.key});
@override
State<CheckoutPage> createState() => _CheckoutPageState();
}
class _CheckoutPageState extends State<CheckoutPage> {
final CartService _cartService = CartService();
final _formKey = GlobalKey<FormState>();
bool _isLoading = false;
// To store mock final total from cart page
late double _finalTotal;
@override
void initState() {
super.initState();
// Calculate final total once when page loads
_finalTotal = _cartService.totalPrice +
(_cartService.totalPrice * 0.07) + // Mock tax
(_cartService.totalPrice > 50 ? 0 : 5.00); // Mock shipping
// If cart is empty when navigating here, redirect.
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_cartService.items.isEmpty && mounted) {
Navigator.pushNamedAndRemoveUntil(context, '/home', (route) => false);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Your cart is empty. Cannot proceed to checkout.')),
);
}
});
}
void _placeOrder() async {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
setState(() => _isLoading = true);
await Future.delayed(const Duration(seconds: 2)); // Simulate network call
final orderTotal = _finalTotal; // Use the calculated final total
_cartService.clearCart();
setState(() => _isLoading = false);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Mock order placed successfully for \$${orderTotal.toStringAsFixed(2)}!'),
duration: const Duration(seconds: 3),
backgroundColor: Theme.of(context).colorScheme.primary, // Or a specific success color
),
);
Navigator.pushNamedAndRemoveUntil(context, '/home', (route) => false);
}
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final cartItems = _cartService.items;
// This check is a fallback, initState handles initial redirection
if (cartItems.isEmpty && !_isLoading) {
return const Scaffold(body: Center(child: CircularProgressIndicator()));
}
return Scaffold(
appBar: AppBar(
title: const Text('Checkout'),
centerTitle: true,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text('Order Summary', style: theme.textTheme.headlineSmall),
const SizedBox(height: 8),
Card(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
children: [
...cartItems.map((item) => ListTile(
leading: Image.network(item.product.imageUrl, width: 40, height: 40, fit: BoxFit.cover, errorBuilder: (c,o,s) => const Icon(Icons.image)),
title: Text('${item.product.name} (x${item.quantity})', style: theme.textTheme.bodyMedium),
trailing: Text('\$${item.subtotal.toStringAsFixed(2)}', style: theme.textTheme.bodyMedium),
)),
const Divider(height: 20, thickness: 0.5),
ListTile(
title: Text('Subtotal', style: theme.textTheme.titleSmall),
trailing: Text('\$${_cartService.totalPrice.toStringAsFixed(2)}', style: theme.textTheme.titleSmall),
),
ListTile(
title: Text('Est. Tax (7%)', style: theme.textTheme.titleSmall),
trailing: Text('\$${(_cartService.totalPrice * 0.07).toStringAsFixed(2)}', style: theme.textTheme.titleSmall),
),
ListTile(
title: Text('Shipping', style: theme.textTheme.titleSmall),
trailing: Text(_cartService.totalPrice > 50 ? 'FREE' : '\$5.00', style: theme.textTheme.titleSmall),
),
const Divider(height: 20, thickness: 1),
ListTile(
title: Text('Total Amount', style: theme.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold)),
trailing: Text('\$${_finalTotal.toStringAsFixed(2)}', style: theme.textTheme.titleLarge?.copyWith(color: theme.colorScheme.primary, fontWeight: FontWeight.bold)),
),
],
),
),
),
const SizedBox(height: 24),
Text('Shipping Information (Mock)', style: theme.textTheme.headlineSmall),
const SizedBox(height: 12),
_buildTextField(label: 'Full Name', icon: Icons.person_outline, validator: (val) => val!.isEmpty ? 'Enter full name' : null),
const SizedBox(height: 12),
_buildTextField(label: 'Address Line 1', icon: Icons.home_outlined, validator: (val) => val!.isEmpty ? 'Enter address' : null),
const SizedBox(height: 12),
_buildTextField(label: 'City', icon: Icons.location_city_outlined, validator: (val) => val!.isEmpty ? 'Enter city' : null),
const SizedBox(height: 12),
_buildTextField(label: 'Postal Code', icon: Icons.markunread_mailbox_outlined, keyboardType: TextInputType.number, validator: (val) => val!.isEmpty ? 'Enter postal code' : null),
const SizedBox(height: 24),
Text('Payment Details (Mock)', style: theme.textTheme.headlineSmall),
const SizedBox(height: 12),
_buildTextField(label: 'Card Number', icon: Icons.credit_card_outlined, keyboardType: TextInputType.number, validator: (val) => val!.isEmpty ? 'Enter card number' : null),
const SizedBox(height: 12),
Row(
children: [
Expanded(child: _buildTextField(label: 'Expiry Date (MM/YY)', icon: Icons.calendar_today_outlined, keyboardType: TextInputType.datetime, validator: (val) => val!.isEmpty ? 'Enter expiry' : null)),
const SizedBox(width: 12),
Expanded(child: _buildTextField(label: 'CVV', icon: Icons.lock_outline, keyboardType: TextInputType.number, validator: (val) => val!.isEmpty ? 'Enter CVV' : null)),
],
),
const SizedBox(height: 32),
_isLoading
? const Center(child: CircularProgressIndicator())
: ElevatedButton(
onPressed: _placeOrder,
child: const Text('Place Mock Order'),
),
const SizedBox(height: 16),
],
),
),
),
);
}
Widget _buildTextField({required String label, required IconData icon, TextInputType? keyboardType, required FormFieldValidator<String> validator}) {
return TextFormField(
decoration: InputDecoration(
labelText: label,
prefixIcon: Icon(icon),
),
keyboardType: keyboardType,
validator: validator,
autovalidateMode: AutovalidateMode.onUserInteraction,
);
}
}