627 lines
19 KiB
Dart
627 lines
19 KiB
Dart
|
|
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';
|
||
|
|
|
||
|
|
import '../../Entity/angulardatatype/Basicp1/Basicp1View/Basicp1_entity_list_screen.dart';
|
||
|
|
import '../../Entity/angulardatatype/Basicp1/Basicp1_viewModel/Basicp1_view_model_screen.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
|
||
|
|
|
||
|
|
DrawerItem(
|
||
|
|
icon: Icons.data_object,
|
||
|
|
title: 'Basicp1 Management',
|
||
|
|
subtitle: 'Manage Basicp1 entities',
|
||
|
|
onTap: (context) {
|
||
|
|
Navigator.push(
|
||
|
|
context,
|
||
|
|
MaterialPageRoute(
|
||
|
|
builder: (context) => ChangeNotifierProvider(
|
||
|
|
create: (context) => Basicp1ViewModelScreen(),
|
||
|
|
child: const Basicp1EntityListScreen(),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
},
|
||
|
|
),
|
||
|
|
];
|
||
|
|
|
||
|
|
@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
|
||
|
|
_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,
|
||
|
|
));
|
||
|
|
|
||
|
|
// Cards Animations
|
||
|
|
_cardsScaleAnimation = Tween<double>(
|
||
|
|
begin: 0.8,
|
||
|
|
end: 1.0,
|
||
|
|
).animate(CurvedAnimation(
|
||
|
|
parent: _cardsAnimationController,
|
||
|
|
curve: UIConstants.curveElastic,
|
||
|
|
));
|
||
|
|
|
||
|
|
// Actions Animations
|
||
|
|
_actionsScaleAnimation = Tween<double>(
|
||
|
|
begin: 0.8,
|
||
|
|
end: 1.0,
|
||
|
|
).animate(CurvedAnimation(
|
||
|
|
parent: _actionsAnimationController,
|
||
|
|
curve: UIConstants.curveElastic,
|
||
|
|
));
|
||
|
|
}
|
||
|
|
|
||
|
|
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);
|
||
|
|
},
|
||
|
|
),
|
||
|
|
drawer: ModernDrawer(
|
||
|
|
items: _drawerItems,
|
||
|
|
),
|
||
|
|
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(
|
||
|
|
height: UIConstants.getResponsiveSpacing(
|
||
|
|
context,
|
||
|
|
mobile: UIConstants.spacing32,
|
||
|
|
tablet: UIConstants.spacing40,
|
||
|
|
desktop: UIConstants.spacing48,
|
||
|
|
)),
|
||
|
|
|
||
|
|
// Dashboard Stats
|
||
|
|
_buildDashboardStats(theme, colorScheme),
|
||
|
|
|
||
|
|
SizedBox(
|
||
|
|
height: UIConstants.getResponsiveSpacing(
|
||
|
|
context,
|
||
|
|
mobile: UIConstants.spacing40,
|
||
|
|
tablet: UIConstants.spacing56,
|
||
|
|
desktop: UIConstants.spacing72,
|
||
|
|
)),
|
||
|
|
|
||
|
|
// Quick Actions
|
||
|
|
_buildQuickActions(theme, colorScheme),
|
||
|
|
|
||
|
|
SizedBox(
|
||
|
|
height: UIConstants.getResponsiveSpacing(
|
||
|
|
context,
|
||
|
|
mobile: UIConstants.spacing40,
|
||
|
|
tablet: UIConstants.spacing56,
|
||
|
|
desktop: UIConstants.spacing72,
|
||
|
|
)),
|
||
|
|
|
||
|
|
// 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(
|
||
|
|
ThemeData theme, ColorScheme colorScheme, String? userName) {
|
||
|
|
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)
|
||
|
|
? 3
|
||
|
|
: 4;
|
||
|
|
|
||
|
|
// 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)
|
||
|
|
? 4
|
||
|
|
: 6;
|
||
|
|
|
||
|
|
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),
|
||
|
|
),
|
||
|
|
child: Icon(
|
||
|
|
icon,
|
||
|
|
color: iconColor,
|
||
|
|
size: UIConstants.iconSizeMedium,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
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,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|