303 lines
9.1 KiB
Dart
303 lines
9.1 KiB
Dart
// lib/features/home/tabs/profile_tab.dart
|
|
|
|
import 'package:cached_network_image/cached_network_image.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/features/home/widgets/profile_class_card.dart';
|
|
|
|
class ProfileTab extends StatelessWidget {
|
|
final StudentModel student;
|
|
final String currentViewingClass;
|
|
final Function(String, String) onClassTap;
|
|
final Future<void> Function() onRefresh; // Callback untuk refresh data dari parent
|
|
|
|
const ProfileTab({
|
|
super.key,
|
|
required this.student,
|
|
required this.currentViewingClass,
|
|
required this.onClassTap,
|
|
required this.onRefresh,
|
|
});
|
|
|
|
// Fungsi untuk menampilkan dialog konfirmasi sebelum pindah kelas
|
|
void _showSelectionDialog(
|
|
BuildContext context,
|
|
String className,
|
|
String academicYear,
|
|
) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(16.r),
|
|
),
|
|
title: const Text("Ganti Tampilan Kelas"),
|
|
content: Text(
|
|
"Apakah Anda ingin melihat riwayat data untuk Kelas $className (TA $academicYear)?",
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: Text("Batal", style: TextStyle(color: Colors.grey[600])),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.pop(context); // Tutup dialog
|
|
onClassTap(className, academicYear); // Jalankan perpindahan data
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: AppColors.primaryBlue,
|
|
foregroundColor: Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8.r),
|
|
),
|
|
),
|
|
child: const Text("Ya, Lihat"),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: AppColors.backgroundWhite,
|
|
// RefreshIndicator membungkus CustomScrollView
|
|
body: RefreshIndicator(
|
|
onRefresh: onRefresh,
|
|
color: AppColors.primaryBlue,
|
|
edgeOffset: 100, // Mengatur posisi munculnya loading spinner agar tidak tertutup AppBar
|
|
child: CustomScrollView(
|
|
// AlwaysScrollable wajib agar fitur refresh aktif walau konten sedikit
|
|
physics: const AlwaysScrollableScrollPhysics(
|
|
parent: BouncingScrollPhysics(),
|
|
),
|
|
slivers: [
|
|
// HEADER DENGAN BACKGROUND BIRU
|
|
SliverAppBar(
|
|
expandedHeight: 180.h,
|
|
pinned: true,
|
|
automaticallyImplyLeading: false,
|
|
backgroundColor: AppColors.primaryBlue,
|
|
flexibleSpace: FlexibleSpaceBar(
|
|
background: Container(
|
|
decoration: const BoxDecoration(
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [AppColors.primaryBlue, Color(0xFF2C64B8)],
|
|
),
|
|
),
|
|
child: Stack(
|
|
children: [
|
|
Positioned(
|
|
right: -20,
|
|
top: -20,
|
|
child: Icon(
|
|
Icons.person,
|
|
size: 150.sp,
|
|
color: Colors.white.withOpacity(0.1),
|
|
),
|
|
),
|
|
Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [SizedBox(height: 20.h), _buildAvatar()],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// CONTENT SECTION
|
|
SliverToBoxAdapter(
|
|
child: Padding(
|
|
padding: EdgeInsets.all(24.w),
|
|
child: Column(
|
|
children: [
|
|
_buildNameSection(),
|
|
SizedBox(height: 32.h),
|
|
_buildDetailInfoSection(),
|
|
SizedBox(height: 32.h),
|
|
_buildHistorySection(context),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildAvatar() {
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
border: Border.all(color: Colors.white, width: 4),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.2),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 5),
|
|
),
|
|
],
|
|
),
|
|
child: CachedNetworkImage(
|
|
imageUrl: student.photoUrl,
|
|
imageBuilder: (context, imageProvider) =>
|
|
CircleAvatar(radius: 50.r, backgroundImage: imageProvider),
|
|
placeholder: (context, url) => CircleAvatar(
|
|
radius: 50.r,
|
|
backgroundColor: Colors.white,
|
|
child: const CircularProgressIndicator(),
|
|
),
|
|
errorWidget: (context, url, error) => CircleAvatar(
|
|
radius: 50.r,
|
|
backgroundColor: Colors.white,
|
|
child: Icon(
|
|
Icons.person,
|
|
size: 50.sp,
|
|
color: AppColors.primaryBlue,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildNameSection() {
|
|
return Column(
|
|
children: [
|
|
Text(
|
|
student.name,
|
|
style: TextStyle(fontSize: 22.sp, fontWeight: FontWeight.bold),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
SizedBox(height: 4.h),
|
|
Text(
|
|
"NISN: ${student.nisn}",
|
|
style: TextStyle(
|
|
color: Colors.grey,
|
|
fontSize: 14.sp,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildDetailInfoSection() {
|
|
String genderFull = student.gender == "L"
|
|
? "Laki-laki"
|
|
: student.gender == "P"
|
|
? "Perempuan"
|
|
: "-";
|
|
|
|
return Container(
|
|
padding: EdgeInsets.all(20.w),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(16.r),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.03),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
children: [
|
|
_infoRow("Kelas Aktif", student.className),
|
|
_infoRow("Jenis Kelamin", genderFull),
|
|
_infoRow("Status Siswa", student.statusSiswa, isStatus: true),
|
|
_infoRow("Angkatan", student.angkatan),
|
|
_infoRow("Tahun Masuk", student.tahunMasuk, isLast: true),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildHistorySection(BuildContext context) {
|
|
final sortedHistory = List.from(student.history);
|
|
sortedHistory.sort((a, b) {
|
|
if (a.className == student.className) return -1;
|
|
if (b.className == student.className) return 1;
|
|
return b.academicYear.compareTo(a.academicYear);
|
|
});
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Padding(
|
|
padding: EdgeInsets.only(left: 4.w),
|
|
child: Text(
|
|
"RIWAYAT BELAJAR",
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.w800,
|
|
fontSize: 11.sp,
|
|
color: AppColors.textSecondary,
|
|
letterSpacing: 1.2,
|
|
),
|
|
),
|
|
),
|
|
SizedBox(height: 16.h),
|
|
...sortedHistory.map(
|
|
(h) => ProfileClassCard(
|
|
className: h.className,
|
|
academicYear: h.academicYear,
|
|
isSelected: currentViewingClass == h.className,
|
|
isCurrent: h.className == student.className,
|
|
onTap: () {
|
|
if (currentViewingClass != h.className) {
|
|
_showSelectionDialog(context, h.className, h.academicYear);
|
|
}
|
|
},
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _infoRow(
|
|
String label,
|
|
String val, {
|
|
bool isStatus = false,
|
|
bool isLast = false,
|
|
}) {
|
|
return Padding(
|
|
padding: EdgeInsets.only(bottom: isLast ? 0 : 16.h),
|
|
child: Column(
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
label,
|
|
style: TextStyle(color: Colors.grey[600], fontSize: 13.sp),
|
|
),
|
|
Text(
|
|
val,
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 14.sp,
|
|
color:
|
|
isStatus ? AppColors.successGreen : AppColors.textPrimary,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
if (!isLast) ...[
|
|
SizedBox(height: 12.h),
|
|
Divider(color: Colors.grey.shade100, height: 1),
|
|
],
|
|
],
|
|
),
|
|
);
|
|
}
|
|
} |