import 'dart:math'; import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:intaleq_maps/intaleq_maps.dart'; import 'package:sefer_driver/env/env.dart'; import 'package:sefer_driver/controller/profile/setting_controller.dart'; import 'package:sefer_driver/constant/box_name.dart'; import 'package:sefer_driver/constant/colors.dart'; import 'package:sefer_driver/main.dart'; import 'package:sefer_driver/views/widgets/error_snakbar.dart'; import 'navigation_controller.dart'; // ─── Color Palette ────────────────────────────────────────────────────────── Color get _kSurface => Get.isDarkMode ? const Color(0xFF0F172A) : const Color(0xFFF8FAFC); Color get _kCardColor => Get.isDarkMode ? const Color(0xFF1E293B) : const Color(0xFFFFFFFF); Color get _kOnSurface => Get.isDarkMode ? const Color(0xFFF1F5F9) : const Color(0xFF0F172A); Color get _kPrimary => const Color(0xFF2563EB); class NavigationView extends StatelessWidget { const NavigationView({super.key}); @override Widget build(BuildContext context) { final NavigationController c = Get.put(NavigationController()); final SettingController s = Get.find(); return AnnotatedRegion( value: Get.isDarkMode ? SystemUiOverlayStyle.light : SystemUiOverlayStyle.dark, child: Scaffold( backgroundColor: _kSurface, body: GetBuilder( builder: (_) => Stack( children: [ // ── 1. Map Layer ────────────────────────────────────────────── GetBuilder( builder: (s) => IntaleqMap( apiKey: Env.mapSaasKey, onMapCreated: c.onMapCreated, onLongPress: (pos) => c.onMapLongPressed(Point(0, 0), pos), onTap: (pos) => c.onMapTapped(Point(0, 0), pos), markers: c.markers, polylines: c.polylines, circles: c.circles, polygons: c.polygons, mapType: s.isMapDarkMode ? IntaleqMapType.normal // Normal in IntaleqMap seems to be Dark : IntaleqMapType.light, initialCameraPosition: CameraPosition( target: c.myLocation ?? const LatLng(33.5138, 36.2765), zoom: 16.0), myLocationEnabled: false, ), ), // ── 2. Top UI (Explore Mode) ────────────────────────────────── if (!c.isNavigating) _ExploreTopUI(controller: c), // ── 3. Top UI (Active Navigation Banner) ── if (c.isNavigating && c.currentInstruction.isNotEmpty) _ActiveTopInstruction(controller: c), // ── 4. Explore Action Row ───────────────────────────────────── if (!c.isNavigating) _ExploreActionRow(controller: c), // ── 5. Bottom Panel (Explore Mode) ──────────────────────────── if (!c.isNavigating) _ExploreBottomPanel(controller: c), // ── 6. Bottom HUD (Active Navigation) ───────────────────────── if (c.isNavigating) _ActiveBottomHUD(controller: c), // ── 7. Search Results Dropdown ──────────────────────────────── if (c.placesDestination.isNotEmpty && !c.isNavigating) _SearchResults(controller: c), // ── 8. Loading Overlay ──────────────────────────────────────── if (c.isLoading) const _LoadingOverlay(), // ── 9. Location Picker Overlay (Place Creation) ────────────── _LocationPickerOverlay(controller: c), // ── 10. Recenter Button ─────────────────────────────────────── if (!c.isCameraLocked && !c.isNavigating) Positioned( right: 16, bottom: 250, child: _buildMapFAB( icon: Icons.my_location_rounded, onTap: () => c.relockCameraToUser(), ), ), // ── 11. Navigation Icon (Fixed Center) ──────────────────────── if (c.isNavigating) const _NavigationCenterIcon(), ], ), ), ), ); } Widget _buildMapFAB({required IconData icon, required VoidCallback onTap}) { return GestureDetector( onTap: onTap, child: Container( width: 48, height: 48, decoration: BoxDecoration( color: _kCardColor, shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.15), blurRadius: 12, offset: const Offset(0, 4), ) ], ), child: Icon(icon, color: _kPrimary, size: 22), ), ); } } class _NavigationCenterIcon extends StatelessWidget { const _NavigationCenterIcon({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return IgnorePointer( child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( decoration: BoxDecoration( color: _kPrimary, shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.3), blurRadius: 12, offset: const Offset(0, 4), ) ], border: Border.all(color: Colors.white, width: 3), ), padding: const EdgeInsets.all(12), child: const Icon( Icons.navigation_rounded, color: Colors.white, size: 32, ), ), const SizedBox(height: 40), ], ), ), ); } } class _ExploreTopUI extends StatelessWidget { final NavigationController controller; const _ExploreTopUI({required this.controller}); @override Widget build(BuildContext context) { return Positioned( top: 0, left: 0, right: 0, child: SafeArea( bottom: false, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Container( decoration: BoxDecoration( color: _kCardColor, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.08), blurRadius: 24, offset: const Offset(0, 8), ) ], ), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), child: Row( children: [ IconButton( icon: Icon(Icons.menu_rounded, color: _kOnSurface, size: 24), onPressed: () => Scaffold.of(context).openDrawer(), ), const SizedBox(width: 8), Expanded( child: TextField( controller: controller.placeDestinationController, onChanged: controller.onSearchChanged, textInputAction: TextInputAction.search, style: TextStyle( fontSize: 16, color: _kOnSurface, fontWeight: FontWeight.w600), decoration: InputDecoration( hintText: box.read(BoxName.lang) == 'ar' ? 'إلى أين؟' : 'Where to?', hintStyle: TextStyle( color: _kOnSurface.withOpacity(0.4), fontSize: 16), border: InputBorder.none, isDense: true, ), ), ), if (controller.placeDestinationController.text.isNotEmpty || controller.routes.isNotEmpty) IconButton( icon: Icon(Icons.close_rounded, color: Colors.red.shade400, size: 22), onPressed: () => controller.clearEverything(), ) else Padding( padding: const EdgeInsets.only(right: 8), child: CircleAvatar( radius: 18, backgroundColor: _kPrimary, child: const Icon(Icons.person_rounded, color: Colors.white, size: 20), ), ), ], ), ), ), ), ); } } class _ExploreBottomPanel extends StatelessWidget { final NavigationController controller; const _ExploreBottomPanel({required this.controller}); @override Widget build(BuildContext context) { final bool hasRoutes = controller.routes.isNotEmpty; final isArabic = box.read(BoxName.lang) == 'ar'; final bottomPad = MediaQuery.of(context).padding.bottom; if (!hasRoutes) return const SizedBox.shrink(); return Positioned( bottom: 0, left: 0, right: 0, child: Container( padding: EdgeInsets.only(bottom: bottomPad + 16, top: 16), decoration: BoxDecoration( color: _kCardColor, borderRadius: const BorderRadius.vertical(top: Radius.circular(24)), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 32, offset: const Offset(0, -4), ) ], ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( children: [ Text( isArabic ? 'المسارات المتاحة' : 'Available Routes', style: TextStyle( color: _kOnSurface, fontWeight: FontWeight.bold, fontSize: 14, ), ), ], ), ), const SizedBox(height: 12), SizedBox( height: 90, child: ListView.builder( padding: const EdgeInsets.symmetric(horizontal: 12), scrollDirection: Axis.horizontal, itemCount: controller.routes.length, itemBuilder: (context, index) { final r = controller.routes[index]; final isSelected = controller.selectedRouteIndex == index; return GestureDetector( onTap: () => controller.selectRoute(index), child: Container( width: 140, margin: const EdgeInsets.symmetric(horizontal: 4), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: isSelected ? _kPrimary : _kSurface, borderRadius: BorderRadius.circular(16), border: Border.all( color: isSelected ? _kPrimary : Colors.grey.shade300, width: 1.5, ), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.route_rounded, color: isSelected ? Colors.white : _kOnSurface, ), const SizedBox(height: 6), Text( "${(r.distanceM / 1000).toStringAsFixed(1)} km", style: TextStyle( color: isSelected ? Colors.white : _kOnSurface, fontWeight: FontWeight.bold, ), ), Text( "${(r.durationS / 60).toInt()} min", style: TextStyle( color: isSelected ? Colors.white70 : _kOnSurface.withOpacity(0.6), fontSize: 12, ), ), ], ), ), ); }, ), ), const SizedBox(height: 16), Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: SizedBox( width: double.infinity, height: 52, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: _kPrimary, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16)), ), onPressed: () { HapticFeedback.mediumImpact(); controller.startActiveNavigation(); }, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.navigation_rounded, color: Colors.white, size: 20), const SizedBox(width: 8), Text(isArabic ? 'ابدأ الملاحة' : 'Start Navigation', style: const TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.w700)), ], ), ), ), ), ], ), ), ); } } class _ActiveTopInstruction extends StatelessWidget { final NavigationController controller; const _ActiveTopInstruction({required this.controller}); @override Widget build(BuildContext context) { return Positioned( top: MediaQuery.of(context).padding.top + 12, left: 16, right: 16, child: Container( decoration: BoxDecoration( color: const Color(0xFF1B5E20), borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.25), blurRadius: 16, offset: const Offset(0, 8)) ], ), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), child: Row( children: [ Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.white.withOpacity(0.15), shape: BoxShape.circle, ), child: Icon(controller.currentManeuverIcon, color: Colors.white, size: 28), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( controller.currentInstruction, style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.w700), maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 2), Text( controller.distanceToNextStep, style: TextStyle( color: Colors.white.withOpacity(0.8), fontSize: 14, fontWeight: FontWeight.w600), ), ], ), ), ], ), ), ); } } class _ActiveBottomHUD extends StatelessWidget { final NavigationController controller; const _ActiveBottomHUD({required this.controller}); @override Widget build(BuildContext context) { final bottomPad = MediaQuery.of(context).padding.bottom; final isArabic = box.read(BoxName.lang) == 'ar'; return Positioned( bottom: bottomPad + 16, left: 16, right: 16, child: Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ Container( width: 70, height: 70, decoration: BoxDecoration( color: _kCardColor, shape: BoxShape.circle, border: Border.all(color: Colors.red, width: 3), boxShadow: [ BoxShadow(color: Colors.black.withOpacity(0.1), blurRadius: 10) ], ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( controller.currentSpeed.toStringAsFixed(0), style: const TextStyle( fontSize: 22, fontWeight: FontWeight.w900, color: Colors.black, ), ), const Text( 'km/h', style: TextStyle( fontSize: 10, fontWeight: FontWeight.w600, color: Colors.grey, ), ), ], ), ), const SizedBox(width: 12), Expanded( child: Container( decoration: BoxDecoration( color: _kCardColor, borderRadius: BorderRadius.circular(24), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.15), blurRadius: 24, offset: const Offset(0, 8)) ], ), padding: const EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( controller.arrivalTime, style: const TextStyle( fontSize: 22, fontWeight: FontWeight.w900, color: Color(0xFF2563EB), ), ), Text( isArabic ? '${controller.estimatedTimeRemaining} د' : '${controller.estimatedTimeRemaining} min', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: _kOnSurface, ), ), Text( controller.distanceWithUnit, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: _kOnSurface.withOpacity(0.7), ), ), ], ), const SizedBox(height: 14), Row( children: [ _CircleIconBtn( icon: controller.isMuted ? Icons.volume_off_rounded : Icons.volume_up_rounded, color: const Color(0xFFE3F2FD), iconColor: _kPrimary, onTap: () => controller.toggleMute()), const Spacer(), GestureDetector( onTap: () => controller.clearRoute(), child: Container( height: 44, padding: const EdgeInsets.symmetric(horizontal: 32), decoration: BoxDecoration( color: Colors.red.shade600, borderRadius: BorderRadius.circular(22), ), child: Center( child: Text( isArabic ? 'إنهاء' : 'End', style: const TextStyle( color: Colors.white, fontSize: 15, fontWeight: FontWeight.bold, ), ), ), ), ), ], ), ], ), ), ), ], ), ); } } class _CircleIconBtn extends StatelessWidget { final IconData icon; final Color color; final Color iconColor; final VoidCallback onTap; const _CircleIconBtn( {required this.icon, required this.color, required this.iconColor, required this.onTap}); @override Widget build(BuildContext context) { return GestureDetector( onTap: () { HapticFeedback.lightImpact(); onTap(); }, child: Container( width: 44, height: 44, decoration: BoxDecoration(color: color, shape: BoxShape.circle), child: Icon(icon, color: iconColor, size: 22), ), ); } } class _SearchResults extends StatelessWidget { final NavigationController controller; const _SearchResults({required this.controller}); @override Widget build(BuildContext context) { return Positioned( top: MediaQuery.of(context).padding.top + 80, left: 16, right: 16, child: Material( color: _kCardColor, borderRadius: BorderRadius.circular(16), elevation: 4, child: Container( constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.4, ), child: ListView.separated( shrinkWrap: true, itemCount: controller.placesDestination.length, separatorBuilder: (_, __) => Divider(height: 1, color: _kOnSurface.withOpacity(0.1)), itemBuilder: (context, index) { final place = controller.placesDestination[index]; return ListTile( onTap: () => controller.selectDestination(place), leading: Icon(Icons.location_on_rounded, color: _kPrimary), title: Text( place['name'] ?? 'Unknown', style: TextStyle( color: _kOnSurface, fontWeight: FontWeight.w600), ), subtitle: Text( place['address'] ?? place['formatted_address'] ?? '', maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( color: _kOnSurface.withOpacity(0.5), fontSize: 12), ), ); }, ), ), ), ); } } class _LoadingOverlay extends StatelessWidget { const _LoadingOverlay(); @override Widget build(BuildContext context) { return Positioned.fill( child: Container( color: Colors.black38, child: Center( child: Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: _kCardColor, borderRadius: BorderRadius.circular(20), ), child: const CircularProgressIndicator(color: AppColor.primaryColor), ), ), ), ); } } class _ExploreActionRow extends StatelessWidget { final NavigationController controller; const _ExploreActionRow({required this.controller}); @override Widget build(BuildContext context) { final bool hasRoutes = controller.routes.isNotEmpty; final isAr = box.read(BoxName.lang) == 'ar'; final double safeBottom = MediaQuery.of(context).padding.bottom; if (hasRoutes) return const SizedBox.shrink(); return Positioned( bottom: safeBottom + 20, left: 0, right: 0, child: SingleChildScrollView( scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric(horizontal: 16), physics: const BouncingScrollPhysics(), child: Row( children: [ _ActionCapsule( icon: Icons.add_rounded, label: isAr ? 'إضافة' : 'Add', onTap: () => controller.togglePlaceSelectionMode(), isPrimary: true, ), _ActionCapsule( icon: Icons.home_rounded, label: isAr ? 'المنزل' : 'Home', onTap: () => controller.goToFavorite('home'), ), _ActionCapsule( icon: Icons.work_rounded, label: isAr ? 'العمل' : 'Work', onTap: () => controller.goToFavorite('work'), ), _ActionCapsule( icon: Icons.bookmark_rounded, label: isAr ? 'المحفوظات' : 'Saved', onTap: () {}, ), ], ), ), ); } } class _ActionCapsule extends StatelessWidget { final IconData icon; final String label; final VoidCallback onTap; final bool isPrimary; const _ActionCapsule({ required this.icon, required this.label, required this.onTap, this.isPrimary = false, }); @override Widget build(BuildContext context) { Color bgColor; Color textColor; if (isPrimary) { bgColor = const Color(0xFF0D47A1).withOpacity(0.9); textColor = Colors.white; } else { bgColor = _kCardColor.withOpacity(0.85); textColor = _kOnSurface; } return Padding( padding: const EdgeInsets.only(right: 8), child: GestureDetector( onTap: () { HapticFeedback.lightImpact(); onTap(); }, child: ClipRRect( borderRadius: BorderRadius.circular(50), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 12, sigmaY: 12), child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( color: bgColor, borderRadius: BorderRadius.circular(50), border: Border.all( color: Colors.white.withOpacity(0.2), width: 1.2), boxShadow: const [ BoxShadow( color: Color(0x14000000), blurRadius: 8, offset: Offset(0, 4)) ], ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 18, color: textColor), const SizedBox(width: 8), Text( label, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w700, color: textColor, ), ), ], ), ), ), ), ), ); } } class _LocationPickerOverlay extends StatelessWidget { final NavigationController controller; const _LocationPickerOverlay({required this.controller}); @override Widget build(BuildContext context) { if (!controller.isSelectingPlaceLocation) return const SizedBox.shrink(); final isAr = box.read(BoxName.lang) == 'ar'; return Stack( children: [ IgnorePointer( child: Container(color: Colors.black.withOpacity(0.1)), ), IgnorePointer( child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.white.withOpacity(0.9), shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 10, offset: const Offset(0, 4)) ], ), child: const Icon(Icons.add_location_alt_rounded, color: Color(0xFF0D47A1), size: 40), ), const SizedBox(height: 40), ], ), ), ), Positioned( bottom: 110, left: 32, right: 32, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF1B5E20), padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16)), elevation: 8, shadowColor: const Color(0xFF1B5E20).withOpacity(0.5), ), onPressed: () => _showAddPlaceFormDialog(context, controller), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.check_circle_rounded, color: Colors.white), const SizedBox(width: 12), Text( isAr ? 'تأكيد الموقع' : 'Confirm Location', style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.w800), ), ], ), ), ), Positioned( top: 140, left: 40, right: 40, child: IgnorePointer( child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), decoration: BoxDecoration( color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(30), ), child: Text( isAr ? "حرك الخريطة لتحديد موقع المكان" : "Move map to pick place location", textAlign: TextAlign.center, style: const TextStyle(color: Colors.white, fontSize: 13), ), ), ), ), Positioned( top: 60, right: 20, child: FloatingActionButton.small( backgroundColor: Colors.white, elevation: 4, child: const Icon(Icons.close_rounded, color: Colors.black87), onPressed: () => controller.togglePlaceSelectionMode(), ), ), ], ); } } void _showAddPlaceFormDialog( BuildContext context, NavigationController controller) { final nameController = TextEditingController(); final categoryNotifier = ValueNotifier?>(null); final isAr = box.read(BoxName.lang) == 'ar'; Get.dialog( AlertDialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)), backgroundColor: _kCardColor, title: Row( children: [ const Icon(Icons.add_business_rounded, color: Color(0xFF0D47A1), size: 28), const SizedBox(width: 12), Text(isAr ? 'إضافة مكان جديد' : 'Add New Place', style: TextStyle(color: _kOnSurface, fontWeight: FontWeight.bold)), ], ), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( isAr ? "ساهم في تحسين الخريطة بإضافة الأماكن الناقصة." : "Help improve the map by adding missing places.", style: TextStyle(color: _kOnSurface.withOpacity(0.6), fontSize: 13), ), const SizedBox(height: 20), TextField( controller: nameController, style: TextStyle(color: _kOnSurface), decoration: InputDecoration( labelText: isAr ? 'اسم المكان' : 'Place Name', labelStyle: TextStyle(color: _kOnSurface.withOpacity(0.6)), prefixIcon: Icon(Icons.label_rounded, color: _kOnSurface.withOpacity(0.6)), filled: true, fillColor: _kSurface.withOpacity(0.3), border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)), ), ), const SizedBox(height: 16), ValueListenableBuilder?>( valueListenable: categoryNotifier, builder: (context, selected, _) { return InkWell( onTap: () => _showCategoryPicker(context, (cat) { categoryNotifier.value = cat; }), borderRadius: BorderRadius.circular(12), child: Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 16), decoration: BoxDecoration( color: _kSurface.withOpacity(0.3), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey.withOpacity(0.3)), ), child: Row( children: [ Icon(Icons.category_rounded, color: _kOnSurface.withOpacity(0.6)), const SizedBox(width: 12), Expanded( child: Text( selected != null ? (isAr ? selected['ar']! : selected['en']!) : (isAr ? 'اختر الفئة' : 'Select Category'), style: TextStyle( color: selected != null ? _kOnSurface : _kOnSurface.withOpacity(0.4), fontSize: 16, ), ), ), Icon(Icons.keyboard_arrow_down_rounded, color: _kOnSurface.withOpacity(0.6)), ], ), ), ); }), const SizedBox(height: 24), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF0D47A1), padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12)), ), onPressed: () { if (nameController.text.isNotEmpty && categoryNotifier.value != null) { Get.back(); controller.submitNewPlace( nameController.text, categoryNotifier.value!['id']!); } else { mySnackbarWarning( isAr ? 'يرجى إكمال البيانات' : 'Please fill all fields'); } }, child: Text(isAr ? 'إرسال' : 'Submit', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold)), ), TextButton( onPressed: () => Get.back(), child: Text(isAr ? 'إلغاء' : 'Cancel', style: TextStyle(color: _kOnSurface.withOpacity(0.6))), ), ], ), ), ), ); } void _showCategoryPicker( BuildContext context, Function(Map) onSelected) { final isAr = box.read(BoxName.lang) == 'ar'; Get.bottomSheet( Container( decoration: BoxDecoration( color: _kCardColor, borderRadius: const BorderRadius.vertical(top: Radius.circular(24)), ), padding: const EdgeInsets.only(top: 12, bottom: 24), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 40, height: 4, decoration: BoxDecoration( color: Colors.grey.withOpacity(0.3), borderRadius: BorderRadius.circular(2), ), ), const SizedBox(height: 16), Text(isAr ? 'اختر الفئة' : 'Select Category', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: _kOnSurface)), const SizedBox(height: 16), Flexible( child: ListView.builder( shrinkWrap: true, itemCount: NavigationController.placeCategories.length, itemBuilder: (context, index) { final cat = NavigationController.placeCategories[index]; return ListTile( leading: Icon(_getIconData(cat['icon']!), color: const Color(0xFF0D47A1)), title: Text(isAr ? cat['ar']! : cat['en']!, style: TextStyle( color: _kOnSurface, fontWeight: FontWeight.w600)), onTap: () { HapticFeedback.lightImpact(); onSelected(cat); Get.back(); }, ); }, ), ), ], ), ), isScrollControlled: true, ); } IconData _getIconData(String name) { switch (name) { case 'restaurant': return Icons.restaurant_rounded; case 'coffee': return Icons.coffee_rounded; case 'shopping_basket': return Icons.shopping_basket_rounded; case 'local_pharmacy': return Icons.local_pharmacy_rounded; case 'local_gas_station': return Icons.local_gas_station_rounded; case 'atm': return Icons.atm_rounded; case 'account_balance': return Icons.account_balance_rounded; case 'mosque': return Icons.mosque_rounded; case 'local_hospital': return Icons.local_hospital_rounded; case 'school': return Icons.school_rounded; case 'park': return Icons.park_rounded; case 'hotel': return Icons.hotel_rounded; case 'shopping_mall': return Icons.store_rounded; case 'fitness_center': return Icons.fitness_center_rounded; case 'content_cut': return Icons.content_cut_rounded; case 'bakery_dining': return Icons.bakery_dining_rounded; case 'local_laundry_service': return Icons.local_laundry_service_rounded; case 'build': return Icons.build_rounded; case 'gavel': return Icons.gavel_rounded; default: return Icons.place_rounded; } }