import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:intaleq_maps/intaleq_maps.dart'; import '../../../core/l10n/app_localizations.dart'; import '../../../core/design/tokens.dart'; import '../../auth/cubit/auth_cubit.dart'; import '../../auth/cubit/auth_state.dart'; import '../../trip/bloc/trip_bloc.dart'; import '../../trip/bloc/trip_state.dart'; import '../../trip/bloc/trip_event.dart'; import '../cubit/home_cubit.dart'; import '../cubit/saved_places_cubit.dart'; import '../data/models/place_model.dart'; import '../data/maps_repository.dart'; import '../../../core/storage/saved_places_store.dart'; import '../../../core/di.dart'; /// زر عائم دائري صغير — للقائمة وإعادة التوسيط فوق الخريطة. class _MapCircleButton extends StatelessWidget { const _MapCircleButton({required this.icon, required this.onPressed}); final IconData icon; final VoidCallback onPressed; @override Widget build(BuildContext context) { final cs = Theme.of(context).colorScheme; return Container( decoration: BoxDecoration( color: cs.surface, shape: BoxShape.circle, boxShadow: const [T.shadowSm], ), child: IconButton( icon: Icon(icon, color: cs.onSurface), onPressed: onPressed, ), ); } } class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State createState() => _HomeScreenState(); } class _HomeScreenState extends State { IntaleqMapController? _mapController; bool _isSelectingOnMap = false; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { context.read().add(const TripCheckActive()); context.read().load(); }); } String _timeBasedGreeting(String? name, AppLocalizations l10n) { final hour = DateTime.now().hour; final isMorning = hour >= 5 && hour < 12; final isAr = Localizations.localeOf(context).languageCode == 'ar'; final prefix = isMorning ? (isAr ? 'صباح الخير' : 'Good morning') : (isAr ? 'مساء الخير' : 'Good evening'); final firstName = (name != null && name.trim().isNotEmpty) ? name.trim().split(' ').first : ''; return firstName.isNotEmpty ? '$prefix، $firstName' : prefix; } String _whereToPrompt(String? name, AppLocalizations l10n) { final firstName = (name != null && name.trim().isNotEmpty) ? name.trim().split(' ').first : ''; if (firstName.isNotEmpty) { final isAr = Localizations.localeOf(context).languageCode == 'ar'; return isAr ? 'إلى أين يا $firstName؟' : 'Where to, $firstName?'; } return l10n.whereTo; } @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; final cs = Theme.of(context).colorScheme; final isDark = Theme.of(context).brightness == Brightness.dark; return BlocListener( listenWhen: (prev, cur) => prev.trip?.status != cur.trip?.status, listener: (context, state) { if (state.trip != null && !state.isCompleted) { context.push('/trip-tracking'); } }, child: Scaffold( backgroundColor: cs.surface, // الخريطة تملأ الشاشة حتى الحافة العلوية (docs/29 §3.6) — كل العناصر // العائمة فوقها تعيش داخل SafeArea الخاصة بها لا الشاشة كلها. extendBodyBehindAppBar: true, body: Stack( children: [ // ── الخريطة (75% من الشاشة) ── Positioned( top: 0, left: 0, right: 0, height: MediaQuery.of(context).size.height * 0.75, child: BlocConsumer( listenWhen: (prev, cur) => prev.currentLocation == null && cur.currentLocation != null, buildWhen: (prev, cur) => prev.currentLocation != cur.currentLocation || prev.pickup != cur.pickup || prev.destination != cur.destination, listener: (context, state) { if (state.currentLocation != null && _mapController != null) { _mapController!.animateCamera( CameraUpdate.newLatLngZoom(state.currentLocation!, 16), ); } }, builder: (context, state) { final center = state.currentLocation ?? const LatLng(31.9539, 35.9106); return IntaleqMap( apiKey: '', initialCameraPosition: CameraPosition(target: center, zoom: 15), mapType: isDark ? IntaleqMapType.normal : IntaleqMapType.light, myLocationEnabled: true, myLocationButtonEnabled: false, zoomControlsEnabled: false, compassEnabled: false, onMapCreated: (controller) { _mapController = controller; if (state.currentLocation != null) { Future.delayed(const Duration(milliseconds: 300), () { controller.animateCamera( CameraUpdate.newLatLngZoom( state.currentLocation!, 16), ); }); } }, markers: _buildMarkers(state), onCameraMove: (position) { context .read() .updateCameraTarget(position.target); }, ); }, ), ), // زر القائمة أعلى-بداية PositionedDirectional( top: MediaQuery.of(context).padding.top + T.s8, start: T.pagePadding, child: _MapCircleButton( icon: Icons.menu_rounded, onPressed: () => _showLogoutDialog(context, l10n), ), ), // زر إعادة التوسيط عائم أعلى البطاقة السفلية PositionedDirectional( end: T.pagePadding, bottom: (_isSelectingOnMap ? 100 : MediaQuery.of(context).size.height * 0.28) + T.s16, child: BlocBuilder( builder: (context, state) { return _MapCircleButton( icon: Icons.my_location_rounded, onPressed: state.currentLocation == null ? () {} : () => _mapController?.animateCamera( CameraUpdate.newLatLngZoom( state.currentLocation!, 16), ), ); }, ), ), if (_isSelectingOnMap) // علامة المركز (دبوس) لاختيار الموقع Positioned( top: (MediaQuery.of(context).size.height * 0.75) / 2 - 20, left: MediaQuery.of(context).size.width / 2 - 20, child: const Icon(Icons.location_on, size: 40, color: Colors.black), ), // ── البطاقة السفلية: «إلى أين؟» والأماكن المحفوظة ── if (!_isSelectingOnMap) Positioned( left: 0, right: 0, bottom: 0, height: MediaQuery.of(context).size.height * 0.30, child: Container( width: double.infinity, padding: const EdgeInsets.fromLTRB( T.pagePadding, T.s24, T.pagePadding, T.s16), decoration: BoxDecoration( color: cs.surface, borderRadius: const BorderRadius.vertical( top: Radius.circular(T.r24), ), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.1), blurRadius: 20, offset: const Offset(0, -5), ) ], ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // التحية الزمنية مع اسم الراكب بتصميم أنيق BlocBuilder( builder: (context, authState) { final greeting = _timeBasedGreeting(authState.user?.name, l10n); return Text( greeting, style: Theme.of(context) .textTheme .titleLarge ?.copyWith( fontWeight: FontWeight.w800, color: cs.onSurface, letterSpacing: -0.5, ), ); }, ), const SizedBox(height: T.s24), // حقل «إلى أين؟» BlocBuilder( builder: (context, authState) { final searchPrompt = _whereToPrompt(authState.user?.name, l10n); return Material( color: cs.surfaceContainerLowest, elevation: 2, shadowColor: cs.shadow.withValues(alpha: 0.05), borderRadius: BorderRadius.circular(T.r16), child: InkWell( borderRadius: BorderRadius.circular(T.r16), onTap: () => _openDestinationSearch(context), child: Container( height: 60, padding: const EdgeInsets.symmetric(horizontal: T.s16), decoration: BoxDecoration( borderRadius: BorderRadius.circular(T.r16), border: Border.all( color: cs.outlineVariant.withValues(alpha: 0.5), width: 1, ), ), child: Row( children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: cs.primaryContainer.withValues(alpha: 0.5), shape: BoxShape.circle, ), child: Icon( Icons.search_rounded, color: cs.primary, size: 22, ), ), const SizedBox(width: T.s16), Expanded( child: Text( searchPrompt, style: Theme.of(context) .textTheme .titleMedium ?.copyWith( color: cs.onSurface, fontWeight: FontWeight.w700, fontSize: 18, ), ), ), ], ), ), ), ); }, ), const Spacer(), Divider(color: cs.outlineVariant.withValues(alpha: 0.3), height: 1), const SizedBox(height: T.s12), // الأماكن المحفوظة (سطر أفقي أنيق) BlocBuilder( builder: (context, savedState) { final items = savedState.places.take(3).toList(); return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _buildQuickAction(context, Icons.home_rounded, l10n.homePlace, items.isNotEmpty ? items.first : null), Container(width: 1, height: 32, color: cs.outlineVariant.withValues(alpha: 0.3)), _buildQuickAction(context, Icons.work_rounded, l10n.workPlace, items.length > 1 ? items[1] : null), Container(width: 1, height: 32, color: cs.outlineVariant.withValues(alpha: 0.3)), _buildQuickAction(context, Icons.star_rounded, l10n.favorites, items.length > 2 ? items[2] : null), ], ); }, ), const SizedBox(height: T.s8), ], ), ), ) else // زر تأكيد الوجهة في وضع اختيار الخريطة Positioned( left: 0, right: 0, bottom: 0, child: Container( padding: const EdgeInsets.fromLTRB( T.pagePadding, T.s16, T.pagePadding, T.s32), decoration: BoxDecoration( color: cs.surface, borderRadius: const BorderRadius.vertical( top: Radius.circular(T.r24), ), boxShadow: const [T.shadowLg], ), child: FilledButton( onPressed: _confirmMapDestination, style: FilledButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(T.r12), ), ), child: Text('تأكيد الوجهة', style: Theme.of(context).textTheme.titleMedium?.copyWith(color: cs.onPrimary)), ), ), ), ], ), ), ); } void _onPlaceTap(BuildContext context, SavedPlace place) { context.read().setDestination(PlaceModel( name: place.name, address: place.address, lat: place.lat, lng: place.lng, )); final state = context.read().state; if (state.pickup != null) { _openRoutePreview(context, state); } } Set _buildMarkers(HomeState state) { final markers = {}; if (state.pickup != null) { markers.add(Marker( markerId: const MarkerId('pickup'), position: LatLng(state.pickup!.lat, state.pickup!.lng), icon: InlqBitmap.defaultMarkerWithHue(210), anchor: const Offset(0.5, 1.0), zIndex: 2, )); } if (state.destination != null) { markers.add(Marker( markerId: const MarkerId('destination'), position: LatLng(state.destination!.lat, state.destination!.lng), icon: InlqBitmap.defaultMarkerWithHue(0), anchor: const Offset(0.5, 1.0), zIndex: 1, )); } return markers; } void _openDestinationSearch(BuildContext context) { final homeCubit = context.read(); final pickup = homeCubit.state.pickup; final currentLocation = homeCubit.state.currentLocation; context.push('/destination', extra: { 'currentLocation': currentLocation, }).then((result) { if (!mounted) return; if (result is Map && result['pickFromMap'] == true) { setState(() { _isSelectingOnMap = true; }); return; } final place = result as PlaceModel?; if (place != null && pickup != null) { homeCubit.setDestination(place); // ignore: use_build_context_synchronously context.push('/route-preview', extra: { 'pickup': pickup, 'destination': place, }); } }); } Future _confirmMapDestination() async { final homeCubit = context.read(); final target = homeCubit.state.cameraTarget; if (target == null) return; // We reverse geocode the target to get the address name final repo = getIt(); try { final place = await repo.reverseGeocode(target.latitude, target.longitude); setState(() { _isSelectingOnMap = false; }); homeCubit.setDestination(place); if (homeCubit.state.pickup != null) { _openRoutePreview(context, homeCubit.state); } } catch (_) { // In case of error, still set it with empty name setState(() { _isSelectingOnMap = false; }); homeCubit.setDestination(PlaceModel( name: 'موقع محدد على الخريطة', lat: target.latitude, lng: target.longitude, )); if (homeCubit.state.pickup != null) { _openRoutePreview(context, homeCubit.state); } } } void _openRoutePreview(BuildContext context, HomeState state) { if (state.pickup == null || state.destination == null) return; context.push('/route-preview', extra: { 'pickup': state.pickup, 'destination': state.destination, }); } void _showLogoutDialog(BuildContext context, AppLocalizations l10n) { final cs = Theme.of(context).colorScheme; showDialog( context: context, builder: (ctx) => AlertDialog( title: Text(l10n.logout), content: Text(l10n.logout), actions: [ TextButton( onPressed: () => Navigator.pop(ctx), child: Text(l10n.cancel), ), FilledButton( onPressed: () async { Navigator.pop(ctx); await context.read().logout(); if (context.mounted) context.go('/phone'); }, style: FilledButton.styleFrom(backgroundColor: cs.error), child: Text(l10n.logout), ), ], ), ); } Widget _buildQuickAction( BuildContext context, IconData icon, String label, SavedPlace? place) { final cs = Theme.of(context).colorScheme; return InkWell( onTap: place != null ? () => _onPlaceTap(context, place) : null, borderRadius: BorderRadius.circular(T.r12), child: Padding( padding: const EdgeInsets.symmetric(horizontal: T.s12, vertical: T.s8), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, color: place != null ? cs.primary : cs.onSurfaceVariant.withValues(alpha: 0.5), size: 28), const SizedBox(height: T.s4), Text( label, style: Theme.of(context).textTheme.labelMedium?.copyWith( color: place != null ? cs.onSurface : cs.onSurfaceVariant.withValues(alpha: 0.5), fontWeight: place != null ? FontWeight.w600 : FontWeight.normal, ), ), ], ), ), ); } }