login-page/lib/notification_page.dart
2025-05-17 11:13:16 +03:00

153 lines
7.5 KiB
Dart

import 'package:flutter/material.dart';
import 'models/notification_model.dart';
import 'widgets/empty_state_widget.dart';
class NotificationPage extends StatefulWidget {
const NotificationPage({super.key});
@override
State<NotificationPage> createState() => _NotificationPageState();
}
class _NotificationPageState extends State<NotificationPage> {
final List<NotificationModel> _notifications = [
NotificationModel(id: '1', title: 'Order Shipped!', message: 'Your order #AMZ12345 for Nighthawk AX12 Router has been shipped.', timestamp: DateTime.now().subtract(const Duration(hours: 2)), icon: Icons.local_shipping_outlined),
NotificationModel(id: '2', title: 'Weekend Deal!', message: 'Get 15% off on all Mesh Systems this weekend. Use code WEEKEND15.', timestamp: DateTime.now().subtract(const Duration(days: 1)), icon: Icons.campaign_outlined, isRead: true),
NotificationModel(id: '3', title: 'Password Reset Confirmation', message: 'Your password was successfully reset on a new device an hour ago.', timestamp: DateTime.now().subtract(const Duration(days: 3)), icon: Icons.lock_reset_outlined),
NotificationModel(id: '4', title: 'New Login Detected', message: 'We detected a new login to your account from a new device in California, US.', timestamp: DateTime.now().subtract(const Duration(days: 3, hours: 2)), icon: Icons.login_outlined, isRead: true),
NotificationModel(id: '5', title: 'Feature Update: Dark Mode', message: 'Dark mode is now even better! Check out the latest app improvements and enjoy a smoother experience.', timestamp: DateTime.now().subtract(const Duration(days: 5)), icon: Icons.new_releases_outlined),
];
void _markAsRead(NotificationModel notification) {
setState(() {
notification.isRead = true;
});
}
void _deleteNotification(String id) {
setState(() {
_notifications.removeWhere((n) => n.id == id);
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Notification dismissed'), duration: Duration(seconds: 1)),
);
}
String _timeAgo(DateTime date) {
final Duration diff = DateTime.now().difference(date);
if (diff.inDays >= 365) return "${(diff.inDays / 365).floor()} year(s)";
if (diff.inDays >= 30) return "${(diff.inDays / 30).floor()} month(s)";
if (diff.inDays > 0) return "${diff.inDays} day(s)";
if (diff.inHours > 0) return "${diff.inHours} hour(s)";
if (diff.inMinutes > 0) return "${diff.inMinutes} minute(s)";
return "Just now";
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(
title: const Text('Notifications'),
centerTitle: true,
actions: [
if (_notifications.any((n) => !n.isRead))
IconButton(
icon: const Icon(Icons.mark_chat_read_outlined),
tooltip: 'Mark all as read',
onPressed: () {
setState(() {
for (var n in _notifications) {
n.isRead = true;
}
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('All notifications marked as read.')),
);
},
)
],
),
body: _notifications.isEmpty
? const EmptyStateWidget(
icon: Icons.notifications_off_outlined,
message: 'You have no notifications yet.',
)
: ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 8.0),
itemCount: _notifications.length,
itemBuilder: (context, index) {
final notification = _notifications[index];
return Dismissible(
key: Key(notification.id),
direction: DismissDirection.endToStart,
onDismissed: (direction) {
_deleteNotification(notification.id);
},
background: Container(
color: theme.colorScheme.errorContainer,
alignment: Alignment.centerRight,
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Icon(Icons.delete_outline, color: theme.colorScheme.onErrorContainer),
),
child: Card(
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
elevation: notification.isRead ? 1 : 2.5,
shape: RoundedRectangleBorder(
side: BorderSide(
color: notification.isRead ? theme.dividerColor.withOpacity(0.5) : theme.colorScheme.primary.withOpacity(0.6),
width: notification.isRead ? 0.8 : 1.2,
),
borderRadius: BorderRadius.circular(theme.cardTheme.shape is RoundedRectangleBorder ? (theme.cardTheme.shape as RoundedRectangleBorder).borderRadius.resolve(Directionality.of(context)).topLeft.x : 8)
),
child: ListTile(
leading: CircleAvatar(
backgroundColor: notification.isRead
? theme.colorScheme.secondaryContainer.withOpacity(0.5)
: theme.colorScheme.primaryContainer,
child: Icon(
notification.icon,
color: notification.isRead
? theme.colorScheme.onSecondaryContainer.withOpacity(0.7)
: theme.colorScheme.onPrimaryContainer,
size: 22,
),
),
title: Text(
notification.title,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: notification.isRead ? FontWeight.normal : FontWeight.w600,
color: notification.isRead ? theme.textTheme.titleMedium?.color?.withOpacity(0.8) : theme.textTheme.titleMedium?.color,
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(notification.message, maxLines: 2, overflow: TextOverflow.ellipsis, style: theme.textTheme.bodySmall),
const SizedBox(height: 4),
Text(
'${_timeAgo(notification.timestamp)} ago',
style: theme.textTheme.bodySmall?.copyWith(color: theme.textTheme.bodySmall?.color?.withOpacity(0.6), fontSize: 11),
),
],
),
isThreeLine: true,
trailing: !notification.isRead
? Icon(Icons.circle, size: 10, color: theme.colorScheme.primary)
: null,
onTap: () {
_markAsRead(notification);
// Mock action: show snackbar
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Notification "${notification.title}" viewed.'), duration: const Duration(seconds: 2)),
);
},
),
),
);
},
),
);
}
}