MIF_E31230549/lib/ibu/crud_edukasi/edukasi_balita.dart

274 lines
8.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:flutter_quill/flutter_quill.dart'
as quill; // Pastikan package ini ada di pubspec.yaml
import '../../layout/main_layout.dart';
import '../ibu_drawer.dart';
import '../dashboard_ibu.dart';
class EdukasiBalitaPage extends StatefulWidget {
const EdukasiBalitaPage({super.key});
@override
State<EdukasiBalitaPage> createState() => _EdukasiBalitaPageState();
}
class _EdukasiBalitaPageState extends State<EdukasiBalitaPage> {
List _listEdukasi = [];
bool _isLoading = true;
// Base URL Gambar disesuaikan dengan folder edukasi
final String baseImageUrl =
"http://ta.myhost.id/E31230549/mposyandu_api/upload/edukasi/";
@override
void initState() {
super.initState();
_fetchData();
}
Future<void> _fetchData() async {
try {
final response = await http.get(
Uri.parse(
"http://ta.myhost.id/E31230549/mposyandu_api/edukasi_balita/get_edukasi_balita.php",
),
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
if (data['success'] == true) {
setState(() {
_listEdukasi = data['data'];
_isLoading = false;
});
} else {
setState(() => _isLoading = false);
}
} else {
setState(() => _isLoading = false);
}
} catch (e) {
setState(() => _isLoading = false);
debugPrint("Error fetching data: $e");
}
}
// Fungsi untuk membersihkan JSON Quill menjadi teks paragraf biasa
String _parseDescription(String? description) {
String cleanText = description ?? "";
if (cleanText.startsWith('[') && cleanText.endsWith(']')) {
try {
final List<dynamic> json = jsonDecode(cleanText);
final doc = quill.Document.fromJson(json);
return doc.toPlainText().trim();
} catch (e) {
return cleanText;
}
}
return cleanText;
}
@override
Widget build(BuildContext context) {
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, result) async {
if (didPop) return;
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (_) => const DashboardIbuPage(),
),
(route) => false,
);
},
child: MainLayout(
title: "",
drawer: const IbuDrawer(),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
child: Column(
children: [
/// ===== JUDUL =====
Text(
"Edukasi Balita",
style: GoogleFonts.poppins(
fontSize: 18,
fontWeight: FontWeight.w600,
color: const Color.fromARGB(255, 24, 25, 26),
),
),
const SizedBox(height: 16),
Expanded(
child: _isLoading
? const Center(child: CircularProgressIndicator())
: _listEdukasi.isEmpty
? const Center(
child: Text("Belum ada data edukasi balita."),
)
: RefreshIndicator(
onRefresh: _fetchData,
child: ListView.builder(
itemCount: _listEdukasi.length,
itemBuilder: (context, index) {
final item = _listEdukasi[index];
// Gabungkan Base URL dengan nama file gambar
String fullImageUrl = "";
if (item['gambar'] != null &&
item['gambar'].toString().isNotEmpty) {
fullImageUrl =
"$baseImageUrl${item['gambar']}";
}
return _EdukasiCard(
title: item['judul'] ?? '',
content: _parseDescription(item['deskripsi']),
ageRange:
"Edukasi untuk balita umur ${item['umur_min']} - ${item['umur_max']} bulan",
imageUrl: fullImageUrl,
icon: Icons.menu_book_rounded,
);
},
),
),
),
],
),
),
),
);
}
}
/// ================= CARD EDUKASI =================
class _EdukasiCard extends StatelessWidget {
final String title;
final String content;
final String ageRange;
final String imageUrl;
final IconData icon;
const _EdukasiCard({
required this.title,
required this.content,
required this.ageRange,
required this.imageUrl,
required this.icon,
});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(14),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// ===== GAMBAR EDUKASI =====
if (imageUrl.isNotEmpty)
ClipRRect(
borderRadius:
const BorderRadius.vertical(top: Radius.circular(14)),
child: Image.network(
imageUrl,
width: double.infinity,
height: 200,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Container(
width: double.infinity,
height: 150,
color: Colors.grey[200],
child: const Icon(Icons.broken_image,
color: Colors.grey, size: 50),
);
},
),
),
Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// ===== HEADER =====
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: const Color(0xFFE8F1FF),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
icon,
color: Colors.blueAccent,
size: 20,
),
),
const SizedBox(width: 12),
Expanded(
child: Text(
title,
style: GoogleFonts.poppins(
fontWeight: FontWeight.bold,
fontSize: 15,
color: Colors.black87,
),
),
),
],
),
const SizedBox(height: 12),
/// ===== UMUR =====
Text(
ageRange,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Colors.blueAccent,
),
),
const SizedBox(height: 8),
/// ===== DESKRIPSI (Rapi & Justify) =====
Text(
content,
textAlign: TextAlign.justify,
style: GoogleFonts.poppins(
fontSize: 13,
height: 1.6,
color: Colors.black54,
),
),
],
),
),
],
),
);
}
}