709 lines
23 KiB
Dart
709 lines
23 KiB
Dart
// ignore_for_file: must_be_immutable
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:ui/routes/app_routes.dart';
|
|
import 'package:ui/views/siswa/controllers/siswa_controller.dart';
|
|
|
|
class ProfileSiswa extends StatelessWidget {
|
|
ProfileSiswa({super.key});
|
|
SiswaController siswaC = Get.find<SiswaController>();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
siswaC.getAnalysis();
|
|
final size = MediaQuery.of(context).size;
|
|
final isTablet = size.width > 600;
|
|
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFFF8FAFC),
|
|
appBar: AppBar(
|
|
title: const Text(
|
|
'Profil Siswa',
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.w700,
|
|
fontSize: 20,
|
|
color: Colors.white,
|
|
fontFamily: 'Poppins',
|
|
),
|
|
),
|
|
centerTitle: true,
|
|
elevation: 0,
|
|
backgroundColor: Colors.transparent,
|
|
flexibleSpace: Container(
|
|
decoration: const BoxDecoration(
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [
|
|
Color(0xFF667EEA),
|
|
Color(0xFF764BA2),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
leading: IconButton(
|
|
icon: const Icon(Icons.arrow_back_ios, color: Colors.white),
|
|
onPressed: () => Get.back(),
|
|
),
|
|
),
|
|
body: RefreshIndicator(
|
|
onRefresh: () async {
|
|
await siswaC.getAnalysis();
|
|
},
|
|
child: SingleChildScrollView(
|
|
physics: const AlwaysScrollableScrollPhysics(),
|
|
child: Column(
|
|
children: [
|
|
// Header profil dengan gradient
|
|
_buildProfileHeader(isTablet),
|
|
|
|
// Konten utama
|
|
Padding(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: isTablet ? size.width * 0.1 : 20, vertical: 20),
|
|
child: Column(
|
|
children: [
|
|
// Card untuk konten analisis
|
|
_buildAnalysisCard(context, isTablet),
|
|
|
|
SizedBox(height: isTablet ? 32 : 24),
|
|
|
|
// Tombol-tombol aksi
|
|
_buildActionButtons(context, isTablet),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildProfileHeader(bool isTablet) {
|
|
return Container(
|
|
padding: EdgeInsets.symmetric(
|
|
vertical: isTablet ? 40 : 30, horizontal: isTablet ? 40 : 20),
|
|
decoration: const BoxDecoration(
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [
|
|
Color(0xFF667EEA),
|
|
Color(0xFF764BA2),
|
|
],
|
|
),
|
|
borderRadius: BorderRadius.only(
|
|
bottomLeft: Radius.circular(30),
|
|
bottomRight: Radius.circular(30),
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Color(0xFF667EEA),
|
|
blurRadius: 20,
|
|
offset: Offset(0, 10),
|
|
),
|
|
],
|
|
),
|
|
child: Obx(
|
|
() => Column(
|
|
children: [
|
|
// Avatar dengan animasi
|
|
Container(
|
|
padding: const EdgeInsets.all(4),
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
border: Border.all(
|
|
color: Colors.white,
|
|
width: 3,
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.2),
|
|
blurRadius: 15,
|
|
offset: const Offset(0, 8),
|
|
),
|
|
],
|
|
),
|
|
child: CircleAvatar(
|
|
radius: isTablet ? 70 : 60,
|
|
backgroundColor: Colors.white,
|
|
child: Icon(
|
|
Icons.person,
|
|
size: isTablet ? 70 : 60,
|
|
color: const Color(0xFF667EEA),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 20),
|
|
Text(
|
|
siswaC.dataUser['user']['nama'],
|
|
style: TextStyle(
|
|
fontSize: isTablet ? 28 : 24,
|
|
fontWeight: FontWeight.w700,
|
|
color: Colors.white,
|
|
fontFamily: 'Poppins',
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 8),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.2),
|
|
borderRadius: BorderRadius.circular(20),
|
|
border: Border.all(
|
|
color: Colors.white.withOpacity(0.3),
|
|
width: 1,
|
|
),
|
|
),
|
|
child: Text(
|
|
'Kelas ${siswaC.dataUser['user']['kelas']}',
|
|
style: TextStyle(
|
|
fontSize: isTablet ? 18 : 16,
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.w600,
|
|
fontFamily: 'Poppins',
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.2),
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: const Icon(
|
|
Icons.email_outlined,
|
|
color: Colors.white,
|
|
size: 18,
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Text(
|
|
siswaC.dataUser['user']['user']['email'].toString(),
|
|
style: TextStyle(
|
|
fontSize: isTablet ? 16 : 14,
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.w500,
|
|
fontFamily: 'Poppins',
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildAnalysisCard(BuildContext context, bool isTablet) {
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(20),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.grey.withOpacity(0.1),
|
|
blurRadius: 20,
|
|
offset: const Offset(0, 10),
|
|
),
|
|
],
|
|
),
|
|
child: Padding(
|
|
padding: EdgeInsets.all(isTablet ? 24 : 20),
|
|
child: Obx(
|
|
() {
|
|
if (siswaC.isLoadingAnalysis.value) {
|
|
return Container(
|
|
height: 200,
|
|
alignment: Alignment.center,
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(20),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(20),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.grey.withOpacity(0.1),
|
|
blurRadius: 20,
|
|
offset: const Offset(0, 10),
|
|
),
|
|
],
|
|
),
|
|
child: const CircularProgressIndicator(
|
|
valueColor: AlwaysStoppedAnimation<Color>(
|
|
Color(0xFF6366F1),
|
|
),
|
|
strokeWidth: 3,
|
|
),
|
|
),
|
|
const SizedBox(height: 20),
|
|
const Text(
|
|
'Memuat data analisis...',
|
|
style: TextStyle(
|
|
color: Colors.grey,
|
|
fontSize: 16,
|
|
fontFamily: 'Poppins',
|
|
),
|
|
)
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
var kelebihan = siswaC.dataAnalysis['kelebihan'];
|
|
var kekurangan = siswaC.dataAnalysis['kekurangan'];
|
|
|
|
return Column(
|
|
children: [
|
|
if (siswaC.kelebihanIsEmpty.value)
|
|
const SizedBox()
|
|
else
|
|
Column(children: [
|
|
Row(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF10B981).withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: const Icon(
|
|
Icons.quiz,
|
|
color: Color(0xFF10B981),
|
|
size: 24,
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
const Text(
|
|
'Rata rata Kuis',
|
|
style: TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.black87,
|
|
fontFamily: 'Poppins',
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 16),
|
|
ListView.builder(
|
|
shrinkWrap: true,
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
itemCount: siswaC.dataAnalysis['kelebihan'].length,
|
|
itemBuilder: (context, index) {
|
|
return AnimatedProgressItem(
|
|
title: kelebihan[index]['mapel'],
|
|
percentage: kelebihan[index]['persentase'],
|
|
color: const Color(0xFF10B981),
|
|
isTablet: isTablet,
|
|
animationDelay: (index * 200),
|
|
);
|
|
},
|
|
),
|
|
]),
|
|
if (siswaC.kekuranganIsEmpty.value)
|
|
const SizedBox()
|
|
else
|
|
Column(children: [
|
|
const SizedBox(height: 24),
|
|
Row(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFEF4444).withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: const Icon(
|
|
Icons.trending_down,
|
|
color: Color(0xFFEF4444),
|
|
size: 24,
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
const Text(
|
|
'Perlu Perbaikan',
|
|
style: TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.black87,
|
|
fontFamily: 'Poppins',
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 16),
|
|
ListView.builder(
|
|
shrinkWrap: true,
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
itemCount: siswaC.dataAnalysis['kekurangan'].length,
|
|
itemBuilder: (context, index) {
|
|
return AnimatedProgressItem(
|
|
title: kekurangan[index]['mapel'],
|
|
percentage: kekurangan[index]['persentase'],
|
|
color: const Color(0xFFEF4444),
|
|
isTablet: isTablet,
|
|
animationDelay: (index * 200) + 500,
|
|
);
|
|
},
|
|
),
|
|
]),
|
|
if (siswaC.kelebihanIsEmpty.value &&
|
|
siswaC.kekuranganIsEmpty.value)
|
|
Container(
|
|
padding: const EdgeInsets.all(40),
|
|
child: Column(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(20),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF6366F1).withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: const Icon(
|
|
Icons.analytics_outlined,
|
|
size: 60,
|
|
color: Color(0xFF6366F1),
|
|
),
|
|
),
|
|
const SizedBox(height: 20),
|
|
const Text(
|
|
'Belum ada data analisis',
|
|
style: TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.grey,
|
|
fontFamily: 'Poppins',
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
const Text(
|
|
'Data akan muncul setelah mengerjakan kuis',
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
color: Colors.grey,
|
|
fontFamily: 'Poppins',
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildActionButtons(BuildContext context, bool isTablet) {
|
|
return Column(
|
|
children: [
|
|
// Tombol Ubah Password
|
|
GestureDetector(
|
|
onTap: () {
|
|
Get.toNamed(AppRoutes.ubahPassord);
|
|
},
|
|
child: Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.symmetric(vertical: 18),
|
|
decoration: BoxDecoration(
|
|
gradient: const LinearGradient(
|
|
colors: [Color(0xFF6366F1), Color(0xFF8B5CF6)],
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
),
|
|
borderRadius: BorderRadius.circular(20),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: const Color(0xFF6366F1).withOpacity(0.3),
|
|
blurRadius: 20,
|
|
offset: const Offset(0, 10),
|
|
),
|
|
],
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.2),
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: const Icon(
|
|
Icons.lock_reset,
|
|
color: Colors.white,
|
|
size: 20,
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Text(
|
|
'Ubah Password',
|
|
style: TextStyle(
|
|
fontSize: isTablet ? 18 : 16,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.white,
|
|
fontFamily: 'Poppins',
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
|
|
SizedBox(height: isTablet ? 20 : 16),
|
|
|
|
// Tombol Logout
|
|
GestureDetector(
|
|
onTap: () {
|
|
showDialog(
|
|
context: context,
|
|
builder: (ctx) => AlertDialog(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
title: const Text(
|
|
'Konfirmasi Logout',
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.w600,
|
|
fontFamily: 'Poppins',
|
|
),
|
|
),
|
|
content: const Text(
|
|
'Apakah Anda yakin ingin logout?',
|
|
style: TextStyle(
|
|
fontFamily: 'Poppins',
|
|
),
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () {
|
|
Navigator.of(ctx).pop();
|
|
},
|
|
child: const Text(
|
|
'Batal',
|
|
style: TextStyle(
|
|
color: Colors.grey,
|
|
fontFamily: 'Poppins',
|
|
),
|
|
),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
siswaC.logout(role: "siswa");
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: const Color(0xFFEF4444),
|
|
foregroundColor: Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(15),
|
|
),
|
|
),
|
|
child: const Text(
|
|
'Logout',
|
|
style: TextStyle(
|
|
fontFamily: 'Poppins',
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
child: Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.symmetric(vertical: 18),
|
|
decoration: BoxDecoration(
|
|
gradient: const LinearGradient(
|
|
colors: [Color(0xFFEF4444), Color(0xFFDC2626)],
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
),
|
|
borderRadius: BorderRadius.circular(20),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: const Color(0xFFEF4444).withOpacity(0.3),
|
|
blurRadius: 20,
|
|
offset: const Offset(0, 10),
|
|
),
|
|
],
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.2),
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: const Icon(
|
|
Icons.logout,
|
|
color: Colors.white,
|
|
size: 20,
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Text(
|
|
'Logout',
|
|
style: TextStyle(
|
|
fontSize: isTablet ? 18 : 16,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.white,
|
|
fontFamily: 'Poppins',
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
// Widget untuk progress item dengan animasi
|
|
class AnimatedProgressItem extends StatefulWidget {
|
|
final String title;
|
|
final int percentage;
|
|
final Color color;
|
|
final bool isTablet;
|
|
final int animationDelay;
|
|
|
|
const AnimatedProgressItem({
|
|
super.key,
|
|
required this.title,
|
|
required this.percentage,
|
|
required this.color,
|
|
required this.isTablet,
|
|
this.animationDelay = 0,
|
|
});
|
|
|
|
@override
|
|
State<AnimatedProgressItem> createState() => _AnimatedProgressItemState();
|
|
}
|
|
|
|
class _AnimatedProgressItemState extends State<AnimatedProgressItem>
|
|
with SingleTickerProviderStateMixin {
|
|
late AnimationController _controller;
|
|
late Animation<double> _animation;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_controller = AnimationController(
|
|
duration: const Duration(milliseconds: 1000),
|
|
vsync: this,
|
|
);
|
|
_animation = Tween<double>(begin: 0, end: widget.percentage / 100)
|
|
.animate(CurvedAnimation(
|
|
parent: _controller,
|
|
curve: Curves.easeOutQuart,
|
|
));
|
|
|
|
// Tunda animasi sesuai dengan delay yang diberikan
|
|
Future.delayed(Duration(milliseconds: widget.animationDelay), () {
|
|
if (mounted) {
|
|
_controller.forward();
|
|
}
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_controller.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final textStyle = TextStyle(
|
|
fontSize: widget.isTablet ? 16 : 14,
|
|
fontWeight: FontWeight.w500,
|
|
color: Colors.black87,
|
|
fontFamily: 'Poppins',
|
|
);
|
|
|
|
return Container(
|
|
margin: const EdgeInsets.only(bottom: 16),
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: Colors.grey.shade50,
|
|
borderRadius: BorderRadius.circular(15),
|
|
border: Border.all(
|
|
color: widget.color.withOpacity(0.1),
|
|
width: 1,
|
|
),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
widget.title,
|
|
style: textStyle.copyWith(
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
),
|
|
AnimatedBuilder(
|
|
animation: _animation,
|
|
builder: (context, child) {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 12,
|
|
vertical: 6,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: widget.color.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: Text(
|
|
'${(_animation.value * 100).toInt()}%',
|
|
style: textStyle.copyWith(
|
|
color: widget.color,
|
|
fontWeight: FontWeight.w700,
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 12),
|
|
AnimatedBuilder(
|
|
animation: _animation,
|
|
builder: (context, child) {
|
|
return ClipRRect(
|
|
borderRadius: BorderRadius.circular(10),
|
|
child: LinearProgressIndicator(
|
|
value: _animation.value,
|
|
backgroundColor: widget.color.withOpacity(0.1),
|
|
color: widget.color,
|
|
minHeight: widget.isTablet ? 12 : 10,
|
|
),
|
|
);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|