mirror of
https://github.com/Iconica-Development/flutter_notification_center.git
synced 2025-05-19 09:03:45 +02:00
Merge remote-tracking branch 'origin/refactor/notification-service' into feat/front_end
This commit is contained in:
commit
2ad7ab5028
7 changed files with 268 additions and 81 deletions
|
@ -18,32 +18,31 @@ class NotificationCenterDemo extends StatefulWidget {
|
||||||
|
|
||||||
class _NotificationCenterDemoState extends State<NotificationCenterDemo> {
|
class _NotificationCenterDemoState extends State<NotificationCenterDemo> {
|
||||||
var config = NotificationConfig(
|
var config = NotificationConfig(
|
||||||
service: NotificationService(
|
service: LocalNotificationService(
|
||||||
listOfNotifications: [
|
listOfActiveNotifications: [
|
||||||
NotificationModel(
|
NotificationModel(
|
||||||
|
id: 1,
|
||||||
title: 'Notification title 1',
|
title: 'Notification title 1',
|
||||||
body: 'Notification body 1',
|
body: 'Notification body 1',
|
||||||
dateTime: DateTime.now(),
|
dateTimePushed: DateTime.now(),
|
||||||
isRead: false,
|
|
||||||
),
|
),
|
||||||
NotificationModel(
|
NotificationModel(
|
||||||
|
id: 2,
|
||||||
title: 'RECURRING',
|
title: 'RECURRING',
|
||||||
body: 'RECURRING',
|
body: 'RECURRING',
|
||||||
dateTime: DateTime.now(),
|
dateTimePushed: DateTime.now(),
|
||||||
isRead: false,
|
|
||||||
isScheduled: true,
|
|
||||||
),
|
),
|
||||||
NotificationModel(
|
NotificationModel(
|
||||||
|
id: 3,
|
||||||
title: 'Notification title 2',
|
title: 'Notification title 2',
|
||||||
body: 'Notification body 2',
|
body: 'Notification body 2',
|
||||||
dateTime: DateTime.now(),
|
dateTimePushed: DateTime.now(),
|
||||||
isRead: false,
|
|
||||||
),
|
),
|
||||||
NotificationModel(
|
NotificationModel(
|
||||||
|
id: 4,
|
||||||
title: 'Notification title 3',
|
title: 'Notification title 3',
|
||||||
body: 'Notification body 3',
|
body: 'Notification body 3',
|
||||||
dateTime: DateTime.now(),
|
dateTimePushed: DateTime.now(),
|
||||||
isRead: false,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
library notification_center;
|
library notification_center;
|
||||||
|
|
||||||
export 'package:flutter_notification_center/src/services/notification_service.dart';
|
export 'package:flutter_notification_center/src/services/notification_service.dart';
|
||||||
|
export 'package:flutter_notification_center/src/services/local_notification_service.dart';
|
||||||
export 'package:flutter_notification_center/src/notification_center.dart';
|
export 'package:flutter_notification_center/src/notification_center.dart';
|
||||||
export 'package:flutter_notification_center/src/models/notification.dart';
|
export 'package:flutter_notification_center/src/models/notification.dart';
|
||||||
export 'package:flutter_notification_center/src/models/notification_theme.dart';
|
export 'package:flutter_notification_center/src/models/notification_theme.dart';
|
||||||
|
|
|
@ -1,26 +1,32 @@
|
||||||
enum ScheduleType {
|
enum OcurringInterval {
|
||||||
minute,
|
|
||||||
daily,
|
daily,
|
||||||
weekly,
|
weekly,
|
||||||
monthly,
|
monthly,
|
||||||
|
debug
|
||||||
}
|
}
|
||||||
|
|
||||||
class NotificationModel {
|
class NotificationModel {
|
||||||
NotificationModel({
|
NotificationModel({
|
||||||
this.id,
|
required this.id,
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.body,
|
required this.body,
|
||||||
required this.dateTime,
|
this.dateTimePushed,
|
||||||
required this.isRead,
|
|
||||||
this.isScheduled = false,
|
|
||||||
this.scheduledFor,
|
this.scheduledFor,
|
||||||
|
this.recurring = false,
|
||||||
|
this.occuringInterval,
|
||||||
});
|
});
|
||||||
|
|
||||||
int? id;
|
int id;
|
||||||
String title;
|
String title;
|
||||||
String body;
|
String body;
|
||||||
DateTime dateTime;
|
DateTime? dateTimePushed;
|
||||||
bool isRead;
|
DateTime? scheduledFor;
|
||||||
bool isScheduled;
|
bool recurring;
|
||||||
ScheduleType? scheduledFor;
|
OcurringInterval? occuringInterval;
|
||||||
|
|
||||||
|
// Override toString() to provide custom string representation
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'NotificationModel{id: $id, title: $title, body: $body, dateTimePushed: $dateTimePushed, scheduledFor: $scheduledFor, recurring: $recurring, occuringInterval: $occuringInterval}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,28 @@ class NotificationBell extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _NotificationBellState extends State<NotificationBell> {
|
class _NotificationBellState extends State<NotificationBell> {
|
||||||
|
var notificationAmount = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
var amount = await widget.config.service.getActiveNotifications();
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
notificationAmount = amount.length;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
onPressed: widget.onTap,
|
onPressed: widget.onTap,
|
||||||
icon: AnimatedNotificationBell(
|
icon: AnimatedNotificationBell(
|
||||||
duration: const Duration(seconds: 1),
|
duration: const Duration(seconds: 1),
|
||||||
notificationCount: widget.config.service.listOfNotifications.length,
|
notificationCount: notificationAmount,
|
||||||
notificationIconSize: 45,
|
notificationIconSize: 45,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,8 +6,8 @@ class NotificationCenter extends StatefulWidget {
|
||||||
final NotificationConfig config;
|
final NotificationConfig config;
|
||||||
|
|
||||||
const NotificationCenter({
|
const NotificationCenter({
|
||||||
super.key,
|
|
||||||
required this.config,
|
required this.config,
|
||||||
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -15,20 +15,16 @@ class NotificationCenter extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _NotificationCenterState extends State<NotificationCenter> {
|
class _NotificationCenterState extends State<NotificationCenter> {
|
||||||
late List<NotificationModel> listOfNotifications;
|
late Future<List<NotificationModel>> _notificationsFuture;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
listOfNotifications = widget.config.service.getNotifications();
|
_notificationsFuture = widget.config.service.getActiveNotifications();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final unreadNotifications = listOfNotifications
|
|
||||||
.where((notification) => !notification.isRead)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(
|
title: Text(
|
||||||
|
@ -43,55 +39,80 @@ class _NotificationCenterState extends State<NotificationCenter> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: unreadNotifications.isEmpty
|
body: FutureBuilder<List<NotificationModel>>(
|
||||||
? widget.config.style.emptyNotificationsBuilder?.call() ??
|
future: _notificationsFuture,
|
||||||
Center(
|
builder: (context, snapshot) {
|
||||||
child: Text(widget.config.translations.appBarTitle),
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
)
|
return const Center(child: CircularProgressIndicator());
|
||||||
: ListView.builder(
|
} else if (snapshot.hasError) {
|
||||||
|
return Center(child: Text('Error: ${snapshot.error}'));
|
||||||
|
} else if (snapshot.data == null || snapshot.data!.isEmpty) {
|
||||||
|
return const Center(
|
||||||
|
child: Text('No unread notifications available.'));
|
||||||
|
} else {
|
||||||
|
final unreadNotifications = snapshot.data!.toList();
|
||||||
|
|
||||||
|
return ListView.builder(
|
||||||
itemCount: unreadNotifications.length,
|
itemCount: unreadNotifications.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final notification = unreadNotifications[index];
|
final notification = unreadNotifications[index];
|
||||||
final formattedDateTime = DateFormat('yyyy-MM-dd HH:mm')
|
final formattedDateTime = notification.dateTimePushed != null
|
||||||
.format(notification.dateTime);
|
? DateFormat('yyyy-MM-dd HH:mm')
|
||||||
return Container(
|
.format(notification.dateTimePushed!)
|
||||||
decoration: widget.config.style.tileDecoration,
|
: 'Pending';
|
||||||
child: ListTile(
|
return ListTile(
|
||||||
title: Column(
|
title: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
children: [
|
||||||
children: [
|
Text(
|
||||||
Text(
|
notification.title,
|
||||||
notification.title,
|
style: widget.config.style.titleTextStyle ??
|
||||||
style: widget.config.style.titleTextStyle ??
|
const TextStyle(),
|
||||||
const TextStyle(),
|
),
|
||||||
|
Text(
|
||||||
|
notification.body,
|
||||||
|
style: widget.config.style.subtitleTextStyle ??
|
||||||
|
const TextStyle(),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
formattedDateTime,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.grey,
|
||||||
),
|
),
|
||||||
Text(
|
),
|
||||||
notification.body,
|
],
|
||||||
style: widget.config.style.subtitleTextStyle ??
|
),
|
||||||
const TextStyle(),
|
trailing: IconButton(
|
||||||
),
|
icon: const Icon(Icons.clear),
|
||||||
Text(
|
onPressed: () {
|
||||||
formattedDateTime,
|
setState(() {
|
||||||
style: const TextStyle(
|
widget.config.service
|
||||||
fontSize: 12,
|
.dismissActiveNotification(notification);
|
||||||
color: Colors.grey,
|
print('Notification dismissed: $notification');
|
||||||
),
|
});
|
||||||
),
|
},
|
||||||
],
|
|
||||||
),
|
|
||||||
trailing: IconButton(
|
|
||||||
icon: const Icon(Icons.clear),
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
notification.isRead = true;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await widget.config.service.createRecurringNotification(
|
||||||
|
NotificationModel(
|
||||||
|
id: 3,
|
||||||
|
title: 'HALLO',
|
||||||
|
body: 'DIT IS DE BODY',
|
||||||
|
recurring: true,
|
||||||
|
occuringInterval: OcurringInterval.debug,
|
||||||
|
scheduledFor: DateTime.now().add(const Duration(seconds: 5))),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const Icon(Icons.add),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
136
lib/src/services/local_notification_service.dart
Normal file
136
lib/src/services/local_notification_service.dart
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_notification_center/src/models/notification.dart';
|
||||||
|
import 'package:flutter_notification_center/src/services/notification_service.dart';
|
||||||
|
|
||||||
|
class LocalNotificationService implements NotificationService {
|
||||||
|
@override
|
||||||
|
List<NotificationModel> listOfActiveNotifications;
|
||||||
|
@override
|
||||||
|
List<NotificationModel> listOfPlannedNotifications;
|
||||||
|
|
||||||
|
late Timer _timer;
|
||||||
|
|
||||||
|
LocalNotificationService(
|
||||||
|
{this.listOfActiveNotifications = const [],
|
||||||
|
this.listOfPlannedNotifications = const []}) {
|
||||||
|
_startTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _startTimer() {
|
||||||
|
_timer = Timer.periodic(const Duration(seconds: 5), (timer) {
|
||||||
|
debugPrint('Checking for scheduled notifications...');
|
||||||
|
checkForScheduledNotifications();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cancelTimer() {
|
||||||
|
_timer.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future pushNotification(NotificationModel notification) async {
|
||||||
|
notification.dateTimePushed = DateTime.now();
|
||||||
|
listOfActiveNotifications.add(notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<NotificationModel>> getActiveNotifications() async {
|
||||||
|
print('Getting all active notifications...');
|
||||||
|
return listOfActiveNotifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future createScheduledNotification(NotificationModel notification) async {
|
||||||
|
listOfPlannedNotifications = [...listOfPlannedNotifications, notification];
|
||||||
|
print('Creating scheduled notification: $notification');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future createRecurringNotification(NotificationModel notification) async {
|
||||||
|
// If recurring, update the scheduled date for the next occurrence
|
||||||
|
notification.title = notification.id.toString();
|
||||||
|
await pushNotification(notification);
|
||||||
|
if (notification.recurring) {
|
||||||
|
switch (notification.occuringInterval) {
|
||||||
|
case OcurringInterval.daily:
|
||||||
|
notification.scheduledFor =
|
||||||
|
notification.scheduledFor!.add(const Duration(days: 1));
|
||||||
|
break;
|
||||||
|
case OcurringInterval.weekly:
|
||||||
|
notification.scheduledFor =
|
||||||
|
notification.scheduledFor!.add(const Duration(days: 7));
|
||||||
|
break;
|
||||||
|
case OcurringInterval.monthly:
|
||||||
|
// Add logic for monthly recurrence, e.g., adding 1 month to the scheduled date
|
||||||
|
break;
|
||||||
|
case OcurringInterval.debug:
|
||||||
|
notification.scheduledFor =
|
||||||
|
notification.scheduledFor!.add(const Duration(seconds: 5));
|
||||||
|
break;
|
||||||
|
case null:
|
||||||
|
// TODO: Handle this case.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the next recurring notification
|
||||||
|
listOfPlannedNotifications = [
|
||||||
|
...listOfPlannedNotifications,
|
||||||
|
notification
|
||||||
|
];
|
||||||
|
print('Created recurring notification for: ${notification.scheduledFor}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future deleteScheduledNotification(NotificationModel notification) async {
|
||||||
|
listOfPlannedNotifications =
|
||||||
|
listOfPlannedNotifications.where((n) => n != notification).toList();
|
||||||
|
print('Notification deleted: $notification');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future dismissActiveNotification(NotificationModel notification) async {
|
||||||
|
int id = notification.id;
|
||||||
|
listOfActiveNotifications.removeWhere((n) => n.id == id);
|
||||||
|
print('Notification with ID $id dismissed');
|
||||||
|
print('List of active notifications: $listOfActiveNotifications');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> checkForScheduledNotifications() async {
|
||||||
|
DateTime currentTime = DateTime.now();
|
||||||
|
|
||||||
|
if (listOfPlannedNotifications.isEmpty) {
|
||||||
|
print('There are no scheduled notifications to be pushed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (NotificationModel notification
|
||||||
|
in listOfPlannedNotifications.toList()) {
|
||||||
|
// Check if scheduledFor is not null
|
||||||
|
if (notification.scheduledFor != null) {
|
||||||
|
// Check if the scheduled date and time is before or equal to the current date and time
|
||||||
|
if (notification.scheduledFor!.isBefore(currentTime) ||
|
||||||
|
notification.scheduledFor!.isAtSameMomentAs(currentTime)) {
|
||||||
|
// Push the notification if it's due
|
||||||
|
await pushNotification(notification);
|
||||||
|
print('Scheduled notification pushed: $notification');
|
||||||
|
|
||||||
|
// If recurring, update the scheduled date for the next occurrence
|
||||||
|
if (notification.recurring) {
|
||||||
|
// Increment the ID for recurring notifications
|
||||||
|
notification.id += 1;
|
||||||
|
notification.title = notification.id.toString();
|
||||||
|
print('New RECURRING ID IS: ${notification.id}');
|
||||||
|
|
||||||
|
// Create the next recurring notification
|
||||||
|
await createRecurringNotification(notification);
|
||||||
|
} else {
|
||||||
|
// Delete the notification if not recurring
|
||||||
|
print('Non-recurring notification removed: $notification');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,24 @@
|
||||||
import 'package:flutter_notification_center/src/models/notification.dart';
|
import 'package:flutter_notification_center/src/models/notification.dart';
|
||||||
|
|
||||||
class NotificationService {
|
abstract class NotificationService {
|
||||||
List<NotificationModel> listOfNotifications;
|
List<NotificationModel> listOfActiveNotifications;
|
||||||
|
List<NotificationModel> listOfPlannedNotifications;
|
||||||
|
|
||||||
NotificationService({this.listOfNotifications = const []});
|
NotificationService(
|
||||||
|
{this.listOfActiveNotifications = const [],
|
||||||
|
this.listOfPlannedNotifications = const []});
|
||||||
|
|
||||||
void addNotification(NotificationModel notification) {
|
Future pushNotification(NotificationModel notification);
|
||||||
listOfNotifications.add(notification);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<NotificationModel> getNotifications() {
|
Future<List<NotificationModel>> getActiveNotifications();
|
||||||
return listOfNotifications;
|
|
||||||
}
|
Future createScheduledNotification(NotificationModel notification);
|
||||||
|
|
||||||
|
Future createRecurringNotification(NotificationModel notification);
|
||||||
|
|
||||||
|
Future deleteScheduledNotification(NotificationModel notificationId);
|
||||||
|
|
||||||
|
Future dismissActiveNotification(NotificationModel notificationId);
|
||||||
|
|
||||||
|
Future checkForScheduledNotifications();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue