diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..c5f3f6b
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "java.configuration.updateBuildConfiguration": "interactive"
+}
\ No newline at end of file
diff --git a/app/README.md b/app/README.md
index 2db82b7..4ab2ffc 100644
--- a/app/README.md
+++ b/app/README.md
@@ -9,12 +9,15 @@ lib/
│ ├── cabinetdb.dart
│ └── profiledb.dart
├── home/
-│ ├── addMenu.dart
+│ ├── add_menu.dart
│ └── home.dart
├── models/
│ ├── cabinet.dart
│ └── profile.dart
├── profile/
│ └── profile.dart
+├── services/
+│ ├── alarm_service.dart
+│ └── notification_service.dart
├── dose.dart
└── main.dart
\ No newline at end of file
diff --git a/app/android/app/build.gradle.kts b/app/android/app/build.gradle.kts
index e272c33..b661ee0 100644
--- a/app/android/app/build.gradle.kts
+++ b/app/android/app/build.gradle.kts
@@ -11,6 +11,7 @@ android {
ndkVersion = flutter.ndkVersion
compileOptions {
+ isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
@@ -42,3 +43,7 @@ android {
flutter {
source = "../.."
}
+
+dependencies {
+ coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
+}
\ No newline at end of file
diff --git a/app/android/app/src/main/AndroidManifest.xml b/app/android/app/src/main/AndroidManifest.xml
index 6ee3df0..af36229 100644
--- a/app/android/app/src/main/AndroidManifest.xml
+++ b/app/android/app/src/main/AndroidManifest.xml
@@ -1,21 +1,26 @@
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/lib/db/cabinetdb.dart b/app/lib/db/cabinetdb.dart
index 86c81d4..9a0bade 100644
--- a/app/lib/db/cabinetdb.dart
+++ b/app/lib/db/cabinetdb.dart
@@ -8,6 +8,30 @@ class DatabaseHelper {
DatabaseHelper._init();
+ Future update(Cabinet cabinet) async {
+ final db = await instance.database;
+ return db.update(
+ 'cabinet',
+ cabinet.toMap(),
+ where: 'id = ?',
+ whereArgs: [cabinet.id],
+ );
+ }
+
+ Future readMedicine(int id) async {
+ final db = await instance.database;
+ final maps = await db.query(
+ 'cabinet',
+ where: 'id = ?',
+ whereArgs: [id],
+ );
+
+ if (maps.isNotEmpty) {
+ return Cabinet.fromMap(maps.first);
+ }
+ return null;
+ }
+
Future get database async {
if (_database != null) return _database!;
_database = await _initDB('dose.db');
diff --git a/app/lib/dose.dart b/app/lib/dose.dart
index bf5da0f..5801601 100644
--- a/app/lib/dose.dart
+++ b/app/lib/dose.dart
@@ -2,7 +2,10 @@ import 'package:flutter/material.dart';
import 'package:app/home/home.dart';
import 'package:app/analytics/analytics.dart';
import 'package:app/profile/profile.dart';
-import 'package:app/home/addMenu.dart';
+import 'package:app/home/add_menu.dart';
+import 'dart:async';
+import 'package:alarm/alarm.dart';
+import 'package:app/services/alarm_ring.dart';
class Dose extends StatefulWidget {
const Dose({super.key});
@@ -43,6 +46,27 @@ class _DoseState extends State {
}
}
+ StreamSubscription? ringSubscription;
+
+ @override
+ void initState() {
+ super.initState();
+ ringSubscription = Alarm.ringStream.stream.listen((alarmSettings) {
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => AlarmRingScreen(alarmSettings: alarmSettings),
+ ),
+ );
+ });
+ }
+
+ @override
+ void dispose() {
+ ringSubscription?.cancel();
+ super.dispose();
+ }
+
@override
Widget build(BuildContext context) {
return Scaffold(
diff --git a/app/lib/home/addMenu.dart b/app/lib/home/add_menu.dart
similarity index 93%
rename from app/lib/home/addMenu.dart
rename to app/lib/home/add_menu.dart
index b110793..91231a9 100644
--- a/app/lib/home/addMenu.dart
+++ b/app/lib/home/add_menu.dart
@@ -1,6 +1,8 @@
-import 'package:app/models/cabinet.dart';
import 'package:flutter/material.dart';
+import 'package:app/models/cabinet.dart';
import 'package:app/db/cabinetdb.dart';
+import 'package:app/services/notification_service.dart';
+import 'package:app/services/alarm_service.dart';
class AddMedicineMenu extends StatefulWidget {
final VoidCallback onSave;
@@ -49,12 +51,21 @@ class _AddMedicineMenuState extends State {
priority: _priority,
);
- await DatabaseHelper.instance.create(medicine);
+ int newId = await DatabaseHelper.instance.create(medicine);
+
+ await AlarmService().scheduleMedicineAlarm(newId, medicine);
+ await NotificationHelper().scheduleMedicineNotification(
+ newId,
+ medicine.name,
+ timeString
+ );
+
widget.onSave();
if (mounted) {
Navigator.pop(context);
}
+
}
}
@@ -129,7 +140,7 @@ class _AddMedicineMenuState extends State {
const SizedBox(width: 12),
Expanded(
child: DropdownButtonFormField(
- value: _cycle,
+ initialValue: _cycle,
decoration: const InputDecoration(
labelText: "Cycle",
border: OutlineInputBorder(),
@@ -177,7 +188,7 @@ class _AddMedicineMenuState extends State {
const SizedBox(height: 16),
DropdownButtonFormField(
- value: _priority,
+ initialValue: _priority,
decoration: const InputDecoration(
labelText: "Priority",
border: OutlineInputBorder(),
diff --git a/app/lib/home/home.dart b/app/lib/home/home.dart
index 38d76a0..1f9de2c 100644
--- a/app/lib/home/home.dart
+++ b/app/lib/home/home.dart
@@ -1,6 +1,8 @@
import 'package:app/models/cabinet.dart';
import 'package:flutter/material.dart';
import 'package:app/db/cabinetdb.dart';
+import 'package:app/services/notification_service.dart';
+import 'package:app/services/alarm_service.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@@ -44,6 +46,8 @@ class _HomePageState extends State {
if (result == 'delete') {
await DatabaseHelper.instance.delete(id);
+ await NotificationHelper().cancelNotification(id);
+ await AlarmService().cancelAlarm(id);
setState(() {
_refreshList();
});
@@ -54,121 +58,132 @@ class _HomePageState extends State {
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
- return FutureBuilder>(
- future: _medicinesFuture,
- builder: (context, snapshot) {
- if (snapshot.hasError) {
- return Center(
- child: Padding(
- padding: const EdgeInsets.all(16.0),
- child: Text(
- "Error loading data: ${snapshot.error}",
- textAlign: TextAlign.center,
- style: TextStyle(color: colorScheme.error),
- ),
- ),
- );
- }
-
- if (snapshot.connectionState == ConnectionState.waiting) {
- return const Center(child: CircularProgressIndicator());
- }
+ return Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: ElevatedButton(
+ onPressed: () async {
+ await AlarmService().triggerTestAlarm();
+ },
+ child: const Text('Test'),
+ ),
+ ),
+ Expanded(
+ child: FutureBuilder>(
+ future: _medicinesFuture,
+ builder: (context, snapshot) {
+ if (snapshot.hasError) {
+ return Center(
+ child: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Text(
+ "Error loading data: ${snapshot.error}",
+ textAlign: TextAlign.center,
+ style: TextStyle(color: colorScheme.error),
+ ),
+ ),
+ );
+ }
- final medicines = snapshot.data ?? [];
+ if (snapshot.connectionState == ConnectionState.waiting) {
+ return const Center(child: CircularProgressIndicator());
+ }
- if (medicines.isEmpty) {
- return Center(
- child: Text(
- "No reminders added yet.",
- style: TextStyle(color: colorScheme.onSurfaceVariant),
- ),
- );
- }
+ final medicines = snapshot.data ?? [];
- return ListView.builder(
- padding: const EdgeInsets.all(16),
- itemCount: medicines.length,
- itemBuilder: (context, index) {
- final med = medicines[index];
- return GestureDetector(
- onLongPressStart: (details) {
- _deleteMedicine(med.id!, details.globalPosition);
- },
- child: Card(
- elevation: 0,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(16),
- side: BorderSide(color: colorScheme.outlineVariant),
- ),
- color: colorScheme.surfaceContainer,
- margin: const EdgeInsets.only(bottom: 12),
- child: Padding(
- padding: const EdgeInsets.all(16.0),
- child: Row(
- children: [
- // Icon
- Container(
- width: 48,
- height: 48,
- decoration: BoxDecoration(
- color: colorScheme.tertiaryContainer,
- shape: BoxShape.circle,
- ),
- child: Icon(Icons.medication, color: colorScheme.onTertiaryContainer),
+ if (medicines.isEmpty) {
+ return Center(
+ child: Text(
+ "No reminders added yet.",
+ style: TextStyle(color: colorScheme.onSurfaceVariant),
+ ),
+ );
+ }
+
+ return ListView.builder(
+ padding: const EdgeInsets.all(16),
+ itemCount: medicines.length,
+ itemBuilder: (context, index) {
+ final med = medicines[index];
+ return GestureDetector(
+ onLongPressStart: (details) {
+ _deleteMedicine(med.id!, details.globalPosition);
+ },
+ child: Card(
+ elevation: 0,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(16),
+ side: BorderSide(color: colorScheme.outlineVariant),
),
- const SizedBox(width: 16),
- // Med name
- Expanded(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
+ color: colorScheme.surfaceContainer,
+ margin: const EdgeInsets.only(bottom: 12),
+ child: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Row(
children: [
+ Container(
+ width: 48,
+ height: 48,
+ decoration: BoxDecoration(
+ color: colorScheme.tertiaryContainer,
+ shape: BoxShape.circle,
+ ),
+ child: Icon(Icons.medication, color: colorScheme.onTertiaryContainer),
+ ),
+ const SizedBox(width: 16),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ med.name,
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.bold,
+ color: colorScheme.onSurface,
+ ),
+ ),
+ const SizedBox(height: 4),
+ Text(
+ med.dosage,
+ style: TextStyle(
+ fontSize: 14,
+ color: colorScheme.onSurfaceVariant,
+ ),
+ ),
+ ],
+ ),
+ ),
Text(
- med.name,
+ med.time,
style: TextStyle(
fontSize: 16,
- fontWeight: FontWeight.bold,
+ fontWeight: FontWeight.w600,
color: colorScheme.onSurface,
),
),
- const SizedBox(height: 4),
- Text(
- med.dosage,
- style: TextStyle(
- fontSize: 14,
- color: colorScheme.onSurfaceVariant,
+ const SizedBox(width: 16),
+ Container(
+ width: 40,
+ height: 40,
+ decoration: BoxDecoration(
+ color: colorScheme.primaryContainer,
+ borderRadius: BorderRadius.circular(12),
),
+ child: Icon(Icons.check, color: colorScheme.onPrimaryContainer),
),
],
),
),
- // Time
- Text(
- med.time,
- style: TextStyle(
- fontSize: 16,
- fontWeight: FontWeight.w600,
- color: colorScheme.onSurface,
- ),
- ),
- const SizedBox(width: 16),
- // Checkbox
- Container(
- width: 40,
- height: 40,
- decoration: BoxDecoration(
- color: colorScheme.primaryContainer,
- borderRadius: BorderRadius.circular(12),
- ),
- child: Icon(Icons.check, color: colorScheme.onPrimaryContainer),
- ),
- ],
- ),
- ),
- ),
- );
- },
- );
- },
+ ),
+ );
+ },
+ );
+ },
+ ),
+ ),
+ ],
);
}
}
\ No newline at end of file
diff --git a/app/lib/main.dart b/app/lib/main.dart
index 0bd93c7..8e30ed9 100644
--- a/app/lib/main.dart
+++ b/app/lib/main.dart
@@ -1,14 +1,29 @@
import 'package:flutter/material.dart';
import 'package:dynamic_color/dynamic_color.dart';
import 'package:app/dose.dart';
+import 'package:app/services/notification_service.dart';
+import 'package:app/services/alarm_service.dart';
-void main() {
- runApp(const MyApp());
+void main() async {
+ WidgetsFlutterBinding.ensureInitialized();
+ await NotificationHelper().init();
+ await AlarmService().init();
+ runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
+ static final _defaultLightColorScheme = ColorScheme.fromSeed(
+ seedColor: Colors.deepPurple,
+ brightness: Brightness.light,
+ );
+
+ static final _defaultDarkColorScheme = ColorScheme.fromSeed(
+ seedColor: Colors.deepPurple,
+ brightness: Brightness.dark,
+ );
+
@override
Widget build(BuildContext context) {
return DynamicColorBuilder(
@@ -16,15 +31,14 @@ class MyApp extends StatelessWidget {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
- colorScheme: lightDynamic,
+ colorScheme: lightDynamic ?? _defaultLightColorScheme,
useMaterial3: true,
),
darkTheme: ThemeData(
- colorScheme: darkDynamic,
+ colorScheme: darkDynamic ?? _defaultDarkColorScheme,
useMaterial3: true,
),
themeMode: ThemeMode.system,
-
home: const Dose(),
);
},
diff --git a/app/lib/services/alarm_ring.dart b/app/lib/services/alarm_ring.dart
new file mode 100644
index 0000000..81774a1
--- /dev/null
+++ b/app/lib/services/alarm_ring.dart
@@ -0,0 +1,35 @@
+import 'package:flutter/material.dart';
+import 'package:alarm/alarm.dart';
+
+class AlarmRingScreen extends StatelessWidget {
+ final AlarmSettings alarmSettings;
+
+ const AlarmRingScreen({super.key, required this.alarmSettings});
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ body: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const Icon(Icons.alarm, size: 100),
+ const SizedBox(height: 20),
+ Text(
+ alarmSettings.notificationSettings.title,
+ style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
+ ),
+ const SizedBox(height: 40),
+ ElevatedButton(
+ onPressed: () async {
+ await Alarm.stop(alarmSettings.id);
+ if (context.mounted) Navigator.pop(context);
+ },
+ child: const Text('Stop Alarm'),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
\ No newline at end of file
diff --git a/app/lib/services/alarm_service.dart b/app/lib/services/alarm_service.dart
new file mode 100644
index 0000000..aeedba6
--- /dev/null
+++ b/app/lib/services/alarm_service.dart
@@ -0,0 +1,81 @@
+import 'package:alarm/alarm.dart';
+import 'package:app/models/cabinet.dart';
+
+class AlarmService {
+ static final AlarmService _instance = AlarmService._internal();
+ factory AlarmService() => _instance;
+ AlarmService._internal();
+
+ Future triggerTestAlarm() async {
+ final alarmSettings = AlarmSettings(
+ id: 999,
+ dateTime: DateTime.now().add(const Duration(seconds: 20)),
+ assetAudioPath: null,
+ loopAudio: true,
+ vibrate: true,
+ warningNotificationOnKill: true,
+ androidFullScreenIntent: true,
+ volumeSettings: VolumeSettings.fade(
+ volume: 1.0,
+ fadeDuration: const Duration(seconds: 3),
+ ),
+ notificationSettings: NotificationSettings(
+ title: 'Test Alarm',
+ body: 'Testing the alarm system.',
+ stopButton: 'Stop',
+ ),
+ );
+
+ await Alarm.set(alarmSettings: alarmSettings);
+ }
+
+ Future init() async {
+ await Alarm.init();
+ }
+
+ Future scheduleMedicineAlarm(int id, Cabinet medicine) async {
+ if (medicine.priority != 2) return;
+
+ final parts = medicine.time.split(':');
+ final int hour = int.parse(parts[0]);
+ final int minute = int.parse(parts[1]);
+
+ DateTime now = DateTime.now();
+ DateTime scheduledDate = DateTime(
+ now.year,
+ now.month,
+ now.day,
+ hour,
+ minute,
+ );
+
+ if (scheduledDate.isBefore(now)) {
+ scheduledDate = scheduledDate.add(const Duration(days: 1));
+ }
+
+ final alarmSettings = AlarmSettings(
+ id: id,
+ dateTime: scheduledDate,
+ assetAudioPath: null,
+ loopAudio: true,
+ vibrate: true,
+ warningNotificationOnKill: true,
+ androidFullScreenIntent: true,
+ volumeSettings: VolumeSettings.fade(
+ volume: 1.0,
+ fadeDuration: const Duration(seconds: 3),
+ ),
+ notificationSettings: NotificationSettings(
+ title: 'Time to take ${medicine.name}',
+ body: 'Please take your scheduled dose.',
+ stopButton: 'Stop',
+ ),
+ );
+
+ await Alarm.set(alarmSettings: alarmSettings);
+}
+
+ Future cancelAlarm(int id) async {
+ await Alarm.stop(id);
+ }
+}
\ No newline at end of file
diff --git a/app/lib/services/notification_service.dart b/app/lib/services/notification_service.dart
new file mode 100644
index 0000000..f981b9f
--- /dev/null
+++ b/app/lib/services/notification_service.dart
@@ -0,0 +1,93 @@
+import 'package:flutter_local_notifications/flutter_local_notifications.dart';
+import 'package:timezone/data/latest_all.dart' as tz;
+import 'package:timezone/timezone.dart' as tz;
+import 'package:flutter_timezone/flutter_timezone.dart';
+import 'package:app/db/cabinetdb.dart';
+import 'package:app/models/cabinet.dart';
+
+class NotificationHelper {
+ static final NotificationHelper _instance = NotificationHelper._internal();
+ factory NotificationHelper() => _instance;
+ NotificationHelper._internal();
+
+ final FlutterLocalNotificationsPlugin plugin = FlutterLocalNotificationsPlugin();
+
+ Future init() async {
+ tz.initializeTimeZones();
+ final TimezoneInfo timeZoneInfo = await FlutterTimezone.getLocalTimezone();
+ tz.setLocalLocation(tz.getLocation(timeZoneInfo.identifier));
+
+ const AndroidInitializationSettings androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
+ const InitializationSettings settings = InitializationSettings(android: androidSettings);
+
+ await plugin.initialize(
+ settings: settings,
+ onDidReceiveNotificationResponse: (NotificationResponse response) async {
+ if (response.payload != null && response.payload!.startsWith('med_')) {
+ final int id = int.parse(response.payload!.split('_')[1]);
+
+ if (response.actionId == 'action_done') {
+ final med = await DatabaseHelper.instance.readMedicine(id);
+ if (med != null && med.currstock > 0) {
+ final updatedMed = Cabinet(
+ id: med.id,
+ name: med.name,
+ dosage: med.dosage,
+ time: med.time,
+ initstock: med.initstock,
+ currstock: med.currstock - 1,
+ priority: med.priority,
+ );
+ await DatabaseHelper.instance.update(updatedMed);
+ }
+ }
+ }
+ },
+ );
+
+ final androidImplementation = plugin.resolvePlatformSpecificImplementation();
+ await androidImplementation?.requestNotificationsPermission();
+ await androidImplementation?.requestExactAlarmsPermission();
+ }
+
+ Future scheduleMedicineNotification(int id, String name, String timeString) async {
+ final parts = timeString.split(':');
+ final int hour = int.parse(parts[0]);
+ final int minute = int.parse(parts[1]);
+
+ final tz.TZDateTime now = tz.TZDateTime.now(tz.local);
+ tz.TZDateTime scheduledDate = tz.TZDateTime(tz.local, now.year, now.month, now.day, hour, minute);
+
+ if (scheduledDate.isBefore(now)) {
+ scheduledDate = scheduledDate.add(const Duration(days: 1));
+ }
+
+ const AndroidNotificationDetails androidDetails = AndroidNotificationDetails(
+ 'priority_channel',
+ 'Priority Reminders',
+ importance: Importance.max,
+ priority: Priority.high,
+ actions: [
+ AndroidNotificationAction('action_done', 'Done'),
+ AndroidNotificationAction('action_not_taken', 'Not taken'),
+ ],
+ );
+
+ const NotificationDetails details = NotificationDetails(android: androidDetails);
+
+ await plugin.zonedSchedule(
+ id: id,
+ title: 'Time to take $name',
+ body: 'Did you take your dose?',
+ scheduledDate: scheduledDate,
+ notificationDetails: details,
+ androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
+ matchDateTimeComponents: DateTimeComponents.time,
+ payload: 'med_$id',
+ );
+ }
+
+ Future cancelNotification(int id) async {
+ await plugin.cancel(id: id);
+ }
+}
\ No newline at end of file
diff --git a/app/linux/flutter/generated_plugin_registrant.cc b/app/linux/flutter/generated_plugin_registrant.cc
index 675b719..8a93927 100644
--- a/app/linux/flutter/generated_plugin_registrant.cc
+++ b/app/linux/flutter/generated_plugin_registrant.cc
@@ -7,9 +7,13 @@
#include "generated_plugin_registrant.h"
#include
+#include
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) dynamic_color_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin");
dynamic_color_plugin_register_with_registrar(dynamic_color_registrar);
+ g_autoptr(FlPluginRegistrar) flutter_timezone_registrar =
+ fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterTimezonePlugin");
+ flutter_timezone_plugin_register_with_registrar(flutter_timezone_registrar);
}
diff --git a/app/linux/flutter/generated_plugins.cmake b/app/linux/flutter/generated_plugins.cmake
index 3e303c1..35c8ab1 100644
--- a/app/linux/flutter/generated_plugins.cmake
+++ b/app/linux/flutter/generated_plugins.cmake
@@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
dynamic_color
+ flutter_timezone
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
diff --git a/app/macos/Flutter/GeneratedPluginRegistrant.swift b/app/macos/Flutter/GeneratedPluginRegistrant.swift
index 0c2a31a..8d243d8 100644
--- a/app/macos/Flutter/GeneratedPluginRegistrant.swift
+++ b/app/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -6,9 +6,15 @@ import FlutterMacOS
import Foundation
import dynamic_color
+import flutter_local_notifications
+import flutter_timezone
+import shared_preferences_foundation
import sqflite_darwin
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
+ FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
+ FlutterTimezonePlugin.register(with: registry.registrar(forPlugin: "FlutterTimezonePlugin"))
+ SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
}
diff --git a/app/pubspec.lock b/app/pubspec.lock
index 98b1bb0..21af346 100644
--- a/app/pubspec.lock
+++ b/app/pubspec.lock
@@ -1,6 +1,22 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
+ alarm:
+ dependency: "direct main"
+ description:
+ name: alarm
+ sha256: f50869fb28d46ce44922340a44a0ad9ee4be298349efeb20bbfd44aeba0c257d
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.2.1"
+ args:
+ dependency: transitive
+ description:
+ name: args
+ sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.7.0"
async:
dependency: transitive
description:
@@ -41,14 +57,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.19.1"
- cupertino_icons:
- dependency: "direct main"
+ dbus:
+ dependency: transitive
description:
- name: cupertino_icons
- sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
+ name: dbus
+ sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270
url: "https://pub.dev"
source: hosted
- version: "1.0.8"
+ version: "0.7.12"
dynamic_color:
dependency: "direct main"
description:
@@ -57,6 +73,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.8.1"
+ equatable:
+ dependency: transitive
+ description:
+ name: equatable
+ sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.8"
fake_async:
dependency: transitive
description:
@@ -65,11 +89,35 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.3"
+ ffi:
+ dependency: transitive
+ description:
+ name: ffi
+ sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.0"
+ file:
+ dependency: transitive
+ description:
+ name: file
+ sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.1"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
+ flutter_fgbg:
+ dependency: transitive
+ description:
+ name: flutter_fgbg
+ sha256: eb6da9b2047372566a6e17b505975fe5bace94af01f6fc825c4b6f81baa6c447
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.7.1"
flutter_lints:
dependency: "direct dev"
description:
@@ -78,11 +126,80 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.0.0"
+ flutter_local_notifications:
+ dependency: "direct main"
+ description:
+ name: flutter_local_notifications
+ sha256: "2b50e938a275e1ad77352d6a25e25770f4130baa61eaf02de7a9a884680954ad"
+ url: "https://pub.dev"
+ source: hosted
+ version: "20.1.0"
+ flutter_local_notifications_linux:
+ dependency: transitive
+ description:
+ name: flutter_local_notifications_linux
+ sha256: dce0116868cedd2cdf768af0365fc37ff1cbef7c02c4f51d0587482e625868d0
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.0"
+ flutter_local_notifications_platform_interface:
+ dependency: transitive
+ description:
+ name: flutter_local_notifications_platform_interface
+ sha256: "23de31678a48c084169d7ae95866df9de5c9d2a44be3e5915a2ff067aeeba899"
+ url: "https://pub.dev"
+ source: hosted
+ version: "10.0.0"
+ flutter_local_notifications_windows:
+ dependency: transitive
+ description:
+ name: flutter_local_notifications_windows
+ sha256: e97a1a3016512437d9c0b12fae7d1491c3c7b9aa7f03a69b974308840656b02a
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.1"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
+ flutter_timezone:
+ dependency: "direct main"
+ description:
+ name: flutter_timezone
+ sha256: "978192f2f9ea6d019a4de4f0211d76a9af955ca24865828fa98ca4e20cf0cb3c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.0.1"
+ flutter_web_plugins:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ http:
+ dependency: transitive
+ description:
+ name: http
+ sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.6.0"
+ http_parser:
+ dependency: transitive
+ description:
+ name: http_parser
+ sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.1.2"
+ json_annotation:
+ dependency: transitive
+ description:
+ name: json_annotation
+ sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.11.0"
leak_tracker:
dependency: transitive
description:
@@ -115,6 +232,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.1.0"
+ logging:
+ dependency: transitive
+ description:
+ name: logging
+ sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.0"
matcher:
dependency: transitive
description:
@@ -147,6 +272,86 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.1"
+ path_provider_linux:
+ dependency: transitive
+ description:
+ name: path_provider_linux
+ sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.1"
+ path_provider_platform_interface:
+ dependency: transitive
+ description:
+ name: path_provider_platform_interface
+ sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.2"
+ path_provider_windows:
+ dependency: transitive
+ description:
+ name: path_provider_windows
+ sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.3.0"
+ permission_handler:
+ dependency: "direct main"
+ description:
+ name: permission_handler
+ sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1
+ url: "https://pub.dev"
+ source: hosted
+ version: "12.0.1"
+ permission_handler_android:
+ dependency: transitive
+ description:
+ name: permission_handler_android
+ sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6"
+ url: "https://pub.dev"
+ source: hosted
+ version: "13.0.1"
+ permission_handler_apple:
+ dependency: transitive
+ description:
+ name: permission_handler_apple
+ sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023
+ url: "https://pub.dev"
+ source: hosted
+ version: "9.4.7"
+ permission_handler_html:
+ dependency: transitive
+ description:
+ name: permission_handler_html
+ sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.1.3+5"
+ permission_handler_platform_interface:
+ dependency: transitive
+ description:
+ name: permission_handler_platform_interface
+ sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.3.0"
+ permission_handler_windows:
+ dependency: transitive
+ description:
+ name: permission_handler_windows
+ sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.2.1"
+ petitparser:
+ dependency: transitive
+ description:
+ name: petitparser
+ sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.2"
platform:
dependency: transitive
description:
@@ -163,6 +368,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.8"
+ rxdart:
+ dependency: transitive
+ description:
+ name: rxdart
+ sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.28.0"
+ shared_preferences:
+ dependency: transitive
+ description:
+ name: shared_preferences
+ sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.5.4"
+ shared_preferences_android:
+ dependency: transitive
+ description:
+ name: shared_preferences_android
+ sha256: "8374d6200ab33ac99031a852eba4c8eb2170c4bf20778b3e2c9eccb45384fb41"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.21"
+ shared_preferences_foundation:
+ dependency: transitive
+ description:
+ name: shared_preferences_foundation
+ sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.5.6"
+ shared_preferences_linux:
+ dependency: transitive
+ description:
+ name: shared_preferences_linux
+ sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.1"
+ shared_preferences_platform_interface:
+ dependency: transitive
+ description:
+ name: shared_preferences_platform_interface
+ sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.1"
+ shared_preferences_web:
+ dependency: transitive
+ description:
+ name: shared_preferences_web
+ sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.3"
+ shared_preferences_windows:
+ dependency: transitive
+ description:
+ name: shared_preferences_windows
+ sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.1"
sky_engine:
dependency: transitive
description: flutter
@@ -264,6 +533,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.9"
+ timezone:
+ dependency: "direct main"
+ description:
+ name: timezone
+ sha256: dd14a3b83cfd7cb19e7888f1cbc20f258b8d71b54c06f79ac585f14093a287d1
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.10.1"
+ typed_data:
+ dependency: transitive
+ description:
+ name: typed_data
+ sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.4.0"
vector_math:
dependency: transitive
description:
@@ -280,6 +565,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "15.0.2"
+ web:
+ dependency: transitive
+ description:
+ name: web
+ sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.1"
+ xdg_directories:
+ dependency: transitive
+ description:
+ name: xdg_directories
+ sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.0"
+ xml:
+ dependency: transitive
+ description:
+ name: xml
+ sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.6.1"
sdks:
dart: ">=3.10.1 <4.0.0"
- flutter: ">=3.24.0"
+ flutter: ">=3.35.0"
diff --git a/app/pubspec.yaml b/app/pubspec.yaml
index 29055bf..8c5bff2 100644
--- a/app/pubspec.yaml
+++ b/app/pubspec.yaml
@@ -30,13 +30,15 @@ environment:
dependencies:
flutter:
sdk: flutter
-
- # The following adds the Cupertino Icons font to your application.
- # Use with the CupertinoIcons class for iOS style icons.
- cupertino_icons: ^1.0.8
+
sqflite: ^2.4.2
path: ^1.9.1
dynamic_color: ^1.8.1
+ flutter_local_notifications: ^20.0.0
+ timezone: ^0.10.1
+ flutter_timezone: ^5.0.1
+ permission_handler: ^12.0.1
+ alarm: ^5.2.1
dev_dependencies:
flutter_test:
diff --git a/app/windows/flutter/generated_plugin_registrant.cc b/app/windows/flutter/generated_plugin_registrant.cc
index e4899a6..03882ab 100644
--- a/app/windows/flutter/generated_plugin_registrant.cc
+++ b/app/windows/flutter/generated_plugin_registrant.cc
@@ -7,8 +7,14 @@
#include "generated_plugin_registrant.h"
#include
+#include
+#include
void RegisterPlugins(flutter::PluginRegistry* registry) {
DynamicColorPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("DynamicColorPluginCApi"));
+ FlutterTimezonePluginCApiRegisterWithRegistrar(
+ registry->GetRegistrarForPlugin("FlutterTimezonePluginCApi"));
+ PermissionHandlerWindowsPluginRegisterWithRegistrar(
+ registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
}
diff --git a/app/windows/flutter/generated_plugins.cmake b/app/windows/flutter/generated_plugins.cmake
index 841e8c4..65dc5f8 100644
--- a/app/windows/flutter/generated_plugins.cmake
+++ b/app/windows/flutter/generated_plugins.cmake
@@ -4,9 +4,12 @@
list(APPEND FLUTTER_PLUGIN_LIST
dynamic_color
+ flutter_timezone
+ permission_handler_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
+ flutter_local_notifications_windows
)
set(PLUGIN_BUNDLED_LIBRARIES)