271 lines
10 KiB
Dart
271 lines
10 KiB
Dart
// food_home_page.dart — الصفحة الرئيسية لتبويب "طعام": تصفح المطاعم
|
|
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
|
|
import '../../constant/box_name.dart';
|
|
import '../../constant/colors.dart';
|
|
import '../../constant/style.dart';
|
|
import '../../main.dart';
|
|
import '../../controller/food/food_controller.dart';
|
|
import '../../controller/food/food_models.dart';
|
|
import '../widgets/my_scafold.dart';
|
|
import 'food_merchant_page.dart';
|
|
import 'food_cart_page.dart';
|
|
import 'food_order_history_page.dart';
|
|
|
|
class FoodHomePage extends StatelessWidget {
|
|
const FoodHomePage({super.key});
|
|
|
|
bool get _isAr => box.read(BoxName.lang) == 'ar';
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final c = Get.put(FoodController());
|
|
c.fetchMerchants();
|
|
|
|
return GetBuilder<FoodController>(
|
|
builder: (c) => MyScafolld(
|
|
title: _isAr ? 'طعام' : 'Food',
|
|
isleading: true,
|
|
action: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
IconButton(
|
|
icon: Icon(Icons.history_rounded, color: AppColor.primaryColor),
|
|
onPressed: () => Get.to(() => const FoodOrderHistoryPage()),
|
|
),
|
|
Stack(
|
|
clipBehavior: Clip.none,
|
|
children: [
|
|
IconButton(
|
|
icon: Icon(Icons.shopping_cart_rounded, color: AppColor.primaryColor),
|
|
onPressed: () => Get.to(() => const FoodCartPage()),
|
|
),
|
|
if (c.cartItemsCount > 0)
|
|
Positioned(
|
|
right: 4,
|
|
top: 4,
|
|
child: Container(
|
|
padding: const EdgeInsets.all(4),
|
|
decoration: const BoxDecoration(color: AppColor.redColor, shape: BoxShape.circle),
|
|
constraints: const BoxConstraints(minWidth: 18, minHeight: 18),
|
|
child: Text(
|
|
c.cartItemsCount.toString(),
|
|
textAlign: TextAlign.center,
|
|
style: const TextStyle(color: Colors.white, fontSize: 11, fontWeight: FontWeight.bold),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
body: [
|
|
Column(
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
child: _citySearchBar(c),
|
|
),
|
|
Expanded(
|
|
child: RefreshIndicator(
|
|
onRefresh: () => c.fetchMerchants(),
|
|
child: c.isLoadingMerchants
|
|
? const Center(child: CircularProgressIndicator())
|
|
: c.merchants.isEmpty
|
|
? _emptyState()
|
|
: ListView.builder(
|
|
padding: const EdgeInsets.fromLTRB(16, 4, 16, 24),
|
|
itemCount: c.merchants.length,
|
|
itemBuilder: (_, i) => _merchantCard(c.merchants[i]),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _citySearchBar(FoodController c) {
|
|
return Row(
|
|
children: [
|
|
Expanded(
|
|
child: InkWell(
|
|
onTap: () => _showCityPicker(c),
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
|
decoration: BoxDecoration(
|
|
color: AppColor.cardColor,
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(color: AppColor.borderColor),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Icon(Icons.location_on_rounded, color: AppColor.accentColor, size: 20),
|
|
const SizedBox(width: 8),
|
|
Expanded(
|
|
child: Text(c.city, style: AppStyle.title, overflow: TextOverflow.ellipsis),
|
|
),
|
|
Icon(Icons.keyboard_arrow_down_rounded, color: AppColor.grayColor),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
InkWell(
|
|
onTap: () => _showSearchDialog(c),
|
|
child: Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: AppColor.cardColor,
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(color: AppColor.borderColor),
|
|
),
|
|
child: Icon(Icons.search_rounded, color: AppColor.primaryColor),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
void _showCityPicker(FoodController c) {
|
|
final controller = TextEditingController(text: c.city);
|
|
Get.dialog(
|
|
AlertDialog(
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
|
title: Text(_isAr ? 'اختر المدينة' : 'Select City'),
|
|
content: TextField(
|
|
controller: controller,
|
|
decoration: InputDecoration(hintText: _isAr ? 'اسم المدينة' : 'City name'),
|
|
),
|
|
actions: [
|
|
TextButton(onPressed: () => Get.back(), child: Text(_isAr ? 'إلغاء' : 'Cancel')),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
Get.back();
|
|
c.setCity(controller.text);
|
|
},
|
|
child: Text(_isAr ? 'تم' : 'Done'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
void _showSearchDialog(FoodController c) {
|
|
final controller = TextEditingController();
|
|
Get.dialog(
|
|
AlertDialog(
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
|
title: Text(_isAr ? 'بحث عن مطعم أو صنف' : 'Search restaurant or dish'),
|
|
content: TextField(
|
|
controller: controller,
|
|
autofocus: true,
|
|
decoration: InputDecoration(hintText: _isAr ? 'اكتب هنا...' : 'Type here...'),
|
|
),
|
|
actions: [
|
|
TextButton(onPressed: () => Get.back(), child: Text(_isAr ? 'إلغاء' : 'Cancel')),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
Get.back();
|
|
c.searchMerchants(controller.text);
|
|
},
|
|
child: Text(_isAr ? 'بحث' : 'Search'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _emptyState() {
|
|
return ListView(
|
|
padding: const EdgeInsets.all(24),
|
|
children: [
|
|
const SizedBox(height: 60),
|
|
Icon(Icons.restaurant_menu_rounded, size: 72, color: AppColor.grayColor),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
_isAr ? 'لا توجد مطاعم متاحة في هذه المدينة حالياً' : 'No restaurants available in this city yet',
|
|
textAlign: TextAlign.center,
|
|
style: AppStyle.title.copyWith(fontWeight: FontWeight.bold),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _merchantCard(FoodMerchant m) {
|
|
return Card(
|
|
margin: const EdgeInsets.only(bottom: 12),
|
|
color: AppColor.cardColor,
|
|
elevation: 0,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(16),
|
|
side: BorderSide(color: AppColor.borderColor),
|
|
),
|
|
clipBehavior: Clip.antiAlias,
|
|
child: InkWell(
|
|
onTap: () => Get.to(() => FoodMerchantPage(merchantId: m.id)),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
if (m.coverUrl != null && m.coverUrl!.isNotEmpty)
|
|
Image.network(m.coverUrl!, height: 120, width: double.infinity, fit: BoxFit.cover,
|
|
errorBuilder: (_, __, ___) => Container(height: 120, color: AppColor.borderColor)),
|
|
Padding(
|
|
padding: const EdgeInsets.all(12),
|
|
child: Row(
|
|
children: [
|
|
CircleAvatar(
|
|
radius: 24,
|
|
backgroundColor: AppColor.accentColor.withOpacity(0.15),
|
|
backgroundImage: (m.logoUrl != null && m.logoUrl!.isNotEmpty)
|
|
? NetworkImage(m.logoUrl!)
|
|
: null,
|
|
child: (m.logoUrl == null || m.logoUrl!.isEmpty)
|
|
? Icon(Icons.restaurant_rounded, color: AppColor.accentColor)
|
|
: null,
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(m.nameAr, style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
|
const SizedBox(height: 4),
|
|
Row(
|
|
children: [
|
|
const Icon(Icons.star_rounded, color: Color(0xFFF59E0B), size: 16),
|
|
const SizedBox(width: 2),
|
|
Text(m.ratingAvg.toStringAsFixed(1), style: AppStyle.subtitle),
|
|
const SizedBox(width: 10),
|
|
Icon(Icons.access_time_rounded, size: 14, color: AppColor.grayColor),
|
|
const SizedBox(width: 2),
|
|
Text('${m.avgPrepMinutes} ${_isAr ? "د" : "min"}', style: AppStyle.subtitle),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
if (!m.isOpen)
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: AppColor.redColor.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Text(_isAr ? 'مغلق' : 'Closed',
|
|
style: TextStyle(color: AppColor.redColor, fontSize: 11, fontWeight: FontWeight.bold)),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|