Merge pull request 'feature/notification-date' (#49) from feature/notification-date into main

Reviewed-on: #49
This commit is contained in:
2025-08-27 21:53:17 +00:00
8 changed files with 174 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:timezone/timezone.dart' as tz;
import 'package:timezone/data/latest_all.dart' as tz;
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
class NotificationService {
static final FlutterLocalNotificationsPlugin _notificationsPlugin =
FlutterLocalNotificationsPlugin();
/// Initialisation (à appeler dans main())
static Future<void> initialize() async {
tz.initializeTimeZones();
const AndroidInitializationSettings androidInitSettings =
AndroidInitializationSettings('@mipmap/ic_launcher');
const InitializationSettings initSettings = InitializationSettings(
android: androidInitSettings,
iOS: DarwinInitializationSettings(),
);
await _notificationsPlugin.initialize(initSettings);
// Demande les permissions au lancement
await requestPermissions();
}
/// Demander les permissions (Android 13+ et iOS)
static Future<void> requestPermissions() async {
// Android 13+
final androidImplementation =
_notificationsPlugin.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>();
await androidImplementation?.requestNotificationsPermission();
// iOS
final iosImplementation =
_notificationsPlugin.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>();
await iosImplementation?.requestPermissions(
alert: true,
badge: true,
sound: true,
);
}
/// Planifie une notification 1h avant lévènement
static Future<void> scheduleEventNotification({
required String eventId,
required String title,
required String body,
required DateTime eventDate,
}) async {
final scheduledDate = eventDate.subtract(const Duration(hours: 1));
if (scheduledDate.isBefore(DateTime.now())) {
// Trop tard pour notifier
return;
}
await _notificationsPlugin.zonedSchedule(
eventId.hashCode, // identifiant unique pour lévènement
title,
body,
tz.TZDateTime.from(scheduledDate, tz.local),
const NotificationDetails(
android: AndroidNotificationDetails(
'events_channel',
'Events',
channelDescription: 'Favorite event notifications',
importance: Importance.high,
priority: Priority.high,
),
iOS: DarwinNotificationDetails(),
),
androidScheduleMode: AndroidScheduleMode
.inexactAllowWhileIdle, // évite l'erreur Exact Alarm
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
matchDateTimeComponents: DateTimeComponents.dateAndTime,
);
}
/// Annule une notification planifiée
static Future<void> cancel(String eventId) async {
await _notificationsPlugin.cancel(eventId.hashCode);
}
}

View File

@@ -5,10 +5,12 @@ import 'package:provider/provider.dart';
import 'pages/LoginDemo.dart';
import 'locale_provider.dart'; // <-- à adapter selon ton arborescence
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'classes/notification_service.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await MobileAds.instance.initialize();
await NotificationService.initialize();
runApp(
ChangeNotifierProvider(

View File

@@ -18,6 +18,7 @@ import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart'; //
import '../classes/notification_service.dart';
// app starting point
void main() async {
@@ -232,6 +233,17 @@ class _MyHomePageState extends State<ListItemOrganizers> {
post.interested = result["interested"];
post.interestedCount = result["interested_count"];
});
if (result["interested"] == true) {
NotificationService.scheduleEventNotification(
eventId: post.id!,
title: "Rappel évènement",
body:
"Ton évènement '${post.name}' commence dans 1 heure !",
eventDate: DateTime.parse(post.startDate!),
);
} else {
NotificationService.cancel(post.id!);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(

View File

@@ -19,6 +19,7 @@ import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart'; //
import '../classes/notification_service.dart';
// app starting point
void main() async {
@@ -234,6 +235,17 @@ class _MyHomePageState extends State<ListItemTags> {
post.interested = result["interested"];
post.interestedCount = result["interested_count"];
});
if (result["interested"] == true) {
NotificationService.scheduleEventNotification(
eventId: post.id!,
title: "Rappel évènement",
body:
"Ton évènement '${post.name}' commence dans 1 heure !",
eventDate: DateTime.parse(post.startDate!),
);
} else {
NotificationService.cancel(post.id!);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(

View File

@@ -23,6 +23,7 @@ import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart'; // Créé plus loin
import '../classes/notification_service.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
@@ -911,6 +912,18 @@ class _MyHomePageState extends State<ListItemMenu> {
post.interested = result["interested"];
post.interestedCount = result["interested_count"];
});
if (result["interested"] == true) {
NotificationService.scheduleEventNotification(
eventId: post.id!,
title: "Rappel évènement",
body:
"Ton évènement '${post.name}' commence dans 1 heure !",
eventDate: DateTime.parse(post.startDate!),
);
} else {
NotificationService.cancel(post.id!);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(

View File

@@ -6,6 +6,7 @@ import FlutterMacOS
import Foundation
import file_selector_macos
import flutter_local_notifications
import geolocator_apple
import path_provider_foundation
import shared_preferences_foundation
@@ -14,6 +15,7 @@ import webview_flutter_wkwebview
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))

View File

@@ -145,6 +145,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.1.0"
dbus:
dependency: transitive
description:
name: dbus
sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c"
url: "https://pub.dev"
source: hosted
version: "0.7.11"
dio:
dependency: transitive
description:
@@ -270,6 +278,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.0"
flutter_local_notifications:
dependency: "direct main"
description:
name: flutter_local_notifications
sha256: "674173fd3c9eda9d4c8528da2ce0ea69f161577495a9cc835a2a4ecd7eadeb35"
url: "https://pub.dev"
source: hosted
version: "17.2.4"
flutter_local_notifications_linux:
dependency: transitive
description:
name: flutter_local_notifications_linux
sha256: c49bd06165cad9beeb79090b18cd1eb0296f4bf4b23b84426e37dd7c027fc3af
url: "https://pub.dev"
source: hosted
version: "4.0.1"
flutter_local_notifications_platform_interface:
dependency: transitive
description:
name: flutter_local_notifications_platform_interface
sha256: "85f8d07fe708c1bdcf45037f2c0109753b26ae077e9d9e899d55971711a4ea66"
url: "https://pub.dev"
source: hosted
version: "7.2.0"
flutter_localizations:
dependency: "direct main"
description: flutter
@@ -850,6 +882,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.1"
timezone:
dependency: "direct main"
description:
name: timezone
sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d"
url: "https://pub.dev"
source: hosted
version: "0.9.4"
typed_data:
dependency: transitive
description:

View File

@@ -36,6 +36,8 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
flutter_local_notifications: ^17.2.0
timezone: ^0.9.4
cupertino_icons: ^1.0.2
http: ^1.2.1
shared_preferences: ^2.2.3