nhance_partner/lib/presentation/layouts/appheader.dart
2026-04-04 17:38:00 +05:30

1073 lines
35 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:jwt_decode/jwt_decode.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../core/routing/routes.dart';
import '../../core/services/api_service.dart';
import '../../data/services/auth_service.dart';
import '../providers/manager_provider.dart';
import '../providers/userRoleProvider.dart';
import '../screens/UserManagement/Profile/profile_web.dart';
import '../screens/staff/Enquiry/Proposal_QuickCreation/Proposal_QuickCreation.dart';
import '../themes/indicators/side_Drawer_Panel.dart';
class AppHeader extends ConsumerStatefulWidget implements PreferredSizeWidget {
const AppHeader({super.key});
@override
ConsumerState<AppHeader> createState() => _AppHeaderState();
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
}
class _AppHeaderState extends ConsumerState<AppHeader> {
String? role;
ApiService apiService = ApiService();
Map<String, dynamic>? profileData;
String? _token;
OverlayEntry? _overlayEntry;
String? _activeMenu;
Timer? _queueTimer;
int totalAssigned = 0;
int totalPending = 0;
int todayAssigned = 0;
int todayPending = 0;
int todayInProgress = 0;
int todayCompleted = 0;
int queueCount = 0; // badge count
@override
void initState() {
super.initState();
_loadUser();
_initializeToken();
_setActiveMenu();
}
@override
void dispose() {
_queueTimer?.cancel();
_hidePopup();
super.dispose();
}
// Set active menu based on current route
void _setActiveMenu() {
WidgetsBinding.instance.addPostFrameCallback((_) {
final currentRoute = GoRouterState.of(context).uri.path.toLowerCase();
setState(() {
_activeMenu = _menuKeyForRoute(currentRoute);
});
});
}
String? _menuKeyForRoute(String currentRoute) {
final route = currentRoute.toLowerCase();
if (route.contains('dashboard') ||
route.contains('partnerportaldashboard')) {
return 'Dashboard';
}
if (route.contains('enquiry')) {
return 'Enquiry';
}
if (route.contains('endorsement')) {
return 'Endorsement';
}
if (route.contains('agent') ||
route.contains('staff') ||
route.contains('salesexecutive') ||
route.contains('pos')) {
return 'User';
}
if (route.contains('claim') ||
route.contains('policy') ||
route.contains('attendance') ||
route.contains('monthlycommission') ||
route.contains('gridlist') ||
route.contains('gridview')) {
return 'Reports';
}
if (route.contains('broker') ||
route.contains('payment') ||
route.contains('vehicletype') ||
route.contains('endorsementtype')) {
return 'Masters';
}
if (route.contains('payoutgrid')) {
return 'PayoutGrid';
}
if (route.contains('payoutupload')) {
return 'PayoutUpload';
}
if (route.contains('payoutreport')) {
return 'PayoutReport';
}
if (route.contains('invoice') ||
(route.contains('payout') && !route.contains('payoutgrid'))) {
return 'Payout';
}
return null;
}
Future<void> _initializeToken() async {
_token = await AuthService.getToken();
if (_token != null) {
final Map<String, dynamic> decodedToken = Jwt.parseJwt(_token!);
setState(() {
profileData = decodedToken['data'];
});
}
}
Future<void> _loadUser() async {
final prefs = await SharedPreferences.getInstance();
setState(() {
role = prefs.getString('userRole') ?? "Guest";
});
print('AppHeader Role - $role');
// Initial fetch + auto refresh
WidgetsBinding.instance.addPostFrameCallback((_) {
print('111');
if (role == 'staff') {
print('staff 111');
getStaffLevelCount(profileData?['manager_id'], profileData?['id']);
_queueTimer = Timer.periodic(
const Duration(seconds: 30),
(_) => getStaffLevelCount(
profileData?['manager_id'],
profileData?['id'],
),
);
}
});
}
Future<void> getStaffLevelCount(managerId, userId) async {
print('staff 222');
print('$managerId ,$userId');
print('staff 333');
try {
final response = await apiService.fetchStaffLevelCount(managerId, userId);
if (response['status'] == 'success') {
final List summary =
response['data']['staff_level_pending_summary'] ?? [];
int _totalAssigned = 0;
int _totalPending = 0;
int _todayAssigned = 0;
int _todayPending = 0;
int _todayInProgress = 0;
int _todayCompleted = 0;
for (final item in summary) {
_totalAssigned += int.parse(item['total_assigned'] ?? '0');
_totalPending += int.parse(item['total_pending'] ?? '0');
_todayAssigned += int.parse(item['today_assigned'] ?? '0');
_todayPending += int.parse(item['today_pending'] ?? '0');
_todayInProgress += int.parse(item['today_in_progress'] ?? '0');
_todayCompleted += int.parse(item['today_completed'] ?? '0');
}
if (!mounted) return;
setState(() {
totalAssigned = _totalAssigned;
totalPending = _totalPending;
todayAssigned = _todayAssigned;
todayPending = _todayPending;
todayInProgress = _todayInProgress;
todayCompleted = _todayCompleted;
queueCount = totalPending + todayPending + todayInProgress;
});
}
} catch (e) {
debugPrint('Queue API Exception: $e');
}
}
// Clear dashboard filters
Future<void> _clearDashboardFilters() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('dashboardKeyProvider');
await prefs.remove('dashboardStatusProvider');
await prefs.remove('dashboardStaffIdProvider');
}
// Show hover popup
void _showPopup(BuildContext context, Offset offset, Size size, String key) {
_hidePopup();
_overlayEntry = OverlayEntry(
builder: (_) {
return Positioned(
left: offset.dx,
// top: offset.dy + size.height + 8,
// Reduced +8 to +2 to create an overlap so the mouse never leaves
// a "hoverable" area, which prevents the exit flicker.
top: offset.dy + size.height + 2,
child: MouseRegion(
onExit: (_) => _hidePopup(),
child: Material(
elevation: 8,
shadowColor: Colors.black26,
borderRadius: BorderRadius.circular(8),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Colors.white,
border: Border.all(color: Colors.grey.shade200),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (key == 'User') ...[
_buildPopupItem(
label: "Partner",
onTap: () {
_hidePopup();
setState(() => _activeMenu = 'User');
context.go(AppRoutes.agentLst);
},
),
const SizedBox(height: 2),
_buildPopupItem(
label: "Staff",
onTap: () {
_hidePopup();
setState(() => _activeMenu = 'User');
context.go(AppRoutes.staffLst);
},
),
const SizedBox(height: 2),
_buildPopupItem(
label: "Sales Executive",
onTap: () {
_hidePopup();
setState(() => _activeMenu = 'User');
context.go(AppRoutes.salesExecutiveLst);
},
),
const SizedBox(height: 2),
_buildPopupItem(
label: "POS",
onTap: () {
_hidePopup();
setState(() => _activeMenu = 'User');
context.go(AppRoutes.posLst);
},
),
],
if (key == 'Reports') ...[
if (role == 'manager' ||
role == 'Accounts' ||
role == 'agent') ...[
_buildPopupItem(
label: "Grid List",
onTap: () {
_hidePopup();
setState(() => _activeMenu = 'Reports');
context.go(
role == 'agent'
? AppRoutes.gridView
: AppRoutes.gridList,
);
},
),
const SizedBox(height: 2),
],
if (role == 'manager') ...[
_buildPopupItem(
label: "Attendance",
onTap: () {
_hidePopup();
setState(() => _activeMenu = 'Reports');
context.go(AppRoutes.allStaffAttendance);
},
),
],
SizedBox(height: 2),
_buildPopupItem(
label: "Claims",
onTap: () {
_hidePopup();
setState(() => _activeMenu = 'Reports');
context.go(AppRoutes.claimlist);
},
),
const SizedBox(height: 2),
_buildPopupItem(
label: "Policy",
onTap: () {
setState(() => _activeMenu = 'Reports');
_hidePopup();
context.go(AppRoutes.policylist);
},
),
],
if (key == 'Masters') ...[
if (role == 'manager') ...[
_buildPopupItem(
label: "Broker",
onTap: () {
_hidePopup();
setState(() => _activeMenu = 'Masters');
context.go(AppRoutes.brokerLst);
},
),
],
SizedBox(height: 2),
_buildPopupItem(
label: "Endorsement Type",
onTap: () {
setState(() => _activeMenu = 'Masters');
_hidePopup();
context.go(AppRoutes.endorsementTypeLst);
},
),
_buildPopupItem(
label: "Vehicle Type",
onTap: () {
setState(() => _activeMenu = 'Masters');
_hidePopup();
context.go(AppRoutes.vehicleTypeLst);
},
),
SizedBox(height: 2),
_buildPopupItem(
label: "Payment Mode",
onTap: () {
setState(() => _activeMenu = 'Masters');
_hidePopup();
context.go(AppRoutes.paymentModeLst);
},
),
],
if (key == 'Payout' && role == 'Accounts') ...[
_buildPopupItem(
label: "Payout",
onTap: () {
_hidePopup();
setState(() => _activeMenu = 'Payout');
context.go(AppRoutes.payoutList);
},
),
const SizedBox(height: 2),
_buildPopupItem(
label: "Payout upload",
onTap: () {
_hidePopup();
setState(() => _activeMenu = 'PayoutUpload');
context.go(AppRoutes.payoutUpload);
},
),
const SizedBox(height: 2),
_buildPopupItem(
label: "Payout Reports",
onTap: () {
_hidePopup();
setState(() => _activeMenu = 'PayoutReport');
context.go(AppRoutes.payoutReport);
},
),
],
],
),
),
),
),
);
},
);
Overlay.of(context, rootOverlay: true).insert(_overlayEntry!);
}
void _hidePopup() {
if (_overlayEntry != null) {
_overlayEntry?.remove();
_overlayEntry = null;
// Optional: add a small setState if needed to refresh the UI layer
if (mounted) setState(() {});
}
}
@override
Widget build(BuildContext context) {
final roleId = ref.watch(userRoleProvider);
final currentRouteLower =
GoRouterState.of(context).uri.path.toLowerCase();
var activeMenu =
_menuKeyForRoute(currentRouteLower) ?? _activeMenu;
if (currentRouteLower.contains('payoutgrid') && roleId != 'agent') {
activeMenu = 'Reports';
}
return AppBar(
// backgroundColor: const Color(0xFFD6F6F4),
backgroundColor: const Color(0xFFE3F9F8),
elevation: 0,
automaticallyImplyLeading: false,
titleSpacing: 24,
toolbarHeight: 64,
title: Row(
children: [
// Logo
Image.asset(
"assets/login/nhance-partner-logo.png",
height: 45,
width: 95,
),
Spacer(),
// Dashboard
if (roleId != 'staff' && role != 'Accounts') ...[
_buildMenuItem(
icon: Icons.dashboard,
label: "Dashboard",
isActive: activeMenu == 'Dashboard',
onTap: () async {
_hidePopup();
setState(() => _activeMenu = 'Dashboard');
await _clearDashboardFilters();
final dashboardRoute = roleId == 'agent'
? AppRoutes.partnerPortalDashboard
: AppRoutes.dashboard;
context.go(dashboardRoute);
},
),
const SizedBox(width: 15),
],
SizedBox(width: 8),
// Enquiry
if (role != 'Accounts') ...[
_buildMenuItem(
icon: Icons.list_alt_rounded,
label: "Enquiry",
isActive: activeMenu == 'Enquiry',
onTap: () async {
_hidePopup();
setState(() => _activeMenu = 'Enquiry');
await _clearDashboardFilters();
if (roleId == 'agent') {
context.go(AppRoutes.enquiryLst);
} else {
context.go(AppRoutes.enquiryForStaff);
}
},
),
],
const SizedBox(width: 8),
_buildMenuItem(
isActive: activeMenu == 'Endorsement',
icon: Icons.checklist_outlined,
label: "Endorsement",
onTap: () async {
_hidePopup();
setState(() => _activeMenu = 'Endorsement');
await _clearDashboardFilters();
context.go(AppRoutes.Endorsement);
},
),
const SizedBox(width: 8),
// User (Manager Only) - with hover popup
if (roleId == 'manager' || role == 'Accounts') ...[
_buildHoverMenuItem(
icon: Icons.person_add_alt,
label: "User",
popupKey: 'User',
isActive: activeMenu == 'User',
),
const SizedBox(width: 8),
],
// User (Manager Only) - with hover popup
if (roleId == 'manager') ...[
_buildHoverMenuItem(
icon: Icons.settings_suggest_outlined,
label: "Masters",
popupKey: 'Masters',
isActive: activeMenu == 'Masters',
),
const SizedBox(width: 8),
],
// Reports (Not Staff) - with hover popup
if (roleId != 'staff') ...[
_buildHoverMenuItem(
icon: Icons.receipt_long,
label: "Reports",
popupKey: 'Reports',
isActive: activeMenu == 'Reports',
),
const SizedBox(width: 8),
],
// Payout (Accounts only) — one top-level menu with submenu
if (role == 'Accounts') ...[
_buildHoverMenuItem(
icon: Icons.payments_outlined,
label: "Payout",
popupKey: 'Payout',
isActive: activeMenu == 'Payout' ||
activeMenu == 'PayoutUpload' ||
activeMenu == 'PayoutReport',
),
const SizedBox(width: 8),
],
// Partner (agent): Payout Report — same route as Accounts
if (role == 'agent') ...[
const SizedBox(width: 8),
_buildMenuItem(
icon: Icons.table_chart_outlined,
label: "Payout Report",
isActive: activeMenu == 'PayoutReport',
onTap: () {
_hidePopup();
setState(() => _activeMenu = 'PayoutReport');
context.go(AppRoutes.payoutReport);
},
),
],
const Spacer(),
// Raise Enquiry button for agents
if (role == 'agent') ...[
Container(
height: 36,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: const Color(0xFF425B5B),
borderRadius: BorderRadius.circular(6),
),
child: InkWell(
onTap: () async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('enqAgentDataId');
ref.read(enquiryIdProvider.notifier).state = null;
context.go(AppRoutes.tabEnquiry);
},
child: Center(
child: Text(
'Raise Enquiry',
style: GoogleFonts.inter(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 13,
),
),
),
),
),
const SizedBox(width: 16),
],
if (role == 'staff') ...[
_buildQueueIcon(),
const SizedBox(width: 12),
],
// if (role != 'Accounts') ...[
if (!['Accounts', 'agent'].contains(role)) ...[
// Proposal button
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey.shade200, width: 1),
),
child: IconButton(
padding: EdgeInsets.zero,
icon: const Icon(Icons.quora, color: Colors.black87, size: 20),
onPressed: () {
SideDrawerPanel.show(
context: context,
title: 'Quote Creation',
child: CreateProposal_Quick(),
);
},
tooltip: 'Quick Quote',
),
),
const SizedBox(width: 12),
],
// Notifications button
// Container(
// width: 36,
// height: 36,
// decoration: BoxDecoration(
// color: Colors.white,
// borderRadius: BorderRadius.circular(8),
// border: Border.all(color: Colors.grey.shade200, width: 1),
// ),
// child: IconButton(
// padding: EdgeInsets.zero,
// icon: const Icon(
// Icons.notifications_none,
// color: Colors.black87,
// size: 20,
// ),
// onPressed: () {
// debugPrint("Notifications tapped");
// },
// tooltip: 'Notifications',
// ),
// ),
//
// const SizedBox(width: 12),
// Profile with popup
PopupMenuButton<String>(
color: Colors.white,
offset: const Offset(0, 52),
child: Container(
height: 36,
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
// border: Border.all(color: Colors.grey.shade200, width: 1),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
CircleAvatar(
radius: 14,
// backgroundColor: const Color(0xFF4a90e2),
backgroundColor: const Color(0xFF2E7D6E),
child: Text(
profileData?['name']?.substring(0, 2).toUpperCase() ??
'A',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 10,
),
),
),
const SizedBox(width: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
profileData?['name'] ?? 'Manager',
style: GoogleFonts.inter(
fontWeight: FontWeight.w600,
fontSize: 11,
color: Colors.black87,
),
),
Text(
(role == 'agent') ? 'Partner' : role ?? 'Manager',
style: GoogleFonts.inter(
fontSize: 9,
color: Colors.grey.shade600,
),
),
],
),
const SizedBox(width: 4),
Icon(
Icons.keyboard_arrow_down,
size: 16,
color: Colors.grey.shade700,
),
],
),
),
itemBuilder: (context) => [
PopupMenuItem(
enabled: false,
child: Container(
width: 480,
color: Colors.white,
child: ProfilePopUp(),
),
),
],
),
const SizedBox(width: 12),
// Logout button
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey.shade200, width: 1),
),
child: IconButton(
padding: EdgeInsets.zero,
icon: const Icon(Icons.logout, color: Colors.black87, size: 18),
onPressed: () async {
debugPrint("Logout tapped");
AuthService.clearToken();
if (role != 'handler') {
await apiService.logoutUsingAPI(context);
}
context.go(AppRoutes.login);
},
tooltip: 'Logout',
),
),
],
),
);
}
Widget _buildQueueIcon() {
return Builder(
builder: (iconContext) {
return MouseRegion(
onEnter: (_) {
final box = iconContext.findRenderObject() as RenderBox;
_showQueuePopup(context, box.localToGlobal(Offset.zero), box.size);
},
onExit: (_) => _hidePopup(),
child: Stack(
clipBehavior: Clip.none,
children: [
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey.shade200),
),
child: const Icon(Icons.priority_high, size: 20),
),
// 🔴 Badge
// if (queueCount > 0)
Positioned(
right: -6,
top: -6,
child: Container(
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
constraints: const BoxConstraints(
minWidth: 18,
minHeight: 18,
),
child: Text(
queueCount.toString(),
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
);
},
);
}
void _showQueuePopup(BuildContext context, Offset offset, Size size) {
_hidePopup();
_overlayEntry = OverlayEntry(
builder: (_) => Positioned(
left: offset.dx,
top: offset.dy + size.height + 4,
child: MouseRegion(
onExit: (_) => _hidePopup(),
child: Material(
elevation: 10,
borderRadius: BorderRadius.circular(8),
child: Container(
width: 180,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_queueRow('Total Assigned', totalAssigned),
_queueRow('Total Pending', totalPending),
const Divider(),
_queueRow('Today Assigned', todayAssigned),
_queueRow('Today Pending', todayPending),
_queueRow('Today In Progress', todayInProgress),
_queueRow('Today Completed', todayCompleted),
],
),
),
),
),
),
);
Overlay.of(context, rootOverlay: true).insert(_overlayEntry!);
}
Widget _queueRow(String label, int value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label,
style:
GoogleFonts.inter(fontSize: 12, color: Colors.grey.shade700)),
Text(value.toString(),
style: GoogleFonts.inter(
fontSize: 12, fontWeight: FontWeight.w600)),
],
),
);
}
// Simple menu item widget
Widget _buildMenuItem({
required IconData icon,
required String label,
required VoidCallback onTap,
bool isActive = false,
}) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(6),
child: MouseRegion(
onEnter: (_) => _hidePopup(),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration(
color: isActive
? const Color(0xFF2E7D6E).withOpacity(0.03)
: Colors.transparent,
borderRadius: BorderRadius.circular(6),
border: isActive
? Border(
bottom: BorderSide(
color: const Color(0xFF2E7D6E),
width: 2,
),
)
: null,
),
// decoration: BoxDecoration(borderRadius: BorderRadius.circular(6)),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
icon,
size: 18,
color: isActive ? const Color(0xFF2E7D6E) : Colors.black87,
),
const SizedBox(width: 6),
Text(
label,
style: GoogleFonts.inter(
color: isActive ? const Color(0xFF2E7D6E) : Colors.black87,
fontSize: 13,
fontWeight: isActive ? FontWeight.w600 : FontWeight.w500,
),
),
],
),
),
),
);
}
// Menu item with hover popup
Widget _buildHoverMenuItem({
required IconData icon,
required String label,
required String popupKey,
bool isActive = false,
}) {
return Builder(
builder: (itemContext) {
return MouseRegion(
onEnter: (_) {
final renderBox = itemContext.findRenderObject() as RenderBox;
final offset = renderBox.localToGlobal(Offset.zero);
final size = renderBox.size;
_showPopup(context, offset, size, popupKey);
},
onExit: (event) {
// We check if the mouse is moving downwards (toward the popup)
// If not moving toward popup, hide it.
if (event.localPosition.dy < 0 ||
event.localPosition.dx < 0 ||
event.localPosition.dx > 100) {
_hidePopup();
}
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration(
color: isActive
? const Color(0xFF2E7D6E).withOpacity(0.02)
: Colors.transparent,
borderRadius: BorderRadius.circular(6),
border: isActive
? Border(
bottom: BorderSide(
color: const Color(0xFF2E7D6E),
width: 2,
),
)
: null,
),
// decoration: BoxDecoration(borderRadius: BorderRadius.circular(6)),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
icon,
size: 18,
color: isActive ? const Color(0xFF2E7D6E) : Colors.black87,
),
const SizedBox(width: 6),
Text(
label,
style: GoogleFonts.inter(
color: isActive ? const Color(0xFF2E7D6E) : Colors.black87,
fontSize: 13,
fontWeight: isActive ? FontWeight.w600 : FontWeight.w500,
),
),
const SizedBox(width: 2),
Icon(
Icons.arrow_drop_down,
color: isActive ? const Color(0xFF2E7D6E) : Colors.black87,
size: 18,
),
],
),
),
);
},
);
}
// Popup item widget
Widget _buildPopupItem({required String label, required VoidCallback onTap}) {
return _HoverableContainer(
label: label,
onTap: () {
_hidePopup(); // Force hide before executing the specific onTap logic
onTap();
},
normalColor: Colors.white,
hoverColor: const Color(0xFFF5F5F5),
);
}
@override
Size get preferredSize => const Size.fromHeight(64);
}
// Hoverable container for popup items
class _HoverableContainer extends StatefulWidget {
final String label;
final VoidCallback onTap;
final Color normalColor;
final Color hoverColor;
const _HoverableContainer({
required this.label,
required this.onTap,
required this.normalColor,
required this.hoverColor,
});
@override
_HoverableContainerState createState() => _HoverableContainerState();
}
class _HoverableContainerState extends State<_HoverableContainer> {
bool _isHovered = false;
@override
Widget build(BuildContext context) {
return MouseRegion(
onEnter: (_) => setState(() => _isHovered = true),
onExit: (_) => setState(() => _isHovered = false),
child: InkWell(
onTap: widget.onTap,
child: Container(
width: 140,
decoration: BoxDecoration(
color: _isHovered ? widget.hoverColor : widget.normalColor,
borderRadius: BorderRadius.circular(6),
),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.label,
style: GoogleFonts.inter(
fontSize: 13,
fontWeight: FontWeight.w500,
color: Colors.black87,
),
),
// Icon(
// Icons.arrow_forward_ios_rounded,
// size: 10,
// color: Colors.grey.shade600,
// ),
],
),
),
),
);
}
}