import 'dart:io'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:ui/constans/api_constans.dart'; import 'package:ui/views/siswa/tugas/controllers/submit_tugas_controller.dart'; import 'package:ui/widgets/my_text.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:ui/widgets/my_date_format.dart'; class TugasCommit extends StatefulWidget { const TugasCommit({super.key}); @override State createState() => _TugasCommitState(); } enum SubmissionMethod { file, text } class _TugasCommitState extends State with TickerProviderStateMixin { String? fileName; File? selectedFile; SubmissionMethod _method = SubmissionMethod.file; final TextEditingController _textController = TextEditingController(); SubmitTugasController submitTugasC = Get.find(); late AnimationController _scaleAnimationController; late Animation _scaleAnimation; @override void initState() { super.initState(); _scaleAnimationController = AnimationController( duration: const Duration(milliseconds: 150), vsync: this, ); _scaleAnimation = Tween( begin: 1.0, end: 0.95, ).animate(CurvedAnimation( parent: _scaleAnimationController, curve: Curves.easeInOut, )); if (Get.arguments['submitTugas'] != null) { if (Get.arguments['submitTugas']['text'] == null) { _method = SubmissionMethod.file; fileName = Get.arguments['submitTugas']['file']; } else { _method = SubmissionMethod.text; _textController.text = Get.arguments['submitTugas']['text']; } setState(() {}); } } @override void dispose() { _scaleAnimationController.dispose(); super.dispose(); } Future _launchUrl(String url) async { final uri = Uri.parse(url); if (!await launchUrl(uri, mode: LaunchMode.externalApplication)) { throw Exception('Could not launch $url'); } } Future pickFile() async { FilePickerResult? result = await FilePicker.platform.pickFiles(); if (result != null && result.files.single.path != null) { setState(() { fileName = result.files.single.name; selectedFile = File(result.files.single.path!); }); } } void submitTask() { if (_method == SubmissionMethod.file) { if (fileName != null) { if (Get.arguments['submitTugas'] != null) { submitTugasC.updateTugas( id: Get.arguments['submitTugas']['id'], file: selectedFile, text: _textController.text, ); } else { submitTugasC.postTugas( tugasId: Get.arguments['id'].toString(), text: _textController.text, file: selectedFile, ); } } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("Silakan tambahkan file terlebih dahulu")), ); } } else { if (_textController.text.trim().isNotEmpty) { if (Get.arguments['submitTugas'] != null) { submitTugasC.updateTugas( id: Get.arguments['submitTugas']['id'], file: selectedFile, text: _textController.text, ); } else { submitTugasC.postTugas( tugasId: Get.arguments['id'].toString(), text: _textController.text, file: selectedFile, ); } } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("Silakan tulis tugas terlebih dahulu")), ); } } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF8FAFC), appBar: AppBar( title: const Text( "Pengumpulan Tugas", style: TextStyle( fontWeight: FontWeight.w700, fontSize: 20, color: Colors.white, fontFamily: 'Poppins', ), ), backgroundColor: Colors.transparent, elevation: 0, flexibleSpace: Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Color(0xFF667EEA), Color(0xFF764BA2), ], ), ), ), centerTitle: true, leading: IconButton( icon: const Icon(Icons.arrow_back_ios, color: Colors.white), onPressed: () => Get.back(), ), ), body: SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header Soal Container( width: double.infinity, padding: const EdgeInsets.all(24), decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Color(0xFF6366F1), Color(0xFF8B5CF6), ], ), borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: const Color(0xFF6366F1).withOpacity(0.3), blurRadius: 20, offset: const Offset(0, 10), ), ], ), child: Column( children: [ Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(15), ), child: const Icon( Icons.assignment, size: 32, color: Colors.white, ), ), const SizedBox(height: 16), const Text( "Soal Tugas", style: TextStyle( fontWeight: FontWeight.w600, fontSize: 16, color: Colors.white, fontFamily: 'Poppins', ), ), const SizedBox(height: 8), Text( Get.arguments['title'], textAlign: TextAlign.center, style: const TextStyle( fontWeight: FontWeight.w500, fontSize: 14, color: Colors.white, fontFamily: 'Poppins', ), ), const SizedBox(height: 8), Text( Get.arguments['deskripsi'] ?? 'Tidak ada deskripsi tugas.', textAlign: TextAlign.center, style: const TextStyle( fontWeight: FontWeight.w400, fontSize: 12, color: Colors.white70, fontFamily: 'Poppins', fontStyle: FontStyle.italic, ), ), ], ), ), if (Get.arguments['submitTugas'] != null && Get.arguments['submitTugas']['created_at'] != null) Padding( padding: const EdgeInsets.only(top: 8.0, bottom: 8.0), child: Row( children: [ const Icon(Icons.access_time, size: 16, color: Colors.grey), const SizedBox(width: 4), Text( DateTime.parse(Get.arguments['submitTugas']['created_at']) .fullDateTime(), style: const TextStyle(fontSize: 12, color: Colors.grey), ), ], ), ), const SizedBox(height: 24), // Pilihan Metode Pengumpulan 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: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: const Color(0xFF6366F1).withOpacity(0.1), borderRadius: BorderRadius.circular(10), ), child: const Icon( Icons.upload_file, color: Color(0xFF6366F1), size: 20, ), ), const SizedBox(width: 12), const Text( "Metode Pengumpulan", style: TextStyle( fontWeight: FontWeight.w600, fontSize: 16, color: Colors.black87, fontFamily: 'Poppins', ), ), ], ), const SizedBox(height: 16), Row( children: [ Expanded( child: GestureDetector( onTap: () { setState(() { _method = SubmissionMethod.file; }); }, child: AnimatedContainer( duration: const Duration(milliseconds: 200), padding: const EdgeInsets.symmetric( vertical: 16, horizontal: 12), decoration: BoxDecoration( gradient: _method == SubmissionMethod.file ? const LinearGradient( colors: [ Color(0xFF6366F1), Color(0xFF8B5CF6) ], begin: Alignment.topLeft, end: Alignment.bottomRight, ) : null, color: _method == SubmissionMethod.file ? null : Colors.grey.shade100, borderRadius: BorderRadius.circular(15), border: Border.all( color: _method == SubmissionMethod.file ? Colors.transparent : Colors.grey.shade300, width: 1, ), ), child: Column( children: [ Icon( Icons.file_upload, color: _method == SubmissionMethod.file ? Colors.white : Colors.grey.shade600, size: 24, ), const SizedBox(height: 8), Text( "Upload File", style: TextStyle( fontWeight: FontWeight.w600, fontSize: 14, color: _method == SubmissionMethod.file ? Colors.white : Colors.grey.shade700, fontFamily: 'Poppins', ), ), ], ), ), ), ), const SizedBox(width: 12), Expanded( child: GestureDetector( onTap: () { setState(() { _method = SubmissionMethod.text; }); }, child: AnimatedContainer( duration: const Duration(milliseconds: 200), padding: const EdgeInsets.symmetric( vertical: 16, horizontal: 12), decoration: BoxDecoration( gradient: _method == SubmissionMethod.text ? const LinearGradient( colors: [ Color(0xFF10B981), Color(0xFF059669) ], begin: Alignment.topLeft, end: Alignment.bottomRight, ) : null, color: _method == SubmissionMethod.text ? null : Colors.grey.shade100, borderRadius: BorderRadius.circular(15), border: Border.all( color: _method == SubmissionMethod.text ? Colors.transparent : Colors.grey.shade300, width: 1, ), ), child: Column( children: [ Icon( Icons.edit_note, color: _method == SubmissionMethod.text ? Colors.white : Colors.grey.shade600, size: 24, ), const SizedBox(height: 8), Text( "Tulis Tugas", style: TextStyle( fontWeight: FontWeight.w600, fontSize: 14, color: _method == SubmissionMethod.text ? Colors.white : Colors.grey.shade700, fontFamily: 'Poppins', ), ), ], ), ), ), ), ], ), ], ), ), const SizedBox(height: 20), // Area Input if (_method == SubmissionMethod.file) GestureDetector( onTap: pickFile, onTapDown: (_) => _scaleAnimationController.forward(), onTapUp: (_) => _scaleAnimationController.reverse(), onTapCancel: () => _scaleAnimationController.reverse(), child: AnimatedBuilder( animation: _scaleAnimation, builder: (context, child) { return Transform.scale( scale: _scaleAnimation.value, child: Container( width: double.infinity, padding: const EdgeInsets.all(32), decoration: BoxDecoration( gradient: LinearGradient( colors: [ Colors.blue.shade50, Colors.blue.shade100, ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(20), border: Border.all( color: Colors.blue.shade200, width: 2, style: BorderStyle.solid, ), ), child: Column( children: [ Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.blue.shade500, borderRadius: BorderRadius.circular(15), ), child: const Icon( Icons.cloud_upload, size: 32, color: Colors.white, ), ), const SizedBox(height: 16), Text( fileName ?? "Pilih File", style: TextStyle( fontWeight: FontWeight.w600, fontSize: 16, color: fileName != null ? Colors.blue.shade700 : Colors.blue.shade600, fontFamily: 'Poppins', ), ), const SizedBox(height: 8), Text( fileName != null ? "File siap diupload" : "Tap untuk memilih file", style: TextStyle( fontSize: 14, color: Colors.blue.shade500, fontFamily: 'Poppins', ), ), ], ), ), ); }, ), ) else 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: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( gradient: LinearGradient( colors: [ Colors.green.shade50, Colors.green.shade100, ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: const BorderRadius.only( topLeft: Radius.circular(20), topRight: Radius.circular(20), ), ), child: Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.green.shade500, borderRadius: BorderRadius.circular(10), ), child: const Icon( Icons.edit_note, color: Colors.white, size: 20, ), ), const SizedBox(width: 12), const Text( "Tulis Jawaban Tugas", style: TextStyle( fontWeight: FontWeight.w600, fontSize: 16, color: Colors.black87, fontFamily: 'Poppins', ), ), ], ), ), Padding( padding: const EdgeInsets.all(20), child: TextField( controller: _textController, maxLines: 12, style: const TextStyle( fontSize: 14, fontFamily: 'Poppins', ), decoration: InputDecoration( hintText: "Tulis jawaban tugas Anda di sini...", hintStyle: TextStyle( color: Colors.grey.shade400, fontFamily: 'Poppins', ), filled: true, fillColor: Colors.grey.shade50, border: OutlineInputBorder( borderRadius: BorderRadius.circular(15), borderSide: BorderSide.none, ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(15), borderSide: BorderSide( color: Colors.green.shade300, width: 2, ), ), ), ), ), ], ), ), // File yang sudah diupload (untuk tugas selesai) if (Get.arguments['tipe_tugas'] == "selesai" && Get.arguments['submitTugas']['text'] == null) Container( margin: const EdgeInsets.only(top: 20), child: GestureDetector( onTap: () { _launchUrl( "${ApiConstants.baseUrl}/storage/${Get.arguments['submitTugas']['file']}"); }, child: Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( gradient: LinearGradient( colors: [ Colors.orange.shade50, Colors.orange.shade100, ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(20), border: Border.all( color: Colors.orange.shade200, width: 1, ), ), child: Row( children: [ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.orange.shade500, borderRadius: BorderRadius.circular(12), ), child: const Icon( Icons.file_present, color: Colors.white, size: 24, ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( "File yang Sudah Diupload", style: TextStyle( fontWeight: FontWeight.w600, fontSize: 14, color: Colors.black87, fontFamily: 'Poppins', ), ), const SizedBox(height: 4), Text( "Tap untuk melihat file", style: TextStyle( fontSize: 12, color: Colors.orange.shade600, fontFamily: 'Poppins', ), ), ], ), ), Icon( Icons.arrow_forward_ios, color: Colors.orange.shade600, size: 16, ), ], ), ), ), ), const SizedBox(height: 30), // Tombol Submit Obx(() { return GestureDetector( onTap: submitTugasC.isLoading.value ? null : submitTask, onTapDown: (_) { if (!submitTugasC.isLoading.value) { _scaleAnimationController.forward(); } }, onTapUp: (_) { if (!submitTugasC.isLoading.value) { _scaleAnimationController.reverse(); } }, onTapCancel: () { if (!submitTugasC.isLoading.value) { _scaleAnimationController.reverse(); } }, child: AnimatedBuilder( animation: _scaleAnimation, builder: (context, child) { return Transform.scale( scale: _scaleAnimation.value, child: Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 18), decoration: BoxDecoration( gradient: submitTugasC.isLoading.value ? null : const LinearGradient( colors: [ Color(0xFF10B981), Color(0xFF059669) ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), color: submitTugasC.isLoading.value ? Colors.grey.shade300 : null, borderRadius: BorderRadius.circular(20), boxShadow: submitTugasC.isLoading.value ? null : [ BoxShadow( color: const Color(0xFF10B981) .withOpacity(0.3), blurRadius: 20, offset: const Offset(0, 10), ), ], ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ if (submitTugasC.isLoading.value) const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( Colors.grey, ), ), ) else Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(10), ), child: const Icon( Icons.send, color: Colors.white, size: 20, ), ), const SizedBox(width: 12), Text( submitTugasC.isLoading.value ? "Menyimpan..." : "Kirim Tugas", style: TextStyle( fontWeight: FontWeight.w600, fontSize: 16, color: submitTugasC.isLoading.value ? Colors.grey.shade600 : Colors.white, fontFamily: 'Poppins', ), ), ], ), ), ); }, ), ); }), ], ), ), ); } }