389 lines
12 KiB
Dart
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';
|
|
}
|
|
}
|
|
}
|