chitfund/luckychit/lib/features/notifications/notification_center_page.dart

356 lines
11 KiB
Dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../core/services/notification_service.dart';
import '../../core/models/notification.dart';
import '../../core/utils/snackbar_util.dart';
import '../../shared/widgets/skeleton_loader.dart';
import '../../shared/widgets/empty_state_widget.dart';
class NotificationCenterPage extends StatefulWidget {
const NotificationCenterPage({super.key});
@override
State<NotificationCenterPage> createState() => _NotificationCenterPageState();
}
class _NotificationCenterPageState extends State<NotificationCenterPage> {
final _scrollController = ScrollController();
@override
void initState() {
super.initState();
// Initialize service if not already
if (!Get.isRegistered<NotificationService>()) {
Get.put(NotificationService());
}
// Load notifications
NotificationService.to.loadNotifications(refresh: true);
// Setup infinite scroll
_scrollController.addListener(_onScroll);
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
void _onScroll() {
if (_scrollController.position.pixels >=
_scrollController.position.maxScrollExtent * 0.8) {
NotificationService.to.loadMore();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Notifications'),
actions: [
// Mark all as read
Obx(() {
final unreadCount = NotificationService.to.unreadCount.value;
if (unreadCount > 0) {
return IconButton(
icon: const Icon(Icons.done_all_rounded),
tooltip: 'Mark all as read',
onPressed: () async {
final success = await NotificationService.to.markAllAsRead();
if (success) {
SnackbarUtil.showSuccess('All notifications marked as read');
}
},
);
}
return const SizedBox();
}),
],
),
body: Obx(() {
final service = NotificationService.to;
// Loading state (first load)
if (service.isLoading.value && service.notifications.isEmpty) {
return _buildSkeleton();
}
// Empty state
if (service.notifications.isEmpty && !service.isLoading.value) {
return EmptyStateWidget(
type: EmptyStateType.noActivities,
customTitle: 'No Notifications',
customMessage: 'You\'re all caught up!\nNotifications will appear here.',
onActionPressed: () => service.refresh(),
actionLabel: 'Refresh',
);
}
// Notifications list
return RefreshIndicator(
onRefresh: () => service.refresh(),
child: ListView.builder(
controller: _scrollController,
padding: EdgeInsets.symmetric(vertical: 8.h),
itemCount: service.notifications.length + (service.hasMore.value ? 1 : 0),
itemBuilder: (context, index) {
// Loading indicator at bottom
if (index == service.notifications.length) {
return Padding(
padding: EdgeInsets.all(16.w),
child: const Center(child: CircularProgressIndicator()),
);
}
final notification = service.notifications[index];
return _buildNotificationCard(notification);
},
),
);
}),
);
}
Widget _buildNotificationCard(NotificationModel notification) {
return Dismissible(
key: Key(notification.id),
direction: DismissDirection.endToStart,
background: Container(
alignment: Alignment.centerRight,
padding: EdgeInsets.only(right: 20.w),
color: Colors.red.shade600,
child: const Icon(Icons.delete, color: Colors.white),
),
onDismissed: (direction) async {
await NotificationService.to.deleteNotification(notification.id);
SnackbarUtil.showInfo('Notification deleted');
},
child: Card(
margin: EdgeInsets.symmetric(horizontal: 12.w, vertical: 4.h),
color: notification.isUnread
? notification.getColor().withOpacity(0.05)
: null,
child: InkWell(
onTap: () => _handleNotificationTap(notification),
borderRadius: BorderRadius.circular(16.r),
child: Padding(
padding: EdgeInsets.all(16.w),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Icon
Container(
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: notification.getColor().withOpacity(0.1),
borderRadius: BorderRadius.circular(12.r),
),
child: Icon(
notification.getIcon(),
color: notification.getColor(),
size: 24.w,
),
),
SizedBox(width: 16.w),
// Content
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
notification.title,
style: TextStyle(
fontSize: 16.sp,
fontWeight: notification.isUnread
? FontWeight.bold
: FontWeight.w600,
color: Colors.grey.shade800,
),
),
),
if (notification.isUnread)
Container(
width: 8.w,
height: 8.h,
decoration: BoxDecoration(
color: notification.getColor(),
shape: BoxShape.circle,
),
),
],
),
SizedBox(height: 6.h),
Text(
notification.message,
style: TextStyle(
fontSize: 14.sp,
color: Colors.grey.shade600,
height: 1.4,
),
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 8.h),
Row(
children: [
if (notification.groupName != null) ...[
Icon(
Icons.group,
size: 14.w,
color: Colors.grey.shade500,
),
SizedBox(width: 4.w),
Text(
notification.groupName!,
style: TextStyle(
fontSize: 13.sp,
color: Colors.grey.shade500,
),
),
SizedBox(width: 12.w),
],
Icon(
Icons.access_time_rounded,
size: 14.w,
color: Colors.grey.shade500,
),
SizedBox(width: 4.w),
Text(
notification.getTimeAgo(),
style: TextStyle(
fontSize: 13.sp,
color: Colors.grey.shade500,
),
),
],
),
],
),
),
],
),
),
),
),
);
}
void _handleNotificationTap(NotificationModel notification) {
// Mark as read if unread
if (notification.isUnread) {
NotificationService.to.markAsRead(notification.id);
}
// Show full notification details
_showNotificationDetails(notification);
}
void _showNotificationDetails(NotificationModel notification) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Row(
children: [
Icon(
notification.getIcon(),
color: notification.getColor(),
size: 24.w,
),
SizedBox(width: 12.w),
Expanded(
child: Text(
notification.title,
style: TextStyle(fontSize: 18.sp),
),
),
],
),
content: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
notification.message,
style: TextStyle(
fontSize: 15.sp,
height: 1.5,
),
),
SizedBox(height: 16.h),
if (notification.groupName != null)
_buildDetailRow(
'Group',
notification.groupName!,
Icons.group,
),
_buildDetailRow(
'Date',
notification.getTimeAgo(),
Icons.access_time_rounded,
),
_buildDetailRow(
'Channel',
notification.channel.toUpperCase(),
Icons.send_rounded,
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Close'),
),
],
),
);
}
Widget _buildDetailRow(String label, String value, IconData icon) {
return Padding(
padding: EdgeInsets.only(bottom: 8.h),
child: Row(
children: [
Icon(icon, size: 16.w, color: Colors.grey.shade600),
SizedBox(width: 8.w),
Text(
'$label: ',
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w600,
color: Colors.grey.shade700,
),
),
Expanded(
child: Text(
value,
style: TextStyle(
fontSize: 14.sp,
color: Colors.grey.shade600,
),
),
),
],
),
);
}
Widget _buildSkeleton() {
return ListView.builder(
padding: EdgeInsets.symmetric(vertical: 8.h),
itemCount: 10,
itemBuilder: (context, index) => const SkeletonListItem(),
);
}
}