import 'dart:developer'; import 'package:wanisdriver/widgets/custom_text.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import '../Provider/RideProvider.dart'; import '../Provider/driver_provider.dart'; import '../Provider/utils_singleton.dart'; import '../config/custom_loader.dart'; import '../utils/colors.dart'; import '../widgets/CalendarFloatingButton.dart'; import '../widgets/PendingRideCard.dart'; import '../widgets/pickupcardupcomming.dart'; import '../widgets/tab_section.dart'; import '../widgets/tab_section_upcoming_trip.dart'; import '../widgets/trip_card.dart'; import '../widgets/summary_item.dart'; // Constants for better maintainability class _Constants { static const int tabCount = 3; static const int pendingTabIndex = 0; static const int scheduleTabIndex = 1; static const int historyTabIndex = 2; static const List requestedStatus = ["REQUESTED"]; static const List completedStatus = ["COMPLETED"]; static const List completedAndOngoingStatus = ["REQUESTED", "ONGOING",]; static const List cancelledStatus = ["CANCELLED"]; static const String scheduleType = 'SCHEDULE'; static const String instantType = 'INSTANT'; static const String dateFormat = 'yyyy-MM-dd'; static const String displayDateFormat = 'EEE, MMM d, yyyy'; } // Main Screen Widget class HistoryScreen extends StatefulWidget { @override _HistoryScreenState createState() => _HistoryScreenState(); } class _HistoryScreenState extends State with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin { // State variables int _selectedTabIndex = 0; int _selectedTabIndexChild = 0; int page = 0; DateTime _currentDate = DateTime.now(); // Controllers final ScrollController _scrollController = ScrollController(); late TabController _tabController; // Computed properties String get formattedDate => DateFormat(_Constants.dateFormat).format(_currentDate); // Providers (cached for performance) late RideProvider _rideProvider; late DriverProvider _driverProvider; @override bool get wantKeepAlive => true; @override void initState() { super.initState(); _initializeProviders(); _initializeTabController(); WidgetsBinding.instance.addPostFrameCallback((_) { _updateCurrentDate(_currentDate); onTapHeader(); }); } void _initializeProviders() { _rideProvider = context.read(); _driverProvider = context.read(); } void _initializeTabController() { _tabController = TabController(length: _Constants.tabCount, vsync: this); _tabController.addListener(_onTabChanged); } void _onTabChanged() { // Only update if the controller index actually changed and it's not programmatic if (_selectedTabIndex != _tabController.index) { setState(() { _selectedTabIndex = _tabController.index; }); // Always call onTapHeader regardless of tab index onTapHeader(); } } // Update the onTapHeader method to handle all tabs properly void onTapHeader() { if (_selectedTabIndex == _Constants.pendingTabIndex) { _rideProvider.fetchPendingRides(page: page); } else { // For schedule and history tabs, call OnTap with current date OnTap(); } } void OnTap() { final Map>> statusMapping = { _Constants.scheduleTabIndex: { 0: _Constants.requestedStatus, 1: _Constants.completedAndOngoingStatus, }, _Constants.historyTabIndex: { 0: _Constants.completedStatus, 1: _Constants.cancelledStatus, }, }; final statusList = statusMapping[_selectedTabIndex]?[_selectedTabIndexChild] ?? (_selectedTabIndex == _Constants.scheduleTabIndex ? _Constants.requestedStatus : _Constants.completedStatus); log("_selectedTabIndexChild : $_selectedTabIndexChild"); log("_selectedTabIndex : $_selectedTabIndex"); // Always use the current formattedDate, not a static date _rideProvider.fetchridebystatus( page: page, date: formattedDate, // This will use the current _currentDate status: statusList, isCorporate: _selectedTabIndex == 1 && _selectedTabIndexChild == 2 ?true:false, type: _selectedTabIndex == 2 && _selectedTabIndexChild == 0 ? _Constants.instantType : _Constants.scheduleType, ); } @override void dispose() { _tabController.removeListener(_onTabChanged); _tabController.dispose(); _scrollController.dispose(); super.dispose(); } // Data fetching methods void _fetchDataForDate(DateTime date) { final formattedDateString = DateFormat(_Constants.dateFormat).format(date); _rideProvider.refresh(date: formattedDateString); _driverProvider.summarydriver(date); setState(() { _selectedTabIndex = _Constants.scheduleTabIndex; _selectedTabIndexChild = 0; }); // IMPORTANT: Update the TabController index to match _selectedTabIndex _tabController.index = _Constants.scheduleTabIndex; // Add this line to ensure data is fetched for the new date onTapHeader(); } // Update the _updateCurrentDate method to ensure proper refresh void _updateCurrentDate(DateTime newDate) { setState(() { _currentDate = newDate; }); _fetchDataForDate(_currentDate); // If we're not on the schedule tab, we need to refresh the current tab's data // if (_selectedTabIndex != _Constants.scheduleTabIndex) { // onTapHeader(); // } } // Date navigation methods Future _showCalendar() async { final DateTime? pickedDate = await showDatePicker( context: context, initialDate: _currentDate, firstDate: DateTime(2020), lastDate: DateTime(2030), ); if (pickedDate != null && pickedDate != _currentDate) { _updateCurrentDate(pickedDate); } } void _goToPreviousDay() { _updateCurrentDate(_currentDate.subtract(const Duration(days: 1))); } void _goToNextDay() { _updateCurrentDate(_currentDate.add(const Duration(days: 1))); } // Tab change handlers void _onChildTabChanged(int index) { setState(() { _selectedTabIndexChild = index; }); OnTap(); } @override Widget build(BuildContext context) { super.build(context); return SafeArea( child: Scaffold( body: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildTopSection(), _buildTabBar(), const SizedBox(height: 5), Expanded(child: _buildTabBarView()), ], ), ), ); } Widget _buildTopSection() { return Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0), child: _TopSectionWidget( currentDate: _currentDate, onPrevious: _goToPreviousDay, onNext: _goToNextDay, onCenterTap: _showCalendar, ), ); } Widget _buildTabBar() { final isDark = Theme.of(context).brightness == Brightness.dark; return Container( margin: const EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.symmetric(horizontal: 5), // decoration: BoxDecoration( // color: isDark ? AppColors.darkCardColor : AppColors.lightGrey, // borderRadius: BorderRadius.circular(12), // boxShadow: [ // BoxShadow( // // color: Colors.black.withOpacity(0.1), // offset: const Offset(0, 2), // blurRadius: 8, // ), // ], // ), child: Padding( padding: const EdgeInsets.all(4.0), child: TabBar( controller: _tabController, labelColor: isDark ? AppColors.darkPrimaryColor : AppColors.primaryColor, unselectedLabelColor: Colors.grey, indicator: BoxDecoration( color: isDark ? AppColors.darkPrimaryColor : AppColors.primaryColor, borderRadius: BorderRadius.circular(8), ), indicatorSize: TabBarIndicatorSize.tab, labelStyle: const TextStyle(fontWeight: FontWeight.w600), unselectedLabelStyle: const TextStyle(fontWeight: FontWeight.w600), indicatorColor: Colors.transparent, dividerColor: Colors.transparent, tabs: _buildTabs(), ), ), ); } List _buildTabs() { final isDark = Theme.of(context).brightness == Brightness.dark; final tabData = [ ('Pending', _Constants.pendingTabIndex), ('Scheduled', _Constants.scheduleTabIndex), ('History', _Constants.historyTabIndex), ]; return tabData.map((data) { final (text, index) = data; return Tab( child: Txt( text: text, style: TextStyle( color: _selectedTabIndex == index ? Colors.white : isDark ? Colors.white : Colors.grey, ), ), ); }).toList(); } Widget _buildTabBarView() { return TabBarView( controller: _tabController, physics: const NeverScrollableScrollPhysics(), children: [ _buildPendingTab(), _buildScheduleTab(), _buildHistoryTab(), ], ); } Widget _buildPendingTab() { return RefreshIndicator( onRefresh: () => _rideProvider.fetchPendingRides(), child: Consumer( builder: (context, rideProvider, child) { if (rideProvider.isLoading && rideProvider.ridehistory.isEmpty) { return const Center(child: CustomLoader()); } if (rideProvider.ridehistory.isEmpty) { return _buildEmptyStateWithScroll(); } return ListView.builder( physics: const AlwaysScrollableScrollPhysics(), itemCount: rideProvider.ridehistory.length, itemBuilder: (context, index) => Padding( padding: const EdgeInsets.all(8.0), child: PendingRideCard(ride: rideProvider.ridehistory[index]), ), ); }, ), ); } Widget _buildScheduleTab() { return RefreshIndicator( onRefresh: () async => OnTap(), child: Column( children: [ TabSectionUpCommingTrip( selectedTabIndex: _selectedTabIndexChild, onTabTap: _onChildTabChanged, ), Expanded( child: Consumer( builder: (context, rideProvider, child) { if (rideProvider.isRefreshing) { return const Center(child: CustomLoader()); } return _buildListView( items: rideProvider.ridehistory, showLoader: rideProvider.isLoading, itemBuilder: (item) => PickupCard( details: item, myPickUp: item.driverId == utils.user?.driver?.id, isLoadingAccept: rideProvider.isLoadingAccept(item.id), isLoadingDecline: rideProvider.isLoadingDecline(item.id), onAddToPickups: () => rideProvider.acceptRide(item.id), onDecline: () => rideProvider.declineRide(item.id), ), ); }, ), ), ], ), ); } Widget _buildHistoryTab() { return Column( children: [ TabSection( selectedTabIndex: _selectedTabIndexChild, onTabTap: _onChildTabChanged, ), Expanded( child: Consumer( builder: (context, rideProvider, child) { if (rideProvider.isRefreshing) { return const Center(child: CustomLoader()); } return _buildListView( items: rideProvider.ridehistory, showLoader: rideProvider.isLoading, itemBuilder: (item) => TripCard(cancelled: false, trip: item), ); }, ), ), ], ); } Widget _buildEmptyStateWithScroll() { return LayoutBuilder( builder: (context, constraints) { return ListView( physics: const AlwaysScrollableScrollPhysics(), children: [ Container( constraints: BoxConstraints(minHeight: constraints.maxHeight), child: Center(child: _buildEmptyState()), ), ], ); }, ); } Widget _buildEmptyState() { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset('assets/images/no_ride_found.png'), const SizedBox(height: 18), const Txt( text: 'No rides found for this date.', style: TextStyle(fontSize: 18, color: Colors.grey), ), ], ); } Widget _buildListView({ required List items, required bool showLoader, required Widget Function(T item) itemBuilder, }) { if (items.isEmpty && !showLoader) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset('assets/images/no_ride_found.png'), const SizedBox(height: 16), const Txt( text: 'No Data Found', style: TextStyle(fontSize: 18, color: Colors.grey), ), ], ), ); } return ListView.builder( controller: _scrollController, padding: const EdgeInsets.only(left: 0, right: 0, top: 8, bottom: 100), itemCount: items.length + (showLoader ? 1 : 0), itemBuilder: (context, index) { if (index < items.length) { return itemBuilder(items[index]); } return const Padding( padding: EdgeInsets.all(16.0), child: Center(child: CustomLoader()), ); }, ); } } // --- OPTIMIZED TOP SECTION WIDGET --- class _TopSectionWidget extends StatelessWidget { final DateTime currentDate; final VoidCallback onPrevious; final VoidCallback onNext; final VoidCallback onCenterTap; const _TopSectionWidget({ required this.currentDate, required this.onPrevious, required this.onNext, required this.onCenterTap, }); @override Widget build(BuildContext context) { return Column( children: [ const SizedBox(height: 24), _buildMainContainer(context), const SizedBox(height: 20), ], ); } Widget _buildMainContainer(BuildContext context) { final isDark = Theme.of(context).brightness == Brightness.dark; return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: isDark ? AppColors.darkCardColor : AppColors.whiteCardColor, borderRadius: BorderRadius.circular(32), border: Border.all(color: const Color(0xffD5D5D6), width: 1.0), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: Column( children: [ _buildCalendarSection(), const SizedBox(height: 12), _buildEarningsSection(context), const SizedBox(height: 16), _buildStatsSection(context), ], ), ); } Widget _buildCalendarSection() { return CalendarFloatingButton( date: DateFormat(_Constants.displayDateFormat).format(currentDate), onPrevious: onPrevious, onNext: onNext, onCenterTap: onCenterTap, ); } Widget _buildEarningsSection(BuildContext context) { return Consumer( builder: (context, driverProvider, child) { final summary = driverProvider.currentdriversummarydate; return Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Expanded( child: _buildSummaryItem( context, value: '${summary?.beemEarnings.toStringAsFixed(2) ?? "0.00"} TND', label: 'Wanis', valueSize: 14, ), ), Expanded( child: _buildSummaryItem( context, value: '${summary?.totalEarnings.toStringAsFixed(2) ?? "0.00"} TND', label: 'Total Earning', valueSize: 17, isBold: true, ), ), Expanded( child: _buildSummaryItem( context, value: '${summary?.driverEarnings.toStringAsFixed(2) ?? "0.00"} TND', label: "Your's", valueSize: 14, ), ), ], ); }, ); } Widget _buildStatsSection(BuildContext context) { return Consumer( builder: (context, driverProvider, child) { final summary = driverProvider.currentdriversummarydate; return Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Expanded( child: SummaryItem( value: summary?.totalRidesCompleted.toString() ?? '0', label: 'Trips', ), ), Expanded( child: SummaryItem( value: summary?.totalDuration != null ? (summary!.totalDuration / 60).toStringAsFixed(1) : '0', label: 'Online hrs', ), ), Expanded( child: SummaryItem( value: summary?.totalRidesCanceled.toString() ?? '0', label: 'Cancelled', ), ), ], ); }, ); } Widget _buildSummaryItem( BuildContext context, { required String value, required String label, required double valueSize, bool isBold = false, }) { final isDark = Theme.of(context).brightness == Brightness.dark; return SummaryItem( value: value, label: label, valueStyle: TextStyle( fontSize: valueSize, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: isDark ? AppColors.white : AppColors.black, ), labelStyle: TextStyle( fontSize: 13, color: isDark ? AppColors.whiteCardColor : AppColors.darkCardColor, ), ); } }