TIF_E41221079/lib/features/notification/notification_screen.dart

339 lines
12 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smpn1_lengkong/core/constants/app_colors.dart';
import 'package:smpn1_lengkong/core/models/student_model.dart';
import 'package:smpn1_lengkong/core/routes/app_routes.dart';
import 'package:smpn1_lengkong/core/service/student_service.dart';
import 'package:smpn1_lengkong/main.dart';
class NotificationScreen extends StatefulWidget {
const NotificationScreen({super.key});
@override
State<NotificationScreen> createState() => _NotificationScreenState();
}
class _NotificationScreenState extends State<NotificationScreen> {
late Future<List<Map<String, dynamic>>> _notificationsFuture;
late StudentModel student;
bool _isInitialized = false;
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (!_isInitialized) {
student = ModalRoute.of(context)!.settings.arguments as StudentModel;
_notificationsFuture = StudentService().getNotifications(
student.id,
student.className,
);
_isInitialized = true;
}
}
Future<void> _refreshData() async {
setState(() {
_notificationsFuture = StudentService().getNotifications(
student.id,
student.className,
);
});
await _notificationsFuture;
}
Future<void> _handleReadAll() async {
await StudentService().markAllAsRead(student.id, student.className);
await _refreshData();
if (mounted) {}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.backgroundWhite,
appBar: AppBar(
title: Text(
"Pemberitahuan Kelas ${student.className}",
style: const TextStyle(color: Colors.white),
),
backgroundColor: AppColors.primaryBlue,
foregroundColor: Colors.white,
elevation: 0,
),
body: RefreshIndicator(
onRefresh: _refreshData,
color: AppColors.primaryBlue,
child: FutureBuilder<List<Map<String, dynamic>>>(
future: _notificationsFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(
child: Text("Terjadi kesalahan: ${snapshot.error}"),
);
}
// Ambil data dan lakukan pengurutan (Sorting)
final List<Map<String, dynamic>> notifications =
snapshot.data ?? [];
if (notifications.isNotEmpty) {
notifications.sort((a, b) {
// Parsing tanggal, berikan fallback waktu sekarang jika null/error
DateTime dateA =
DateTime.tryParse(a['tanggal']?.toString() ?? '') ??
DateTime.now();
DateTime dateB =
DateTime.tryParse(b['tanggal']?.toString() ?? '') ??
DateTime.now();
// b.compareTo(a) untuk urutan descending (Terbaru -> Terlama)
return dateB.compareTo(dateA);
});
}
final int unreadCount =
notifications.where((n) => n['status'] == 'unread').length;
if (notifications.isEmpty) {
return Stack(
children: [
_buildEmptyState(),
ListView(physics: const AlwaysScrollableScrollPhysics()),
],
);
}
return CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(
parent: BouncingScrollPhysics(),
),
slivers: [
SliverToBoxAdapter(
child: Padding(
padding: EdgeInsets.fromLTRB(20.w, 24.h, 20.w, 16.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
unreadCount > 0
? "Anda memiliki $unreadCount pemberitahuan baru"
: "Semua pemberitahuan sudah diperiksa",
style: TextStyle(
fontSize: 13.sp,
color: AppColors.textSecondary,
),
),
],
),
if (unreadCount > 0)
GestureDetector(
onTap: _handleReadAll,
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 12.w,
vertical: 8.h,
),
decoration: BoxDecoration(
color: AppColors.primaryBlue.withOpacity(0.08),
borderRadius: BorderRadius.circular(10.r),
),
child: Text(
"Baca Semua",
style: TextStyle(
color: AppColors.primaryBlue,
fontWeight: FontWeight.bold,
fontSize: 12.sp,
),
),
),
),
],
),
),
),
SliverPadding(
padding: EdgeInsets.symmetric(horizontal: 20.w),
sliver: SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
final item = notifications[index];
return Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: _buildNotificationCard(context, item),
);
}, childCount: notifications.length),
),
),
SliverToBoxAdapter(child: SizedBox(height: 30.h)),
],
);
},
),
),
);
}
Widget _buildNotificationCard(
BuildContext context,
Map<String, dynamic> item,
) {
final bool isUnread = item['status'] == 'unread';
return InkWell(
onTap: () async {
if (isUnread) {
await StudentService().markNotificationAsRead(item['id']);
await _refreshData();
}
// Gunakan navigatorKey sebagai pengganti Navigator.pushNamed(context, ...)
navigatorKey.currentState?.pushNamed(
AppRoutes.notificationDetail,
arguments: {
'title': item['title'] ?? 'Pengumuman',
'body': item['body'] ?? '',
'date': item['tanggal'] ?? '',
},
);
},
borderRadius: BorderRadius.circular(16.r),
child: Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16.r),
border: Border.all(
color:
isUnread
? AppColors.primaryBlue.withOpacity(0.3)
: Colors.grey.shade100,
width: isUnread ? 1.5 : 1,
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.02),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color:
isUnread
? AppColors.primaryBlue.withOpacity(0.1)
: Colors.grey.shade50,
borderRadius: BorderRadius.circular(12.r),
),
child: Icon(
Icons.notifications_active_outlined,
color: isUnread ? AppColors.primaryBlue : Colors.grey.shade400,
size: 24.sp,
),
),
SizedBox(width: 16.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
item['title'] ?? '-',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight:
isUnread ? FontWeight.w800 : FontWeight.w600,
fontSize: 15.sp,
color: AppColors.textPrimary,
),
),
),
if (isUnread)
Container(
width: 8.w,
height: 8.w,
decoration: const BoxDecoration(
color: AppColors.dangerRed,
shape: BoxShape.circle,
),
),
],
),
SizedBox(height: 4.h),
Text(
item['body'] ?? '-',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: isUnread ? Colors.black87 : Colors.grey.shade600,
fontSize: 13.sp,
height: 1.5,
),
),
SizedBox(height: 10.h),
Row(
children: [
Icon(
Icons.access_time_rounded,
size: 12.sp,
color: Colors.grey,
),
SizedBox(width: 4.w),
Text(
item['tanggal']?.toString().length == 16
? item['tanggal']
: (item['tanggal']?.toString().substring(0, 16) ??
''),
style: TextStyle(
fontSize: 11.sp,
color: Colors.grey,
fontWeight: FontWeight.w500,
),
),
],
),
],
),
),
],
),
),
);
}
Widget _buildEmptyState() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.notifications_none_rounded,
size: 60.sp,
color: Colors.grey.shade300,
),
SizedBox(height: 16.h),
Text(
"Belum Ada Notifikasi",
style: TextStyle(color: Colors.grey.shade500),
),
],
),
);
}
}