262 lines
9.2 KiB
Dart
262 lines
9.2 KiB
Dart
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 '../../auth/cubit/auth_cubit.dart';
|
|
import '../../auth/cubit/auth_state.dart';
|
|
import '../../../core/ui/tripz_button.dart';
|
|
import '../../../core/design/tokens.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 '../data/models/place_model.dart';
|
|
|
|
class HomeScreen extends StatefulWidget {
|
|
const HomeScreen({super.key});
|
|
|
|
@override
|
|
State<HomeScreen> createState() => _HomeScreenState();
|
|
}
|
|
|
|
class _HomeScreenState extends State<HomeScreen> {
|
|
IntaleqMapController? _mapController;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
// فحص رحلة نشطة عند فتح الشاشة.
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
context.read<TripBloc>().add(const TripCheckActive());
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l10n = AppLocalizations.of(context)!;
|
|
final cs = Theme.of(context).colorScheme;
|
|
|
|
return BlocListener<TripBloc, TripState>(
|
|
listenWhen: (prev, cur) => prev.trip?.status != cur.trip?.status,
|
|
listener: (context, state) {
|
|
if (state.trip != null && !state.isCompleted) {
|
|
// رحلة نشطة — انتقل للتتبع.
|
|
context.push('/trip-tracking');
|
|
}
|
|
},
|
|
child: Scaffold(
|
|
body: Stack(
|
|
children: [
|
|
// الخريطة
|
|
BlocBuilder<HomeCubit, HomeState>(
|
|
builder: (context, state) {
|
|
final center = state.currentLocation ?? const LatLng(31.9539, 35.9106);
|
|
return IntaleqMap(
|
|
apiKey: '',
|
|
initialCameraPosition: CameraPosition(target: center, zoom: 15),
|
|
myLocationEnabled: true,
|
|
zoomControlsEnabled: false,
|
|
compassEnabled: false,
|
|
onMapCreated: (controller) => _mapController = controller,
|
|
markers: _buildMarkers(state),
|
|
onCameraMove: (position) {
|
|
context.read<HomeCubit>().updateCameraTarget(position.target);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
|
|
// زر إعادة التوسيط
|
|
Positioned(
|
|
right: T.pagePadding,
|
|
bottom: 180,
|
|
child: FloatingActionButton.small(
|
|
heroTag: 'recenter',
|
|
backgroundColor: cs.surface,
|
|
onPressed: () {
|
|
final loc = context.read<HomeCubit>().state.currentLocation;
|
|
if (loc != null) {
|
|
_mapController?.animateCamera(CameraUpdate.newLatLngZoom(loc, 15));
|
|
}
|
|
},
|
|
child: Icon(Icons.my_location_rounded, color: cs.onSurface),
|
|
),
|
|
),
|
|
|
|
// الشريط السفلي
|
|
Positioned(
|
|
left: T.pagePadding,
|
|
right: T.pagePadding,
|
|
bottom: T.pagePadding,
|
|
child: Card(
|
|
elevation: 2,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(T.pagePadding),
|
|
child: BlocBuilder<HomeCubit, HomeState>(
|
|
builder: (context, state) {
|
|
return Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
BlocBuilder<AuthCubit, AuthState>(
|
|
builder: (context, authState) {
|
|
final name = authState.user?.name ?? '';
|
|
if (name.isNotEmpty) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: T.s12),
|
|
child: Text(
|
|
l10n.welcome(name),
|
|
style: Theme.of(context).textTheme.titleMedium,
|
|
),
|
|
);
|
|
}
|
|
return const SizedBox.shrink();
|
|
},
|
|
),
|
|
if (state.destination != null)
|
|
_DestinationPreview(
|
|
destination: state.destination!,
|
|
onTap: () => _openDestinationSearch(context),
|
|
onClear: () => context.read<HomeCubit>().clearTripData(),
|
|
)
|
|
else
|
|
TripzButton(
|
|
label: l10n.whereTo,
|
|
style: TripzButtonStyle.secondary,
|
|
icon: Icons.search_rounded,
|
|
onPressed: () => _openDestinationSearch(context),
|
|
),
|
|
if (state.destination != null) ...[
|
|
const SizedBox(height: T.s12),
|
|
TripzButton(
|
|
label: l10n.requestRide,
|
|
onPressed: () => _openRoutePreview(context, state),
|
|
),
|
|
],
|
|
],
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// حالة التحميل
|
|
BlocBuilder<HomeCubit, HomeState>(
|
|
builder: (context, state) {
|
|
if (state.loading) {
|
|
return Container(
|
|
color: Colors.black26,
|
|
child: const Center(child: CircularProgressIndicator()),
|
|
);
|
|
}
|
|
return const SizedBox.shrink();
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Set<Marker> _buildMarkers(HomeState state) {
|
|
final markers = <Marker>{};
|
|
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) {
|
|
context.push<PlaceModel>('/destination').then((place) {
|
|
if (place != null && mounted) {
|
|
context.read<HomeCubit>().setDestination(place);
|
|
_openRoutePreview(context, context.read<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,
|
|
});
|
|
}
|
|
}
|
|
|
|
class _DestinationPreview extends StatelessWidget {
|
|
final PlaceModel destination;
|
|
final VoidCallback onTap;
|
|
final VoidCallback onClear;
|
|
|
|
const _DestinationPreview({
|
|
required this.destination,
|
|
required this.onTap,
|
|
required this.onClear,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final cs = Theme.of(context).colorScheme;
|
|
return InkWell(
|
|
onTap: onTap,
|
|
borderRadius: BorderRadius.circular(T.r12),
|
|
child: Container(
|
|
padding: const EdgeInsets.all(T.s12),
|
|
decoration: BoxDecoration(
|
|
color: cs.surfaceContainerHighest,
|
|
borderRadius: BorderRadius.circular(T.r12),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Icon(Icons.location_on_outlined, color: cs.error, size: 20),
|
|
const SizedBox(width: T.s8),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
destination.name,
|
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w500),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
if (destination.address != null)
|
|
Text(
|
|
destination.address!,
|
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: cs.onSurfaceVariant),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
IconButton(
|
|
onPressed: onClear,
|
|
icon: Icon(Icons.close_rounded, color: cs.onSurfaceVariant, size: 20),
|
|
visualDensity: VisualDensity.compact,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|