taxgilde/lib/view/Main_controller/main_controller.dart
2026-04-11 10:21:31 +05:30

452 lines
14 KiB
Dart

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<MainController> createState() => _MainControllerState();
}
class _MainControllerState extends ConsumerState<MainController> {
int currentIndex = 0;
DateTime? lastBackPressed;
String? userRole;
/// 📌 Bottom bar labels
final List<String> _labels = [
'Home',
'Services',
'Service Details',
'Profile',
];
/// 📌 Screens list
List<Widget> _screens = [];
@override
void initState() {
super.initState();
currentIndex = widget.initialIndex ?? 0;
_loadRole();
}
/// 🔹 Load user role from local storage
Future<void> _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<bool> _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<Offset> 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;
}