diff --git a/.metadata b/.metadata index a3e76dd..b5d435a 100644 --- a/.metadata +++ b/.metadata @@ -4,8 +4,8 @@ # This file should be version controlled and should not be manually edited. version: - revision: "ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a" - channel: "stable" + revision: "58ba6c295d8ccf125366b7e871b1c41fe2255f75" + channel: "master" project_type: app @@ -13,17 +13,26 @@ project_type: app migration: platforms: - platform: root - create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a + create_revision: 58ba6c295d8ccf125366b7e871b1c41fe2255f75 + base_revision: 58ba6c295d8ccf125366b7e871b1c41fe2255f75 + - platform: android + create_revision: 58ba6c295d8ccf125366b7e871b1c41fe2255f75 + base_revision: 58ba6c295d8ccf125366b7e871b1c41fe2255f75 + - platform: ios + create_revision: 58ba6c295d8ccf125366b7e871b1c41fe2255f75 + base_revision: 58ba6c295d8ccf125366b7e871b1c41fe2255f75 - platform: linux - create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a + create_revision: 58ba6c295d8ccf125366b7e871b1c41fe2255f75 + base_revision: 58ba6c295d8ccf125366b7e871b1c41fe2255f75 - platform: macos - create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a + create_revision: 58ba6c295d8ccf125366b7e871b1c41fe2255f75 + base_revision: 58ba6c295d8ccf125366b7e871b1c41fe2255f75 + - platform: web + create_revision: 58ba6c295d8ccf125366b7e871b1c41fe2255f75 + base_revision: 58ba6c295d8ccf125366b7e871b1c41fe2255f75 - platform: windows - create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a + create_revision: 58ba6c295d8ccf125366b7e871b1c41fe2255f75 + base_revision: 58ba6c295d8ccf125366b7e871b1c41fe2255f75 # User provided section diff --git a/.vs/CMakeWorkspaceSettings.json b/.vs/CMakeWorkspaceSettings.json new file mode 100644 index 0000000..d3e1057 --- /dev/null +++ b/.vs/CMakeWorkspaceSettings.json @@ -0,0 +1,3 @@ +{ + "enableCMake": false +} \ No newline at end of file diff --git a/.vs/ProjectSettings.json b/.vs/ProjectSettings.json new file mode 100644 index 0000000..0cf5ea5 --- /dev/null +++ b/.vs/ProjectSettings.json @@ -0,0 +1,3 @@ +{ + "CurrentProjectSetting": "No Configurations" +} \ No newline at end of file diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json new file mode 100644 index 0000000..6648b61 --- /dev/null +++ b/.vs/VSWorkspaceState.json @@ -0,0 +1,9 @@ +{ + "ExpandedNodes": [ + "", + "\\lib", + "\\lib\\screens" + ], + "SelectedNode": "\\lib\\screens\\detail_doa_screen.dart", + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/.vs/quran/FileContentIndex/9f612705-2a03-4eca-9969-5fc81d26e92f.vsidx b/.vs/quran/FileContentIndex/9f612705-2a03-4eca-9969-5fc81d26e92f.vsidx new file mode 100644 index 0000000..ab132a0 Binary files /dev/null and b/.vs/quran/FileContentIndex/9f612705-2a03-4eca-9969-5fc81d26e92f.vsidx differ diff --git a/.vs/quran/v17/.wsuo b/.vs/quran/v17/.wsuo new file mode 100644 index 0000000..0296ef4 Binary files /dev/null and b/.vs/quran/v17/.wsuo differ diff --git a/.vs/quran/v17/Browse.VC.db b/.vs/quran/v17/Browse.VC.db new file mode 100644 index 0000000..bc5a138 Binary files /dev/null and b/.vs/quran/v17/Browse.VC.db differ diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite new file mode 100644 index 0000000..0e1233d Binary files /dev/null and b/.vs/slnx.sqlite differ diff --git a/android/app/src/main/res/drawable/icon.png b/android/app/src/main/res/drawable/icon.png new file mode 100644 index 0000000..3917205 Binary files /dev/null and b/android/app/src/main/res/drawable/icon.png differ diff --git a/android/app/src/main/res/raw/adzan.mp3 b/android/app/src/main/res/raw/adzan.mp3 new file mode 100644 index 0000000..581d32d Binary files /dev/null and b/android/app/src/main/res/raw/adzan.mp3 differ diff --git a/android/app/src/main/res/raw/keep.xml b/android/app/src/main/res/raw/keep.xml new file mode 100644 index 0000000..149f705 --- /dev/null +++ b/android/app/src/main/res/raw/keep.xml @@ -0,0 +1,2 @@ + + diff --git a/assets/sound/adzan.mp3 b/assets/sound/adzan.mp3 new file mode 100644 index 0000000..581d32d Binary files /dev/null and b/assets/sound/adzan.mp3 differ diff --git a/ios/RunnerTests/RunnerTests.swift b/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..86a7c3b --- /dev/null +++ b/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/lib/main.dart b/lib/main.dart index e1d0904..3fbb625 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:quran/screens/home_screen.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'services/notification.dart'; +import 'package:timezone/data/latest.dart' as tz; final colorScheme = ColorScheme.fromSeed( brightness: Brightness.light, @@ -16,6 +18,9 @@ final theme = ThemeData().copyWith( textTheme: GoogleFonts.poppinsTextTheme()); void main() { + WidgetsFlutterBinding.ensureInitialized(); + NotificationService().initNotification(); + tz.initializeTimeZones(); runApp( const ProviderScope(child: MyApp()), ); diff --git a/lib/screens/jadwal_sholat_screen.dart b/lib/screens/jadwal_sholat_screen.dart index 29cde3b..01446e0 100644 --- a/lib/screens/jadwal_sholat_screen.dart +++ b/lib/screens/jadwal_sholat_screen.dart @@ -22,7 +22,6 @@ class _JadwalScreenState extends ConsumerState { bool isLoading = false; bool isMocked = false; bool errorLocation = false; - bool delay = true; Future _determinePosition() async { LocationPermission permission; @@ -33,9 +32,6 @@ class _JadwalScreenState extends ConsumerState { permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); - setState(() { - errorLocation = true; - }); if (permission == LocationPermission.denied) { // Permissions are denied, next time you could try @@ -44,6 +40,9 @@ class _JadwalScreenState extends ConsumerState { // returned true. According to Android guidelines // your App should show an explanatory UI now. + setState(() { + errorLocation = true; + }); return Future.error('Acceses denied'); } } @@ -57,6 +56,7 @@ class _JadwalScreenState extends ConsumerState { setState(() { isLoading = true; }); + final getPosition = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.high); @@ -72,7 +72,9 @@ class _JadwalScreenState extends ConsumerState { position = getPosition; userPlace = '${place.subAdministrativeArea}'; }); - } catch (e) {} + } catch (e) { + print(e); + } setState(() { isLoading = false; }); @@ -82,17 +84,8 @@ class _JadwalScreenState extends ConsumerState { @override void initState() { - super.initState(); - Future.delayed( - Duration(seconds: 2), - () { - setState(() { - delay = false; - }); - }, - ); - _determinePosition(); + super.initState(); } @override @@ -121,7 +114,7 @@ class _JadwalScreenState extends ConsumerState { )) ], ) - : delay == true + : isLoading == true ? Center( child: CircularProgressIndicator(), ) @@ -154,9 +147,8 @@ class _JadwalScreenState extends ConsumerState { height: 18, ), data.when( - error: (error, stackTrace) => const Center( - child: Text('No data'), - ), + error: (error, stackTrace) => + Text(error.toString()), loading: () => const Center( child: CircularProgressIndicator(), ), diff --git a/lib/services/notification.dart b/lib/services/notification.dart new file mode 100644 index 0000000..67cbe6f --- /dev/null +++ b/lib/services/notification.dart @@ -0,0 +1,64 @@ +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; + +import 'package:timezone/timezone.dart' as tz; + +class NotificationService { + final FlutterLocalNotificationsPlugin notificationsPlugin = + FlutterLocalNotificationsPlugin(); + + final sound = 'adzan.mp3'; + + Future initNotification() async { + AndroidInitializationSettings initializationSettingsAndroid = + const AndroidInitializationSettings('icon'); + + var initializationSettingsIOS = DarwinInitializationSettings( + requestAlertPermission: true, + requestBadgePermission: true, + requestSoundPermission: true, + onDidReceiveLocalNotification: + (int id, String? title, String? body, String? payload) async {}); + + var initializationSettings = InitializationSettings( + android: initializationSettingsAndroid, iOS: initializationSettingsIOS); + await notificationsPlugin.initialize(initializationSettings, + onDidReceiveNotificationResponse: + (NotificationResponse notificationResponse) async {}); + } + + notificationDetails() { + return NotificationDetails( + android: AndroidNotificationDetails( + 'your_channel_id', + 'channelName', + playSound: true, + sound: RawResourceAndroidNotificationSound('adzan'), + importance: Importance.max, + priority: Priority.high, + ), + iOS: DarwinNotificationDetails()); + } + + Future showNotification( + {int id = 0, String? title, String? body, String? payLoad}) async { + return notificationsPlugin.show( + id, title, body, await notificationDetails()); + } + + Future scheduleNotification( + {int id = 0, + String? title, + String? body, + String? payLoad, + required DateTime scheduledNotificationDateTime}) async { + return notificationsPlugin.zonedSchedule( + id, + title, + body, + tz.TZDateTime.from(scheduledNotificationDateTime, tz.local), + await notificationDetails(), + androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, + uiLocalNotificationDateInterpretation: + UILocalNotificationDateInterpretation.absoluteTime); + } +} diff --git a/lib/widgets/jadwal_sholat_list.dart b/lib/widgets/jadwal_sholat_list.dart index 242a256..f044c43 100644 --- a/lib/widgets/jadwal_sholat_list.dart +++ b/lib/widgets/jadwal_sholat_list.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; import 'package:quran/models/jadwal_sholat_model.dart'; +import 'package:quran/services/notification.dart'; +import 'package:intl/intl.dart'; class JadwalList extends StatelessWidget { const JadwalList({super.key, required this.data}); @@ -20,56 +22,164 @@ class JadwalList extends StatelessWidget { ), Padding( padding: const EdgeInsets.all(8.0), - child: ListTile( - shape: Border.all(), - title: const Text( - 'Imsak :', - style: TextStyle(fontSize: 18), - ), - trailing: Text( - data.data.imsak, - style: const TextStyle(fontSize: 18), - )), + child: InkWell( + onTap: () { + NotificationService().showNotification( + title: 'Waktu Imsak', + body: 'Imsak', + ); + }, + child: ListTile( + shape: Border.all(), + title: const Text( + 'Imsak :', + style: TextStyle(fontSize: 18), + ), + trailing: Text( + data.data.imsak, + style: const TextStyle(fontSize: 18), + )), + ), ), Padding( padding: const EdgeInsets.all(8.0), - child: ListTile( - shape: Border.all(), - title: const Text('Subuh :', style: TextStyle(fontSize: 18)), - trailing: - Text(data.data.subuh, style: const TextStyle(fontSize: 18))), + child: InkWell( + onTap: () { + List timeParts = data.data.subuh.split(':'); + int hour = int.parse(timeParts[0]); + int minute = int.parse(timeParts[1]); + + DateTime now = DateTime.now(); + DateTime subuhTime = + DateTime(now.year, now.month, now.day, hour, minute); + + if (subuhTime.isBefore(now)) { + subuhTime = subuhTime.add(Duration(days: 1)); + } + + NotificationService().scheduleNotification( + title: 'Waktu Dzuhur', + body: 'Sholat Dzuhur', + scheduledNotificationDateTime: subuhTime); + }, + child: ListTile( + shape: Border.all(), + title: const Text('Subuh :', style: TextStyle(fontSize: 18)), + trailing: Text(data.data.subuh, + style: const TextStyle(fontSize: 18))), + ), ), Padding( padding: const EdgeInsets.all(8.0), - child: ListTile( - shape: Border.all(), - title: const Text('Dzuhur :', style: TextStyle(fontSize: 18)), - trailing: - Text(data.data.dzuhur, style: const TextStyle(fontSize: 18))), + child: InkWell( + onTap: () { + List timeParts = data.data.dzuhur.split(':'); + int hour = int.parse(timeParts[0]); + int minute = int.parse(timeParts[1]); + + DateTime now = DateTime.now(); + DateTime dzuhurTime = + DateTime(now.year, now.month, now.day, hour, minute); + + if (dzuhurTime.isBefore(now)) { + dzuhurTime = dzuhurTime.add(Duration(days: 1)); + } + + NotificationService().scheduleNotification( + title: 'Waktu Dzuhur', + body: 'Sholat Dzuhur', + scheduledNotificationDateTime: dzuhurTime); + }, + child: ListTile( + shape: Border.all(), + title: const Text('Dzuhur :', style: TextStyle(fontSize: 18)), + trailing: Text(data.data.dzuhur, + style: const TextStyle(fontSize: 18))), + ), ), Padding( padding: const EdgeInsets.all(8.0), - child: ListTile( - shape: Border.all(), - title: const Text('Ashar :', style: TextStyle(fontSize: 18)), - trailing: - Text(data.data.ashar, style: const TextStyle(fontSize: 18))), + child: InkWell( + onTap: () { + List timeParts = data.data.ashar.split(':'); + int hour = int.parse(timeParts[0]); + int minute = int.parse(timeParts[1]); + + DateTime now = DateTime.now(); + DateTime asharTime = + DateTime(now.year, now.month, now.day, hour, minute); + + if (asharTime.isBefore(now)) { + asharTime = asharTime.add(Duration(days: 1)); + } + + NotificationService().scheduleNotification( + title: 'Waktu Ashar', + body: 'Sholat Ashar', + scheduledNotificationDateTime: asharTime); + }, + child: ListTile( + shape: Border.all(), + title: const Text('Ashar :', style: TextStyle(fontSize: 18)), + trailing: Text(data.data.ashar, + style: const TextStyle(fontSize: 18))), + ), ), Padding( padding: const EdgeInsets.all(8.0), - child: ListTile( - shape: Border.all(), - title: const Text('Maghrib :'), - trailing: Text(data.data.maghrib, - style: const TextStyle(fontSize: 18))), + child: InkWell( + onTap: () { + List timeParts = data.data.maghrib.split(':'); + int hour = int.parse(timeParts[0]); + int minute = int.parse(timeParts[1]); + + DateTime now = DateTime.now(); + DateTime maghribTime = + DateTime(now.year, now.month, now.day, hour, minute); + + if (maghribTime.isBefore(now)) { + maghribTime = maghribTime.add(Duration(days: 1)); + } + + NotificationService().scheduleNotification( + title: 'Waktu Maghrib', + body: 'Sholat Maghrib', + scheduledNotificationDateTime: maghribTime); + }, + child: ListTile( + shape: Border.all(), + title: const Text('Maghrib :'), + trailing: Text(data.data.maghrib, + style: const TextStyle(fontSize: 18))), + ), ), Padding( padding: const EdgeInsets.all(8.0), - child: ListTile( - shape: Border.all(), - title: const Text('Isya :', style: TextStyle(fontSize: 18)), - trailing: - Text(data.data.isya, style: const TextStyle(fontSize: 18))), + child: InkWell( + onTap: () { + List timeParts = data.data.isya.split(':'); + int hour = int.parse(timeParts[0]); + int minute = int.parse(timeParts[1]); + + DateTime now = DateTime.now(); + DateTime isyaTime = + DateTime(now.year, now.month, now.day, hour, minute); + + if (isyaTime.isBefore(now)) { + isyaTime = isyaTime.add(Duration(days: 1)); + } + + NotificationService().scheduleNotification( + title: 'Waktu Isya', + body: 'Sholat Isya', + scheduledNotificationDateTime: isyaTime); + }, + child: ListTile( + shape: Border.all(), + title: const Text('Isya :', style: TextStyle(fontSize: 18)), + trailing: + Text(data.data.isya, style: const TextStyle(fontSize: 18))), + ), ), ], ); diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 32aadf6..fd45162 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,10 +5,12 @@ import FlutterMacOS import Foundation +import flutter_local_notifications import geolocator_apple import path_provider_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index d7d121d..94bbb74 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: archive - sha256: "49b1fad315e57ab0bbc15bcbb874e83116a1d78f77ebd500a4af6c9407d6b28e" + sha256: e0902a06f0e00414e4e3438a084580161279f137aeb862274710f29ec10cf01e url: "https://pub.dev" source: hosted - version: "3.3.8" + version: "3.3.9" args: dependency: transitive description: @@ -73,6 +73,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.2" + console: + dependency: transitive + description: + name: console + sha256: e04e7824384c5b39389acdd6dc7d33f3efe6b232f6f16d7626f194f6a01ad69a + url: "https://pub.dev" + source: hosted + version: "4.1.0" convert: dependency: transitive description: @@ -97,6 +105,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.6" + dbus: + dependency: transitive + description: + name: dbus + sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + url: "https://pub.dev" + source: hosted + version: "0.7.8" fake_async: dependency: transitive description: @@ -126,6 +142,30 @@ packages: url: "https://pub.dev" source: hosted version: "0.13.1" + flutter_local_notifications: + dependency: "direct main" + description: + name: flutter_local_notifications + sha256: "3002092e5b8ce2f86c3361422e52e6db6776c23ee21e0b2f71b892bf4259ef04" + url: "https://pub.dev" + source: hosted + version: "15.1.1" + flutter_local_notifications_linux: + dependency: transitive + description: + name: flutter_local_notifications_linux + sha256: "33f741ef47b5f63cc7f78fe75eeeac7e19f171ff3c3df054d84c1e38bedb6a03" + url: "https://pub.dev" + source: hosted + version: "4.0.0+1" + flutter_local_notifications_platform_interface: + dependency: transitive + description: + name: flutter_local_notifications_platform_interface + sha256: "7cf643d6d5022f3baed0be777b0662cce5919c0a7b86e700299f22dc4ae660ef" + url: "https://pub.dev" + source: hosted + version: "7.0.0+1" flutter_riverpod: dependency: "direct main" description: @@ -180,18 +220,18 @@ packages: dependency: "direct main" description: name: geolocator - sha256: "5c23f3613f50586c0bbb2b8f970240ae66b3bd992088cf60dd5ee2e6f7dde3a8" + sha256: "9f1c9a70dd25fc9d9574ff17ba714cf3bc7894258efd7d1ce0debfafe2f8e1d8" url: "https://pub.dev" source: hosted - version: "9.0.2" + version: "10.0.1" geolocator_android: dependency: transitive description: name: geolocator_android - sha256: "835ff5b4888a2f8eba128996494faf9c5d422785322a81dc0565b99e0f6c379d" + sha256: db75aacf93fcdc2c0ba20066019da30e3fde7b102cf579d4bd4ea542e8a8cf17 url: "https://pub.dev" source: hosted - version: "4.2.2" + version: "4.2.4" geolocator_apple: dependency: transitive description: @@ -204,10 +244,10 @@ packages: dependency: transitive description: name: geolocator_platform_interface - sha256: af4d69231452f9620718588f41acc4cb58312368716bfff2e92e770b46ce6386 + sha256: b2a0c09d2cfe8ad6a8cc44fada5c1092c49286e8c30922914dfe2b23799a682f url: "https://pub.dev" source: hosted - version: "4.0.7" + version: "4.0.8" geolocator_web: dependency: transitive description: @@ -220,26 +260,34 @@ packages: dependency: transitive description: name: geolocator_windows - sha256: "4f4218f122a6978d0ad655fa3541eea74c67417440b09f0657238810d5af6bdc" + sha256: "463045515b08bd83f73e014359c4ad063b902eb3899952cfb784497ae6c6583b" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + get_it: + dependency: transitive + description: + name: get_it + sha256: f79870884de16d689cf9a7d15eedf31ed61d750e813c538a6efb92660fea83c3 url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "7.6.4" google_fonts: dependency: "direct main" description: name: google_fonts - sha256: "6b6f10f0ce3c42f6552d1c70d2c28d764cf22bb487f50f66cca31dcd5194f4d6" + sha256: e20ff62b158b96f392bfc8afe29dee1503c94fbea2cbe8186fd59b756b8ae982 url: "https://pub.dev" source: hosted - version: "4.0.4" + version: "5.1.0" http: dependency: "direct main" description: name: http - sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "0.13.6" + version: "1.1.0" http_parser: dependency: transitive description: @@ -256,6 +304,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.17" + intl: + dependency: "direct main" + description: + name: intl + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + url: "https://pub.dev" + source: hosted + version: "0.18.1" js: dependency: transitive description: @@ -304,6 +360,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + msix: + dependency: "direct dev" + description: + name: msix + sha256: "76c87b8207323803169626a55afd78bbb8413c984df349a76598b9fbf9224677" + url: "https://pub.dev" + source: hosted + version: "3.16.1" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" path: dependency: transitive description: @@ -392,6 +464,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.7.3" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" riverpod: dependency: transitive description: @@ -469,6 +549,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.0" + timezone: + dependency: transitive + description: + name: timezone + sha256: "1cfd8ddc2d1cfd836bc93e67b9be88c3adaeca6f40a00ca999104c30693cdca0" + url: "https://pub.dev" + source: hosted + version: "0.9.2" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 70e410d..7744c3f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,23 +34,30 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 - google_fonts: ^4.0.4 + google_fonts: ^5.1.0 lottie: ^2.3.2 - http: ^0.13.6 + http: ^1.1.0 flutter_riverpod: ^2.3.6 search_page: ^2.3.0 - geolocator: ^9.0.2 - geocoding: ^2.1.0 + geolocator: ^10.0.1 + geocoding: ^2.0.6 + flutter_local_notifications: ^15.1.1 + intl: ^0.18.1 dev_dependencies: flutter_test: sdk: flutter flutter_launcher_icons: "^0.13.1" + msix: ^3.16.1 flutter_launcher_icons: android: "launcher_icon" ios: true image_path: "assets/icon/icon.png" + windows: + generate: true + image_path: "assets/icon/icon.png" + icon_size: 48 # The "flutter_lints" package below contains a set of recommended lints to # encourage good coding practices. The lint set provided by the package is @@ -73,6 +80,7 @@ flutter: assets: - assets/lottie/ - assets/image/ + - assets/sound/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware diff --git a/test/widget_test.dart b/test/widget_test.dart index 4e0461e..8b13789 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -1,31 +1 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. -import 'dart:convert'; - -import 'package:http/http.dart' as http; -import 'package:quran/models/jadwal_sholat_model.dart'; - -main() async { - final url = Uri.parse( - 'https://api.banghasan.com/sholat/format/json/kota/nama/kota serang'); - final response = await http.get(url); - - List data = (jsonDecode(response.body) as Map)['kota']; - final dataId = data.map((e) => e['id']).toList(); - - DateTime dateToday = DateTime.now(); - String date = dateToday.toString().substring(0, 10); - - final uri = Uri.parse( - 'https://api.banghasan.com/sholat/format/json/jadwal/kota/${dataId[0]}/tanggal/$date'); - - final responseData = await http.get(uri); - Map jadwal = - (jsonDecode(responseData.body) as Map)['jadwal']; - JadwalSholat jadwalDataApi = JadwalSholat.fromJson(jadwal); -} diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index b8098a5..070dad6 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -8,7 +8,7 @@ set(BINARY_NAME "quran") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. -cmake_policy(SET CMP0063 NEW) +cmake_policy(VERSION 3.14...3.25) # Define build configuration option. get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) @@ -52,6 +52,7 @@ add_subdirectory(${FLUTTER_MANAGED_DIR}) # Application build; see runner/CMakeLists.txt. add_subdirectory("runner") + # Generated plugin build rules, which manage building the plugins and adding # them to the application. include(flutter/generated_plugins.cmake) diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt index 930d207..903f489 100644 --- a/windows/flutter/CMakeLists.txt +++ b/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/windows/runner/utils.cpp b/windows/runner/utils.cpp index f5bf9fa..b2b0873 100644 --- a/windows/runner/utils.cpp +++ b/windows/runner/utils.cpp @@ -47,16 +47,17 @@ std::string Utf8FromUtf16(const wchar_t* utf16_string) { } int target_length = ::WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, nullptr, 0, nullptr, nullptr); + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); std::string utf8_string; - if (target_length == 0 || target_length > utf8_string.max_size()) { + if (target_length <= 0 || target_length > utf8_string.max_size()) { return utf8_string; } utf8_string.resize(target_length); int converted_length = ::WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, utf8_string.data(), - target_length, nullptr, nullptr); + input_length, utf8_string.data(), target_length, nullptr, nullptr); if (converted_length == 0) { return std::string(); } diff --git a/windows/runner/win32_window.cpp b/windows/runner/win32_window.cpp index 041a385..60608d0 100644 --- a/windows/runner/win32_window.cpp +++ b/windows/runner/win32_window.cpp @@ -60,7 +60,7 @@ class WindowClassRegistrar { public: ~WindowClassRegistrar() = default; - // Returns the singleton registar instance. + // Returns the singleton registrar instance. static WindowClassRegistrar* GetInstance() { if (!instance_) { instance_ = new WindowClassRegistrar(); diff --git a/windows/runner/win32_window.h b/windows/runner/win32_window.h index c86632d..e901dde 100644 --- a/windows/runner/win32_window.h +++ b/windows/runner/win32_window.h @@ -77,7 +77,7 @@ class Win32Window { // OS callback called by message pump. Handles the WM_NCCREATE message which // is passed when the non-client area is being created and enables automatic // non-client DPI scaling so that the non-client area automatically - // responsponds to changes in DPI. All other messages are handled by + // responds to changes in DPI. All other messages are handled by // MessageHandler. static LRESULT CALLBACK WndProc(HWND const window, UINT const message,