249 lines
8.9 KiB
Dart
249 lines
8.9 KiB
Dart
// food_cart_page.dart — مراجعة السلة، عرض السعر الموقّع، وإنشاء الطلب
|
|
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:geolocator/geolocator.dart';
|
|
|
|
import '../../constant/box_name.dart';
|
|
import '../../constant/colors.dart';
|
|
import '../../constant/style.dart';
|
|
import '../../main.dart';
|
|
import '../../views/widgets/error_snakbar.dart';
|
|
import '../../controller/food/food_controller.dart';
|
|
import '../../controller/food/food_models.dart';
|
|
import '../widgets/my_scafold.dart';
|
|
import 'food_order_tracking_page.dart';
|
|
|
|
class FoodCartPage extends StatefulWidget {
|
|
const FoodCartPage({super.key});
|
|
|
|
@override
|
|
State<FoodCartPage> createState() => _FoodCartPageState();
|
|
}
|
|
|
|
class _FoodCartPageState extends State<FoodCartPage> {
|
|
final TextEditingController _addressController = TextEditingController();
|
|
final TextEditingController _noteController = TextEditingController();
|
|
String _paymentMethod = 'wallet';
|
|
Position? _position;
|
|
bool _isFetchingLocation = false;
|
|
|
|
bool get _isAr => box.read(BoxName.lang) == 'ar';
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_fetchLocation();
|
|
}
|
|
|
|
Future<void> _fetchLocation() async {
|
|
setState(() => _isFetchingLocation = true);
|
|
try {
|
|
final permission = await Geolocator.checkPermission();
|
|
if (permission == LocationPermission.denied) {
|
|
await Geolocator.requestPermission();
|
|
}
|
|
_position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
|
|
} catch (_) {
|
|
_position = null;
|
|
}
|
|
if (mounted) setState(() => _isFetchingLocation = false);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return GetBuilder<FoodController>(
|
|
builder: (c) => MyScafolld(
|
|
title: _isAr ? 'السلة' : 'Cart',
|
|
isleading: true,
|
|
body: [
|
|
c.cartLines.isEmpty ? _emptyCart() : _cartContent(c),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _emptyCart() {
|
|
return Center(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(Icons.shopping_cart_outlined, size: 72, color: AppColor.grayColor),
|
|
const SizedBox(height: 16),
|
|
Text(_isAr ? 'سلتك فارغة' : 'Your cart is empty', style: AppStyle.title),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _cartContent(FoodController c) {
|
|
return Column(
|
|
children: [
|
|
Expanded(
|
|
child: ListView(
|
|
padding: const EdgeInsets.all(16),
|
|
children: [
|
|
...c.cartLines.map((line) => _cartLineTile(c, line)),
|
|
const SizedBox(height: 16),
|
|
_summaryRow(_isAr ? 'المجموع الفرعي' : 'Subtotal', foodFormatPrice(c.cartTotal)),
|
|
if (c.currentQuote != null) ...[
|
|
_summaryRow(_isAr ? 'رسوم التوصيل' : 'Delivery fee', foodFormatPrice(c.currentQuote!.deliveryFee)),
|
|
const Divider(),
|
|
_summaryRow(_isAr ? 'الإجمالي' : 'Total', foodFormatPrice(c.currentQuote!.grandTotal), bold: true),
|
|
],
|
|
const SizedBox(height: 20),
|
|
TextField(
|
|
controller: _addressController,
|
|
decoration: InputDecoration(
|
|
labelText: _isAr ? 'عنوان التوصيل' : 'Delivery address',
|
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
|
|
suffixIcon: _isFetchingLocation
|
|
? const Padding(
|
|
padding: EdgeInsets.all(12),
|
|
child: SizedBox(width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2)),
|
|
)
|
|
: Icon(_position != null ? Icons.my_location_rounded : Icons.location_off_rounded,
|
|
color: _position != null ? AppColor.greenColor : AppColor.redColor),
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
TextField(
|
|
controller: _noteController,
|
|
decoration: InputDecoration(
|
|
labelText: _isAr ? 'ملاحظات (اختياري)' : 'Notes (optional)',
|
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
_paymentMethodPicker(),
|
|
],
|
|
),
|
|
),
|
|
Padding(
|
|
padding: EdgeInsets.fromLTRB(16, 8, 16, MediaQuery.of(context).padding.bottom + 16),
|
|
child: SizedBox(
|
|
width: double.infinity,
|
|
height: 52,
|
|
child: ElevatedButton(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: AppColor.primaryColor,
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
|
|
),
|
|
onPressed: c.isQuoting || c.isPlacingOrder ? null : () => _onCheckoutPressed(c),
|
|
child: c.isPlacingOrder
|
|
? const CircularProgressIndicator(color: Colors.white)
|
|
: Text(
|
|
c.currentQuote == null
|
|
? (_isAr ? 'احسب السعر' : 'Get Quote')
|
|
: '${_isAr ? "اطلب الآن" : "Place Order"} · ${foodFormatPrice(c.currentQuote!.grandTotal)}',
|
|
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Future<void> _onCheckoutPressed(FoodController c) async {
|
|
if (c.currentQuote == null) {
|
|
await c.refreshQuote();
|
|
return;
|
|
}
|
|
|
|
if (_addressController.text.trim().isEmpty) {
|
|
mySnackbarWarning(_isAr ? 'أدخل عنوان التوصيل' : 'Enter a delivery address');
|
|
return;
|
|
}
|
|
if (_position == null) {
|
|
mySnackbarWarning(_isAr ? 'تعذّر تحديد موقعك — فعّل خدمة الموقع' : 'Could not get your location — enable location services');
|
|
return;
|
|
}
|
|
|
|
final orderId = await c.placeOrder(
|
|
deliveryAddress: _addressController.text.trim(),
|
|
deliveryLat: _position!.latitude,
|
|
deliveryLng: _position!.longitude,
|
|
paymentMethod: _paymentMethod,
|
|
customerNote: _noteController.text.trim(),
|
|
);
|
|
|
|
if (orderId != null) {
|
|
Get.offAll(() => FoodOrderTrackingPage(orderId: orderId));
|
|
}
|
|
}
|
|
|
|
Widget _cartLineTile(FoodController c, FoodCartLine line) {
|
|
return Card(
|
|
margin: const EdgeInsets.only(bottom: 10),
|
|
color: AppColor.cardColor,
|
|
elevation: 0,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(14),
|
|
side: BorderSide(color: AppColor.borderColor),
|
|
),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(12),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(line.item.nameAr, style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
|
const SizedBox(height: 4),
|
|
Text(foodFormatPrice(line.lineTotal), style: AppStyle.subtitle.copyWith(color: AppColor.accentColor)),
|
|
],
|
|
),
|
|
),
|
|
IconButton(
|
|
icon: const Icon(Icons.remove_circle_outline_rounded),
|
|
onPressed: () => c.updateQuantity(line.lineKey, line.quantity - 1),
|
|
),
|
|
Text(line.quantity.toString(), style: AppStyle.title),
|
|
IconButton(
|
|
icon: const Icon(Icons.add_circle_outline_rounded),
|
|
onPressed: () => c.updateQuantity(line.lineKey, line.quantity + 1),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _summaryRow(String label, String value, {bool bold = false}) {
|
|
final style = bold
|
|
? AppStyle.title.copyWith(fontWeight: FontWeight.bold, fontSize: 18)
|
|
: AppStyle.subtitle;
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [Text(label, style: style), Text(value, style: style)],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _paymentMethodPicker() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(_isAr ? 'طريقة الدفع' : 'Payment method', style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
|
RadioListTile<String>(
|
|
contentPadding: EdgeInsets.zero,
|
|
value: 'wallet',
|
|
groupValue: _paymentMethod,
|
|
title: Text(_isAr ? 'المحفظة' : 'Wallet'),
|
|
onChanged: (v) => setState(() => _paymentMethod = v!),
|
|
),
|
|
RadioListTile<String>(
|
|
contentPadding: EdgeInsets.zero,
|
|
value: 'cash',
|
|
groupValue: _paymentMethod,
|
|
title: Text(_isAr ? 'نقداً عند الاستلام' : 'Cash on delivery'),
|
|
onChanged: (v) => setState(() => _paymentMethod = v!),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|