import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/l10n/app_localizations.dart'; import 'package:go_router/go_router.dart'; import 'package:intaleq_maps/intaleq_maps.dart'; import '../../../core/design/tokens.dart'; import '../bloc/trip_bloc.dart'; import '../bloc/trip_state.dart'; import '../bloc/trip_event.dart'; import 'widgets/driver_card.dart'; import 'widgets/trip_status_banner.dart'; /// شاشة تتبع الرحلة — من searching إلى completed. class TripTrackingScreen extends StatefulWidget { const TripTrackingScreen({super.key}); @override State createState() => _TripTrackingScreenState(); } class _TripTrackingScreenState extends State { IntaleqMapController? _mapController; Timer? _cameraFollowTimer; @override void initState() { super.initState(); // مراقبة حالة التطبيق للتحقق من الرحلة النشطة عند العودة. WidgetsBinding.instance.addObserver(_AppLifecycleObserver(context)); } @override void dispose() { _cameraFollowTimer?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; final cs = Theme.of(context).colorScheme; return BlocConsumer( listenWhen: (prev, cur) => prev.trip?.status != cur.trip?.status, listener: (context, state) { if (state.isCompleted) { // الرحلة اكتملت — انتقل لشاشة الفاتورة. context.pushReplacement('/trip-invoice'); } }, builder: (context, state) { final trip = state.trip; if (trip == null) { return const Scaffold(body: Center(child: CircularProgressIndicator())); } final pickup = LatLng(trip.pickupLat, trip.pickupLng); final dropoff = LatLng(trip.dropoffLat, trip.dropoffLng); final driverPos = state.driverLat != null && state.driverLng != null ? LatLng(state.driverLat!, state.driverLng!) : null; return Scaffold( body: Stack( children: [ // الخريطة IntaleqMap( apiKey: '', initialCameraPosition: CameraPosition( target: driverPos ?? pickup, zoom: 15, ), myLocationEnabled: false, zoomControlsEnabled: false, compassEnabled: false, onMapCreated: (c) => _mapController = c, markers: _buildMarkers(pickup, dropoff, driverPos), ), // شريط الحالة العلوي Positioned( top: MediaQuery.of(context).padding.top + T.s8, left: T.pagePadding, right: T.pagePadding, child: TripStatusBanner(status: trip.status), ), // بطاقة السائق (إذا تم تعيينه) if (state.requestStatus == TripRequestStatus.active) Positioned( left: T.pagePadding, right: T.pagePadding, bottom: T.pagePadding, child: DriverCard( driverName: state.driverName ?? '', vehicleMake: state.vehicleMake ?? '', vehicleModel: state.vehicleModel ?? '', vehiclePlate: state.vehiclePlate ?? '', vehicleColor: state.vehicleColor ?? '', status: trip.status, onCall: () { // TODO: WebRTC calling — Phase 3 }, onChat: () { // TODO: Chat — Phase 3 }, onCancel: state.canCancel ? () => context.read().add(const TripCancelRequested()) : null, ), ), // شاشة البحث (قبل تعيين السائق) if (state.requestStatus == TripRequestStatus.searching) _SearchingBody(elapsed: 0), ], ), ); }, ); } Set _buildMarkers(LatLng pickup, LatLng dropoff, LatLng? driverPos) { final markers = { Marker( markerId: const MarkerId('pickup'), position: pickup, icon: InlqBitmap.defaultMarkerWithHue(210), anchor: const Offset(0.5, 1.0), ), Marker( markerId: const MarkerId('dropoff'), position: dropoff, icon: InlqBitmap.defaultMarkerWithHue(0), anchor: const Offset(0.5, 1.0), ), }; if (driverPos != null) { markers.add(Marker( markerId: const MarkerId('driver'), position: driverPos, icon: InlqBitmap.defaultMarkerWithHue(140), anchor: const Offset(0.5, 0.5), zIndex: 3, )); } return markers; } } /// شاشة البحث عن سائق مع مؤقت. class _SearchingBody extends StatefulWidget { final int elapsed; const _SearchingBody({required this.elapsed}); @override State<_SearchingBody> createState() => _SearchingBodyState(); } class _SearchingBodyState extends State<_SearchingBody> with SingleTickerProviderStateMixin { late AnimationController _pulse; late Animation _scale; @override void initState() { super.initState(); _pulse = AnimationController(vsync: this, duration: const Duration(seconds: 2)) ..repeat(reverse: true); _scale = Tween(begin: 0.8, end: 1.2).animate( CurvedAnimation(parent: _pulse, curve: Curves.easeInOut), ); } @override void dispose() { _pulse.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; final cs = Theme.of(context).colorScheme; return Material( color: Colors.black54, child: Center( child: Container( margin: const EdgeInsets.all(T.pagePadding * 2), padding: const EdgeInsets.all(T.pagePadding * 1.5), decoration: BoxDecoration( color: cs.surface, borderRadius: BorderRadius.circular(T.r16), boxShadow: [T.shadowLg], ), child: Column( mainAxisSize: MainAxisSize.min, children: [ ScaleTransition( scale: _scale, child: Container( width: 80, height: 80, decoration: BoxDecoration( color: cs.primaryContainer, shape: BoxShape.circle, ), child: Icon(Icons.directions_car_filled_rounded, size: 40, color: cs.onPrimaryContainer), ), ), const SizedBox(height: T.s16), Text(l10n.searchingDriver, style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600), textAlign: TextAlign.center), const SizedBox(height: T.s20), SizedBox( width: double.infinity, child: OutlinedButton( onPressed: () => context.read().add(const TripCancelRequested()), style: OutlinedButton.styleFrom(foregroundColor: cs.error, side: BorderSide(color: cs.error)), child: Text(l10n.cancelSearch), ), ), ], ), ), ), ); } } /// مراقب دورة حياة التطبيق — يتحقق من الرحلة عند العودة. class _AppLifecycleObserver extends WidgetsBindingObserver { final BuildContext context; _AppLifecycleObserver(this.context); @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.resumed) { context.read().add(const TripCheckActive()); } } }