TKK_E32220332/lib/screens/image_result_screen.dart

407 lines
14 KiB
Dart

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<void> _exportPDF(
BuildContext context, Map<String, dynamic> 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<String, dynamic>? ??
{
'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)),
],
),
),
],
);
}
}