606 lines
19 KiB
Dart
Raw Normal View History

2025-09-06 19:21:52 +05:30
import 'package:base_project/core/constants/ui_constants.dart';
import 'package:base_project/core/theme/color_scheme.dart';
import 'package:base_project/routes/route_names.dart';
import 'package:base_project/shared/widgets/app_bar/modern_app_bar.dart';
import 'package:base_project/shared/widgets/cards/dashboard_card.dart';
import 'package:base_project/shared/widgets/buttons/quick_action_button.dart';
import 'package:base_project/shared/widgets/navigation/modern_drawer.dart';
import 'package:base_project/utils/managers/user_manager.dart';
import 'package:provider/provider.dart';
import 'package:base_project/view_model/system_params/system_params_view_model.dart';
import 'package:flutter/material.dart';
class HomeView extends StatefulWidget {
const HomeView({super.key});
@override
State<HomeView> createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> with TickerProviderStateMixin {
// Animation Controllers
late AnimationController _pageAnimationController;
late AnimationController _cardsAnimationController;
late AnimationController _actionsAnimationController;
// Animations
late Animation<double> _pageFadeAnimation;
late Animation<Offset> _pageSlideAnimation;
late Animation<double> _cardsScaleAnimation;
late Animation<double> _actionsScaleAnimation;
// Dashboard stats (replace with real API data)
List<Map<String, dynamic>> _dashboardStats = [];
final List<Map<String, dynamic>> _quickActions = [
{
'label': 'Profile',
'icon': Icons.person,
'onTap': () {},
'backgroundColor': null, // Use theme default
},
{
'label': 'Settings',
'icon': Icons.settings,
'onTap': () {},
'backgroundColor': null,
},
{
'label': 'Security',
'icon': Icons.security,
'onTap': () {},
'backgroundColor': null,
},
{
'label': 'Reports',
'icon': Icons.analytics,
'onTap': () {},
'backgroundColor': null,
},
];
final List<DrawerItem> _drawerItems = [
DrawerItem(
icon: Icons.person,
title: 'Profile',
subtitle: 'Manage your account',
onTap: (context) {
Navigator.pushNamed(context, RouteNames.profileView);
},
),
DrawerItem(
icon: Icons.system_security_update,
title: 'System Parameters',
subtitle: 'Configure system settings',
onTap: (context) {
Navigator.pushNamed(context, RouteNames.systemParamsView);
},
),
DrawerItem(
icon: Icons.password,
title: 'Change Password',
subtitle: 'Update your password',
onTap: (context) {
Navigator.pushNamed(context, RouteNames.changePasswordView);
},
),
// NEW ITEMS
2025-09-09 08:48:07 +05:30
// NEW MENU
2025-09-06 19:21:52 +05:30
];
@override
void initState() {
super.initState();
_initializeAnimations();
_startPageAnimation();
_loadDashboardData();
}
void _initializeAnimations() {
// Page Animation Controller
_pageAnimationController = AnimationController(
duration: UIConstants.durationSlow,
vsync: this,
);
// Cards Animation Controller
_cardsAnimationController = AnimationController(
duration: UIConstants.durationNormal,
vsync: this,
);
// Actions Animation Controller
_actionsAnimationController = AnimationController(
duration: UIConstants.durationNormal,
vsync: this,
);
// Page Animations
2025-09-09 08:48:07 +05:30
_pageFadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _pageAnimationController,
curve: UIConstants.curveNormal,
),
);
_pageSlideAnimation =
Tween<Offset>(begin: const Offset(0, 0.3), end: Offset.zero).animate(
CurvedAnimation(
parent: _pageAnimationController,
curve: UIConstants.curveNormal,
),
);
2025-09-06 19:21:52 +05:30
// Cards Animations
2025-09-09 08:48:07 +05:30
_cardsScaleAnimation = Tween<double>(begin: 0.8, end: 1.0).animate(
CurvedAnimation(
parent: _cardsAnimationController,
curve: UIConstants.curveElastic,
),
);
2025-09-06 19:21:52 +05:30
// Actions Animations
2025-09-09 08:48:07 +05:30
_actionsScaleAnimation = Tween<double>(begin: 0.8, end: 1.0).animate(
CurvedAnimation(
parent: _actionsAnimationController,
curve: UIConstants.curveElastic,
),
);
2025-09-06 19:21:52 +05:30
}
void _startPageAnimation() async {
await Future.delayed(const Duration(milliseconds: 300));
_pageAnimationController.forward();
await Future.delayed(const Duration(milliseconds: 400));
_cardsAnimationController.forward();
await Future.delayed(const Duration(milliseconds: 300));
_actionsAnimationController.forward();
}
Future<void> _loadDashboardData() async {
// TODO: replace with actual API calls, e.g., via a DashboardViewModel
// Simulate network delay
await Future.delayed(const Duration(milliseconds: 400));
setState(() {
_dashboardStats = [
{
'title': 'Total Users',
'subtitle': 'Active accounts',
'numericValue': 1234,
'valueSuffix': '',
'icon': Icons.people,
'type': DashboardCardType.primary,
'onTap': () {},
},
{
'title': 'System Uptime',
'subtitle': 'All systems operational',
'numericValue': 99.9,
'valueSuffix': '%',
'icon': Icons.check_circle,
'type': DashboardCardType.success,
'onTap': () {},
},
{
'title': 'Security Alerts',
'subtitle': 'Last 24 hours',
'numericValue': 2,
'valueSuffix': '',
'icon': Icons.security,
'type': DashboardCardType.info,
'onTap': () {},
},
{
'title': 'Avg. Response Time',
'subtitle': 'System performance',
'numericValue': 2.3,
'valueSuffix': 's',
'icon': Icons.speed,
'type': DashboardCardType.secondary,
'onTap': () {},
},
];
});
}
@override
void dispose() {
_pageAnimationController.dispose();
_cardsAnimationController.dispose();
_actionsAnimationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
final size = MediaQuery.of(context).size;
final userName = UserManager().userName;
return Scaffold(
backgroundColor: colorScheme.background,
appBar: ModernAppBar(
title: 'Dashboard',
centerTitle: false,
showLogoInTitle: true,
logoImage: _getDashboardLogoImage(colorScheme),
userAvatar: null,
userAvatarImage: _getDashboardLogoImage(colorScheme),
userName: userName,
onProfilePressed: () {
Navigator.pushNamed(context, RouteNames.profileView);
},
),
2025-09-09 08:48:07 +05:30
drawer: ModernDrawer(items: _drawerItems),
2025-09-06 19:21:52 +05:30
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
colorScheme.background,
colorScheme.surfaceVariant.withOpacity(0.1),
colorScheme.primaryContainer.withOpacity(0.05),
],
),
),
child: SafeArea(
child: SingleChildScrollView(
padding: UIConstants.getResponsivePadding(
context,
mobile: const EdgeInsets.all(UIConstants.spacing24),
tablet: const EdgeInsets.all(UIConstants.spacing32),
desktop: const EdgeInsets.all(UIConstants.spacing40),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Welcome Section
_buildWelcomeSection(theme, colorScheme, userName),
SizedBox(
2025-09-09 08:48:07 +05:30
height: UIConstants.getResponsiveSpacing(
context,
mobile: UIConstants.spacing32,
tablet: UIConstants.spacing40,
desktop: UIConstants.spacing48,
),
),
2025-09-06 19:21:52 +05:30
// Dashboard Stats
_buildDashboardStats(theme, colorScheme),
SizedBox(
2025-09-09 08:48:07 +05:30
height: UIConstants.getResponsiveSpacing(
context,
mobile: UIConstants.spacing40,
tablet: UIConstants.spacing56,
desktop: UIConstants.spacing72,
),
),
2025-09-06 19:21:52 +05:30
// Quick Actions
_buildQuickActions(theme, colorScheme),
SizedBox(
2025-09-09 08:48:07 +05:30
height: UIConstants.getResponsiveSpacing(
context,
mobile: UIConstants.spacing40,
tablet: UIConstants.spacing56,
desktop: UIConstants.spacing72,
),
),
2025-09-06 19:21:52 +05:30
// Recent Activity Section
_buildRecentActivity(theme, colorScheme),
],
),
),
),
),
);
}
ImageProvider<Object>? _getDashboardLogoImage(ColorScheme cs) {
// Prefer dynamic logo from system parameters if present
try {
final sysVm = Provider.of<SystemParamsViewModel>(context, listen: true);
if (sysVm.profileImageBytes != null &&
sysVm.profileImageBytes!.isNotEmpty) {
return MemoryImage(sysVm.profileImageBytes!);
}
} catch (_) {
// Provider not available – fall through to default asset
}
// Default asset logo
return const AssetImage('assets/images/image_not_found.png');
}
Widget _buildWelcomeSection(
2025-09-09 08:48:07 +05:30
ThemeData theme,
ColorScheme colorScheme,
String? userName,
) {
2025-09-06 19:21:52 +05:30
return AnimatedBuilder(
animation: _pageAnimationController,
builder: (context, child) {
return FadeTransition(
opacity: _pageFadeAnimation,
child: SlideTransition(
position: _pageSlideAnimation,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Welcome back, ${userName ?? 'User'}! 👋',
style: theme.textTheme.headlineMedium?.copyWith(
color: colorScheme.onBackground,
fontWeight: FontWeight.w700,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: UIConstants.spacing8),
Text(
'Here\'s what\'s happening with your system today',
style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurfaceVariant,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
);
},
);
}
// Removed in-body logo: AppBar now displays the logo.
Widget _buildDashboardStats(ThemeData theme, ColorScheme colorScheme) {
return AnimatedBuilder(
animation: _cardsAnimationController,
builder: (context, child) {
return Transform.scale(
scale: _cardsScaleAnimation.value,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'System Overview',
style: theme.textTheme.titleLarge?.copyWith(
color: colorScheme.onBackground,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: UIConstants.spacing24),
// Responsive Grid
LayoutBuilder(
builder: (context, constraints) {
final crossAxisCount = UIConstants.isMobile(context)
? 2
: UIConstants.isTablet(context)
2025-09-09 08:48:07 +05:30
? 3
: 4;
2025-09-06 19:21:52 +05:30
// Use fixed mainAxisExtent per breakpoint to avoid overflow
final double mainAxisExtent = UIConstants.isMobile(context)
? 130
: (UIConstants.isTablet(context) ? 160 : 180);
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
crossAxisSpacing: UIConstants.spacing16,
mainAxisSpacing: UIConstants.spacing16,
mainAxisExtent: mainAxisExtent,
),
itemCount: _dashboardStats.length,
itemBuilder: (context, index) {
final stat = _dashboardStats[index];
return DashboardCard(
title: stat['title'],
subtitle: stat['subtitle'],
value: stat['value'],
numericValue: stat['numericValue'],
valueSuffix: stat['valueSuffix'],
animationDuration: UIConstants.durationNormal,
icon: stat['icon'],
type: stat['type'],
onTap: stat['onTap'],
);
},
);
},
),
],
),
);
},
);
}
Widget _buildQuickActions(ThemeData theme, ColorScheme colorScheme) {
return AnimatedBuilder(
animation: _actionsAnimationController,
builder: (context, child) {
return Transform.scale(
scale: _actionsScaleAnimation.value,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Quick Actions',
style: theme.textTheme.titleLarge?.copyWith(
color: colorScheme.onBackground,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: UIConstants.spacing24),
// Responsive Grid
LayoutBuilder(
builder: (context, constraints) {
final crossAxisCount = UIConstants.isMobile(context)
? 2
: UIConstants.isTablet(context)
2025-09-09 08:48:07 +05:30
? 4
: 6;
2025-09-06 19:21:52 +05:30
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
crossAxisSpacing: UIConstants.spacing16,
mainAxisSpacing: UIConstants.spacing16,
childAspectRatio: 1.0,
),
itemCount: _quickActions.length,
itemBuilder: (context, index) {
final action = _quickActions[index];
return QuickActionButton(
label: action['label'],
icon: action['icon'],
onTap: action['onTap'],
backgroundColor: action['backgroundColor'],
size: UIConstants.logoSizeMedium,
);
},
);
},
),
],
),
);
},
);
}
Widget _buildRecentActivity(ThemeData theme, ColorScheme colorScheme) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Recent Activity',
style: theme.textTheme.titleLarge?.copyWith(
color: colorScheme.onBackground,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: UIConstants.spacing24),
Container(
padding: UIConstants.cardPaddingMedium,
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(UIConstants.radius16),
boxShadow: [
BoxShadow(
color: colorScheme.shadow.withOpacity(0.1),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Column(
children: [
_buildActivityItem(
theme,
colorScheme,
Icons.security,
'Security scan completed',
'System security check finished successfully',
'2 minutes ago',
AppColorScheme.success,
),
const Divider(height: UIConstants.spacing24),
_buildActivityItem(
theme,
colorScheme,
Icons.update,
'System updated',
'Latest security patches installed',
'1 hour ago',
AppColorScheme.info,
),
const Divider(height: UIConstants.spacing24),
_buildActivityItem(
theme,
colorScheme,
Icons.backup,
'Backup completed',
'Daily backup process finished',
'3 hours ago',
colorScheme.primary,
),
],
),
),
],
);
}
Widget _buildActivityItem(
ThemeData theme,
ColorScheme colorScheme,
IconData icon,
String title,
String subtitle,
String time,
Color iconColor,
) {
return Row(
children: [
Container(
padding: const EdgeInsets.all(UIConstants.spacing8),
decoration: BoxDecoration(
color: iconColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(UIConstants.radius8),
),
2025-09-09 08:48:07 +05:30
child: Icon(icon, color: iconColor, size: UIConstants.iconSizeMedium),
2025-09-06 19:21:52 +05:30
),
const SizedBox(width: UIConstants.spacing16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: theme.textTheme.titleMedium?.copyWith(
color: colorScheme.onSurface,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: UIConstants.spacing4),
Text(
subtitle,
style: theme.textTheme.bodyMedium?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
],
),
),
Text(
time,
style: theme.textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
],
);
}
}