import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:get/get.dart'; import 'package:taxglide/consts/app_asstes.dart'; import 'package:taxglide/consts/local_store.dart'; import 'package:taxglide/controller/api_contoller.dart'; // ✅ Needed for providers import 'package:taxglide/view/Main_controller/comman_chat_box.dart'; import 'package:taxglide/view/screens/history/serivces_status_screen.dart' show ServicesStatusScreen; import 'package:taxglide/view/screens/home_screen.dart'; import 'package:taxglide/view/screens/list_service_screen.dart'; import 'package:taxglide/view/screens/profile/employee_profile/employee_profile_screen.dart'; import 'package:taxglide/view/screens/profile/profile_screen.dart'; class MainController extends ConsumerStatefulWidget { final Widget? child; // Optional child (like ServiceRequestScreen) final int? initialIndex; // Which tab to show initially final int? sourceTabIndex; // Source tab (Home=0, Services=1) const MainController({ Key? key, this.child, this.initialIndex, this.sourceTabIndex, }) : super(key: key); @override ConsumerState createState() => _MainControllerState(); } class _MainControllerState extends ConsumerState { int currentIndex = 0; DateTime? lastBackPressed; String? userRole; /// 📌 Bottom bar labels final List _labels = [ 'Home', 'Services', 'Service Details', 'Profile', ]; /// 📌 Screens list List _screens = []; @override void initState() { super.initState(); currentIndex = widget.initialIndex ?? 0; _loadRole(); } /// 🔹 Load user role from local storage Future _loadRole() async { userRole = await LocalStore().getRole(); // "employee" or "user" _initScreens(); setState(() {}); } /// 🔹 Initialize screens based on role void _initScreens() { _screens = [ const HomeScreen(), ListServiceScreen(key: UniqueKey()), ServicesStatusScreen(), _getProfileScreen(), // ✅ role based ]; } /// 🔹 Role based profile screen Widget _getProfileScreen() { if (userRole == "employee") { return const EmployeeProfileScreen(); // 👈 employee profile } return const ProfileScreen(); // 👈 normal user profile } /// 🔄 Change tab index void setBottomBarIndex(int index) { setState(() { currentIndex = index; }); _refreshTab(index); } /// ✅ Refresh API data based on the current tab void _refreshTab(int index) { final container = ProviderScope.containerOf(context, listen: false); switch (index) { case 0: // 🔁 Refresh Home screen APIs container.refresh(profileProvider); container.refresh(serviceListProvider); ref.invalidate(chatMessagesProvider); ref.invalidate(notificationCountProvider); ref.invalidate(dashboardProvider); break; case 1: // 🔁 Refresh ListServiceScreen APIs container.refresh(serviceListProvider); ref.invalidate(chatMessagesProvider); ref.invalidate(dashboardProvider); break; case 2: // 🔁 Refresh Service Status APIs try { ref.invalidate(serviceHistoryNotifierProvider); ref.invalidate(serviceDetailProvider); ref.invalidate(chatMessagesProvider); ref.invalidate(dashboardProvider); } catch (_) {} break; case 3: // 🔁 Refresh Profile screen APIs ref.invalidate(chatMessagesProvider); container.refresh(profileProvider); ref.invalidate(staffListProvider); ref.invalidate(dashboardProvider); break; } } /// ✅ Handle Back button presses Future _onWillPop() async { // If inside ServiceRequestScreen → go back to originating tab if (widget.child != null) { Get.offAll( () => MainController(initialIndex: widget.sourceTabIndex ?? 0), ); return false; } // If not in Home tab, go back to Home if (currentIndex != 0) { setState(() => currentIndex = 0); return false; } // Double back press to exit DateTime now = DateTime.now(); if (lastBackPressed == null || now.difference(lastBackPressed!) > const Duration(seconds: 2)) { lastBackPressed = now; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("Press back again to exit"), duration: Duration(seconds: 2), ), ); return false; } await SystemNavigator.pop(); return true; } @override Widget build(BuildContext context) { final Size size = MediaQuery.of(context).size; // ✅ Show loading while screens are being initialized if (_screens.isEmpty) { return const Scaffold(body: Center(child: CircularProgressIndicator())); } final Widget mainContent = widget.child ?? _screens[currentIndex]; final dashboardAsync = ref.watch(dashboardProvider); return WillPopScope( onWillPop: _onWillPop, child: Scaffold( backgroundColor: Colors.white.withAlpha(55), body: Stack( children: [ mainContent, /// ✅ Bottom Navigation Bar Positioned( bottom: 0, left: 0, child: SafeArea( top: false, child: Container( width: size.width, height: 80, child: Stack( clipBehavior: Clip.none, children: [ CustomPaint( size: Size(size.width, 80), painter: BNBCustomPainter(), ), Center( heightFactor: 0.6, child: GestureDetector( onTap: () {}, child: CustomPaint( painter: PentagonPainter( color: const Color(0xFF61277A), ), child: Container( width: 70, height: 70, alignment: Alignment.center, child: Image.asset( AppAssets.maincontroller, color: Colors.white, fit: BoxFit.contain, height: 40, width: 40, ), ), ), ), ), SizedBox( width: size.width, height: 80, child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _buildNavItem(Icons.home, 0, _labels[0]), _buildNavItem(Icons.list_alt_sharp, 1, _labels[1]), SizedBox(width: size.width * 0.20), _buildNavItem(Icons.history_edu, 2, _labels[2]), _buildNavItem(Icons.person_2, 3, _labels[3]), ], ), ), ], ), ), ), ), dashboardAsync.when( data: (dashboard) { final int? chatIdInt = dashboard.generalChatId; if (chatIdInt == null || chatIdInt == 0) { return const SizedBox(); // ❌ No chat box } return CommanChatBox( chatId: chatIdInt.toString(), // ✅ INT → STRING ); }, loading: () => const SizedBox(), error: (e, _) => const SizedBox(), ), ], ), ), ); } /// ✅ Navigation Logic with Refresh Widget _buildNavItem(IconData icon, int index, String label) { bool isSelected = currentIndex == index; return GestureDetector( onTap: () { // 🔹 If inside ServiceRequestScreen if (widget.child != null) { if (widget.initialIndex == index) return; if (widget.sourceTabIndex == 0 && index == 1) { Get.offAll(() => const MainController(initialIndex: 1)); return; } if (widget.sourceTabIndex == 1 && index == 1) return; Get.offAll(() => MainController(initialIndex: index)); return; } // 🔹 If same tab tapped again → refresh if (currentIndex == index) { _refreshTab(index); return; } // 🔹 Switch to a different tab setBottomBarIndex(index); }, child: Column( mainAxisSize: MainAxisSize.min, children: [ AnimatedContainer( duration: const Duration(milliseconds: 200), padding: const EdgeInsets.all(6), decoration: BoxDecoration( shape: BoxShape.circle, border: isSelected ? Border.all(color: const Color(0xFF61277A), width: 2) : null, ), child: Icon( icon, color: isSelected ? const Color(0xFF61277A) : const Color(0xFF6C7278), size: isSelected ? 19.5 : 20.8, ), ), if (isSelected) Padding( padding: const EdgeInsets.only(top: 4), child: Text( label, style: const TextStyle( color: Color(0xFF61277A), fontSize: 12, fontWeight: FontWeight.w600, ), ), ), ], ), ); } } /// ✅ Custom Curved Bottom Navigation Painter class BNBCustomPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { Paint paint = Paint() ..color = Colors.white ..style = PaintingStyle.fill; Path path = Path(); path.moveTo(0, 20); path.quadraticBezierTo(size.width * 0.20, 0, size.width * 0.35, 0); path.quadraticBezierTo(size.width * 0.40, 0, size.width * 0.40, 20); path.arcToPoint( Offset(size.width * 0.60, 20), radius: const Radius.circular(20.0), clockwise: false, ); path.quadraticBezierTo(size.width * 0.60, 0, size.width * 0.65, 0); path.quadraticBezierTo(size.width * 0.80, 0, size.width, 20); path.lineTo(size.width, size.height); path.lineTo(0, size.height); path.close(); canvas.drawShadow(path, Colors.black.withOpacity(0.2), 5, true); canvas.drawPath(path, paint); } @override bool shouldRepaint(CustomPainter oldDelegate) => false; } /// ✅ Pentagon Button Painter /// ✅ Pentagon Button Painter class PentagonPainter extends CustomPainter { final Color color; final double cornerRadius; PentagonPainter({ required this.color, this.cornerRadius = 8.0, // Adjust this value for more/less rounding }); @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = color ..style = PaintingStyle.fill; final double w = size.width; final double cx = w / 2; final double cy = size.height / 2; final double r = w / 2; // Calculate pentagon vertices List vertices = []; for (int i = 0; i < 5; i++) { double angle = (72 * i - 90) * math.pi / 180; double x = cx + r * 0.95 * math.cos(angle); double y = cy + r * 0.95 * math.sin(angle); vertices.add(Offset(x, y)); } // Create path with rounded corners final path = Path(); for (int i = 0; i < vertices.length; i++) { final current = vertices[i]; final next = vertices[(i + 1) % vertices.length]; final prev = vertices[(i - 1 + vertices.length) % vertices.length]; // Calculate direction vectors final toCurrent = Offset(current.dx - prev.dx, current.dy - prev.dy); final toNext = Offset(next.dx - current.dx, next.dy - current.dy); // Normalize and scale by corner radius final lengthToCurrent = math.sqrt( toCurrent.dx * toCurrent.dx + toCurrent.dy * toCurrent.dy, ); final lengthToNext = math.sqrt( toNext.dx * toNext.dx + toNext.dy * toNext.dy, ); final normalizedToCurrent = Offset( toCurrent.dx / lengthToCurrent, toCurrent.dy / lengthToCurrent, ); final normalizedToNext = Offset( toNext.dx / lengthToNext, toNext.dy / lengthToNext, ); // Points before and after the corner final beforeCorner = Offset( current.dx - normalizedToCurrent.dx * cornerRadius, current.dy - normalizedToCurrent.dy * cornerRadius, ); final afterCorner = Offset( current.dx + normalizedToNext.dx * cornerRadius, current.dy + normalizedToNext.dy * cornerRadius, ); if (i == 0) { path.moveTo(beforeCorner.dx, beforeCorner.dy); } else { path.lineTo(beforeCorner.dx, beforeCorner.dy); } // Draw rounded corner using quadratic bezier path.quadraticBezierTo( current.dx, current.dy, afterCorner.dx, afterCorner.dy, ); } path.close(); canvas.drawShadow(path, Colors.black.withOpacity(0.3), 4, true); canvas.drawPath(path, paint); } @override bool shouldRepaint(CustomPainter oldDelegate) => false; }