last63

home_page.dart

Nov 16th, 2025
48
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.84 KB | None | 0 0
  1. // PUT CODE HERE BELOW
  2.  
  3. @override
  4. void initState() {
  5. super.initState();
  6. _updatePrayerTimes(); // jalankan saat awal
  7. }
  8.  
  9. // ================================================================
  10. // LOGIKA INTI: UPDATE DATA WAKTU SHOLAT & LOKASI
  11. // ================================================================
  12. /// 🔹 Mengambil lokasi, mendeteksi kota terdekat, dan memuat jadwal sholat
  13. ///
  14. /// Diagram alur:
  15. ///
  16. /// [GPS Position]
  17. /// [Kota terdekat (fuzzy match)]
  18. /// [Ambil data GitHub jadwal sholat]
  19. /// [Hitung waktu sholat terdekat + countdown]
  20.  
  21. Future<void> _updatePrayerTimes() async {
  22. setState(() => _isLoading = true);
  23.  
  24. if (await _requestLocationPermission()) {
  25. try {
  26. // Ambil posisi GPS user (timeout 10 detik)
  27. Position position =
  28. await Geolocator.getCurrentPosition(
  29. desiredAccuracy: LocationAccuracy.high,
  30. ).timeout(
  31. const Duration(seconds: 10),
  32. onTimeout: () =>
  33. throw Exception("Gagal mendapatkan lokasi (timeout)"),
  34. );
  35.  
  36. // Cari kota terdekat dari koordinat
  37. String city;
  38. try {
  39. city = await getClosestCity(position.latitude, position.longitude);
  40. } catch (_) {
  41. city = "semarang"; // fallback default
  42. }
  43.  
  44. // Konversi koordinat ke nama lokasi manusiawi
  45. List<Placemark> placemarks = await placemarkFromCoordinates(
  46. position.latitude,
  47. position.longitude,
  48. );
  49. Placemark place = placemarks.isNotEmpty
  50. ? placemarks.first
  51. : Placemark();
  52.  
  53. // Ambil bulan & tahun sekarang
  54. String month = DateFormat('MM').format(DateTime.now());
  55. String year = DateFormat('yyyy').format(DateTime.now());
  56.  
  57. // Ambil jadwal sholat & hitung waktu terdekat
  58. _jadwalSholat = await fetchJadwalSholat(city, month, year);
  59. _calculateNextPrayer();
  60.  
  61. // Update tampilan UI
  62. setState(() {
  63. _location =
  64. "${place.subAdministrativeArea ?? ''}, ${place.locality ?? ''}";
  65. _backgroundImage = _getBackgroundImage(DateTime.now());
  66. _isLoading = false;
  67. });
  68. } catch (e) {
  69. _showErrorDialog("Gagal mengambil data: ${e.toString()}");
  70. setState(() => _isLoading = false);
  71. }
  72. } else {
  73. _showErrorDialog(
  74. "Izin lokasi ditolak. Aktifkan lokasi untuk melanjutkan.",
  75. );
  76. setState(() => _isLoading = false);
  77. }
  78. }
  79.  
  80. // ================================================================
  81. // API CALL: AMBIL DATA JADWAL SHOLAT DARI GITHUB
  82. // ================================================================
  83. /// 🔹 Mengambil data JSON jadwal sholat berdasarkan kota, bulan, dan tahun.
  84. /// 🔹 Data disimpan ke cache agar bisa diakses offline.
  85. Future<List<dynamic>> fetchJadwalSholat(
  86. String city,
  87. String month,
  88. String year,
  89. ) async {
  90. final prefs = await SharedPreferences.getInstance();
  91. final cacheKey = "$city-$year-$month";
  92.  
  93. // Gunakan cache jika tersedia
  94. final cachedData = prefs.getString(cacheKey);
  95. if (cachedData != null) {
  96. return json.decode(cachedData) as List<dynamic>;
  97. }
  98.  
  99. // Fetch dari GitHub repo jadwalsholatorg
  100. final url =
  101. 'https://raw.githubusercontent.com/lakuapik/jadwalsholatorg/master/adzan/$city/$year/$month.json';
  102. final response = await http.get(Uri.parse(url));
  103.  
  104. if (response.statusCode == 200) {
  105. await prefs.setString(cacheKey, response.body);
  106. return json.decode(response.body) as List<dynamic>;
  107. }
  108.  
  109. // Fallback ke data tahun 2019 jika gagal
  110. final fallbackUrl =
  111. 'https://raw.githubusercontent.com/lakuapik/jadwalsholatorg/master/adzan/$city/2019/$month.json';
  112. final fallbackResponse = await http.get(Uri.parse(fallbackUrl));
  113.  
  114. if (fallbackResponse.statusCode == 200) {
  115. await prefs.setString(cacheKey, fallbackResponse.body);
  116. return json.decode(fallbackResponse.body) as List<dynamic>;
  117. }
  118.  
  119. throw Exception('Gagal memuat jadwal sholat untuk $city ($month-$year)');
  120. }
  121.  
  122. // ================================================================
  123. // LOGIKA FUZZY MATCH: DETEKSI KOTA TERDEKAT
  124. // ================================================================
  125. /// 🔹 Mencocokkan nama kota pengguna dengan daftar kota dari GitHub.
  126. ///
  127. /// 🔸 Contoh:
  128. /// "Jakarta Selatan" → dicocokkan → "jakarta"
  129. Future<String> getClosestCity(double userLat, double userLon) async {
  130. final response = await http.get(
  131. Uri.parse(
  132. 'https://raw.githubusercontent.com/lakuapik/jadwalsholatorg/master/kota.json',
  133. ),
  134. );
  135. if (response.statusCode != 200) throw Exception('Gagal memuat daftar kota');
  136.  
  137. final List<dynamic> cityList = json.decode(response.body);
  138. List<Placemark> placemarks = await placemarkFromCoordinates(
  139. userLat,
  140. userLon,
  141. );
  142. Placemark place = placemarks.first;
  143.  
  144. String userCity =
  145. (place.subAdministrativeArea ??
  146. place.locality ??
  147. place.administrativeArea ??
  148. "")
  149. .toLowerCase()
  150. .replaceAll(" ", "");
  151.  
  152. double bestScore = 0.0;
  153. String bestMatch = cityList.first;
  154. for (var city in cityList) {
  155. double score = city.toString().similarityTo(userCity);
  156. if (score > bestScore) {
  157. bestScore = score;
  158. bestMatch = city.toString();
  159. }
  160. }
  161. return bestMatch;
  162. }
  163.  
  164. // ================================================================
  165. // PERHITUNGAN: WAKTU SHOLAT BERIKUTNYA & COUNTDOWN
  166. // ================================================================
  167. /// 🔹 Menghitung waktu sholat berikutnya berdasarkan jadwal hari ini.
  168. /// 🔹 Menampilkan countdown waktu tersisa.
  169. void _calculateNextPrayer() {
  170. if (_jadwalSholat == null || _jadwalSholat!.isEmpty) return;
  171.  
  172. final now = DateTime.now();
  173. final format = DateFormat('HH:mm');
  174. final todayDate = DateFormat('yyyy-MM-dd').format(now);
  175.  
  176. var todaySchedule = _jadwalSholat?.firstWhere(
  177. (e) => e['tanggal'] == todayDate,
  178. orElse: () => null,
  179. );
  180. if (todaySchedule == null) return;
  181.  
  182. // Helper untuk parsing waktu
  183. DateTime parseTime(String hhmm) {
  184. final t = format.parse(hhmm);
  185. return DateTime(now.year, now.month, now.day, t.hour, t.minute);
  186. }
  187.  
  188. // Peta jadwal sholat
  189. final prayers = {
  190. "Shubuh": parseTime(todaySchedule['shubuh']),
  191. "Dzuhur": parseTime(todaySchedule['dzuhur']),
  192. "Ashar": parseTime(todaySchedule['ashr']),
  193. "Maghrib": parseTime(todaySchedule['magrib']),
  194. "Isya": parseTime(todaySchedule['isya']),
  195. };
  196.  
  197. // Cari waktu sholat berikutnya
  198. String nextPrayer = "Shubuh";
  199. Duration? closest;
  200. prayers.forEach((name, time) {
  201. final diff = time.difference(now);
  202. if (diff > Duration.zero && (closest == null || diff < closest!)) {
  203. closest = diff;
  204. nextPrayer = name;
  205. }
  206. });
  207.  
  208. setState(() {
  209. _prayerName = nextPrayer;
  210. _timeRemaining = closest;
  211. _prayerTime = closest != null
  212. ? DateFormat('HH:mm').format(prayers[nextPrayer]!)
  213. : "N/A";
  214. });
  215.  
  216. // Timer countdown
  217. _countdownTimer?.cancel();
  218. _countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
  219. final remaining = prayers[nextPrayer]!.difference(DateTime.now());
  220. if (remaining.isNegative) {
  221. timer.cancel();
  222. _calculateNextPrayer();
  223. } else {
  224. setState(() => _timeRemaining = remaining);
  225. }
  226. });
  227. }
  228.  
  229. // ================================================================
  230. // PERMINTAAN IZIN AKSES LOKASI
  231. // ================================================================
  232. /// 🔹 Meminta izin lokasi dari pengguna.
  233. Future<bool> _requestLocationPermission() async {
  234. var status = await Permission.location.status;
  235. if (status.isGranted) return true;
  236. if (status.isDenied) {
  237. status = await Permission.location.request();
  238. return status.isGranted;
  239. }
  240. if (status.isPermanentlyDenied) {
  241. await openAppSettings();
  242. }
  243. return false;
  244. }
  245.  
  246. // ================================================================
  247. // DIALOG ERROR
  248. // ================================================================
  249. /// 🔹 Menampilkan dialog jika terjadi kesalahan (mis. gagal lokasi).
  250. void _showErrorDialog(String msg) {
  251. showDialog(
  252. context: context,
  253. builder: (_) => AlertDialog(
  254. title: const Text("Terjadi Kesalahan"),
  255. content: Text(msg),
  256. actions: [
  257. TextButton(
  258. onPressed: () => Navigator.pop(context),
  259. child: const Text("OK"),
  260. ),
  261. ],
  262. ),
  263. );
  264. }
  265.  
Advertisement
Add Comment
Please, Sign In to add comment