Files
tripz-llc/apps/rider/lib/features/home/view/home_screen.dart
T

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,
),
],
),
),
);
}
}