add account, checkout page

This commit is contained in:
Phuoc Nguyen
2025-11-03 15:54:51 +07:00
parent d8e3ca4c46
commit c689f967d5
16 changed files with 2319 additions and 8 deletions

View File

@@ -443,12 +443,7 @@ class _CartPageState extends ConsumerState<CartPage> {
child: ElevatedButton(
onPressed: cartState.isNotEmpty
? () {
// TODO: Navigate to checkout page when implemented
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Checkout page chưa được triển khai'),
),
);
context.push(RouteNames.checkout);
}
: null,
child: const Text(

View File

@@ -0,0 +1,192 @@
/// Checkout Page
///
/// Complete checkout flow with delivery info, invoice options, payment methods.
/// Features:
/// - Delivery information form with province/ward dropdowns
/// - Invoice toggle section (checkbox reveals invoice fields)
/// - Payment method selection (bank transfer vs COD)
/// - Order summary with items list
/// - Price negotiation option (hides payment, changes button)
/// - Form validation and submission
library;
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:worker/core/constants/ui_constants.dart';
import 'package:worker/features/cart/presentation/widgets/checkout_submit_button.dart';
import 'package:worker/features/cart/presentation/widgets/delivery_information_section.dart';
import 'package:worker/features/cart/presentation/widgets/invoice_section.dart';
import 'package:worker/features/cart/presentation/widgets/order_summary_section.dart';
import 'package:worker/features/cart/presentation/widgets/payment_method_section.dart';
import 'package:worker/features/cart/presentation/widgets/price_negotiation_section.dart';
/// Checkout Page
///
/// Full checkout flow for placing orders.
class CheckoutPage extends HookConsumerWidget {
const CheckoutPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
// Form key for validation
final formKey = useMemoized(() => GlobalKey<FormState>());
// Delivery information controllers
final nameController = useTextEditingController(text: 'Hoàng Minh Hiệp');
final phoneController = useTextEditingController(text: '0347302911');
final addressController = useTextEditingController();
final notesController = useTextEditingController();
// Dropdown selections
final selectedProvince = useState<String?>('TP.HCM');
final selectedWard = useState<String?>('Quận 1');
final selectedPickupDate = useState<DateTime?>(null);
// Invoice section
final needsInvoice = useState<bool>(false);
final companyNameController = useTextEditingController();
final taxIdController = useTextEditingController();
final companyAddressController = useTextEditingController();
final companyEmailController = useTextEditingController();
// Payment method
final paymentMethod = useState<String>('bank_transfer');
// Price negotiation
final needsNegotiation = useState<bool>(false);
// Mock cart items
final cartItems = useState<List<Map<String, dynamic>>>([
{
'id': '1',
'name': 'Gạch Granite 60x60 Marble White',
'sku': 'GT-6060-MW',
'quantity': 20,
'price': 250000,
'image':
'https://images.unsplash.com/photo-1615971677499-5467cbab01c0?w=200',
},
{
'id': '2',
'name': 'Gạch Ceramic 30x60 Wood Effect',
'sku': 'CR-3060-WE',
'quantity': 15,
'price': 180000,
'image':
'https://images.unsplash.com/photo-1604709177225-055f99402ea3?w=200',
},
]);
// Calculate totals
final subtotal = cartItems.value.fold<double>(
0,
(sum, item) => sum + (item['price'] as int) * (item['quantity'] as int),
);
final discount = subtotal * 0.05; // 5% discount
const shipping = 50000.0;
final total = subtotal - discount + shipping;
return Scaffold(
backgroundColor: const Color(0xFFF4F6F8),
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.black),
onPressed: () => context.pop(),
),
title: const Text(
'Thanh toán',
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
centerTitle: false,
actions: const [SizedBox(width: AppSpacing.sm)],
),
body: Form(
key: formKey,
child: SingleChildScrollView(
child: Column(
children: [
const SizedBox(height: AppSpacing.md),
// Delivery Information Section
DeliveryInformationSection(
nameController: nameController,
phoneController: phoneController,
addressController: addressController,
notesController: notesController,
selectedProvince: selectedProvince,
selectedWard: selectedWard,
selectedPickupDate: selectedPickupDate,
),
const SizedBox(height: AppSpacing.md),
// Invoice Section
InvoiceSection(
needsInvoice: needsInvoice,
companyNameController: companyNameController,
taxIdController: taxIdController,
companyAddressController: companyAddressController,
companyEmailController: companyEmailController,
),
const SizedBox(height: AppSpacing.md),
// Payment Method Section (hidden if negotiation is checked)
if (!needsNegotiation.value)
PaymentMethodSection(
paymentMethod: paymentMethod,
),
if (!needsNegotiation.value) const SizedBox(height: AppSpacing.md),
// Order Summary Section
OrderSummarySection(
cartItems: cartItems.value,
subtotal: subtotal,
discount: discount,
shipping: shipping,
total: total,
),
const SizedBox(height: AppSpacing.md),
// Price Negotiation Section
PriceNegotiationSection(
needsNegotiation: needsNegotiation,
),
const SizedBox(height: AppSpacing.md),
// Place Order Button
CheckoutSubmitButton(
formKey: formKey,
needsNegotiation: needsNegotiation.value,
needsInvoice: needsInvoice.value,
name: nameController.text,
phone: phoneController.text,
address: addressController.text,
province: selectedProvince.value,
ward: selectedWard.value,
paymentMethod: paymentMethod.value,
companyName: companyNameController.text,
taxId: taxIdController.text,
total: total,
),
const SizedBox(height: AppSpacing.lg),
],
),
),
),
);
}
}