onufitness_mobile/lib/screens/notification/notification_screen.dart
2026-01-13 11:36:24 +05:30

389 lines
12 KiB
Dart

import 'package:get/get.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:onufitness/constants/color_constant.dart';
import 'package:onufitness/constants/text_constant.dart';
import 'package:onufitness/controller/notification_controller.dart';
import 'package:onufitness/models/notification/get_all_notification_response_model.dart';
import 'package:onufitness/services/notification_services/navigation_controller.dart';
import 'package:onufitness/utils/helper_function.dart';
import 'package:onufitness/widgets/appbars/custom_appbar.dart';
import 'package:onufitness/widgets/Buttons/custom_submit_button.dart';
class NotificationScreen extends StatelessWidget {
const NotificationScreen({super.key});
@override
Widget build(BuildContext context) {
final controller = Get.put(NotificationController());
return Scaffold(
backgroundColor: Colors.white,
appBar: CustomAppBar(
title: "Notifications",
titleFontSize: appBarHeardingText,
textColor: appbarTextColor,
backgroundColor: appBarBackgroundColor,
leading: IconButton(
onPressed: () {
Get.back();
},
icon: Icon(Icons.arrow_back_ios),
),
actions: [
Obx(() {
if (controller.hasUnreadNotifications) {
return Container(
margin: EdgeInsets.only(right: 16.w),
child: TextButton.icon(
onPressed: controller.markAllAsRead,
icon: Icon(Icons.done_all, size: 18.sp, color: Colors.black),
label: Text(
'Mark all read',
style: TextStyle(
fontSize: smallSizeText,
color: Colors.black,
fontWeight: FontWeight.w500,
),
),
style: TextButton.styleFrom(
backgroundColor: Colors.grey.withValues(alpha: 0.2),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.r),
),
),
),
);
}
return const SizedBox.shrink();
}),
],
),
body: RefreshIndicator(
onRefresh: controller.refreshNotifications,
backgroundColor: Colors.white,
color: Colors.black,
child: Obx(() {
if (controller.isLoading.value) {
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Colors.black),
),
);
}
if (controller.errorMessage.value != null) {
return _buildErrorState(controller);
}
if (controller.notifications.isEmpty) {
return _buildEmptyState();
}
return _buildNotificationList(controller);
}),
),
);
}
Widget _buildErrorState(NotificationController controller) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline, size: 60.sp, color: Colors.red[300]),
SizedBox(height: 16.h),
Text(
"Server error!",
style: TextStyle(fontSize: regularSizeText, color: Colors.red[600]),
),
SizedBox(height: 16.h),
CustomSubmitButton(
width: 100.w,
height: 40.h,
text: "Retry",
onPressed: () => controller.fetchNotifications(isRefresh: true),
),
],
),
);
}
Widget _buildEmptyState() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.notifications_none, size: 80.sp, color: Colors.grey[400]),
SizedBox(height: 16.h),
Text(
'No notifications yet',
style: TextStyle(
fontSize: largeSizeText,
color: Colors.grey[600],
fontWeight: FontWeight.w500,
),
),
SizedBox(height: 8.h),
Text(
'You\'ll see notifications here when you have them',
style: TextStyle(fontSize: smallSizeText, color: Colors.grey[500]),
textAlign: TextAlign.center,
),
],
),
);
}
Widget _buildNotificationList(NotificationController controller) {
return Column(
children: [
Obx(() {
if (controller.hasUnreadNotifications) {
return _buildUnreadCountHeader(controller);
}
return const SizedBox.shrink();
}),
Expanded(
child: Obx(
() => ListView.builder(
controller: controller.scrollController,
padding: EdgeInsets.symmetric(horizontal: 15.w),
itemCount:
controller.notifications.length +
(controller.isLoadingMore.value ? 1 : 0),
itemBuilder: (context, index) {
// Show loading indicator at the end when loading more
if (index == controller.notifications.length) {
return _buildLoadingMoreIndicator();
}
final notification = controller.notifications[index];
return _buildNotificationCard(notification, controller);
},
),
),
),
],
);
}
Widget _buildLoadingMoreIndicator() {
return Container(
padding: EdgeInsets.all(16.w),
child: Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Colors.black),
strokeWidth: 2,
),
),
);
}
Widget _buildUnreadCountHeader(NotificationController controller) {
return Container(
width: double.infinity,
padding: EdgeInsets.all(16.w),
margin: EdgeInsets.all(16.w),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Color(primaryColor),
Color(primaryColor).withValues(alpha: 0.2),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(12.r),
),
child: Row(
children: [
Container(
padding: EdgeInsets.all(7.w),
decoration: BoxDecoration(
color: Colors.black,
shape: BoxShape.circle,
),
child: Icon(
Icons.notifications,
color: Colors.white,
size: isTablet ? 25.sp : 20.sp,
),
),
SizedBox(width: 12.w),
Expanded(
child: Text(
'${controller.totalUnseenCount} unread notifications',
style: TextStyle(
fontSize: regularSizeText,
fontWeight: FontWeight.w500,
color: Colors.black,
),
),
),
],
),
);
}
Widget _buildNotificationCard(
NotificationItem notification,
NotificationController controller,
) {
return Container(
margin: EdgeInsets.only(bottom: 12.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16.r),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
border:
notification.isSeen != true
? Border.all(color: Color(primaryColor), width: 2)
: null,
),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(16.r),
onTap: () async {
if (notification.isSeen != true) {
controller.markNotificationAsRead(notification.notificationId!);
}
await Future.delayed(const Duration(milliseconds: 500));
// if (!Get.isRegistered<NotificationNavigationController>()) {
// Get.put(NotificationNavigationController());
// }
final navigationController =
Get.find<NotificationNavigationController>();
await navigationController.navigateBasedOnNotification({
"notificationType": notification.notificationType,
"dataId": notification.dataField,
});
},
child: Padding(
padding: EdgeInsets.all(16.w),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 50.w,
height: 50.w,
decoration: BoxDecoration(
color: controller
.getNotificationColor(notification.notificationType)
.withValues(alpha: 0.1),
shape: BoxShape.circle,
),
child: Icon(
controller.getNotificationIcon(
notification.notificationType,
),
color: controller.getNotificationColor(
notification.notificationType,
),
size: 24.sp,
),
),
SizedBox(width: 16.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
notification.notificationTitle ?? 'No Title',
style: TextStyle(
fontSize: regularSizeText,
fontWeight:
notification.isSeen == true
? FontWeight.w500
: FontWeight.w600,
color:
notification.isSeen == true
? Colors.grey[700]
: Colors.black,
),
maxLines: 2,
),
),
if (notification.isSeen != true)
Container(
width: 8.w,
height: 8.w,
decoration: BoxDecoration(
color: Colors.blue[600],
shape: BoxShape.circle,
),
),
],
),
SizedBox(height: 4.h),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
_formatTimeAgo(notification.createdAt),
style: TextStyle(
fontSize: smallSizeText,
color: Colors.grey[500],
),
),
],
),
],
),
),
],
),
),
),
),
);
}
String _formatTimeAgo(dynamic createdAt) {
if (createdAt == null) return 'Unknown time';
DateTime? dateTime;
// Handle different date formats
if (createdAt is String) {
try {
dateTime = DateTime.parse(createdAt);
} catch (e) {
return 'Unknown time';
}
} else if (createdAt is DateTime) {
dateTime = createdAt;
} else {
return 'Unknown time';
}
final now = DateTime.now();
final difference = now.difference(dateTime);
if (difference.inDays > 7) {
return '${(difference.inDays / 7).floor()}w ago';
} else if (difference.inDays > 0) {
return '${difference.inDays}d ago';
} else if (difference.inHours > 0) {
return '${difference.inHours}h ago';
} else if (difference.inMinutes > 0) {
return '${difference.inMinutes}m ago';
} else {
return 'Just now';
}
}
}