Files
TIS_RMS/frontend/lib/ui/shared/widgets/notification_icon_button.dart
T

218 lines
7.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../providers/notification_provider.dart';
import '../modals/view_activity_modal.dart';
import '../../screens/dashboard/widgets/notification_dropdown.dart';
Color getNotificationColorHelper(String title) {
final t = title.toLowerCase();
if (t.contains('student')) return Colors.blue;
if (t.contains('document')) return const Color(0xFF1C8248);
if (t.contains('password')) return Colors.orange;
return Colors.grey;
}
IconData getNotificationIconHelper(String title) {
final t = title.toLowerCase();
if (t.contains('student')) return Icons.person_add;
if (t.contains('document')) return Icons.upload_file;
if (t.contains('password')) return Icons.lock_reset;
return Icons.notifications;
}
void showNotificationDropdownMenu(BuildContext context, WidgetRef ref) {
final notificationsAsync = ref.read(notificationsProvider);
final list = notificationsAsync.value ?? [];
final RenderBox? button = context.findRenderObject() as RenderBox?;
final RenderBox? overlay =
Navigator.of(context).overlay?.context.findRenderObject() as RenderBox?;
if (button == null || overlay == null) return;
final position = RelativeRect.fromRect(
Rect.fromPoints(
button.localToGlobal(
Offset(0, button.size.height + 4),
ancestor: overlay,
),
button.localToGlobal(
button.size.bottomRight(const Offset(0, 4)),
ancestor: overlay,
),
),
Offset.zero & overlay.size,
);
showMenu(
context: context,
position: position,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
elevation: 8,
items: [
// ── Header row ────────────────────────────────────────────────
PopupMenuItem(
enabled: false,
child: Container(
width: 300,
padding: const EdgeInsets.only(bottom: 8),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(color: Theme.of(context).dividerColor),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Notifications',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: Theme.of(context).colorScheme.onSurface,
),
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
if (list.isNotEmpty) ...[
InkWell(
mouseCursor: SystemMouseCursors.click,
onTap: () {
Navigator.pop(context);
ref
.read(notificationsProvider.notifier)
.markAllAsRead();
},
child: const Text(
'Mark all read',
style: TextStyle(
fontSize: 12,
color: Color(0xFF1C8248),
fontWeight: FontWeight.w600,
),
),
),
const SizedBox(width: 10),
InkWell(
mouseCursor: SystemMouseCursors.click,
onTap: () {
Navigator.pop(context);
ref
.read(notificationsProvider.notifier)
.clearNotifications();
},
child: const Text(
'Clear',
style: TextStyle(
fontSize: 12,
color: Colors.redAccent,
fontWeight: FontWeight.w600,
),
),
),
],
],
),
],
),
),
),
// ── Empty state ───────────────────────────────────────────────
if (list.isEmpty)
PopupMenuItem(
enabled: false,
child: SizedBox(
width: 300,
height: 120,
child: const Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.notifications_off_rounded,
size: 48,
color: Colors.grey,
),
SizedBox(height: 12),
Text(
'No new notifications',
style: TextStyle(color: Colors.grey, fontSize: 13),
),
],
),
),
),
)
// ── Notification items ────────────────────────────────────────
else
PopupMenuItem(
enabled: false,
padding: EdgeInsets.zero,
child: NotificationDropdownWidget(
notifications: list,
onViewActivity: (ctx, title, desc, date, icon, color) {
ViewActivityModal.show(
context: ctx,
title: title,
description: desc,
date: date,
icon: icon,
actionColor: color,
);
},
getIcon: getNotificationIconHelper,
getColor: getNotificationColorHelper,
),
),
],
);
}
class NotificationIconButton extends ConsumerWidget {
final double iconSize;
const NotificationIconButton({super.key, this.iconSize = 24});
@override
Widget build(BuildContext context, WidgetRef ref) {
final notificationsAsync = ref.watch(notificationsProvider);
final unreadCount =
notificationsAsync.value?.where((n) => !n.isRead).length ?? 0;
return Builder(
builder: (ctx) => Stack(
clipBehavior: Clip.none,
children: [
IconButton(
icon: Icon(Icons.notifications, size: iconSize),
tooltip: 'Notifications',
onPressed: () => showNotificationDropdownMenu(ctx, ref),
),
if (unreadCount > 0)
Positioned(
right: 6,
top: 6,
child: Container(
padding: const EdgeInsets.all(3),
decoration: const BoxDecoration(
color: Colors.redAccent,
shape: BoxShape.circle,
),
constraints: const BoxConstraints(minWidth: 16, minHeight: 16),
child: Text(
unreadCount > 99 ? '99+' : unreadCount.toString(),
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.white,
fontSize: 9,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
);
}
}