// collapsible_sidebar.dart import 'package:flutter/material.dart'; // --- Data Structure for Sidebar Menu Items --- class SidebarItem { final IconData? icon; // Now nullable, as we might use an image final String label; final String? route; // Null if it's a parent category final List? children; // Sub-items final bool isParent; final String? imagePath; // New: Optional path to an image asset SidebarItem({ this.icon, required this.label, this.route, this.children, this.isParent = false, this.imagePath, // Initialize the new property }) : assert(icon != null || imagePath != null, 'Either icon or imagePath must be provided'); // Ensure one is present } // --- Collapsible Sidebar Widget --- class CollapsibleSidebar extends StatefulWidget { final Function(String route) onNavigate; final bool isCollapsed; // Receive collapse state from HomePage final VoidCallback onToggle; // Receive toggle callback from HomePage (for AppBar button) const CollapsibleSidebar({ super.key, required this.onNavigate, required this.isCollapsed, required this.onToggle, }); @override State createState() => _CollapsibleSidebarState(); } class _CollapsibleSidebarState extends State { // Define your menu items here based on the routes you provided late final List _menuItems; @override void initState() { super.initState(); // --- MODIFIED: Menu items now match the home pages --- _menuItems = [ // ====== AIR ====== SidebarItem( icon: Icons.cloud, label: "Air", isParent: true, children: [ SidebarItem( icon: Icons.handshake, label: "Manual", isParent: true, children: [ SidebarItem(icon: Icons.description, label: "Info Centre Document", route: '/air/manual/info'), SidebarItem(icon: Icons.construction, label: "Installation", route: '/air/manual/installation'), SidebarItem(icon: Icons.inventory_2, label: "Collection", route: '/air/manual/collection'), SidebarItem(icon: Icons.article, label: "Data Log", route: '/air/manual/data-log'), ], ), SidebarItem( icon: Icons.trending_up, label: "Continuous", isParent: true, children: [ SidebarItem(icon: Icons.description, label: "Info Centre Document", route: '/air/continuous/info'), ]), SidebarItem( icon: Icons.search, label: "Investigative", isParent: true, children: [ SidebarItem(icon: Icons.description, label: "Info Centre Document", route: '/air/investigative/info'), ]), ], ), // ====== RIVER ====== SidebarItem( icon: Icons.water, label: "River", isParent: true, children: [ SidebarItem( icon: Icons.handshake, label: "Manual", isParent: true, children: [ SidebarItem(icon: Icons.description, label: "Info Centre Document", route: '/river/manual/info'), SidebarItem(icon: Icons.pin_drop, label: "In-Situ Sampling", route: '/river/manual/in-situ'), SidebarItem(icon: Icons.date_range, label: "Triennial Sampling", route: '/river/manual/triennial'), SidebarItem(icon: Icons.article, label: "Data Log", route: '/river/manual/data-log'), // *** ADDED: From river_home_page.dart *** SidebarItem(icon: Icons.image, label: "Image Request", route: '/river/manual/image-request'), ], ), SidebarItem( icon: Icons.trending_up, label: "Continuous", isParent: true, children: [ SidebarItem(icon: Icons.description, label: "Info Centre Document", route: '/river/continuous/info'), ]), SidebarItem( icon: Icons.search, label: "Investigative", isParent: true, children: [ SidebarItem(icon: Icons.description, label: "Info Centre Document", route: '/river/investigative/info'), // *** ADDED: From river_home_page.dart *** SidebarItem(icon: Icons.biotech, label: "Investigative Sampling", route: '/river/investigative/manual-sampling'), ]), ], ), // ====== MARINE ====== SidebarItem( icon: Icons.sailing, label: "Marine", isParent: true, children: [ SidebarItem( icon: Icons.handshake, label: "Manual", isParent: true, children: [ SidebarItem(icon: Icons.description, label: "Info Centre Document", route: '/marine/manual/info'), SidebarItem(icon: Icons.assignment, label: "Pre-Sampling", route: '/marine/manual/pre-sampling'), SidebarItem(icon: Icons.pin_drop, label: "In-Situ Sampling", route: '/marine/manual/in-situ'), SidebarItem(icon: Icons.waves, label: "Tarball Sampling", route: '/marine/manual/tarball'), SidebarItem(icon: Icons.article, label: "Data Log", route: '/marine/manual/data-log'), // *** ADDED: From marine_home_page.dart *** SidebarItem(icon: Icons.image, label: "Image Request", route: '/marine/manual/image-request'), // *** ADDED: From marine_home_page.dart *** SidebarItem(icon: Icons.receipt_long, label: "Report", route: '/marine/manual/report'), ], ), SidebarItem( icon: Icons.trending_up, label: "Continuous", isParent: true, children: [ SidebarItem(icon: Icons.description, label: "Info Centre Document", route: '/marine/continuous/info'), ]), SidebarItem( icon: Icons.search, label: "Investigative", isParent: true, children: [ SidebarItem(icon: Icons.description, label: "Info Centre Document", route: '/marine/investigative/info'), // *** ADDED: From marine_home_page.dart *** SidebarItem(icon: Icons.science_outlined, label: "Investigative Sampling", route: '/marine/investigative/manual-sampling'), ]), ], ), // ====== SETTINGS ====== SidebarItem(icon: Icons.settings, label: "Settings", route: '/settings'), ]; } @override Widget build(BuildContext context) { // 1. Get the total screen width final screenWidth = MediaQuery.of(context).size.width; // 2. Define responsive widths for the sidebar container final double collapsedWidth = (screenWidth * 0.08).clamp(55.0, 75.0); // Reduced factor to 8% and max to 75px final double expandedWidth = (screenWidth * 0.2).clamp(200.0, 280.0); // 20% of width, min 200, max 280 // --- MODIFICATION: Initialize responsive size variables in build() --- final double iconSize = (screenWidth * 0.035).clamp(20.0, 28.0); final double textSize = (screenWidth * 0.03).clamp(14.0, 18.0); final double collapsedIconSize = (screenWidth * 0.05).clamp(24.0, 32.0); final double collapsedTextSize = (screenWidth * 0.02).clamp(10.0, 14.0); // --- END MODIFICATION --- return Container( width: widget.isCollapsed ? collapsedWidth : expandedWidth, color: Theme.of(context).primaryColor, // Use theme primary color for sidebar child: Column( children: [ const SizedBox(height: 16), // Spacing below the (now removed) toggle button area Expanded( child: ListView( primary: true, padding: EdgeInsets.zero, // Remove default listview padding children: [ if (!widget.isCollapsed) ...[ // If sidebar is expanded, show full categories with sub-menus for (var item in _menuItems) // --- MODIFICATION: Pass size variables --- _buildExpandableNavItem(item, iconSize, textSize), ] else ...[ // If sidebar is collapsed, only show icons for top-level items for (var item in _menuItems) // --- MODIFICATION: Pass collapsed size variables --- _buildCollapsedNavItem(item, collapsedIconSize, collapsedTextSize), ], ], ), ), const Divider(color: Colors.white24, height: 1), // Separator before logout // Logout item, using an icon as it's a standard action // --- MODIFICATION: Pass size variables for logout item --- _buildNavItem(Icons.logout, "Logout", '/logout', isTopLevel: true, iconSize: iconSize, textSize: textSize, collapsedIconSize: collapsedIconSize), // --- END MODIFICATION --- ], ), ); } // Helper to build the leading widget (Icon or Image) for a sidebar item // --- MODIFICATION: Added size parameter --- Widget _buildLeadingWidget(SidebarItem item, double iconSize) { // Now only checks for icon, as imagePath is not used for top-level items anymore // but the property still exists on SidebarItem for potential future use or other items. return Icon(item.icon, color: Colors.white, size: iconSize); } // Builds an expandable item for parent categories (only when sidebar is expanded) // --- MODIFICATION: Added size parameters --- Widget _buildExpandableNavItem(SidebarItem item, double iconSize, double textSize) { if (item.children == null || item.children!.isEmpty) { // This case handles a top-level item that is NOT a parent, // like the "Settings" item. return _buildNavItem(item.icon, item.label, item.route ?? '', isTopLevel: true, imagePath: item.imagePath, iconSize: iconSize, textSize: textSize); } return Theme( data: Theme.of(context).copyWith(dividerColor: Colors.transparent), // Hide divider in expansion tile child: ExpansionTile( initiallyExpanded: false, // You can set this to true if you want some categories open by default leading: _buildLeadingWidget(item, iconSize), // Use the helper for leading widget // --- MODIFICATION: Use passed text size --- title: Text(item.label, style: TextStyle(color: Colors.white, fontSize: textSize)), // --- END MODIFICATION --- iconColor: Colors.white, collapsedIconColor: Colors.white, childrenPadding: const EdgeInsets.only(left: 20.0), // Indent sub-items children: item.children!.map((childItem) { if (childItem.isParent) { // Nested expansion tiles for sub-categories like "Manual", "Continuous" // --- MODIFICATION: Pass size variables recursively --- return _buildExpandableNavItem(childItem, iconSize, textSize); } else { // Leaf item (actual navigation link) // --- MODIFICATION: Pass size variables --- return _buildNavItem(childItem.icon, childItem.label, childItem.route ?? '', imagePath: childItem.imagePath, iconSize: iconSize, textSize: textSize); } }).toList(), ), ); } // Builds a regular navigation item (for sub-items when expanded, or top-level when collapsed) // --- MODIFICATION: Added size parameters --- Widget _buildNavItem(IconData? icon, String label, String route, {bool isTopLevel = false, String? imagePath, required double iconSize, required double textSize, double? collapsedIconSize}) { return InkWell( onTap: () { if (route.isNotEmpty) { widget.onNavigate(route); } }, child: Padding( padding: EdgeInsets.symmetric( vertical: 12, horizontal: isTopLevel && widget.isCollapsed ? 0 : 8, // Center icon if top-level and collapsed ), child: isTopLevel && widget.isCollapsed ? Center( child: icon != null // --- MODIFICATION: Use collapsed icon size if available, otherwise use regular iconSize --- ? Icon(icon, color: Colors.white, size: collapsedIconSize ?? iconSize) // --- END MODIFICATION --- : const SizedBox.shrink(), // Fallback if no icon or imagePath ) // Only icon when collapsed : Row( children: [ icon != null ? Icon(icon, color: Colors.white, size: iconSize) : const SizedBox.shrink(), // Fallback if no icon or imagePath const SizedBox(width: 12), Expanded( // Use Expanded to prevent text overflow // --- MODIFICATION: Use passed text size --- child: Text(label, style: TextStyle(color: Colors.white, fontSize: textSize)), // --- END MODIFICATION --- ), ], ), ), ); } // Builds a collapsed navigation item (only icon/image) for top-level categories // --- MODIFICATION: Added size parameters --- Widget _buildCollapsedNavItem(SidebarItem item, double collapsedIconSize, double collapsedTextSize) { return InkWell( onTap: () { if (item.route != null && item.route!.isNotEmpty) { widget.onNavigate(item.route!); } else { // If a parent item is tapped when collapsed, expand the sidebar widget.onToggle(); } }, child: Padding( padding: const EdgeInsets.symmetric(vertical: 6.0, horizontal: 0), // Adjusted vertical padding child: Column( mainAxisSize: MainAxisSize.min, // Use minimum space children: [ item.icon != null // --- MODIFICATION: Use passed collapsed icon size --- ? Icon(item.icon, color: Colors.white, size: collapsedIconSize) // --- END MODIFICATION --- : const SizedBox.shrink(), // Fallback if no icon const SizedBox(height: 4), // Small space between icon and text Text( item.label, style: TextStyle( color: Colors.white, // --- MODIFICATION: Use passed collapsed text size --- fontSize: collapsedTextSize, // --- END MODIFICATION --- ), overflow: TextOverflow.ellipsis, // Handle long labels maxLines: 1, // Ensure it stays on one line textAlign: TextAlign.center, // Center the text ), ], ), ), ); } }