diff --git a/README.md b/README.md index abfba57..0db415a 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,66 @@ # flutter_notification_center +A Flutter package for creating notification center displaying a list of notifications. -A new Flutter project. +## Features -## Getting Started +- Notification center: A page containing a list of notifications. +- Notification bell: A clickable bell icon that can be placed anywere showing the amound of unread notifications. Clicking the bell takes the user to the notification center. +- Pinned notifications: A pinned notification can't be dismissed by the user from the notification center. +- Dismissable notifications: A dismissable notification can be dismissed by the user from the notification center. +- Notification detail page: Clicking on a notification takes the user to a notification detail page. +- Notification types: Notification types that can be created are: instant, scheduled, recurring. -This project is a starting point for a Flutter application. +## Setup -A few resources to get you started if this is your first Flutter project: +To use this package, add `flutter_notification_center` as a dependency in your pubspec.yaml file. -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) +- For custom notification styling provide the optional notificationWidgetBuilder with your own implementation. -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +The `NotificationConfig` has its own parameters, as specified below: +| Parameter | Explanation | +|-----------|-------------| +| service | The notification service that will be used | +| seperateNotificationsWithDivider | If true notifications will be seperated with dividers within the notification center | +| translations | The translations that will be used | +| notificationWidgetBuilder | The widget that defines the styles and logic for every notification | + +The `notificationWidgetBuilder` expects the following parameters, as specified below: +| Parameter | Explanation | +|-----------|-------------| +| notification | The notification that is being defined | +| style | The styling that will be used for the notification | +| notificationService | The notification service that will be used | +| notificationTranslations | The translations that will be used for the notification| +| context | The provided context | + +## Usage +Create instant notification: +- Make a call to pushNotification() and provide a NotificationModel with the required attributes. + +Create scheduled notification: +- Make a call to createScheduledNotification() and provide a NotificationModel with the required attributes + scheduledFor. + +Create recurring notification: +- Make a call to createRecurringNotification() and provide a NotificationModel with the required attributes and the following additional attributes: + - scheduledFor + - recurring = true + - occuringInterval with the pre defined interval (daily, weekly, monthly) + +To create a pinned notification, set isPinned = true when creating the notification. + + +### Example + +See [Example Code](example/lib/main.dart) for more info. + +## Issues + +Please file any issues, bugs or feature request as an issue on our [GitHub](https://github.com/Iconica-Development/flutter_notification_center/pulls) page. Commercial support is available if you need help with integration with your app or services. You can contact us at [support@iconica.nl](mailto:support@iconica.nl). + +## Want to contribute + +If you would like to contribute to the plugin (e.g. by improving the documentation, solving a bug or adding a cool new feature), please carefully review our [contribution guide](./CONTRIBUTING.md) and send us your [pull request](https://github.com/Iconica-Development/flutter_notification_center/pulls). + +## Author + +This `flutter_notification_center` for Flutter is developed by [Iconica](https://iconica.nl). You can contact us at diff --git a/packages/flutter_notification_center/README.md b/packages/flutter_notification_center/README.md deleted file mode 100644 index abfba57..0000000 --- a/packages/flutter_notification_center/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# flutter_notification_center - -A new Flutter project. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) - -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/packages/flutter_notification_center/example/README.md b/packages/flutter_notification_center/example/README.md deleted file mode 100644 index 2b3fce4..0000000 --- a/packages/flutter_notification_center/example/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# example - -A new Flutter project. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) - -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/packages/flutter_notification_center/example/lib/customer_notification.dart b/packages/flutter_notification_center/example/lib/custom_notification.dart similarity index 98% rename from packages/flutter_notification_center/example/lib/customer_notification.dart rename to packages/flutter_notification_center/example/lib/custom_notification.dart index 0560baa..2a6c186 100644 --- a/packages/flutter_notification_center/example/lib/customer_notification.dart +++ b/packages/flutter_notification_center/example/lib/custom_notification.dart @@ -92,7 +92,7 @@ class CustomNotificationWidget extends StatelessWidget { height: 12.0, decoration: BoxDecoration( shape: BoxShape.circle, - color: style.isReadDotColor ?? Colors.red, + color: style.isReadDotColor, ), ) : null, diff --git a/packages/flutter_notification_center/example/lib/main.dart b/packages/flutter_notification_center/example/lib/main.dart index dac64a1..60c78b7 100644 --- a/packages/flutter_notification_center/example/lib/main.dart +++ b/packages/flutter_notification_center/example/lib/main.dart @@ -1,11 +1,10 @@ -import 'package:example/customer_notification.dart'; +import 'package:example/custom_notification.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter_notification_center_firebase/flutter_notification_center_firebase.dart'; import 'package:intl/date_symbol_data_local.dart'; -import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter_notification_center/flutter_notification_center.dart'; import 'package:provider/provider.dart'; diff --git a/packages/flutter_notification_center/lib/src/models/notification_config.dart b/packages/flutter_notification_center/lib/src/models/notification_config.dart index b99d407..b6e8f94 100644 --- a/packages/flutter_notification_center/lib/src/models/notification_config.dart +++ b/packages/flutter_notification_center/lib/src/models/notification_config.dart @@ -12,9 +12,9 @@ class NotificationConfig { /// translations for notification messages. const NotificationConfig({ required this.service, - required this.seperateNotificationsWithDivider, + this.seperateNotificationsWithDivider = true, this.translations = const NotificationTranslations(), - required this.notificationWidgetBuilder, + this.notificationWidgetBuilder, }); /// The notification service to use for delivering notifications. @@ -27,6 +27,6 @@ class NotificationConfig { final NotificationTranslations translations; /// Widget for building each notification item. - final Widget Function(NotificationModel, BuildContext) + final Widget Function(NotificationModel, BuildContext)? notificationWidgetBuilder; } diff --git a/packages/flutter_notification_center/lib/src/notification_center.dart b/packages/flutter_notification_center/lib/src/notification_center.dart index cc5f4dc..e1154d7 100644 --- a/packages/flutter_notification_center/lib/src/notification_center.dart +++ b/packages/flutter_notification_center/lib/src/notification_center.dart @@ -82,10 +82,117 @@ class NotificationCenterState extends State { } var notification = snapshot.data![index ~/ 2]; return Padding( - padding: const EdgeInsets.symmetric(horizontal: 12.0), - child: widget.config - .notificationWidgetBuilder(notification, context), - ); + padding: const EdgeInsets.symmetric(horizontal: 12.0), + child: widget.config.notificationWidgetBuilder != null + ? widget.config.notificationWidgetBuilder!( + notification, context) + : notification.isPinned + //Pinned notification + ? GestureDetector( + onTap: () async => + _navigateToNotificationDetail( + context, + notification, + widget.config.service, + widget.config.translations, + const NotificationStyle()), + child: ListTile( + leading: Icon( + notification.icon, + color: Colors.grey, + ), + title: Row( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + notification.title, + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.w400, + fontSize: 16, + ), + ), + ), + ], + ), + trailing: IconButton( + icon: const Icon(Icons.push_pin), + color: Colors.grey, + onPressed: () async => + _navigateToNotificationDetail( + context, + notification, + widget.config.service, + widget.config.translations, + const NotificationStyle()), + padding: + const EdgeInsets.only(left: 60.0), + ), + ), + ) + //Dismissable notification + : Dismissible( + key: Key(notification.id), + onDismissed: (direction) async { + await dismissNotification( + widget.config.service, + notification, + context); + }, + background: Container( + color: Colors.red, + alignment: Alignment.centerRight, + child: const Icon( + Icons.delete, + color: Colors.white, + ), + ), + child: GestureDetector( + onTap: () async => + _navigateToNotificationDetail( + context, + notification, + widget.config.service, + widget.config.translations, + const NotificationStyle()), + child: ListTile( + leading: Icon( + notification.icon, + color: Colors.grey, + ), + title: Row( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + notification.title, + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.w400, + fontSize: 16, + ), + ), + ), + ], + ), + trailing: !notification.isRead + ? Container( + margin: const EdgeInsets.only( + right: 8.0), + width: 12.0, + height: 12.0, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + ), + ) + : null, + ), + ), + )); }, ); } @@ -93,3 +200,47 @@ class NotificationCenterState extends State { ), ); } + +Future _navigateToNotificationDetail( + BuildContext context, + NotificationModel notification, + NotificationService notificationService, + NotificationTranslations notificationTranslations, + NotificationStyle style, +) async { + await markNotificationAsRead(notificationService, notification); + if (context.mounted) { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => NotificationDetailPage( + translations: notificationTranslations, + notification: notification, + notificationStyle: style, + ), + ), + ); + } +} + +Future dismissNotification( + NotificationService notificationService, + NotificationModel notification, + BuildContext context, +) async { + await notificationService.dismissActiveNotification(notification); + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("Notification dismissed"), + ), + ); + } +} + +Future markNotificationAsRead( + NotificationService notificationService, + NotificationModel notification, +) async { + await notificationService.markNotificationAsRead(notification); +} diff --git a/packages/flutter_notification_center/lib/src/services/notification_service.dart b/packages/flutter_notification_center/lib/src/services/notification_service.dart index c3f5f12..2a7e063 100644 --- a/packages/flutter_notification_center/lib/src/services/notification_service.dart +++ b/packages/flutter_notification_center/lib/src/services/notification_service.dart @@ -38,7 +38,7 @@ abstract class NotificationService with ChangeNotifier { Future createRecurringNotification(NotificationModel notification); /// Deletes a scheduled notification. - Future deleteScheduledNotification(NotificationModel notification); + Future deletePlannedNotification(NotificationModel notification); /// Dismisses an active notification. Future dismissActiveNotification(NotificationModel notification); diff --git a/packages/flutter_notification_center_firebase/README.md b/packages/flutter_notification_center_firebase/README.md deleted file mode 100644 index 2b3fce4..0000000 --- a/packages/flutter_notification_center_firebase/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# example - -A new Flutter project. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) - -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/packages/flutter_notification_center_firebase/lib/src/services/firebase_notification_service.dart b/packages/flutter_notification_center_firebase/lib/src/services/firebase_notification_service.dart index 420e67c..1350723 100644 --- a/packages/flutter_notification_center_firebase/lib/src/services/firebase_notification_service.dart +++ b/packages/flutter_notification_center_firebase/lib/src/services/firebase_notification_service.dart @@ -113,7 +113,7 @@ class FirebaseNotificationService } @override - Future deleteScheduledNotification( + Future deletePlannedNotification( NotificationModel notificationModel) async { try { DocumentReference documentReference = FirebaseFirestore.instance @@ -192,7 +192,7 @@ class FirebaseNotificationService if (notification.scheduledFor!.isBefore(currentTime) || notification.scheduledFor!.isAtSameMomentAs(currentTime)) { await pushNotification(notification); - await deleteScheduledNotification(notification); + await deletePlannedNotification(notification); //Plan new recurring notification instance if (notification.recurring) {