import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart' as pw; import 'package:printing/printing.dart'; import 'package:http/http.dart' as http; class ImageResultScreen extends StatelessWidget { const ImageResultScreen({super.key}); // ============================================================ // GENERATE PDF // ============================================================ Future _exportPDF( BuildContext context, Map args) async { // Tampilkan loading showDialog( context: context, barrierDismissible: false, builder: (_) => const Center( child: CircularProgressIndicator(color: Colors.green), ), ); try { final pdf = pw.Document(); // Download foto dari URL untuk dimasukkan ke PDF pw.MemoryImage? fotoImage; final urlFoto = args['url_foto']?.toString() ?? ''; if (urlFoto.isNotEmpty) { try { final response = await http.get(Uri.parse(urlFoto)); if (response.statusCode == 200) { fotoImage = pw.MemoryImage(response.bodyBytes); } } catch (_) { // Foto gagal didownload, lanjut tanpa foto } } // Buat halaman PDF pdf.addPage( pw.Page( pageFormat: PdfPageFormat.a4, margin: const pw.EdgeInsets.all(32), build: (pw.Context context) { return pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, children: [ // ── Header ── pw.Container( width: double.infinity, padding: const pw.EdgeInsets.all(16), decoration: pw.BoxDecoration( color: PdfColors.green800, borderRadius: pw.BorderRadius.circular(8), ), child: pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, children: [ pw.Text( 'Dataset Pengeringan Biji Kopi', style: pw.TextStyle( color: PdfColors.white, fontSize: 18, fontWeight: pw.FontWeight.bold, ), ), pw.SizedBox(height: 4), pw.Text( 'Sistem Monitoring Otomatis', style: const pw.TextStyle( color: PdfColors.white, fontSize: 12, ), ), ], ), ), pw.SizedBox(height: 20), // ── Info Waktu ── pw.Text( 'Tanggal & Waktu: ${args['date']} - ${args['time']}', style: pw.TextStyle( fontSize: 13, fontWeight: pw.FontWeight.bold, ), ), pw.SizedBox(height: 16), pw.Divider(), pw.SizedBox(height: 16), // ── Foto ── if (fotoImage != null) ...[ pw.Center( child: pw.Container( height: 180, width: double.infinity, decoration: pw.BoxDecoration( borderRadius: pw.BorderRadius.circular(8), border: pw.Border.all(color: PdfColors.grey300), ), child: pw.ClipRRect( horizontalRadius: 8, verticalRadius: 8, child: pw.Image( fotoImage, fit: pw.BoxFit.contain, height: 180, width: double.infinity, ), ), ), ), pw.SizedBox(height: 16), pw.Divider(), pw.SizedBox(height: 16), ], // ── Data Sensor ── pw.Text( 'Data Sensor', style: pw.TextStyle( fontSize: 14, fontWeight: pw.FontWeight.bold, ), ), pw.SizedBox(height: 10), pw.Table( border: pw.TableBorder.all(color: PdfColors.grey300), columnWidths: { 0: const pw.FlexColumnWidth(1), 1: const pw.FlexColumnWidth(2), }, children: [ _pdfRow('Suhu', args['temp']), _pdfRow('Kelembapan', args['humidity']), _pdfRow('Intensitas Cahaya', args['light']), _pdfRow('Fan Intake (Kipas 1)', args['intake']), _pdfRow('Fan Exhaust (Kipas 2)', args['exhaust']), ], ), pw.SizedBox(height: 24), pw.Divider(), pw.SizedBox(height: 8), // ── Footer ── pw.Text( 'Diekspor dari Aplikasi Monitoring Kopi', style: const pw.TextStyle( color: PdfColors.grey, fontSize: 10, ), ), ], ); }, ), ); // Tutup loading if (context.mounted) Navigator.pop(context); // Tampilkan preview PDF + opsi simpan/share await Printing.layoutPdf( onLayout: (PdfPageFormat format) async => pdf.save(), name: 'dataset_kopi_${args['date']?.replaceAll('/', '-')}_${args['time']?.replaceAll(':', '-')}.pdf', ); } catch (e) { if (context.mounted) { Navigator.pop(context); // tutup loading ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Gagal membuat PDF: $e'), backgroundColor: Colors.red.shade700, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), margin: const EdgeInsets.all(16), ), ); } } } pw.TableRow _pdfRow(String label, String? value) { return pw.TableRow( children: [ pw.Padding( padding: const pw.EdgeInsets.all(8), child: pw.Text( label, style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 11), ), ), pw.Padding( padding: const pw.EdgeInsets.all(8), child: pw.Text(value ?? '-', style: const pw.TextStyle(fontSize: 11)), ), ], ); } // ============================================================ // BUILD UI // ============================================================ @override Widget build(BuildContext context) { final args = ModalRoute.of(context)?.settings.arguments as Map? ?? { 'id': '1', 'date': '2025/11/18', 'time': '14:35:00', 'temp': '45.5 °C', 'humidity': '52 %', 'light': '200 Lux', 'intake': 'ON', 'exhaust': 'ON', 'url_foto': '', }; return Scaffold( backgroundColor: Colors.black, appBar: AppBar( backgroundColor: Colors.black, title: const Text( 'Detail Data Citra', style: TextStyle(color: Colors.white), ), leading: IconButton( icon: const Icon(Icons.arrow_back, color: Colors.grey), onPressed: () => Navigator.pop(context), ), // Icon download dihapus ), body: Padding( padding: const EdgeInsets.all(20.0), child: Column( children: [ // ── Foto ── Container( height: 250, width: double.infinity, decoration: BoxDecoration( color: const Color(0xFF1A1A1A), borderRadius: BorderRadius.circular(20), ), clipBehavior: Clip.hardEdge, child: args['url_foto'] != null && args['url_foto'].toString().isNotEmpty ? Image.network( args['url_foto'], fit: BoxFit.cover, loadingBuilder: (context, child, progress) { if (progress == null) return child; return const Center( child: CircularProgressIndicator(color: Colors.green), ); }, errorBuilder: (context, error, stack) => Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.broken_image, size: 80, color: Colors.grey), Text('Citra #${args['id']}', style: const TextStyle(color: Colors.grey)), ], ), ) : Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.image, size: 80, color: Colors.grey), Text('Citra #${args['id']}', style: const TextStyle(color: Colors.grey)), ], ), ), const SizedBox(height: 20), // ── Data Card ── Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: const Color(0xFF1A1A1A), borderRadius: BorderRadius.circular(20), ), child: Column( children: [ // Tanggal & waktu Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.calendar_today, color: Colors.green, size: 16), const SizedBox(width: 8), Text( '${args['date']} - ${args['time']}', style: const TextStyle( color: Colors.green, fontWeight: FontWeight.bold, fontSize: 16), ), ], ), // Lokasi dihapus const Divider(color: Colors.grey, height: 30), _buildDataRow( 'Suhu', args['temp'], 'Kelembaban', args['humidity']), const SizedBox(height: 15), _buildDataRow( 'Cahaya', args['light'], 'Mode', 'Terjadwal'), const Divider(color: Colors.grey, height: 30), _buildDataRow( 'Kipas 1 (Intake)', args['intake'], 'Kipas 2 (Exhaust)', args['exhaust'], valueColor: Colors.green, ), ], ), ), const Spacer(), // ── Tombol Export PDF ── SizedBox( width: double.infinity, height: 55, child: ElevatedButton.icon( onPressed: () => _exportPDF(context, args), style: ElevatedButton.styleFrom( backgroundColor: Colors.green, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15), ), ), icon: const Icon(Icons.picture_as_pdf, color: Colors.white), label: const Text( 'Export Data Lengkap (.PDF)', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold), ), ), ), ], ), ), ); } Widget _buildDataRow( String label1, String value1, String label2, String value2, { Color valueColor = Colors.white, }) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label1, style: const TextStyle(color: Colors.grey, fontSize: 12)), const SizedBox(height: 4), Text(': $value1', style: TextStyle( color: valueColor, fontWeight: FontWeight.bold, fontSize: 15)), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label2, style: const TextStyle(color: Colors.grey, fontSize: 12)), const SizedBox(height: 4), Text(': $value2', style: TextStyle( color: valueColor, fontWeight: FontWeight.bold, fontSize: 15)), ], ), ), ], ); } }