refacor: clean up and edit for analysis options

This commit is contained in:
Vick Top 2024-04-10 15:09:35 +02:00
parent fa3b0f14fe
commit 13e950fc43
14 changed files with 406 additions and 393 deletions

2
.gitignore vendored
View file

@ -43,7 +43,7 @@ app.*.map.json
/android/app/profile /android/app/profile
/android/app/release /android/app/release
# iOS related # Platform-specific files
ios/ ios/
android/ android/
web/ web/

5
example/.gitignore vendored
View file

@ -45,3 +45,8 @@ app.*.map.json
# iOS related # iOS related
/ios/ /ios/
lib/config/
pubspec.lock
dotenv

View file

@ -68,8 +68,7 @@ class _NotificationCenterDemoState extends State<NotificationCenterDemo> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var config = NotificationConfig( var config = NotificationConfig(
service: Provider.of<FirebaseNotificationService>( service: Provider.of<FirebaseNotificationService>(context),
context), // Use the same instance of FirebaseNotificationService
style: const NotificationStyle( style: const NotificationStyle(
appTitleTextStyle: TextStyle( appTitleTextStyle: TextStyle(
color: Colors.black, color: Colors.black,
@ -77,8 +76,8 @@ class _NotificationCenterDemoState extends State<NotificationCenterDemo> {
), ),
titleTextStyle: TextStyle( titleTextStyle: TextStyle(
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w400,
fontSize: 20, fontSize: 16,
), ),
), ),
); );

View file

@ -6,7 +6,8 @@ import '../config/firebase_collections.dart';
import 'package:flutter_notification_center/src/models/notification.dart'; import 'package:flutter_notification_center/src/models/notification.dart';
import 'package:flutter_notification_center/src/services/notification_service.dart'; import 'package:flutter_notification_center/src/services/notification_service.dart';
class FirebaseNotificationService with ChangeNotifier class FirebaseNotificationService
with ChangeNotifier
implements NotificationService { implements NotificationService {
@override @override
List<NotificationModel> listOfActiveNotifications; List<NotificationModel> listOfActiveNotifications;
@ -39,11 +40,7 @@ class FirebaseNotificationService with ChangeNotifier
Map<String, dynamic> notificationMap = notification.toMap(); Map<String, dynamic> notificationMap = notification.toMap();
await notifications.doc(notification.id).set(notificationMap); await notifications.doc(notification.id).set(notificationMap);
print('--- Trying to notify listeners ---');
listOfActiveNotifications.add(notification); listOfActiveNotifications.add(notification);
listOfActiveNotifications.forEach((notification) {
print('Notification ID: ${notification.id}');
});
notifyListeners(); notifyListeners();
} catch (e) { } catch (e) {
debugPrint('Error creating document: $e'); debugPrint('Error creating document: $e');
@ -67,12 +64,7 @@ class FirebaseNotificationService with ChangeNotifier
}).toList(); }).toList();
listOfActiveNotifications = activeNotifications; listOfActiveNotifications = activeNotifications;
print('--- Trying to notify listeners ---');
listOfActiveNotifications.forEach((notification) {
print('Notification ID: ${notification.id}');
});
notifyListeners(); notifyListeners();
return listOfActiveNotifications; return listOfActiveNotifications;
} catch (e) { } catch (e) {
debugPrint('Error getting active notifications: $e'); debugPrint('Error getting active notifications: $e');
@ -151,6 +143,9 @@ class FirebaseNotificationService with ChangeNotifier
.collection(FirebaseCollectionNames.active_notifications) .collection(FirebaseCollectionNames.active_notifications)
.doc(notificationModel.id); .doc(notificationModel.id);
await documentReference.delete(); await documentReference.delete();
listOfActiveNotifications
.removeAt(listOfActiveNotifications.indexOf(notificationModel));
notifyListeners();
} catch (e) { } catch (e) {
debugPrint('Error deleting document: $e'); debugPrint('Error deleting document: $e');
} }
@ -164,6 +159,8 @@ class FirebaseNotificationService with ChangeNotifier
.collection(FirebaseCollectionNames.active_notifications) .collection(FirebaseCollectionNames.active_notifications)
.doc(notificationModel.id); .doc(notificationModel.id);
await documentReference.update({'isRead': true}); await documentReference.update({'isRead': true});
notificationModel.isRead = true;
notifyListeners();
} catch (e) { } catch (e) {
debugPrint('Error updating document: $e'); debugPrint('Error updating document: $e');
} }

