import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; import '../../../core/design/tripz_colors.dart'; import '../../../core/di.dart'; import '../../../core/l10n/l10n.dart'; import '../../../core/ui/tripz_button.dart'; import '../cubit/ride_cubit.dart'; import '../cubit/ride_state.dart'; import '../data/models/geo_point.dart'; import 'widgets/active_trip_sheet.dart'; import 'widgets/center_pin.dart'; import 'widgets/confirm_sheet.dart'; import 'widgets/planner_sheet.dart'; import 'widgets/ride_map.dart'; import 'widgets/searching_sheet.dart'; /// شاشة الراكب: خريطة ثابتة + ورقة سفلية تتبدّل بالمرحلة (docs/39 §3). /// /// الخريطة **لا تُعاد بناؤها** عند تبدّل الورقة: هي خارج الـ`BlocBuilder` /// الذي يبني الورقة، ولها `BlocSelector` خاص بما تحتاجه وحدها. هذا هو الفرق /// بين خريطة سلسة وأخرى تهتزّ عند كل تحديث حالة. class RidePage extends StatefulWidget { const RidePage({super.key}); @override State createState() => _RidePageState(); } class _RidePageState extends State { late final RideCubit _cubit = sl()..init(); GeoPoint? _mapCenter; @override void dispose() { _cubit.close(); super.dispose(); } @override Widget build(BuildContext context) { return BlocProvider.value( value: _cubit, child: Scaffold( body: Stack( children: [ // ── الخريطة ──────────────────────────────────────────────── BlocSelector( selector: (s) => _MapData( origin: s.origin, destination: s.destination, driver: s.driverLocation, route: s.route, camera: s.cameraTarget, ), builder: (context, data) => RideMap( origin: data.origin, destination: data.destination, driver: data.driver, route: data.route, cameraTarget: data.camera, onCameraIdle: (p) => _mapCenter = p, ), ), // ── دبّوس الاختيار ───────────────────────────────────────── BlocSelector( selector: (s) => s.phase == RidePhase.pickingOnMap, builder: (context, picking) => picking ? const CenterPin() : const SizedBox.shrink(), ), // ── الورقة السفلية ───────────────────────────────────────── Align( alignment: Alignment.bottomCenter, child: BlocBuilder( builder: (context, state) => _Sheet( state: state, onConfirmPick: () { final center = _mapCenter; if (center != null) _cubit.confirmMapPick(center); }, ), ), ), ], ), ), ); } } /// ما تحتاجه الخريطة وحدها — تغيّرُ أي شيء آخر في الحالة لا يعيد بناءها. class _MapData { const _MapData({ this.origin, this.destination, this.driver, this.route, this.camera, }); final GeoPoint? origin; final GeoPoint? destination; final GeoPoint? driver; final dynamic route; final GeoPoint? camera; @override bool operator ==(Object other) => other is _MapData && other.origin == origin && other.destination == destination && other.driver == driver && other.route == route && other.camera == camera; @override int get hashCode => Object.hash(origin, destination, driver, route, camera); } class _Sheet extends StatelessWidget { const _Sheet({required this.state, required this.onConfirmPick}); final RideState state; final VoidCallback onConfirmPick; @override Widget build(BuildContext context) { final child = switch (state.phase) { RidePhase.idle => const _Collapsed(), RidePhase.planning => PlannerSheet(state: state), RidePhase.pickingOnMap => _PickConfirm(onConfirm: onConfirmPick), RidePhase.confirming => ConfirmSheet(state: state), RidePhase.searching => const SearchingSheet(), RidePhase.active => ActiveTripSheet(trip: state.trip!), }; return Container( width: double.infinity, decoration: BoxDecoration( color: context.colors.surface, borderRadius: Radii.sheetTop, ), child: SafeArea( top: false, child: Padding( padding: Space.page, child: AnimatedSize( duration: Motion.inScreen, curve: Motion.curve, alignment: Alignment.bottomCenter, child: child, ), ), ), ); } } class _Collapsed extends StatelessWidget { const _Collapsed(); @override Widget build(BuildContext context) { return TripzButton.secondary( label: context.l10n.rideWhereTo, icon: Icons.search_rounded, expanded: true, onPressed: context.read().openPlanner, ); } } class _PickConfirm extends StatelessWidget { const _PickConfirm({required this.onConfirm}); final VoidCallback onConfirm; @override Widget build(BuildContext context) { return TripzButton.primary( label: context.l10n.rideConfirmPoint, onPressed: onConfirm, ); } }