Files
Siro/siro_rider/lib/views/food/food_cart_page.dart
T

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!),
),
],
);
}
}