299 lines
10 KiB
Dart
299 lines
10 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:environment_monitoring_app/auth_provider.dart';
|
|
import 'package:environment_monitoring_app/collapsible_sidebar.dart';
|
|
|
|
// Define a breakpoint for switching to a persistent sidebar or a mobile drawer
|
|
const double kDrawerBreakpoint = 800.0;
|
|
|
|
class HomePage extends StatefulWidget {
|
|
const HomePage({super.key});
|
|
|
|
@override
|
|
State<HomePage> createState() => _HomePageState();
|
|
}
|
|
|
|
class _HomePageState extends State<HomePage> {
|
|
// isCollapsed is only relevant for the persistent (desktop) layout
|
|
bool _isSidebarCollapsed = false;
|
|
String _currentSelectedRoute = '/home';
|
|
|
|
// Helper method used by both the persistent sidebar and the drawer
|
|
void _handleNavigation(String route) {
|
|
setState(() {
|
|
_currentSelectedRoute = route;
|
|
});
|
|
Navigator.pushNamed(context, route);
|
|
// If using the drawer (mobile layout), close it after navigation
|
|
if (MediaQuery.of(context).size.width < kDrawerBreakpoint) {
|
|
// NOTE: This pop needs the proper context, which is available inside the Builder.
|
|
// We will handle the pop inside the onPressed function.
|
|
}
|
|
}
|
|
|
|
// A simplified toggle for the persistent sidebar's state
|
|
void _toggleSidebarState() {
|
|
setState(() {
|
|
_isSidebarCollapsed = !_isSidebarCollapsed;
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final auth = Provider.of<AuthProvider>(context);
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
|
|
final screenWidth = MediaQuery.of(context).size.width;
|
|
|
|
// --- Determine Layout Type ---
|
|
final bool isMobileLayout = screenWidth < kDrawerBreakpoint;
|
|
|
|
// --- Responsive Size Calculations ---
|
|
final double collapsedWidth = (screenWidth * 0.08).clamp(55.0, 75.0);
|
|
// --- MODIFICATION START: Increased Expanded Width for Drawer ---
|
|
final double expandedWidth = (screenWidth * 0.3).clamp(250.0, 350.0); // Increased factor to 30% and max to 350px
|
|
// --- MODIFICATION END ---
|
|
|
|
final double sidebarWidth = _isSidebarCollapsed ? collapsedWidth : expandedWidth;
|
|
|
|
// Grid properties are set based on whether the persistent sidebar is open OR if it's the mobile layout
|
|
final double effectiveContentWidth = isMobileLayout ? screenWidth : screenWidth - sidebarWidth;
|
|
|
|
final bool useCompactLayout = effectiveContentWidth < 600.0 && !isMobileLayout && !_isSidebarCollapsed;
|
|
|
|
final int crossAxisCount = useCompactLayout ? 1 : 2;
|
|
final double childAspectRatio = useCompactLayout ? 4.0 : 1.6;
|
|
|
|
final double iconSize = (screenWidth * 0.05).clamp(26.0, 40.0);
|
|
final double textSize = (screenWidth * 0.03).clamp(13.0, 18.0);
|
|
// --------------------------------------------------------------------------
|
|
|
|
final sidebar = CollapsibleSidebar(
|
|
isCollapsed: _isSidebarCollapsed,
|
|
onToggle: _toggleSidebarState,
|
|
onNavigate: _handleNavigation,
|
|
);
|
|
|
|
// Calculate required top padding for the Drawer content
|
|
final double statusBarHeight = MediaQuery.of(context).padding.top;
|
|
final double appBarHeight = kToolbarHeight;
|
|
final double totalDrawerTopPadding = statusBarHeight + appBarHeight;
|
|
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
leading: Builder(
|
|
builder: (BuildContext innerContext) {
|
|
return IconButton(
|
|
icon: Icon(
|
|
isMobileLayout
|
|
? Icons.menu
|
|
: (_isSidebarCollapsed ? Icons.menu : Icons.close),
|
|
color: Colors.white
|
|
),
|
|
onPressed: () {
|
|
if (isMobileLayout) {
|
|
Scaffold.of(innerContext).openDrawer();
|
|
} else {
|
|
_toggleSidebarState();
|
|
}
|
|
},
|
|
);
|
|
},
|
|
),
|
|
title: const Text("MMS Version 3.12.03"),
|
|
actions: [
|
|
IconButton(
|
|
icon: const Icon(Icons.person),
|
|
onPressed: () {
|
|
Navigator.pushNamed(context, '/profile');
|
|
},
|
|
),
|
|
],
|
|
),
|
|
drawer: isMobileLayout ? Drawer(
|
|
width: expandedWidth,
|
|
child: Padding(
|
|
padding: EdgeInsets.only(top: totalDrawerTopPadding),
|
|
child: sidebar,
|
|
),
|
|
) : null,
|
|
body: Row(
|
|
children: [
|
|
if (!isMobileLayout)
|
|
sidebar,
|
|
Expanded(
|
|
child: SingleChildScrollView(
|
|
padding: const EdgeInsets.all(12),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Welcome, ${auth.userEmail ?? 'User'}",
|
|
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
|
fontWeight: FontWeight.bold,
|
|
color: colorScheme.onBackground,
|
|
),
|
|
),
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
"Select a Department:",
|
|
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
|
fontWeight: FontWeight.w600,
|
|
color: colorScheme.onBackground,
|
|
),
|
|
),
|
|
const SizedBox(height: 4),
|
|
|
|
GridView.count(
|
|
crossAxisCount: crossAxisCount,
|
|
mainAxisSpacing: 8,
|
|
crossAxisSpacing: 8,
|
|
childAspectRatio: childAspectRatio,
|
|
padding: EdgeInsets.zero,
|
|
shrinkWrap: true,
|
|
physics: const ClampingScrollPhysics(),
|
|
children: [
|
|
_buildMiniCategoryCard(
|
|
context,
|
|
title: "Air",
|
|
icon: Icons.air,
|
|
color: Colors.blue.shade700,
|
|
route: '/air/home',
|
|
iconSize: iconSize,
|
|
textSize: textSize,
|
|
),
|
|
_buildMiniCategoryCard(
|
|
context,
|
|
title: "River",
|
|
icon: Icons.water,
|
|
color: Colors.teal.shade700,
|
|
route: '/river/home',
|
|
iconSize: iconSize,
|
|
textSize: textSize,
|
|
),
|
|
_buildMiniCategoryCard(
|
|
context,
|
|
title: "Marine",
|
|
icon: Icons.sailing,
|
|
color: Colors.indigo.shade700,
|
|
route: '/marine/home',
|
|
iconSize: iconSize,
|
|
textSize: textSize,
|
|
),
|
|
_buildMiniSettingsCard(
|
|
context,
|
|
iconSize: iconSize,
|
|
textSize: textSize,
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildMiniCategoryCard(
|
|
BuildContext context, {
|
|
required String title,
|
|
required IconData icon,
|
|
required Color color,
|
|
required String route,
|
|
required double iconSize,
|
|
required double textSize,
|
|
}) {
|
|
return Card(
|
|
elevation: 1,
|
|
margin: EdgeInsets.zero,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(6),
|
|
),
|
|
child: InkWell(
|
|
borderRadius: BorderRadius.circular(6),
|
|
onTap: () => Navigator.pushNamed(context, route),
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(6),
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [color.withOpacity(0.9), color],
|
|
),
|
|
),
|
|
child: FittedBox(
|
|
fit: BoxFit.scaleDown,
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(icon, size: iconSize, color: Colors.white),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
title,
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontSize: textSize,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildMiniSettingsCard(BuildContext context, {
|
|
required double iconSize,
|
|
required double textSize,
|
|
}) {
|
|
return Card(
|
|
elevation: 1,
|
|
margin: EdgeInsets.zero,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(6),
|
|
),
|
|
child: InkWell(
|
|
borderRadius: BorderRadius.circular(6),
|
|
onTap: () => Navigator.pushNamed(context, '/settings'),
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(6),
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [Colors.grey.shade700, Colors.grey.shade800],
|
|
),
|
|
),
|
|
child: FittedBox(
|
|
fit: BoxFit.scaleDown,
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(Icons.settings, size: iconSize, color: Colors.white),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
"Settings",
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontSize: textSize,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
} |