MIF_E31230549/lib/kader/crud_pemeriksaan/tambah_pemeriksaan.dart

317 lines
11 KiB
Dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
class TambahPemeriksaanBalitaPage extends StatefulWidget {
final Map<String, dynamic> balita;
const TambahPemeriksaanBalitaPage({
super.key,
required this.balita,
});
@override
State<TambahPemeriksaanBalitaPage> createState() =>
_TambahPemeriksaanBalitaPageState();
}
class _TambahPemeriksaanBalitaPageState
extends State<TambahPemeriksaanBalitaPage> {
final _formKey = GlobalKey<FormState>();
final bbController = TextEditingController();
final tbController = TextEditingController();
final lkController = TextEditingController();
final catatanController = TextEditingController();
final tanggalController = TextEditingController();
String? vitaminA;
String? pmt;
bool _isSaving = false;
DateTime selectedDate = DateTime.now();
List imunisasiList = [];
Map<int, bool> selectedImunisasi = {};
bool loadingImunisasi = true;
@override
void initState() {
super.initState();
tanggalController.text = DateFormat('dd-MM-yyyy').format(selectedDate);
ambilImunisasiOtomatis();
}
@override
void dispose() {
bbController.dispose();
tbController.dispose();
lkController.dispose();
catatanController.dispose();
tanggalController.dispose();
super.dispose();
}
Future ambilImunisasiOtomatis() async {
try {
final response = await http.get(
Uri.parse(
"http://ta.myhost.id/E31230549/mposyandu_api/pemeriksaan_balita/get_imunisasi.php?id_balita=${widget.balita["id_balita"] ?? widget.balita["id"]}"),
);
final data = json.decode(response.body);
if (data["success"] == true) {
imunisasiList = data["imunisasi"];
for (var item in imunisasiList) {
selectedImunisasi[item["id"]] = item["checked"];
}
setState(() => loadingImunisasi = false);
}
} catch (e) {
setState(() => loadingImunisasi = false);
}
}
Future<void> _pilihTanggal() async {
FocusScope.of(context).requestFocus(FocusNode());
final picked = await showDatePicker(
context: context,
initialDate: selectedDate,
firstDate: DateTime(2000),
lastDate: DateTime(2101),
);
if (picked != null) {
setState(() {
selectedDate = picked;
tanggalController.text = DateFormat('dd-MM-yyyy').format(picked);
});
}
}
Future<void> _simpan() async {
// Validasi form dilewati untuk field opsional, tapi ID balita tetap dicek di logic
setState(() => _isSaving = true);
try {
final url = Uri.parse(
"http://ta.myhost.id/E31230549/mposyandu_api/pemeriksaan_balita/tambah_pemeriksaan_balita.php");
String formatTanggalDb = DateFormat('yyyy-MM-dd').format(selectedDate);
String imunisasiDipilih = selectedImunisasi.entries
.where((e) => e.value == true)
.map((e) => e.key.toString())
.join(",");
final response = await http.post(url, body: {
"id_balita": widget.balita["id_balita"]?.toString() ??
widget.balita["id"]?.toString() ??
"0",
"tanggal_pemeriksaan": formatTanggalDb,
"bb": bbController.text.trim(),
"tb": tbController.text.trim(),
"lk": lkController.text.trim(),
"vitamin_a": vitaminA ?? "",
"pmt": pmt ?? "",
"catatan": catatanController.text.trim(),
"imunisasi": imunisasiDipilih
});
final data = json.decode(response.body);
if (data["success"] == true) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Data berhasil disimpan")),
);
Navigator.pop(context, true);
} else {
throw Exception(data["message"] ?? "Terjadi kesalahan server");
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Gagal: $e")),
);
} finally {
if (mounted) setState(() => _isSaving = false);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(""),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
backgroundColor: const Color(0xfff4f6fb),
body: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 420),
child: Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: const [
BoxShadow(
blurRadius: 16,
color: Colors.black12,
offset: Offset(0, 8))
],
),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Text(
"Tambah Data Pemeriksaan",
style: GoogleFonts.poppins(
fontSize: 18, fontWeight: FontWeight.bold),
),
),
const SizedBox(height: 20),
TextFormField(
controller: tanggalController,
readOnly: true,
onTap: _pilihTanggal,
style: GoogleFonts.poppins(fontSize: 12),
decoration: _decoration("Tanggal Periksa").copyWith(
suffixIcon: const Icon(Icons.calendar_month,
size: 18, color: Colors.blue),
),
),
const SizedBox(height: 16),
_inputNumber(bbController, "Berat Badan (kg)"),
const SizedBox(height: 16),
_inputNumber(tbController, "Tinggi Badan (cm)"),
const SizedBox(height: 16),
_inputNumber(lkController, "Lingkar Kepala (cm)"),
const SizedBox(height: 20),
Text("Imunisasi Sesuai Usia",
style: GoogleFonts.poppins(
fontSize: 13, fontWeight: FontWeight.w600)),
const SizedBox(height: 8),
_buildCheckboxImunisasi(),
const SizedBox(height: 16),
_dropdown(
label: "Vitamin A",
hint: "Pilih Vitamin A (Opsional)",
items: const ["Diberikan", "Belum"],
value: vitaminA,
onChanged: (v) => setState(() => vitaminA = v),
),
const SizedBox(height: 16),
_dropdown(
label: "PMT",
hint: "Pilih PMT (Opsional)",
items: const ["Diberikan", "Belum"],
value: pmt,
onChanged: (v) => setState(() => pmt = v),
),
const SizedBox(height: 16),
TextFormField(
controller: catatanController,
maxLines: 3,
style: GoogleFonts.poppins(fontSize: 12),
decoration: _decoration("Catatan Pemeriksaan (Opsional)"),
),
const SizedBox(height: 28),
SizedBox(
width: double.infinity,
height: 50,
child: OutlinedButton(
onPressed: _isSaving ? null : _simpan,
style: OutlinedButton.styleFrom(
side: const BorderSide(
color: Colors.blueAccent, width: 2),
shape: const StadiumBorder(),
),
child: _isSaving
? const SizedBox(
height: 20,
width: 20,
child:
CircularProgressIndicator(strokeWidth: 2),
)
: Text("Simpan",
style: GoogleFonts.poppins(
fontWeight: FontWeight.bold,
color: Colors.blueAccent)),
),
),
],
),
),
),
),
),
),
);
}
Widget _inputNumber(TextEditingController controller, String label) {
return TextFormField(
controller: controller,
keyboardType: const TextInputType.numberWithOptions(decimal: true),
style: GoogleFonts.poppins(fontSize: 12),
// Validator dihapus agar bisa kosong
decoration: _decoration(label + " (Opsional)"),
);
}
Widget _dropdown({
required String label,
required String hint,
required List<String> items,
required String? value,
required Function(String?) onChanged,
}) {
return DropdownButtonFormField<String>(
value: value,
hint: Text(hint, style: GoogleFonts.poppins(fontSize: 12)),
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
items:
items.map((e) => DropdownMenuItem(value: e, child: Text(e))).toList(),
onChanged: onChanged,
decoration: _decoration(label),
);
}
InputDecoration _decoration(String label) {
return InputDecoration(
filled: true,
fillColor: Colors.white,
labelText: label,
labelStyle: GoogleFonts.poppins(fontSize: 12),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
);
}
Widget _buildCheckboxImunisasi() {
if (loadingImunisasi)
return const Center(child: CircularProgressIndicator());
if (imunisasiList.isEmpty) return const Text("Tidak ada imunisasi");
return Column(
children: imunisasiList.map((item) {
final bool sudah = item["status"] == "Sudah";
return CheckboxListTile(
value: selectedImunisasi[item["id"]] ?? false,
onChanged: sudah
? null
: (v) => setState(() => selectedImunisasi[item["id"]] = v!),
title: Text(item["nama_imunisasi"],
style: GoogleFonts.poppins(fontSize: 12)),
controlAffinity: ListTileControlAffinity.leading,
);
}).toList(),
);
}
}