View file

@ -2,13 +2,11 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
library notification_center; export "package:flutter_notification_center/src/models/notification.dart";
export "package:flutter_notification_center/src/models/notification_config.dart";
export 'package:flutter_notification_center/src/services/notification_service.dart'; export "package:flutter_notification_center/src/models/notification_theme.dart";
export 'package:flutter_notification_center/src/notification_center.dart'; export "package:flutter_notification_center/src/models/notification_translation.dart";
export 'package:flutter_notification_center/src/models/notification.dart'; export "package:flutter_notification_center/src/notification_bell.dart";
export 'package:flutter_notification_center/src/models/notification_theme.dart'; export "package:flutter_notification_center/src/notification_bell_story.dart";
export 'package:flutter_notification_center/src/models/notification_config.dart'; export "package:flutter_notification_center/src/notification_center.dart";
export 'package:flutter_notification_center/src/models/notification_translation.dart'; export "package:flutter_notification_center/src/services/notification_service.dart";
export 'package:flutter_notification_center/src/notification_bell.dart';
export 'package:flutter_notification_center/src/notification_bell_story.dart';

View file

@ -1,4 +1,4 @@
import 'package:flutter/material.dart'; import "package:flutter/material.dart";
/// Enum representing the interval at which notifications occur. /// Enum representing the interval at which notifications occur.
enum OcurringInterval { enum OcurringInterval {
@ -17,6 +17,52 @@ enum OcurringInterval {
/// Model class representing a notification. /// Model class representing a notification.
class NotificationModel { class NotificationModel {
/// Constructs a new NotificationModel instance.
///
/// [id]: Unique identifier for the notification.
/// [title]: Title of the notification.
/// [body]: Body content of the notification.
/// [dateTimePushed]: Date and time when the notification was pushed.
/// [scheduledFor]: Date and time when the notification is scheduled for.
/// [recurring]: Indicates if the notification is recurring.
/// [occuringInterval]: Interval at which the notification occurs,
/// applicable if it's recurring.
/// [isPinned]: Indicates if the notification is pinned.
/// [isRead]: Indicates if the notification has been read.
NotificationModel({
required this.id,
required this.title,
required this.body,
this.dateTimePushed,
this.scheduledFor,
this.recurring = false,
this.occuringInterval,
this.isPinned = false,
this.isRead = false,
this.icon = Icons.notifications,
});
/// Method to create a NotificationModel object from JSON data
NotificationModel.fromJson(Map<String, dynamic> json)
: id = json["id"],
title = json["title"],
body = json["body"],
dateTimePushed = json["dateTimePushed"] != null
? DateTime.parse(json["dateTimePushed"])
: null,
scheduledFor = json["scheduledFor"] != null
? DateTime.parse(json["scheduledFor"])
: null,
recurring = json["recurring"] ?? false,
occuringInterval = json["occuringInterval"] != null
? OcurringInterval.values[json["occuringInterval"]]
: null,
isPinned = json["isPinned"] ?? false,
isRead = json["isRead"] ?? false,
icon = json["icon"] != null
? IconData(json["icon"], fontFamily: Icons.notifications.fontFamily)
: Icons.notifications;
/// Unique identifier for the notification. /// Unique identifier for the notification.
final String id; final String id;
@ -42,80 +88,31 @@ class NotificationModel {
final bool isPinned; final bool isPinned;
/// Indicates if the notification has been read. /// Indicates if the notification has been read.
final bool isRead; bool isRead;
/// Icon to be displayed with the notification. /// Icon to be displayed with the notification.
final IconData icon; final IconData icon;
/// Constructs a new NotificationModel instance.
///
/// [id]: Unique identifier for the notification.
/// [title]: Title of the notification.
/// [body]: Body content of the notification.
/// [dateTimePushed]: Date and time when the notification was pushed.
/// [scheduledFor]: Date and time when the notification is scheduled for.
/// [recurring]: Indicates if the notification is recurring.
/// [occuringInterval]: Interval at which the notification occurs, applicable if it's recurring.
/// [isPinned]: Indicates if the notification is pinned.
/// [isRead]: Indicates if the notification has been read.
NotificationModel({
required this.id,
required this.title,
required this.body,
this.dateTimePushed,
this.scheduledFor,
this.recurring = false,
this.occuringInterval,
this.isPinned = false,
this.isRead = false,
this.icon = Icons.notifications,
});
/// Override toString() to provide custom string representation /// Override toString() to provide custom string representation
@override @override
String toString() { String toString() => "NotificationModel{id: $id, title: $title, body: $body, "
return 'NotificationModel{id: $id, title: $title, body: $body, dateTimePushed: $dateTimePushed, scheduledFor: $scheduledFor, recurring: $recurring, occuringInterval: $occuringInterval, isPinned: $isPinned, icon: $icon}'; "dateTimePushed: $dateTimePushed, scheduledFor: $scheduledFor, "
} "recurring: $recurring, occuringInterval: $occuringInterval, "
"isPinned: $isPinned, icon: $icon}";
/// Method to create a NotificationModel object from JSON data
static NotificationModel fromJson(Map<String, dynamic> json) {
return NotificationModel(
id: json['id'],
title: json['title'],
body: json['body'],
dateTimePushed: json['dateTimePushed'] != null
? DateTime.parse(json['dateTimePushed'])
: null,
scheduledFor: json['scheduledFor'] != null
? DateTime.parse(json['scheduledFor'])
: null,
recurring: json['recurring'] ?? false,
occuringInterval: json['occuringInterval'] != null
? OcurringInterval.values[json['occuringInterval']]
: null,
isPinned: json['isPinned'] ?? false,
isRead: json['isRead'] ?? false,
icon: json['icon'] != null
? IconData(json['icon'], fontFamily: Icons.notifications.fontFamily)
: Icons.notifications,
);
}
/// Convert the NotificationModel object to a Map. /// Convert the NotificationModel object to a Map.
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() => {
return { "id": id,
'id': id, "title": title,
'title': title, "body": body,
'body': body, "dateTimePushed": dateTimePushed?.toIso8601String(),
'dateTimePushed': dateTimePushed?.toIso8601String(), "scheduledFor": scheduledFor?.toIso8601String(),
'scheduledFor': scheduledFor?.toIso8601String(), "recurring": recurring,
'recurring': recurring, "occuringInterval": occuringInterval?.index,
'occuringInterval': occuringInterval?.index, "isPinned": isPinned,
'isPinned': isPinned, "isRead": isRead,
'isRead': isRead, "icon": icon.codePoint,
'icon': icon.codePoint,
}; };
}
/// Create a copy of the NotificationModel with some fields replaced. /// Create a copy of the NotificationModel with some fields replaced.
NotificationModel copyWith({ NotificationModel copyWith({
@ -129,8 +126,8 @@ class NotificationModel {
bool? isPinned, bool? isPinned,
bool? isRead, bool? isRead,
IconData? icon, IconData? icon,
}) { }) =>
return NotificationModel( NotificationModel(
id: id ?? this.id, id: id ?? this.id,
title: title ?? this.title, title: title ?? this.title,
body: body ?? this.body, body: body ?? this.body,
@ -143,4 +140,3 @@ class NotificationModel {
icon: icon ?? this.icon, icon: icon ?? this.icon,
); );
} }
}

View file

@ -1,17 +1,7 @@
import 'package:flutter_notification_center/flutter_notification_center.dart'; import "package:flutter_notification_center/flutter_notification_center.dart";
import 'package:flutter_notification_center/src/models/notification_translation.dart';
/// Configuration class for notifications. /// Configuration class for notifications.
class NotificationConfig { class NotificationConfig {
/// The notification service to use for delivering notifications.
final NotificationService service;
/// The style of the notification.
final NotificationStyle style;
/// Translations for notification messages.
final NotificationTranslations translations;
/// Creates a new [NotificationConfig] instance. /// Creates a new [NotificationConfig] instance.
/// ///
/// The [service] parameter is required and specifies the notification service /// The [service] parameter is required and specifies the notification service
@ -23,4 +13,13 @@ class NotificationConfig {
this.style = const NotificationStyle(), this.style = const NotificationStyle(),
this.translations = const NotificationTranslations(), this.translations = const NotificationTranslations(),
}); });
/// The notification service to use for delivering notifications.
final NotificationService service;
/// The style of the notification.
final NotificationStyle style;
/// Translations for notification messages.
final NotificationTranslations translations;
} }

View file

@ -1,7 +1,28 @@
import 'package:flutter/material.dart'; import "package:flutter/material.dart";
/// Defines the appearance customization for notifications. /// Defines the appearance customization for notifications.
class NotificationStyle { class NotificationStyle {
/// Creates a new [NotificationStyle] instance.
///
/// Each parameter is optional and allows customizing various aspects
/// of the notification appearance.
const NotificationStyle({
this.titleTextStyle,
this.subtitleTextStyle,
this.backgroundColor,
this.leadingIconColor,
this.trailingIconColor,
this.contentPadding,
this.titleTextAlign,
this.subtitleTextAlign,
this.dense,
this.tileDecoration,
this.emptyNotificationsBuilder,
this.appTitleTextStyle,
this.dividerColor,
this.isReadDotColor,
});
/// The text style for the title of the notification. /// The text style for the title of the notification.
final TextStyle? titleTextStyle; final TextStyle? titleTextStyle;
@ -38,22 +59,9 @@ class NotificationStyle {
/// The text style for the application title. /// The text style for the application title.
final TextStyle? appTitleTextStyle; final TextStyle? appTitleTextStyle;
/// Creates a new [NotificationStyle] instance. /// The color of the divider.
/// final Color? dividerColor;
/// Each parameter is optional and allows customizing various aspects
/// of the notification appearance. /// The color of the dot indicating if the notification is read.
const NotificationStyle({ final Color? isReadDotColor;
this.titleTextStyle,
this.subtitleTextStyle,
this.backgroundColor,
this.leadingIconColor,
this.trailingIconColor,
this.contentPadding,
this.titleTextAlign,
this.subtitleTextAlign,
this.dense,
this.tileDecoration,
this.emptyNotificationsBuilder,
this.appTitleTextStyle,
});
} }

View file

@ -1,21 +1,23 @@
/// Defines translations for notification messages. /// Defines translations for notification messages.
class NotificationTranslations { class NotificationTranslations {
/// The title to be displayed in the app bar of the notification center.
final String appBarTitle;
/// The message to be displayed when there are no unread notifications available.
final String noNotifications;
/// Creates a new [NotificationTranslations] instance. /// Creates a new [NotificationTranslations] instance.
/// ///
/// The [appBarTitle] parameter specifies the title to be displayed in the /// The [appBarTitle] parameter specifies the title to be displayed in the
/// app bar of the notification center. The default value is 'Notification Center'. /// app bar of the notification center. The default value
/// is 'Notification Center'.
/// ///
/// The [noNotifications] parameter specifies the message to be displayed when /// The [noNotifications] parameter specifies the message to be displayed when
/// there are no unread notifications available. The default value is /// there are no unread notifications available. The default value is
/// 'No unread notifications available.'. /// 'No unread notifications available.'.
const NotificationTranslations({ const NotificationTranslations({
this.appBarTitle = 'Notification Center', this.appBarTitle = "Notification Center",
this.noNotifications = 'No unread notifications available.', this.noNotifications = "No unread notifications available.",
}); });
/// The title to be displayed in the app bar of the notification center.
final String appBarTitle;
/// The message to be displayed when there are no unread
/// notifications available.
final String noNotifications;
} }

View file

@ -1,15 +1,30 @@
import 'package:flutter/material.dart'; import "package:flutter/material.dart";
import 'package:flutter_animated_widgets/flutter_animated_widgets.dart'; import "package:flutter_animated_widgets/flutter_animated_widgets.dart";
import 'package:flutter_notification_center/flutter_notification_center.dart'; import "package:flutter_notification_center/flutter_notification_center.dart";
/// A bell icon widget that displays the number of active notifications.
///
/// This widget displays a bell icon with an animation indicating the number
/// of active notifications. It interacts with the notification service provided
/// in the [config] to fetch the active notifications and update its display
/// accordingly.
class NotificationBell extends StatefulWidget { class NotificationBell extends StatefulWidget {
/// Constructs a NotificationBell widget.
///
/// [config]: The notification configuration used to interact with the
/// notification service.
/// [onTap]: Callback function to be invoked when the bell icon is tapped.
const NotificationBell({ const NotificationBell({
required this.config, required this.config,
this.onTap, this.onTap,
super.key, super.key,
}); });
/// The notification configuration used to interact with
/// the notification service.
final NotificationConfig config; final NotificationConfig config;
/// Callback function to be invoked when the bell icon is tapped.
final VoidCallback? onTap; final VoidCallback? onTap;
@override @override
@ -17,12 +32,14 @@ class NotificationBell extends StatefulWidget {
} }
class _NotificationBellState extends State<NotificationBell> { class _NotificationBellState extends State<NotificationBell> {
var notificationAmount = 0; /// The number of active notifications.
int notificationAmount = 0;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// Fetch active notifications and update the notification count
WidgetsBinding.instance.addPostFrameCallback((_) async { WidgetsBinding.instance.addPostFrameCallback((_) async {
var amount = await widget.config.service.getActiveNotifications(); var amount = await widget.config.service.getActiveNotifications();
@ -33,8 +50,7 @@ class _NotificationBellState extends State<NotificationBell> {
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => IconButton(
return IconButton(
onPressed: widget.onTap, onPressed: widget.onTap,
icon: AnimatedNotificationBell( icon: AnimatedNotificationBell(
duration: const Duration(seconds: 1), duration: const Duration(seconds: 1),
@ -43,4 +59,3 @@ class _NotificationBellState extends State<NotificationBell> {
), ),
); );
} }
}

View file

@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import "package:flutter/material.dart";
import 'package:flutter_notification_center/flutter_notification_center.dart'; import "package:flutter_notification_center/flutter_notification_center.dart";
/// A widget representing a notification bell. /// A widget representing a notification bell.
class NotificationBellWidgetStory extends StatelessWidget { class NotificationBellWidgetStory extends StatelessWidget {
@ -8,18 +8,17 @@ class NotificationBellWidgetStory extends StatelessWidget {
/// The [config] parameter specifies the notification configuration. /// The [config] parameter specifies the notification configuration.
const NotificationBellWidgetStory({ const NotificationBellWidgetStory({
required this.config, required this.config,
Key? key, super.key,
}) : super(key: key); });
/// The notification configuration. /// The notification configuration.
final NotificationConfig config; final NotificationConfig config;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => NotificationBell(
return NotificationBell(
config: config, config: config,
onTap: () { onTap: () async {
Navigator.of(context).push( await Navigator.of(context).push(
MaterialPageRoute( MaterialPageRoute(
builder: (context) => NotificationCenter( builder: (context) => NotificationCenter(
config: config, config: config,
@ -29,4 +28,3 @@ class NotificationBellWidgetStory extends StatelessWidget {
}, },
); );
} }
}

View file

@ -1,37 +1,48 @@
import 'package:flutter/material.dart'; import "package:flutter/material.dart";
import 'package:flutter_notification_center/flutter_notification_center.dart'; import "package:flutter_notification_center/flutter_notification_center.dart";
import 'package:flutter_notification_center/src/notification_detail.dart'; import "package:flutter_notification_center/src/notification_detail.dart";
import 'package:intl/intl.dart';
/// Widget for displaying the notification center. /// Widget for displaying the notification center.
class NotificationCenter extends StatefulWidget { class NotificationCenter extends StatefulWidget {
/// Configuration for the notification center.
final NotificationConfig config;
/// Constructs a new NotificationCenter instance. /// Constructs a new NotificationCenter instance.
/// ///
/// [config]: Configuration for the notification center. /// [config]: Configuration for the notification center.
const NotificationCenter({ const NotificationCenter({
required this.config, required this.config,
Key? key, super.key,
}) : super(key: key); });
/// Configuration for the notification center.
final NotificationConfig config;
@override @override
_NotificationCenterState createState() => _NotificationCenterState(); NotificationCenterState createState() => NotificationCenterState();
} }
class _NotificationCenterState extends State<NotificationCenter> { /// State for the notification center.
class NotificationCenterState extends State<NotificationCenter> {
late Future<List<NotificationModel>> _notificationsFuture; late Future<List<NotificationModel>> _notificationsFuture;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// ignore: discarded_futures
_notificationsFuture = widget.config.service.getActiveNotifications(); _notificationsFuture = widget.config.service.getActiveNotifications();
widget.config.service.addListener(_listener);
} }
@override @override
Widget build(BuildContext context) { void dispose() {
return Scaffold( widget.config.service.removeListener(_listener);
super.dispose();
}
void _listener() {
setState(() {});
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text( title: Text(
widget.config.translations.appBarTitle, widget.config.translations.appBarTitle,
@ -51,85 +62,117 @@ class _NotificationCenterState extends State<NotificationCenter> {
if (snapshot.connectionState == ConnectionState.waiting) { if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) { } else if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}')); return Center(child: Text("Error: ${snapshot.error}"));
} else if (snapshot.data == null || snapshot.data!.isEmpty) { } else if (snapshot.data == null || snapshot.data!.isEmpty) {
return const Center( return const Center(
child: Text('No unread notifications available.')); child: Text("No unread notifications available."),
);
} else { } else {
return ListView.builder( return ListView.builder(
itemCount: snapshot.data!.length, itemCount: snapshot.data!.length * 2 -
1, // Double the itemCount to include dividers
itemBuilder: (context, index) { itemBuilder: (context, index) {
final notification = snapshot.data![index]; if (index.isOdd) {
final formattedDateTime = notification.dateTimePushed != null // If index is odd, return a Divider with padding
? DateFormat('yyyy-MM-dd HH:mm') return const Padding(
.format(notification.dateTimePushed!) padding: EdgeInsets.symmetric(horizontal: 24.0),
: 'Pending'; child: Divider(
return notification.isPinned color: Colors.grey, // Customize as needed
thickness: 1.0, // Customize thickness as needed
),
);
}
var notification = snapshot.data![index ~/ 2];
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: notification.isPinned
? GestureDetector( ? GestureDetector(
onTap: () => onTap: () async =>
_navigateToNotificationDetail(notification), _navigateToNotificationDetail(notification),
child: Container(
child: ListTile( child: ListTile(
leading: Icon(notification.icon, leading: Icon(
color: widget.config.style.leadingIconColor), notification.icon,
title: _buildNotificationTitle(notification.title, color: widget.config.style.leadingIconColor,
widget.config.style.titleTextStyle), ),
subtitle: _buildNotificationSubtitle( title: Row(
notification.body, crossAxisAlignment: CrossAxisAlignment.start,
formattedDateTime, children: [
notification.isRead), Expanded(
child: Text(
notification.title,
style: widget.config.style.titleTextStyle,
),
),
],
),
trailing: IconButton( trailing: IconButton(
icon: const Icon(Icons.push_pin), icon: const Icon(Icons.push_pin),
color: widget.config.style.trailingIconColor, color: widget.config.style.trailingIconColor,
onPressed: () => onPressed: () async =>
_navigateToNotificationDetail(notification), _navigateToNotificationDetail(notification),
), ),
), ),
),
) )
: Dismissible( : Dismissible(
key: Key(notification.id), key: Key(notification.id),
onDismissed: (direction) { onDismissed: (direction) async {
setState(() { await widget.config.service
widget.config.service
.dismissActiveNotification(notification); .dismissActiveNotification(notification);
_refreshNotifications(); // ignore: use_build_context_synchronously
});
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( const SnackBar(
content: Text('Notification dismissed'), content: Text("Notification dismissed"),
), ),
); );
}, },
background: Container( background: Container(
color: Colors.red, color: Colors.red,
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: Icon( child: const Icon(
Icons.delete, Icons.delete,
color: Colors.white, color: Colors.white,
), ),
), ),
child: GestureDetector( child: GestureDetector(
onTap: () => onTap: () async =>
_navigateToNotificationDetail(notification), _navigateToNotificationDetail(notification),
child: Container(
child: ListTile( child: ListTile(
leading: Icon( leading: Icon(
Icons.notification_important, Icons.notification_important,
color: widget.config.style.leadingIconColor, color: widget.config.style.leadingIconColor,
), ),
title: _buildNotificationTitle( title: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
notification.title, notification.title,
widget.config.style.titleTextStyle), style:
subtitle: _buildNotificationSubtitle( widget.config.style.titleTextStyle,
notification.body, ),
formattedDateTime, ),
notification.isRead), ],
),
trailing: !notification.isRead
? Container(
margin:
const EdgeInsets.only(left: 4.0),
width: 12.0,
height: 12.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: widget.config.style
.isReadDotColor ??
Colors.red,
),
)
: null,
), ),
), ),
), ),
); );
}); },
);
} }
}, },
), ),
@ -138,62 +181,13 @@ class _NotificationCenterState extends State<NotificationCenter> {
child: const Icon(Icons.add), child: const Icon(Icons.add),
), ),
); );
}
Widget _buildNotificationTitle(String title, TextStyle? textStyle) { Future<void> _navigateToNotificationDetail(
return Text( NotificationModel notification,
title, ) async {
style: textStyle ?? const TextStyle(), await widget.config.service.markNotificationAsRead(notification);
); await Navigator.push(
} // ignore: use_build_context_synchronously
Widget _buildNotificationSubtitle(
String body, String formattedDateTime, bool isRead) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
body,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
Text(
formattedDateTime,
style: const TextStyle(
fontSize: 12,
color: Colors.grey,
),
),
],
),
),
if (!isRead)
Container(
margin: const EdgeInsets.only(left: 4.0),
width: 12.0,
height: 12.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.red,
),
),
],
);
}
void _refreshNotifications() {
setState(() {
_notificationsFuture = widget.config.service.getActiveNotifications();
});
}
void _navigateToNotificationDetail(NotificationModel notification) {
widget.config.service.markNotificationAsRead(notification);
Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => NotificationDetailPage( builder: (context) => NotificationDetailPage(
@ -204,16 +198,14 @@ class _NotificationCenterState extends State<NotificationCenter> {
); );
} }
void _addNewNotification() { Future<void> _addNewNotification() async {
widget.config.service.pushNotification( await widget.config.service.pushNotification(
NotificationModel( NotificationModel(
id: UniqueKey().toString(), id: UniqueKey().toString(),
title: UniqueKey().toString(), title: UniqueKey().toString(),
isPinned: true,
icon: Icons.notifications_active, icon: Icons.notifications_active,
body: 'This is a new notification', body: "This is a new notification",
), ),
); );
_refreshNotifications();
} }
} }

