675 lines
26 KiB
Dart
675 lines
26 KiB
Dart
import 'dart:convert';
|
|
import 'dart:typed_data';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:pdf/pdf.dart';
|
|
import 'package:pdf/widgets.dart' as pw;
|
|
import 'package:printing/printing.dart';
|
|
|
|
import '../layout/main_layout.dart';
|
|
import 'bidan_drawer.dart';
|
|
import '../bidan/dashboard_bidan.dart';
|
|
|
|
class DataLaporanPage extends StatefulWidget {
|
|
const DataLaporanPage({super.key});
|
|
|
|
@override
|
|
State<DataLaporanPage> createState() => _DataLaporanPageStatus();
|
|
}
|
|
|
|
class _DataLaporanPageStatus extends State<DataLaporanPage> {
|
|
int _currentPage = 0;
|
|
final int _rowsPerPage = 10;
|
|
String _searchQuery = "";
|
|
List<dynamic> _allData = [];
|
|
|
|
final List<String> _namaBulan = [
|
|
"Januari",
|
|
"Februari",
|
|
"Maret",
|
|
"April",
|
|
"Mei",
|
|
"Juni",
|
|
"Juli",
|
|
"Agustus",
|
|
"September",
|
|
"Oktober",
|
|
"November",
|
|
"Desember"
|
|
];
|
|
|
|
Future<Map<String, dynamic>> fetchPemeriksaanBalita() async {
|
|
try {
|
|
final response = await http.get(Uri.parse(
|
|
'http://ta.myhost.id/E31230549/mposyandu_api/laporan/get_laporan.php'));
|
|
|
|
if (response.statusCode == 200) {
|
|
Map<String, dynamic> data = json.decode(response.body);
|
|
_allData = data['data'] ?? [];
|
|
return data;
|
|
} else {
|
|
throw Exception('Gagal mengambil data dari database');
|
|
}
|
|
} catch (e) {
|
|
throw Exception('Kesalahan koneksi: $e');
|
|
}
|
|
}
|
|
|
|
void _showMonthSelectionDialog() async {
|
|
List<int> selectedMonths = [];
|
|
|
|
await showDialog(
|
|
context: context,
|
|
builder: (context) {
|
|
return StatefulBuilder(
|
|
builder: (context, setDialogState) {
|
|
return AlertDialog(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12)),
|
|
title: Text("Pilih Bulan",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 16, fontWeight: FontWeight.bold)),
|
|
content: SizedBox(
|
|
width: 300,
|
|
height: 300,
|
|
child: GridView.builder(
|
|
shrinkWrap: true,
|
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
|
crossAxisCount: 2,
|
|
childAspectRatio: 3,
|
|
),
|
|
itemCount: _namaBulan.length,
|
|
itemBuilder: (context, index) {
|
|
return CheckboxListTile(
|
|
contentPadding: EdgeInsets.zero,
|
|
title: Text(_namaBulan[index],
|
|
style: GoogleFonts.poppins(fontSize: 12)),
|
|
value: selectedMonths.contains(index + 1),
|
|
controlAffinity: ListTileControlAffinity.leading,
|
|
onChanged: (bool? value) {
|
|
setDialogState(() {
|
|
if (value == true) {
|
|
selectedMonths.add(index + 1);
|
|
} else {
|
|
selectedMonths.remove(index + 1);
|
|
}
|
|
});
|
|
},
|
|
);
|
|
},
|
|
),
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: Text("Batal",
|
|
style: GoogleFonts.poppins(color: Colors.grey)),
|
|
),
|
|
ElevatedButton(
|
|
style: ElevatedButton.styleFrom(backgroundColor: Colors.blue),
|
|
onPressed: () {
|
|
Navigator.pop(context);
|
|
if (selectedMonths.isNotEmpty) {
|
|
_generateFilteredPdf(selectedMonths);
|
|
}
|
|
},
|
|
child: Text("Cetak",
|
|
style: GoogleFonts.poppins(color: Colors.white)),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
// ================= GENERATE PDF DENGAN REKAP GIZI BARU =================
|
|
Future<void> _generateFilteredPdf(List<int> selectedMonths) async {
|
|
List<dynamic> filteredData = _allData.where((item) {
|
|
final tglString = item['tgl_periksa']?.toString() ?? '';
|
|
if (tglString.isEmpty || tglString == '-') return false;
|
|
try {
|
|
final parts = tglString.split('-');
|
|
if (parts.length != 3) return false;
|
|
final bulan = int.parse(parts[1]);
|
|
return selectedMonths.contains(bulan);
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}).toList();
|
|
|
|
if (filteredData.isEmpty) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text("Tidak ada data pada bulan yang dipilih"),
|
|
backgroundColor: Colors.orange),
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Variabel Rekapitulasi (Sudah digunakan di bawah agar garis kuning hilang)
|
|
int bbuGiziBuruk = 0,
|
|
bbuGiziKurang = 0,
|
|
bbuGiziBaik = 0,
|
|
bbuRisikoLebih = 0;
|
|
int tbuSangatPendek = 0, tbuPendek = 0, tbuNormal = 0, tbuTinggi = 0;
|
|
int bbtbGiziBuruk = 0,
|
|
bbtbGiziKurang = 0,
|
|
bbtbGiziBaik = 0,
|
|
bbtbRisikoLebih = 0,
|
|
bbtbObesitas = 0;
|
|
|
|
for (var item in filteredData) {
|
|
String bbu = (item['status_bbu'] ?? '').toString().trim().toLowerCase();
|
|
String tbu = (item['status_tbu'] ?? '').toString().trim().toLowerCase();
|
|
String bbtb = (item['status_bbtb'] ?? '').toString().trim().toLowerCase();
|
|
|
|
if (bbu.contains('buruk'))
|
|
bbuGiziBuruk++;
|
|
else if (bbu.contains('kurang'))
|
|
bbuGiziKurang++;
|
|
else if (bbu.contains('baik') || bbu.contains('normal'))
|
|
bbuGiziBaik++;
|
|
else if (bbu.contains('lebih')) bbuRisikoLebih++;
|
|
|
|
if (tbu.contains('sangat pendek'))
|
|
tbuSangatPendek++;
|
|
else if (tbu.contains('pendek') || tbu.contains('stunting'))
|
|
tbuPendek++;
|
|
else if (tbu.contains('normal'))
|
|
tbuNormal++;
|
|
else if (tbu.contains('tinggi')) tbuTinggi++;
|
|
|
|
if (bbtb.contains('buruk') || bbtb.contains('wasting'))
|
|
bbtbGiziBuruk++;
|
|
else if (bbtb.contains('kurang'))
|
|
bbtbGiziKurang++;
|
|
else if (bbtb.contains('baik') || bbtb.contains('normal'))
|
|
bbtbGiziBaik++;
|
|
else if (bbtb.contains('risiko') || bbtb.contains('overweight'))
|
|
bbtbRisikoLebih++;
|
|
else if (bbtb.contains('obesitas')) bbtbObesitas++;
|
|
}
|
|
|
|
final pdf = pw.Document();
|
|
pw.MemoryImage? logoImage;
|
|
|
|
try {
|
|
final ByteData assetImage =
|
|
await rootBundle.load('assets/images/logo sumberasih.png');
|
|
logoImage = pw.MemoryImage(assetImage.buffer.asUint8List());
|
|
} catch (e) {
|
|
debugPrint("Logo gagal dimuat");
|
|
}
|
|
|
|
const int pdfItemsPerPage = 12;
|
|
final int totalPages = (filteredData.length / pdfItemsPerPage).ceil();
|
|
final currentYear = DateTime.now().year;
|
|
|
|
for (int i = 0; i < totalPages; i++) {
|
|
final int start = i * pdfItemsPerPage;
|
|
final int end = (start + pdfItemsPerPage < filteredData.length)
|
|
? start + pdfItemsPerPage
|
|
: filteredData.length;
|
|
final List<dynamic> pageData = filteredData.sublist(start, end);
|
|
|
|
pdf.addPage(
|
|
pw.Page(
|
|
pageFormat: PdfPageFormat.a4.landscape,
|
|
margin: const pw.EdgeInsets.all(15),
|
|
build: (pw.Context context) {
|
|
return pw.Column(
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
|
children: [
|
|
// ================= HEADER =================
|
|
pw.Row(
|
|
crossAxisAlignment: pw.CrossAxisAlignment.center,
|
|
children: [
|
|
if (logoImage != null)
|
|
pw.Container(
|
|
width: 50, height: 50, child: pw.Image(logoImage)),
|
|
pw.SizedBox(width: 15),
|
|
pw.Expanded(
|
|
child: pw.Column(
|
|
children: [
|
|
pw.Text("LAPORAN KEGIATAN KUNJUNGAN BALITA",
|
|
style: pw.TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: pw.FontWeight.bold)),
|
|
pw.SizedBox(height: 2),
|
|
pw.Text("Posyandu Desa Mentor",
|
|
style: const pw.TextStyle(fontSize: 11)),
|
|
pw.Text("Kecamatan Sumberasih, Kabupaten Probolinggo",
|
|
style: const pw.TextStyle(fontSize: 11)),
|
|
pw.SizedBox(height: 2),
|
|
pw.Text(
|
|
"Bulan: ${selectedMonths.map((m) => _namaBulan[m - 1]).join(', ')} $currentYear",
|
|
style: pw.TextStyle(
|
|
fontSize: 10,
|
|
fontWeight: pw.FontWeight.bold)),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
pw.SizedBox(height: 5),
|
|
pw.Divider(thickness: 1.5),
|
|
pw.SizedBox(height: 5),
|
|
|
|
// ================= TABEL DATA =================
|
|
pw.TableHelper.fromTextArray(
|
|
border:
|
|
pw.TableBorder.all(color: PdfColors.grey700, width: 0.5),
|
|
cellAlignment: pw.Alignment.centerLeft,
|
|
headerAlignment: pw.Alignment.center,
|
|
headers: [
|
|
'No',
|
|
'NIK',
|
|
'Nama Balita',
|
|
'Orang Tua',
|
|
'TTL',
|
|
'Umur',
|
|
'JK',
|
|
'Anak',
|
|
'Alamat',
|
|
'Tgl Periksa',
|
|
'BB',
|
|
'TB',
|
|
'LK',
|
|
'Imunisasi',
|
|
'BB/U',
|
|
'TB/U',
|
|
'BB/TB',
|
|
'Hadir'
|
|
],
|
|
data: List<List<dynamic>>.generate(pageData.length, (index) {
|
|
final data = pageData[index];
|
|
return [
|
|
(start + index + 1).toString(),
|
|
data['nik_balita'] ?? "-",
|
|
data['nama'] ?? "-",
|
|
data['nama_orang_tua'] ?? "-",
|
|
"${data['tempat_lahir'] ?? '-'}, ${data['tgl_lahir'] ?? '-'}",
|
|
"${data['umur'] ?? '-'} Bln",
|
|
data['jenis_kelamin'] ?? "-",
|
|
data['anak_ke']?.toString() ?? "-",
|
|
data['alamat_lengkap'] ?? "-",
|
|
data['tgl_periksa'] ?? "-",
|
|
data['berat_badan']?.toString() ?? "-",
|
|
data['tinggi_badan']?.toString() ?? "-",
|
|
data['lingkar_kepala']?.toString() ?? "-",
|
|
data['pemberian_imunisasi'] ?? "-",
|
|
data['status_bbu'] ?? "-",
|
|
data['status_tbu'] ?? "-",
|
|
data['status_bbtb'] ?? "-",
|
|
data['kehadiran_posyandu'] ?? "-",
|
|
];
|
|
}),
|
|
headerStyle: pw.TextStyle(
|
|
fontSize: 7,
|
|
fontWeight: pw.FontWeight.bold,
|
|
color: PdfColors.white),
|
|
headerDecoration:
|
|
const pw.BoxDecoration(color: PdfColors.blue700),
|
|
cellStyle: const pw.TextStyle(fontSize: 6.5),
|
|
cellHeight: 24,
|
|
columnWidths: {
|
|
0: const pw.FixedColumnWidth(20),
|
|
1: const pw.FixedColumnWidth(60),
|
|
2: const pw.FixedColumnWidth(65),
|
|
3: const pw.FixedColumnWidth(70),
|
|
4: const pw.FixedColumnWidth(75),
|
|
5: const pw.FixedColumnWidth(30),
|
|
6: const pw.FixedColumnWidth(20),
|
|
7: const pw.FixedColumnWidth(25),
|
|
8: const pw.FixedColumnWidth(90),
|
|
9: const pw.FixedColumnWidth(45),
|
|
10: const pw.FixedColumnWidth(25),
|
|
11: const pw.FixedColumnWidth(25),
|
|
12: const pw.FixedColumnWidth(25),
|
|
13: const pw.FixedColumnWidth(50),
|
|
14: const pw.FixedColumnWidth(50),
|
|
15: const pw.FixedColumnWidth(50),
|
|
16: const pw.FixedColumnWidth(50),
|
|
17: const pw.FixedColumnWidth(40),
|
|
},
|
|
),
|
|
|
|
// ================= FOOTER REKAPITULASI (HALAMAN TERAKHIR) =================
|
|
if (i == totalPages - 1) ...[
|
|
pw.SizedBox(height: 12),
|
|
pw.Row(
|
|
// PERBAIKAN: Menggunakan pw.MainAxisAlignment.spaceBetween agar tidak error garis merah
|
|
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
|
children: [
|
|
pw.Text("Total Data: ${filteredData.length} Anak",
|
|
style: pw.TextStyle(
|
|
fontSize: 9, fontWeight: pw.FontWeight.bold)),
|
|
pw.Container(
|
|
width: 480,
|
|
padding: const pw.EdgeInsets.all(6),
|
|
decoration: pw.BoxDecoration(
|
|
border: pw.Border.all(
|
|
color: PdfColors.black, width: 0.5)),
|
|
child: pw.Column(
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
|
children: [
|
|
pw.Text("Keterangan Rekapitulasi Status Gizi",
|
|
style: pw.TextStyle(
|
|
fontWeight: pw.FontWeight.bold,
|
|
fontSize: 9)),
|
|
pw.SizedBox(height: 4),
|
|
pw.Row(
|
|
mainAxisAlignment:
|
|
pw.MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
pw.Column(
|
|
crossAxisAlignment:
|
|
pw.CrossAxisAlignment.start,
|
|
children: [
|
|
pw.Text("[BB/U] Berat Badan / Umur:",
|
|
style: pw.TextStyle(
|
|
fontSize: 7,
|
|
fontWeight: pw.FontWeight.bold)),
|
|
pw.Text(
|
|
"- Gizi Baik: $bbuGiziBaik\n- Gizi Kurang: $bbuGiziKurang\n- Gizi Buruk: $bbuGiziBuruk\n- Risiko Lebih: $bbuRisikoLebih",
|
|
style: const pw.TextStyle(fontSize: 7)),
|
|
],
|
|
),
|
|
pw.Column(
|
|
crossAxisAlignment:
|
|
pw.CrossAxisAlignment.start,
|
|
children: [
|
|
pw.Text("[TB/U] Tinggi Badan / Umur:",
|
|
style: pw.TextStyle(
|
|
fontSize: 7,
|
|
fontWeight: pw.FontWeight.bold)),
|
|
pw.Text(
|
|
"- Normal: $tbuNormal\n- Pendek: $tbuPendek\n- Sangat Pendek: $tbuSangatPendek\n- Tinggi: $tbuTinggi",
|
|
style: const pw.TextStyle(fontSize: 7)),
|
|
],
|
|
),
|
|
pw.Column(
|
|
crossAxisAlignment:
|
|
pw.CrossAxisAlignment.start,
|
|
children: [
|
|
pw.Text("[BB/TB] Berat Badan / Tinggi:",
|
|
style: pw.TextStyle(
|
|
fontSize: 7,
|
|
fontWeight: pw.FontWeight.bold)),
|
|
pw.Text(
|
|
"- Normal: $bbtbGiziBaik\n- Gizi Kurang: $bbtbGiziKurang\n- Gizi Buruk: $bbtbGiziBuruk\n- Overweight: $bbtbRisikoLebih\n- Obesitas: $bbtbObesitas",
|
|
style: const pw.TextStyle(fontSize: 7)),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
],
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
await Printing.layoutPdf(
|
|
onLayout: (PdfPageFormat format) async => pdf.save());
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return PopScope(
|
|
canPop: false,
|
|
onPopInvokedWithResult: (didPop, result) async {
|
|
if (didPop) return;
|
|
Navigator.pushAndRemoveUntil(
|
|
context,
|
|
MaterialPageRoute(builder: (_) => const DashboardBidanPage()),
|
|
(route) => false);
|
|
},
|
|
child: MainLayout(
|
|
title: "",
|
|
drawer: const BidanDrawer(),
|
|
body: Padding(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Center(
|
|
child: Text("Laporan Pemeriksaan Balita",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 18, fontWeight: FontWeight.w600)),
|
|
),
|
|
const SizedBox(height: 10),
|
|
Align(
|
|
alignment: Alignment.centerRight,
|
|
child: SizedBox(
|
|
height: 35,
|
|
child: OutlinedButton.icon(
|
|
onPressed: () => _allData.isNotEmpty
|
|
? _showMonthSelectionDialog()
|
|
: null,
|
|
icon: const Icon(Icons.print, size: 16, color: Colors.blue),
|
|
label: Text("Cetak PDF",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 11,
|
|
color: Colors.blue,
|
|
fontWeight: FontWeight.w500)),
|
|
style: OutlinedButton.styleFrom(
|
|
backgroundColor: Colors.white,
|
|
side: const BorderSide(color: Colors.blue, width: 1),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(6)),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 15),
|
|
TextField(
|
|
onChanged: (value) => setState(() {
|
|
_searchQuery = value;
|
|
_currentPage = 0;
|
|
}),
|
|
style: GoogleFonts.poppins(fontSize: 12),
|
|
decoration: InputDecoration(
|
|
hintText: "Cari nama balita...",
|
|
prefixIcon: const Icon(Icons.search, size: 20),
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(10)),
|
|
contentPadding: const EdgeInsets.symmetric(vertical: 0),
|
|
),
|
|
),
|
|
const SizedBox(height: 20),
|
|
Row(
|
|
children: [
|
|
const Icon(Icons.swipe_left_alt,
|
|
size: 14, color: Colors.grey),
|
|
const SizedBox(width: 5),
|
|
Text("Geser ke kanan untuk melihat lebih lanjut",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 10,
|
|
color: Colors.grey.shade600,
|
|
fontStyle: FontStyle.italic)),
|
|
],
|
|
),
|
|
const SizedBox(height: 5),
|
|
Expanded(
|
|
child: FutureBuilder<Map<String, dynamic>>(
|
|
future: fetchPemeriksaanBalita(),
|
|
builder: (context, snapshot) {
|
|
if (snapshot.connectionState == ConnectionState.waiting)
|
|
return const Center(child: CircularProgressIndicator());
|
|
if (snapshot.hasError)
|
|
return Center(child: Text("Error: ${snapshot.error}"));
|
|
|
|
final List<dynamic> rawData = snapshot.data?['data'] ?? [];
|
|
final filteredData = rawData
|
|
.where((item) => item["nama"]
|
|
.toString()
|
|
.toLowerCase()
|
|
.contains(_searchQuery.toLowerCase()))
|
|
.toList();
|
|
|
|
if (filteredData.isEmpty)
|
|
return const Center(child: Text("Data tidak ditemukan"));
|
|
|
|
final totalPages =
|
|
(filteredData.length / _rowsPerPage).ceil();
|
|
final start = _currentPage * _rowsPerPage;
|
|
final end = (start + _rowsPerPage > filteredData.length)
|
|
? filteredData.length
|
|
: start + _rowsPerPage;
|
|
final paginatedData = filteredData.sublist(start, end);
|
|
|
|
return Column(
|
|
children: [
|
|
Expanded(
|
|
child: SingleChildScrollView(
|
|
scrollDirection: Axis.vertical,
|
|
child: SingleChildScrollView(
|
|
scrollDirection: Axis.horizontal,
|
|
child: _buildTable(paginatedData, start),
|
|
),
|
|
),
|
|
),
|
|
_buildPaginationControls(totalPages),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildTable(List<dynamic> dataList, int startIndex) {
|
|
return DataTable(
|
|
headingRowColor: WidgetStateProperty.all(Colors.blue.shade600),
|
|
columnSpacing: 18,
|
|
horizontalMargin: 10,
|
|
columns: [
|
|
_headerCell("No"),
|
|
_headerCell("NIK"),
|
|
_headerCell("Nama Balita"),
|
|
_headerCell("Nama Orang Tua"),
|
|
_headerCell("TTL"),
|
|
_headerCell("Umur"),
|
|
_headerCell("JK"),
|
|
_headerCell("Anak Ke"),
|
|
_headerCell("Alamat"),
|
|
_headerCell("Tgl Periksa"),
|
|
_headerCell("BB (kg)"),
|
|
_headerCell("TB (cm)"),
|
|
_headerCell("LK (cm)"),
|
|
_headerCell("Imunisasi"),
|
|
_headerCell("BB/U"),
|
|
_headerCell("TB/U"),
|
|
_headerCell("BB/TB"),
|
|
_headerCell("Kehadiran"),
|
|
],
|
|
rows: List.generate(dataList.length, (index) {
|
|
final data = dataList[index];
|
|
return DataRow(cells: [
|
|
DataCell(_textCell((startIndex + index + 1).toString())),
|
|
DataCell(_textCell(data['nik_balita'] ?? "-")),
|
|
DataCell(_textCell(data['nama'] ?? "-")),
|
|
DataCell(_textCell(data['nama_orang_tua'] ?? "-")),
|
|
DataCell(_textCell(
|
|
"${data['tempat_lahir'] ?? ''}, ${data['tgl_lahir'] ?? ''}")),
|
|
DataCell(_textCell("${data['umur'] ?? '-'} Bln")),
|
|
DataCell(_textCell(data['jenis_kelamin'] ?? "-")),
|
|
DataCell(_textCell(data['anak_ke']?.toString() ?? "-")),
|
|
DataCell(_textCell(data['alamat_lengkap'] ?? "-")),
|
|
DataCell(_textCell(data['tgl_periksa'] ?? "-")),
|
|
DataCell(_textCell(data['berat_badan']?.toString() ?? "-")),
|
|
DataCell(_textCell(data['tinggi_badan']?.toString() ?? "-")),
|
|
DataCell(_textCell(data['lingkar_kepala']?.toString() ?? "-")),
|
|
DataCell(_textCell(data['pemberian_imunisasi'] ?? "-")),
|
|
DataCell(_statusGiziBadge(data['status_bbu'])),
|
|
DataCell(_statusGiziBadge(data['status_tbu'])),
|
|
DataCell(_statusGiziBadge(data['status_bbtb'])),
|
|
DataCell(_textCell(data['kehadiran_posyandu'] ?? "-")),
|
|
]);
|
|
}),
|
|
);
|
|
}
|
|
|
|
DataColumn _headerCell(String label) => DataColumn(
|
|
label: Text(label,
|
|
style: GoogleFonts.poppins(
|
|
color: Colors.white, fontWeight: FontWeight.bold, fontSize: 11)));
|
|
|
|
Widget _textCell(String text) =>
|
|
Text(text, style: GoogleFonts.poppins(fontSize: 11));
|
|
|
|
Widget _statusGiziBadge(String? status) {
|
|
if (status == null || status == "-") return _textCell("-");
|
|
|
|
String normalValue = status.toLowerCase();
|
|
Color color = Colors.grey;
|
|
|
|
if (normalValue.contains('baik') || normalValue.contains('normal')) {
|
|
color = Colors.green;
|
|
} else if (normalValue.contains('kurang') ||
|
|
normalValue.contains('pendek')) {
|
|
color = Colors.orange;
|
|
} else if (normalValue.contains('buruk') ||
|
|
normalValue.contains('sangat pendek') ||
|
|
normalValue.contains('wasting')) {
|
|
color = Colors.red;
|
|
} else if (normalValue.contains('lebih') ||
|
|
normalValue.contains('obesitas') ||
|
|
normalValue.contains('overweight')) {
|
|
color = Colors.purple;
|
|
}
|
|
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
|
|
decoration:
|
|
BoxDecoration(color: color, borderRadius: BorderRadius.circular(4)),
|
|
child: Text(status,
|
|
style: GoogleFonts.poppins(
|
|
color: Colors.white, fontSize: 9, fontWeight: FontWeight.bold)),
|
|
);
|
|
}
|
|
|
|
Widget _buildPaginationControls(int totalPages) {
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 10),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text("Halaman ${_currentPage + 1} dari $totalPages",
|
|
style: GoogleFonts.poppins(fontSize: 11)),
|
|
Row(
|
|
children: [
|
|
IconButton(
|
|
icon: const Icon(Icons.arrow_back_ios, size: 16),
|
|
onPressed: _currentPage == 0
|
|
? null
|
|
: () => setState(() => _currentPage--)),
|
|
IconButton(
|
|
icon: const Icon(Icons.arrow_forward_ios, size: 16),
|
|
onPressed: _currentPage >= totalPages - 1
|
|
? null
|
|
: () => setState(() => _currentPage++)),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|