mirror of
https://github.com/Iconica-Development/flutter_notification_center.git
synced 2025-05-19 09:03:45 +02:00
Merge pull request #3 from Iconica-Development/feat/popup
Add option for snackbar and dialog popups
This commit is contained in:
commit
4b3bcb2d6c
13 changed files with 219 additions and 25 deletions
|
@ -1,3 +1,7 @@
|
||||||
|
## [1.1.0] - 15 April 2024
|
||||||
|
|
||||||
|
* Initial Release
|
||||||
|
|
||||||
## [1.0.0] - 14 April 2024
|
## [1.0.0] - 14 April 2024
|
||||||
|
|
||||||
* Initial Release
|
* Initial Release
|
|
@ -9,6 +9,7 @@ A Flutter package for creating notification center displaying a list of notifica
|
||||||
- Dismissable notifications: A dismissable notification can 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 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.
|
- Notification types: Notification types that can be created are: instant, scheduled, recurring.
|
||||||
|
- Notification popups: If a notification is pushed a popup can appear in form of a dialog or snackbar.
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
|
@ -23,6 +24,10 @@ The `NotificationConfig` has its own parameters, as specified below:
|
||||||
| seperateNotificationsWithDivider | If true notifications will be seperated with dividers within the notification center |
|
| seperateNotificationsWithDivider | If true notifications will be seperated with dividers within the notification center |
|
||||||
| translations | The translations that will be used |
|
| translations | The translations that will be used |
|
||||||
| notificationWidgetBuilder | The widget that defines the styles and logic for every notification |
|
| notificationWidgetBuilder | The widget that defines the styles and logic for every notification |
|
||||||
|
| enableNotificationPopups | If set to false no popups will be shown if a new notification is pushed |
|
||||||
|
| showAsSnackBar | If true notifications popups will show as snackbar. If false shown as dialog|
|
||||||
|
|
||||||
|
If you set `enableNotificationPopups` to true, you can use `PopupHandler` in the `newNotificationCallback` to display popups in case a new notification is pushed.
|
||||||
|
|
||||||
The `notificationWidgetBuilder` expects the following parameters, as specified below:
|
The `notificationWidgetBuilder` expects the following parameters, as specified below:
|
||||||
| Parameter | Explanation |
|
| Parameter | Explanation |
|
||||||
|
@ -48,7 +53,6 @@ Create recurring notification:
|
||||||
|
|
||||||
To create a pinned notification, set isPinned = true when creating the notification.
|
To create a pinned notification, set isPinned = true when creating the notification.
|
||||||
|
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
See [Example Code](example/lib/main.dart) for more info.
|
See [Example Code](example/lib/main.dart) for more info.
|
||||||
|
|
|
@ -6,7 +6,6 @@ import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:flutter_notification_center_firebase/flutter_notification_center_firebase.dart';
|
import 'package:flutter_notification_center_firebase/flutter_notification_center_firebase.dart';
|
||||||
import 'package:intl/date_symbol_data_local.dart';
|
import 'package:intl/date_symbol_data_local.dart';
|
||||||
import 'package:flutter_notification_center/flutter_notification_center.dart';
|
import 'package:flutter_notification_center/flutter_notification_center.dart';
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
@ -15,11 +14,8 @@ void main() async {
|
||||||
await _signInUser();
|
await _signInUser();
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
ChangeNotifierProvider(
|
const MaterialApp(
|
||||||
create: (_) => FirebaseNotificationService(),
|
home: NotificationCenterDemo(),
|
||||||
child: const MaterialApp(
|
|
||||||
home: NotificationCenterDemo(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -42,7 +38,7 @@ Future<void> _configureApp() async {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _signInUser() async {
|
Future<void> _signInUser() async {
|
||||||
//TO DO: Implement your own sign in logic
|
/// Implement your own sign in logic here
|
||||||
}
|
}
|
||||||
|
|
||||||
class NotificationCenterDemo extends StatefulWidget {
|
class NotificationCenterDemo extends StatefulWidget {
|
||||||
|
@ -53,10 +49,20 @@ class NotificationCenterDemo extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _NotificationCenterDemoState extends State<NotificationCenterDemo> {
|
class _NotificationCenterDemoState extends State<NotificationCenterDemo> {
|
||||||
|
late NotificationConfig config;
|
||||||
|
late PopupHandler popupHandler;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
void initState() {
|
||||||
var config = NotificationConfig(
|
super.initState();
|
||||||
service: Provider.of<FirebaseNotificationService>(context),
|
var service =
|
||||||
|
FirebaseNotificationService(newNotificationCallback: (notification) {
|
||||||
|
popupHandler.handleNotificationPopup(notification);
|
||||||
|
});
|
||||||
|
config = NotificationConfig(
|
||||||
|
service: service,
|
||||||
|
enableNotificationPopups: true,
|
||||||
|
showAsSnackBar: false,
|
||||||
notificationWidgetBuilder: (notification, context) =>
|
notificationWidgetBuilder: (notification, context) =>
|
||||||
CustomNotificationWidget(
|
CustomNotificationWidget(
|
||||||
notification: notification,
|
notification: notification,
|
||||||
|
@ -75,13 +81,17 @@ class _NotificationCenterDemoState extends State<NotificationCenterDemo> {
|
||||||
isReadDotColor: Colors.red,
|
isReadDotColor: Colors.red,
|
||||||
showNotificationIcon: true,
|
showNotificationIcon: true,
|
||||||
),
|
),
|
||||||
notificationService: Provider.of<FirebaseNotificationService>(context),
|
notificationService: service,
|
||||||
notificationTranslations: const NotificationTranslations(),
|
notificationTranslations: const NotificationTranslations(),
|
||||||
context: context,
|
context: context,
|
||||||
),
|
),
|
||||||
seperateNotificationsWithDivider: true,
|
seperateNotificationsWithDivider: true,
|
||||||
);
|
);
|
||||||
|
popupHandler = PopupHandler(context: context, config: config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Notification Center Demo'),
|
title: const Text('Notification Center Demo'),
|
||||||
|
|
|
@ -24,7 +24,6 @@ dependencies:
|
||||||
firebase_auth: ^4.2.6
|
firebase_auth: ^4.2.6
|
||||||
firebase_core: ^2.5.0
|
firebase_core: ^2.5.0
|
||||||
firebase_storage: ^11.0.14
|
firebase_storage: ^11.0.14
|
||||||
provider: ^6.1.2
|
|
||||||
|
|
||||||
flutter_notification_center:
|
flutter_notification_center:
|
||||||
path: ../
|
path: ../
|
||||||
|
|
|
@ -7,6 +7,9 @@ export "src/models/notification_config.dart";
|
||||||
export "src/models/notification_theme.dart";
|
export "src/models/notification_theme.dart";
|
||||||
export "src/models/notification_translation.dart";
|
export "src/models/notification_translation.dart";
|
||||||
export "src/notification_bell.dart";
|
export "src/notification_bell.dart";
|
||||||
|
export "src/notification_dialog.dart";
|
||||||
|
export "src/popup_handler.dart";
|
||||||
|
export "src/notification_snackbar.dart";
|
||||||
export "src/notification_detail.dart";
|
export "src/notification_detail.dart";
|
||||||
export "src/notification_bell_story.dart";
|
export "src/notification_bell_story.dart";
|
||||||
export "src/notification_center.dart";
|
export "src/notification_center.dart";
|
||||||
|
|
|
@ -10,12 +10,13 @@ class NotificationConfig {
|
||||||
/// to use. The [style] parameter is optional and defines the style of the
|
/// to use. The [style] parameter is optional and defines the style of the
|
||||||
/// notification. The [translations] parameter is also optional and provides
|
/// notification. The [translations] parameter is also optional and provides
|
||||||
/// translations for notification messages.
|
/// translations for notification messages.
|
||||||
const NotificationConfig({
|
const NotificationConfig(
|
||||||
required this.service,
|
{required this.service,
|
||||||
this.seperateNotificationsWithDivider = true,
|
this.seperateNotificationsWithDivider = true,
|
||||||
this.translations = const NotificationTranslations(),
|
this.translations = const NotificationTranslations(),
|
||||||
this.notificationWidgetBuilder,
|
this.notificationWidgetBuilder,
|
||||||
});
|
this.showAsSnackBar = true,
|
||||||
|
this.enableNotificationPopups = true});
|
||||||
|
|
||||||
/// The notification service to use for delivering notifications.
|
/// The notification service to use for delivering notifications.
|
||||||
final NotificationService service;
|
final NotificationService service;
|
||||||
|
@ -29,4 +30,10 @@ class NotificationConfig {
|
||||||
/// Widget for building each notification item.
|
/// Widget for building each notification item.
|
||||||
final Widget Function(NotificationModel, BuildContext)?
|
final Widget Function(NotificationModel, BuildContext)?
|
||||||
notificationWidgetBuilder;
|
notificationWidgetBuilder;
|
||||||
|
|
||||||
|
/// Whether to show notifications as snackbars. If false show notifications as a dialog.
|
||||||
|
final bool showAsSnackBar;
|
||||||
|
|
||||||
|
/// Whether to show notification popups.
|
||||||
|
final bool enableNotificationPopups;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
class NotificationDialog extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
|
final String body;
|
||||||
|
final DateTime? datetimePublished;
|
||||||
|
|
||||||
|
const NotificationDialog({
|
||||||
|
super.key,
|
||||||
|
required this.title,
|
||||||
|
required this.body,
|
||||||
|
this.datetimePublished,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
String formattedDateTime = datetimePublished != null
|
||||||
|
? DateFormat('dd MMM HH:mm').format(datetimePublished!)
|
||||||
|
: 'N/A';
|
||||||
|
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(
|
||||||
|
title,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 20.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
content: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
body,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16.0,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
formattedDateTime,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16.0,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: const Text(
|
||||||
|
'Dismiss',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
class NotificationSnackbar extends SnackBar {
|
||||||
|
NotificationSnackbar({
|
||||||
|
super.key,
|
||||||
|
required String title,
|
||||||
|
required String body,
|
||||||
|
DateTime? datetimePublished,
|
||||||
|
}) : super(
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 20.0,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
body,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16.0,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
datetimePublished != null
|
||||||
|
? DateFormat('dd MMM HH:mm').format(datetimePublished)
|
||||||
|
: 'N/A',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12.0,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
duration: const Duration(seconds: 8),
|
||||||
|
action: SnackBarAction(
|
||||||
|
label: 'Dismiss',
|
||||||
|
onPressed: () {},
|
||||||
|
textColor: Colors.white,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
// Define a PopupHandler class to handle notification popups
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:flutter_notification_center/flutter_notification_center.dart';
|
||||||
|
|
||||||
|
class PopupHandler {
|
||||||
|
final BuildContext context;
|
||||||
|
final NotificationConfig config;
|
||||||
|
|
||||||
|
PopupHandler({
|
||||||
|
required this.context,
|
||||||
|
required this.config,
|
||||||
|
});
|
||||||
|
|
||||||
|
void handleNotificationPopup(NotificationModel notification) {
|
||||||
|
if (!config.enableNotificationPopups) return;
|
||||||
|
|
||||||
|
if (config.showAsSnackBar) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
NotificationSnackbar(
|
||||||
|
title: notification.title,
|
||||||
|
body: notification.body,
|
||||||
|
datetimePublished: DateTime.now(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => NotificationDialog(
|
||||||
|
title: notification.title,
|
||||||
|
body: notification.body,
|
||||||
|
datetimePublished: notification.dateTimePushed,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,7 +26,8 @@ abstract class NotificationService with ChangeNotifier {
|
||||||
List<NotificationModel> listOfPlannedNotifications;
|
List<NotificationModel> listOfPlannedNotifications;
|
||||||
|
|
||||||
/// Pushes a notification to the service.
|
/// Pushes a notification to the service.
|
||||||
Future pushNotification(NotificationModel notification);
|
Future pushNotification(NotificationModel notification,
|
||||||
|
[Function(NotificationModel model)? onNewNotification]);
|
||||||
|
|
||||||
/// Retrieves the list of active notifications.
|
/// Retrieves the list of active notifications.
|
||||||
Future<List<NotificationModel>> getActiveNotifications();
|
Future<List<NotificationModel>> getActiveNotifications();
|
||||||
|
|
|
@ -8,6 +8,8 @@ import '../config/firebase_collections.dart';
|
||||||
class FirebaseNotificationService
|
class FirebaseNotificationService
|
||||||
with ChangeNotifier
|
with ChangeNotifier
|
||||||
implements NotificationService {
|
implements NotificationService {
|
||||||
|
final Function(NotificationModel) newNotificationCallback;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<NotificationModel> listOfActiveNotifications;
|
List<NotificationModel> listOfActiveNotifications;
|
||||||
@override
|
@override
|
||||||
|
@ -17,7 +19,8 @@ class FirebaseNotificationService
|
||||||
late Timer _timer;
|
late Timer _timer;
|
||||||
|
|
||||||
FirebaseNotificationService(
|
FirebaseNotificationService(
|
||||||
{this.listOfActiveNotifications = const [],
|
{required this.newNotificationCallback,
|
||||||
|
this.listOfActiveNotifications = const [],
|
||||||
this.listOfPlannedNotifications = const []}) {
|
this.listOfPlannedNotifications = const []}) {
|
||||||
_startTimer();
|
_startTimer();
|
||||||
}
|
}
|
||||||
|
@ -30,7 +33,8 @@ class FirebaseNotificationService
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> pushNotification(NotificationModel notification) async {
|
Future<void> pushNotification(NotificationModel notification,
|
||||||
|
[Function(NotificationModel model)? onNewNotification]) async {
|
||||||
try {
|
try {
|
||||||
CollectionReference notifications = FirebaseFirestore.instance
|
CollectionReference notifications = FirebaseFirestore.instance
|
||||||
.collection(FirebaseCollectionNames.activeNotifications);
|
.collection(FirebaseCollectionNames.activeNotifications);
|
||||||
|
@ -41,6 +45,14 @@ class FirebaseNotificationService
|
||||||
await notifications.doc(notification.id).set(notificationMap);
|
await notifications.doc(notification.id).set(notificationMap);
|
||||||
|
|
||||||
listOfActiveNotifications.add(notification);
|
listOfActiveNotifications.add(notification);
|
||||||
|
|
||||||
|
//Show popup with notification conte
|
||||||
|
if (onNewNotification != null) {
|
||||||
|
onNewNotification(notification);
|
||||||
|
} else {
|
||||||
|
newNotificationCallback(notification);
|
||||||
|
}
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('Error creating document: $e');
|
debugPrint('Error creating document: $e');
|
||||||
|
@ -191,7 +203,8 @@ class FirebaseNotificationService
|
||||||
for (NotificationModel notification in plannedNotifications) {
|
for (NotificationModel notification in plannedNotifications) {
|
||||||
if (notification.scheduledFor!.isBefore(currentTime) ||
|
if (notification.scheduledFor!.isBefore(currentTime) ||
|
||||||
notification.scheduledFor!.isAtSameMomentAs(currentTime)) {
|
notification.scheduledFor!.isAtSameMomentAs(currentTime)) {
|
||||||
await pushNotification(notification);
|
await pushNotification(notification, newNotificationCallback);
|
||||||
|
|
||||||
await deletePlannedNotification(notification);
|
await deletePlannedNotification(notification);
|
||||||
|
|
||||||
//Plan new recurring notification instance
|
//Plan new recurring notification instance
|
||||||
|
|
|
@ -21,7 +21,6 @@ dependencies:
|
||||||
firebase_storage: ^11.0.14
|
firebase_storage: ^11.0.14
|
||||||
|
|
||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
provider: ^6.1.2
|
|
||||||
|
|
||||||
flutter_notification_center:
|
flutter_notification_center:
|
||||||
path: ../flutter_notification_center
|
path: ../flutter_notification_center
|
||||||
|
|
|
@ -4,7 +4,7 @@ description: "A new Flutter project."
|
||||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
|
|
||||||
version: 1.0.0
|
version: 1.1.0
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.3.2 <4.0.0'
|
sdk: '>=3.3.2 <4.0.0'
|
||||||
|
|
Loading…
Reference in a new issue