last63

doa_page.dart

Nov 16th, 2025
76
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.44 KB | None | 0 0
  1. // ================================================================
  2. // ๐Ÿ•Œ DOA PAGE โ€” Kumpulan Doa Sehari-hari dengan GLOBAL SEARCH
  3. // ================================================================
  4. // ๐Ÿ”น Data doa diambil dari: lib/data/data_doa.dart
  5. // ๐Ÿ”น Fitur:
  6. // โœ… Dropdown kategori (filter manual)
  7. // โœ… Pencarian lintas kategori (global search)
  8. // ================================================================
  9.  
  10. import 'package:flutter/material.dart';
  11. import '../data/data_doa.dart'; // โœ… sumber doa
  12.  
  13. class DoaPage extends StatefulWidget {
  14. const DoaPage({super.key});
  15.  
  16. @override
  17. State<DoaPage> createState() => _DoaPageState();
  18. }
  19.  
  20. class _DoaPageState extends State<DoaPage> {
  21. /// ๐Ÿ”น Kategori aktif
  22. String _selectedCategory = "Makanan & Minuman";
  23.  
  24. /// ๐Ÿ”น Semua doa (gabungan seluruh kategori)
  25. late List<Map<String, String>> _allDoaList;
  26.  
  27. /// ๐Ÿ”น Doa yang ditampilkan (hasil filter atau kategori)
  28. late List<Map<String, String>> _displayedList;
  29.  
  30. /// ๐Ÿ”น Controller pencarian
  31. final TextEditingController _searchController = TextEditingController();
  32.  
  33. /// ๐Ÿ”น Daftar kategori yang tersedia
  34. final List<String> _categories = [
  35. "Makanan & Minuman",
  36. "Pagi & Malam",
  37. "Rumah",
  38. "Perjalanan",
  39. "Sholat",
  40. "Etika Baik"
  41. ];
  42.  
  43. @override
  44. void initState() {
  45. super.initState();
  46.  
  47. /// ๐Ÿ”น Ambil semua doa dari tiap kategori, digabung
  48. _allDoaList = [];
  49. for (var cat in _categories) {
  50. final list = getDoaList(cat)
  51. .map((d) => {...d, "category": cat}) // tambahkan label kategori
  52. .toList();
  53. _allDoaList.addAll(list);
  54. }
  55.  
  56. /// ๐Ÿ”น Default tampilan = kategori pertama
  57. _displayedList = getDoaList(_selectedCategory);
  58. }
  59.  
  60. // ================================================================
  61. // [1] ๐Ÿ”ท Ganti kategori
  62. // ================================================================
  63. void _onCategoryChanged(String? newCategory) {
  64. if (newCategory == null) return;
  65. setState(() {
  66. _selectedCategory = newCategory;
  67. _searchController.clear();
  68. _displayedList = getDoaList(_selectedCategory);
  69. });
  70. }
  71.  
  72. // ================================================================
  73. // [2] ๐Ÿ”ท Fungsi pencarian global lintas kategori
  74. // ================================================================
  75. void _filterGlobal(String query) {
  76. setState(() {
  77. if (query.isEmpty) {
  78. _displayedList = getDoaList(_selectedCategory);
  79. } else {
  80. _displayedList = _allDoaList.where((doa) {
  81. final title = doa['title']?.toLowerCase() ?? '';
  82. final translation = doa['translation']?.toLowerCase() ?? '';
  83. return title.contains(query.toLowerCase()) ||
  84. translation.contains(query.toLowerCase());
  85. }).toList();
  86. }
  87. });
  88. }
  89.  
  90. // ================================================================
  91. // [3] ๐Ÿ”ท Bangun UI
  92. // ================================================================
  93. @override
  94. Widget build(BuildContext context) {
  95. return Scaffold(
  96. backgroundColor: Colors.grey[100],
  97.  
  98. appBar: AppBar(
  99. title: const Text(
  100. "Kumpulan Doa Sehari-hari",
  101. style: TextStyle(fontFamily: 'PoppinsSemiBold'),
  102. ),
  103. backgroundColor: Colors.amber,
  104. ),
  105.  
  106. body: Padding(
  107. padding: const EdgeInsets.all(16),
  108. child: Column(
  109. crossAxisAlignment: CrossAxisAlignment.start,
  110. children: [
  111. // ------------------------------------------------------------
  112. // [3.1] Dropdown kategori doa
  113. // ------------------------------------------------------------
  114. Container(
  115. padding: const EdgeInsets.symmetric(horizontal: 12),
  116. decoration: BoxDecoration(
  117. color: Colors.white,
  118. borderRadius: BorderRadius.circular(12),
  119. boxShadow: [
  120. BoxShadow(
  121. color: Colors.black.withOpacity(0.05),
  122. blurRadius: 5,
  123. offset: const Offset(0, 2),
  124. ),
  125. ],
  126. ),
  127. child: DropdownButtonHideUnderline(
  128. child: DropdownButton<String>(
  129. value: _selectedCategory,
  130. items: _categories
  131. .map((e) => DropdownMenuItem(
  132. value: e,
  133. child: Text(
  134. e,
  135. style: const TextStyle(
  136. fontFamily: 'PoppinsMedium',
  137. fontSize: 14,
  138. ),
  139. ),
  140. ))
  141. .toList(),
  142. onChanged: _onCategoryChanged,
  143. icon: const Icon(Icons.keyboard_arrow_down_rounded),
  144. ),
  145. ),
  146. ),
  147. const SizedBox(height: 12),
  148.  
  149. // ------------------------------------------------------------
  150. // [3.2] Search bar
  151. // ------------------------------------------------------------
  152. TextField(
  153. controller: _searchController,
  154. onChanged: _filterGlobal,
  155. decoration: InputDecoration(
  156. hintText: 'Cari doa (misal: tidur, makan, wudhu...)',
  157. prefixIcon: const Icon(Icons.search),
  158. suffixIcon: _searchController.text.isNotEmpty
  159. ? IconButton(
  160. icon: const Icon(Icons.clear),
  161. onPressed: () {
  162. _searchController.clear();
  163. _filterGlobal('');
  164. },
  165. )
  166. : null,
  167. filled: true,
  168. fillColor: Colors.white,
  169. contentPadding:
  170. const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
  171. border: OutlineInputBorder(
  172. borderRadius: BorderRadius.circular(12),
  173. borderSide: BorderSide.none,
  174. ),
  175. ),
  176. ),
  177. const SizedBox(height: 16),
  178.  
  179. // ------------------------------------------------------------
  180. // [3.3] List Doa
  181. // ------------------------------------------------------------
  182. Expanded(
  183. child: _displayedList.isEmpty
  184. ? const Center(
  185. child: Text(
  186. "โŒ Doa tidak ditemukan.",
  187. style: TextStyle(
  188. fontFamily: 'PoppinsRegular',
  189. color: Colors.grey,
  190. ),
  191. ),
  192. )
  193. : ListView.builder(
  194. itemCount: _displayedList.length,
  195. itemBuilder: (context, index) {
  196. final doa = _displayedList[index];
  197. return _buildDoaCard(doa);
  198. },
  199. ),
  200. ),
  201. ],
  202. ),
  203. ),
  204. );
  205. }
  206.  
  207. // ================================================================
  208. // [4] ๐Ÿ”ท Widget Card Doa
  209. // ================================================================
  210. Widget _buildDoaCard(Map<String, String> doa) {
  211. return Card(
  212. margin: const EdgeInsets.symmetric(vertical: 8),
  213. shape: RoundedRectangleBorder(
  214. borderRadius: BorderRadius.circular(14),
  215. ),
  216. elevation: 2,
  217. color: Colors.white,
  218. child: Padding(
  219. padding: const EdgeInsets.all(16),
  220. child: Column(
  221. crossAxisAlignment: CrossAxisAlignment.start,
  222. children: [
  223. // Judul dan kategori
  224. Row(
  225. crossAxisAlignment: CrossAxisAlignment.start,
  226. children: [
  227. if (doa['image'] != null)
  228. Image.asset(
  229. doa['image']!,
  230. width: 40,
  231. height: 40,
  232. fit: BoxFit.contain,
  233. ),
  234. const SizedBox(width: 12),
  235. Expanded(
  236. child: Column(
  237. crossAxisAlignment: CrossAxisAlignment.start,
  238. children: [
  239. Text(
  240. doa['title'] ?? '',
  241. style: const TextStyle(
  242. fontFamily: 'PoppinsSemiBold',
  243. fontSize: 16,
  244. color: Colors.amber,
  245. ),
  246. ),
  247. if (doa['category'] != null)
  248. Text(
  249. "๐Ÿ“‚ ${doa['category']!}",
  250. style: const TextStyle(
  251. fontFamily: 'PoppinsRegular',
  252. fontSize: 11,
  253. color: Colors.grey,
  254. ),
  255. ),
  256. ],
  257. ),
  258. ),
  259. ],
  260. ),
  261. const SizedBox(height: 10),
  262.  
  263. // Teks Arab
  264. if (doa['arabicText'] != null)
  265. Text(
  266. doa['arabicText']!,
  267. textAlign: TextAlign.right,
  268. style: const TextStyle(
  269. fontFamily: 'ScheherazadeNew',
  270. fontSize: 20,
  271. color: Colors.black87,
  272. ),
  273. ),
  274. const SizedBox(height: 10),
  275.  
  276. // Terjemahan
  277. if (doa['translation'] != null)
  278. Text(
  279. doa['translation']!,
  280. style: const TextStyle(
  281. fontFamily: 'PoppinsRegular',
  282. fontSize: 13,
  283. color: Colors.black87,
  284. height: 1.5,
  285. ),
  286. ),
  287. const SizedBox(height: 8),
  288.  
  289. // Referensi
  290. if (doa['reference'] != null)
  291. Text(
  292. "๐Ÿ“š ${doa['reference']!}",
  293. style: const TextStyle(
  294. fontFamily: 'PoppinsItalic',
  295. fontSize: 11,
  296. color: Colors.grey,
  297. ),
  298. ),
  299. ],
  300. ),
  301. ),
  302. );
  303. }
  304. }
  305.  
Advertisement
Add Comment
Please, Sign In to add comment