View file

@ -1,29 +1,29 @@
import 'package:flutter/material.dart'; import "package:flutter/material.dart";
import 'package:flutter_notification_center/flutter_notification_center.dart'; import "package:flutter_notification_center/flutter_notification_center.dart";
import 'package:intl/intl.dart'; import "package:intl/intl.dart";
/// A page displaying the details of a notification. /// A page displaying the details of a notification.
class NotificationDetailPage extends StatelessWidget { class NotificationDetailPage extends StatelessWidget {
/// Creates a new [NotificationDetailPage] instance.
///
/// The [config] parameter specifies the notification configuration.
///
/// The [notification] parameter specifies the notification
/// to display details for.
const NotificationDetailPage({
required this.config,
required this.notification,
super.key,
});
/// The notification configuration. /// The notification configuration.
final NotificationConfig config; final NotificationConfig config;
/// The notification to display details for. /// The notification to display details for.
final NotificationModel notification; final NotificationModel notification;
/// Creates a new [NotificationDetailPage] instance.
///
/// The [config] parameter specifies the notification configuration.
///
/// The [notification] parameter specifies the notification to display details for.
const NotificationDetailPage({
required this.config,
required this.notification,
Key? key,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => Scaffold(
return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text( title: Text(
config.translations.appBarTitle, config.translations.appBarTitle,
@ -48,8 +48,10 @@ class NotificationDetailPage extends StatelessWidget {
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
Text( Text(
'Date: ${DateFormat('yyyy-MM-dd HH:mm').format(notification.dateTimePushed ?? DateTime.now())}', 'Date: ${DateFormat('yyyy-MM-dd HH:mm').format(
style: TextStyle( notification.dateTimePushed ?? DateTime.now(),
)}',
style: const TextStyle(
fontSize: 12, fontSize: 12,
color: Colors.grey, color: Colors.grey,
), ),
@ -60,4 +62,3 @@ class NotificationDetailPage extends StatelessWidget {
), ),
); );
} }
}

View file

@ -1,27 +1,30 @@
import 'dart:async'; import "dart:async";
import 'package:flutter_notification_center/src/models/notification.dart'; import "package:flutter/material.dart";
import "package:flutter_notification_center/src/models/notification.dart";
/// An abstract class representing a service for managing notifications. /// An abstract class representing a service for managing notifications.
abstract class NotificationService { abstract class NotificationService with ChangeNotifier {
/// A list of active notifications.
List<NotificationModel> listOfActiveNotifications;
/// A list of planned notifications.
List<NotificationModel> listOfPlannedNotifications;
/// Creates a new [NotificationService] instance. /// Creates a new [NotificationService] instance.
/// ///
/// The [listOfActiveNotifications] parameter specifies the list of active notifications, /// The [listOfActiveNotifications] parameter specifies the
/// list of active notifications,
/// with a default value of an empty list. /// with a default value of an empty list.
/// ///
/// The [listOfPlannedNotifications] parameter specifies the list of planned notifications, /// The [listOfPlannedNotifications] parameter specifies the
/// list of planned notifications,
/// with a default value of an empty list. /// with a default value of an empty list.
NotificationService({ NotificationService({
this.listOfActiveNotifications = const [], this.listOfActiveNotifications = const [],
this.listOfPlannedNotifications = const [], this.listOfPlannedNotifications = const [],
}); });
/// A list of active notifications.
List<NotificationModel> listOfActiveNotifications;
/// A list of planned notifications.
List<NotificationModel> listOfPlannedNotifications;
/// Pushes a notification to the service. /// Pushes a notification to the service.
Future pushNotification(NotificationModel notification); Future pushNotification(NotificationModel notification);