mirror of
https://github.com/Iconica-Development/flutter_notification_center.git
synced 2025-05-18 16:43:44 +02:00
feat: rework flutter_notification_center design
This commit is contained in:
parent
b57bc7ce46
commit
169228152e
21 changed files with 496 additions and 471 deletions
|
@ -1,3 +1,8 @@
|
|||
## [2.0.0] - 6 June 2024
|
||||
|
||||
* Rework design for notification center
|
||||
* Added iconica linter
|
||||
|
||||
## [1.4.1] - 4 June 2024
|
||||
|
||||
* Fix notification amount number to properly size and show plus icon when above certain amount
|
||||
|
|
|
@ -1,28 +1,9 @@
|
|||
# This file configures the analyzer, which statically analyzes Dart code to
|
||||
# check for errors, warnings, and lints.
|
||||
#
|
||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||
# invoked from the command line by running `flutter analyze`.
|
||||
include: package:flutter_iconica_analysis/analysis_options.yaml
|
||||
|
||||
# The following line activates a set of recommended lints for Flutter apps,
|
||||
# packages, and plugins designed to encourage good coding practices.
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
# Possible to overwrite the rules from the package
|
||||
|
||||
analyzer:
|
||||
exclude:
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||
# included above or to enable additional rules. A list of all available lints
|
||||
# and their documentation is published at https://dart.dev/lints.
|
||||
#
|
||||
# Instead of disabling a lint rule for the entire project in the
|
||||
# section below, it can also be suppressed for a single line of code
|
||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||
# producing the lint.
|
||||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
|
|
|
@ -23,6 +23,5 @@ linter:
|
|||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
|
|
|
@ -82,7 +82,6 @@ class _NotificationCenterDemoState extends State<NotificationCenterDemo> {
|
|||
notificationTranslations: const NotificationTranslations.empty(),
|
||||
context: context,
|
||||
),
|
||||
seperateNotificationsWithDivider: true,
|
||||
);
|
||||
popupHandler = PopupHandler(context: context, config: config);
|
||||
}
|
||||
|
|
|
@ -2,12 +2,12 @@ name: example
|
|||
description: "A new Flutter project."
|
||||
# The following line prevents the package from being accidentally published to
|
||||
# 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
|
||||
|
||||
environment:
|
||||
sdk: '>=3.3.2 <4.0.0'
|
||||
sdk: ">=3.3.2 <4.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
|
@ -17,20 +17,18 @@ dependencies:
|
|||
flutter_notification_center:
|
||||
git:
|
||||
url: https://github.com/Iconica-Development/flutter_notification_center
|
||||
ref: 1.4.0
|
||||
path: packages/flutter_notification_center
|
||||
ref: 2.0.0
|
||||
|
||||
flutter_notification_center_firebase:
|
||||
git:
|
||||
url: https://github.com/Iconica-Development/flutter_notification_center
|
||||
ref: 1.4.0
|
||||
path: packages/flutter_notification_center_firebase
|
||||
|
||||
|
||||
ref: 2.0.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
sdk: flutter
|
||||
flutter_iconica_analysis:
|
||||
git:
|
||||
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
||||
|
@ -39,4 +37,3 @@ dev_dependencies:
|
|||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
|
|
|
@ -2,16 +2,17 @@
|
|||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
export "package:flutter_animated_widgets/flutter_animated_widgets.dart";
|
||||
|
||||
export "src/models/notification.dart";
|
||||
export "src/models/notification_config.dart";
|
||||
export "src/models/notification_theme.dart";
|
||||
export "src/models/notification_translation.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_bell_story.dart";
|
||||
export "src/notification_center.dart";
|
||||
export "src/notification_detail.dart";
|
||||
export "src/notification_dialog.dart";
|
||||
export "src/notification_snackbar.dart";
|
||||
export "src/popup_handler.dart";
|
||||
export "src/services/notification_service.dart";
|
||||
export "package:flutter_animated_widgets/flutter_animated_widgets.dart";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import "package:flutter/material.dart";
|
||||
|
||||
import '../../flutter_notification_center.dart';
|
||||
import "package:flutter_notification_center/flutter_notification_center.dart";
|
||||
|
||||
/// Configuration class for notifications.
|
||||
class NotificationConfig {
|
||||
|
@ -12,7 +12,6 @@ class NotificationConfig {
|
|||
/// translations for notification messages.
|
||||
const NotificationConfig({
|
||||
required this.service,
|
||||
this.seperateNotificationsWithDivider = true,
|
||||
this.translations = const NotificationTranslations.empty(),
|
||||
this.notificationWidgetBuilder,
|
||||
this.showAsSnackBar = true,
|
||||
|
@ -23,9 +22,6 @@ class NotificationConfig {
|
|||
/// The notification service to use for delivering notifications.
|
||||
final NotificationService service;
|
||||
|
||||
/// Whether to seperate notifications with a divider.
|
||||
final bool seperateNotificationsWithDivider;
|
||||
|
||||
/// Translations for notification messages.
|
||||
final NotificationTranslations translations;
|
||||
|
||||
|
@ -33,7 +29,8 @@ class NotificationConfig {
|
|||
final Widget Function(NotificationModel, BuildContext)?
|
||||
notificationWidgetBuilder;
|
||||
|
||||
/// Whether to show notifications as snackbars. If false show notifications as a dialog.
|
||||
/// Whether to show notifications as snackbars.
|
||||
/// If false show notifications as a dialog.
|
||||
final bool showAsSnackBar;
|
||||
|
||||
/// Whether to show notification popups.
|
||||
|
|
|
@ -14,7 +14,7 @@ class NotificationTranslations {
|
|||
});
|
||||
|
||||
const NotificationTranslations.empty({
|
||||
this.appBarTitle = "Notification Center",
|
||||
this.appBarTitle = "Notifications",
|
||||
this.noNotifications = "No unread notifications available.",
|
||||
this.notificationDismissed = "Notification dismissed.",
|
||||
this.notificationPinned = "Notification pinned.",
|
||||
|
@ -63,18 +63,17 @@ class NotificationTranslations {
|
|||
String? datePrefix,
|
||||
String? notAvailable,
|
||||
String? dissmissDialog,
|
||||
}) {
|
||||
return NotificationTranslations(
|
||||
appBarTitle: appBarTitle ?? this.appBarTitle,
|
||||
noNotifications: noNotifications ?? this.noNotifications,
|
||||
notificationDismissed:
|
||||
notificationDismissed ?? this.notificationDismissed,
|
||||
notificationPinned: notificationPinned ?? this.notificationPinned,
|
||||
notificationUnpinned: notificationUnpinned ?? this.notificationUnpinned,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
datePrefix: datePrefix ?? this.datePrefix,
|
||||
notAvailable: notAvailable ?? this.notAvailable,
|
||||
dissmissDialog: dissmissDialog ?? this.dissmissDialog,
|
||||
);
|
||||
}
|
||||
}) =>
|
||||
NotificationTranslations(
|
||||
appBarTitle: appBarTitle ?? this.appBarTitle,
|
||||
noNotifications: noNotifications ?? this.noNotifications,
|
||||
notificationDismissed:
|
||||
notificationDismissed ?? this.notificationDismissed,
|
||||
notificationPinned: notificationPinned ?? this.notificationPinned,
|
||||
notificationUnpinned: notificationUnpinned ?? this.notificationUnpinned,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
datePrefix: datePrefix ?? this.datePrefix,
|
||||
notAvailable: notAvailable ?? this.notAvailable,
|
||||
dissmissDialog: dissmissDialog ?? this.dissmissDialog,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "../flutter_notification_center.dart";
|
||||
import "package:flutter_notification_center/flutter_notification_center.dart";
|
||||
|
||||
/// A bell icon widget that displays the number of active notifications.
|
||||
///
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "../flutter_notification_center.dart";
|
||||
import "package:flutter_notification_center/flutter_notification_center.dart";
|
||||
|
||||
/// A widget representing a notification bell.
|
||||
class NotificationBellWidgetStory extends StatelessWidget {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import '../flutter_notification_center.dart';
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_notification_center/flutter_notification_center.dart";
|
||||
import "package:intl/intl.dart";
|
||||
|
||||
/// Widget for displaying the notification center.
|
||||
class NotificationCenter extends StatefulWidget {
|
||||
|
@ -27,7 +28,7 @@ class NotificationCenterState extends State<NotificationCenter> {
|
|||
super.initState();
|
||||
// ignore: discarded_futures
|
||||
_notificationsFuture = widget.config.service.getActiveNotifications();
|
||||
widget.config.service.getActiveAmountStream().listen((amount) {
|
||||
widget.config.service.getActiveAmountStream().listen((amount) async {
|
||||
_notificationsFuture = widget.config.service.getActiveNotifications();
|
||||
});
|
||||
widget.config.service.addListener(_listener);
|
||||
|
@ -44,227 +45,269 @@ class NotificationCenterState extends State<NotificationCenter> {
|
|||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
widget.config.translations.appBarTitle,
|
||||
),
|
||||
centerTitle: true,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
Widget build(BuildContext context) {
|
||||
var theme = Theme.of(context);
|
||||
return Scaffold(
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
appBar: AppBar(
|
||||
backgroundColor: theme.appBarTheme.backgroundColor,
|
||||
title: Text(
|
||||
widget.config.translations.appBarTitle,
|
||||
style: theme.appBarTheme.titleTextStyle,
|
||||
),
|
||||
body: FutureBuilder<List<NotificationModel>>(
|
||||
future: _notificationsFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (snapshot.hasError) {
|
||||
debugPrint("Error: ${snapshot.error}");
|
||||
return Center(
|
||||
child: Text(widget.config.translations.errorMessage));
|
||||
} else if (snapshot.data == null || snapshot.data!.isEmpty) {
|
||||
return Center(
|
||||
child: Text(widget.config.translations.noNotifications),
|
||||
);
|
||||
} else {
|
||||
return ListView.builder(
|
||||
itemCount: snapshot.data!.length * 2 - 1,
|
||||
itemBuilder: (context, index) {
|
||||
if (index.isOdd) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: widget.config.seperateNotificationsWithDivider
|
||||
? const Divider(
|
||||
color: Colors.grey,
|
||||
thickness: 1.0,
|
||||
)
|
||||
: Container(),
|
||||
);
|
||||
}
|
||||
var notification = snapshot.data![index ~/ 2];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: widget.config.notificationWidgetBuilder != null
|
||||
? widget.config.notificationWidgetBuilder!(
|
||||
notification, context)
|
||||
: notification.isPinned
|
||||
//Pinned notification
|
||||
? Dismissible(
|
||||
key: Key('${notification.id}_pinned'),
|
||||
onDismissed: (direction) async {
|
||||
await unPinNotification(
|
||||
widget.config.service,
|
||||
notification,
|
||||
widget.config.translations,
|
||||
context);
|
||||
},
|
||||
background: Container(
|
||||
color:
|
||||
const Color.fromRGBO(59, 213, 111, 1),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.only(left: 16.0),
|
||||
child: Icon(
|
||||
Icons.push_pin,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
secondaryBackground: Container(
|
||||
color:
|
||||
const Color.fromRGBO(59, 213, 111, 1),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.only(left: 16.0),
|
||||
child: Icon(
|
||||
Icons.push_pin,
|
||||
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: 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 {
|
||||
if (direction ==
|
||||
DismissDirection.endToStart) {
|
||||
await dismissNotification(
|
||||
widget.config.service,
|
||||
notification,
|
||||
widget.config.translations,
|
||||
context);
|
||||
} else if (direction ==
|
||||
DismissDirection.startToEnd) {
|
||||
await pinNotification(
|
||||
widget.config.service,
|
||||
notification,
|
||||
widget.config.translations,
|
||||
context);
|
||||
}
|
||||
},
|
||||
background: Container(
|
||||
color:
|
||||
const Color.fromRGBO(59, 213, 111, 1),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.only(left: 16.0),
|
||||
child: Icon(
|
||||
Icons.push_pin,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
secondaryBackground: Container(
|
||||
color:
|
||||
const Color.fromRGBO(255, 131, 131, 1),
|
||||
alignment: Alignment.centerRight,
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.only(right: 16.0),
|
||||
child: Icon(
|
||||
Icons.delete,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
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,
|
||||
),
|
||||
),
|
||||
));
|
||||
},
|
||||
);
|
||||
}
|
||||
centerTitle: true,
|
||||
iconTheme: theme.appBarTheme.iconTheme ??
|
||||
const IconThemeData(color: Colors.white),
|
||||
leading: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.arrow_back_ios,
|
||||
),
|
||||
),
|
||||
);
|
||||
),
|
||||
body: FutureBuilder<List<NotificationModel>>(
|
||||
future: _notificationsFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (snapshot.hasError) {
|
||||
debugPrint("Error: ${snapshot.error}");
|
||||
return Center(child: Text(widget.config.translations.errorMessage));
|
||||
} else if (snapshot.data == null || snapshot.data!.isEmpty) {
|
||||
return Center(
|
||||
child: Text(widget.config.translations.noNotifications),
|
||||
);
|
||||
} else {
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 20,
|
||||
horizontal: 20,
|
||||
),
|
||||
itemCount: snapshot.data!.length,
|
||||
itemBuilder: (context, index) {
|
||||
var notification = snapshot.data![index];
|
||||
return notification.isPinned
|
||||
? GestureDetector(
|
||||
onTap: () async => _navigateToNotificationDetail(
|
||||
context,
|
||||
notification,
|
||||
widget.config.service,
|
||||
widget.config.translations,
|
||||
const NotificationStyle(),
|
||||
),
|
||||
child: Dismissible(
|
||||
key: Key("${notification.id}_pinned"),
|
||||
onDismissed: (direction) async {
|
||||
if (direction == DismissDirection.endToStart) {
|
||||
await unPinNotification(
|
||||
widget.config.service,
|
||||
notification,
|
||||
widget.config.translations,
|
||||
context,
|
||||
);
|
||||
} else if (direction ==
|
||||
DismissDirection.startToEnd) {
|
||||
await unPinNotification(
|
||||
widget.config.service,
|
||||
notification,
|
||||
widget.config.translations,
|
||||
context,
|
||||
);
|
||||
}
|
||||
},
|
||||
background: Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Color.fromRGBO(59, 213, 111, 1),
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(6),
|
||||
bottomLeft: Radius.circular(6),
|
||||
),
|
||||
),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.only(left: 16.0),
|
||||
child: Icon(
|
||||
Icons.push_pin,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
secondaryBackground: Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Color.fromRGBO(59, 213, 111, 1),
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(6),
|
||||
bottomRight: Radius.circular(6),
|
||||
),
|
||||
),
|
||||
alignment: Alignment.centerRight,
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.only(right: 16.0),
|
||||
child: Icon(
|
||||
Icons.push_pin,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: _notificationItem(
|
||||
context,
|
||||
notification,
|
||||
),
|
||||
),
|
||||
)
|
||||
: GestureDetector(
|
||||
onTap: () async => _navigateToNotificationDetail(
|
||||
context,
|
||||
notification,
|
||||
widget.config.service,
|
||||
widget.config.translations,
|
||||
const NotificationStyle(),
|
||||
),
|
||||
child: Dismissible(
|
||||
key: Key(notification.id),
|
||||
onDismissed: (direction) async {
|
||||
if (direction == DismissDirection.endToStart) {
|
||||
await dismissNotification(
|
||||
widget.config.service,
|
||||
notification,
|
||||
widget.config.translations,
|
||||
context,
|
||||
);
|
||||
} else if (direction ==
|
||||
DismissDirection.startToEnd) {
|
||||
await pinNotification(
|
||||
widget.config.service,
|
||||
notification,
|
||||
widget.config.translations,
|
||||
context,
|
||||
);
|
||||
}
|
||||
},
|
||||
background: Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Color.fromRGBO(59, 213, 111, 1),
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(6),
|
||||
bottomLeft: Radius.circular(6),
|
||||
),
|
||||
),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.only(left: 16.0),
|
||||
child: Icon(
|
||||
Icons.push_pin,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
secondaryBackground: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Color.fromRGBO(255, 131, 131, 1),
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(6),
|
||||
bottomRight: Radius.circular(6),
|
||||
),
|
||||
),
|
||||
alignment: Alignment.centerRight,
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.only(right: 16.0),
|
||||
child: Icon(
|
||||
Icons.delete,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: _notificationItem(
|
||||
context,
|
||||
notification,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _notificationItem(
|
||||
BuildContext context,
|
||||
NotificationModel notification,
|
||||
) {
|
||||
var theme = Theme.of(context);
|
||||
var dateTimePushed =
|
||||
DateFormat("dd-MM-yyyy HH:mm").format(notification.dateTimePushed!);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
width: double.infinity,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
if (!notification.isPinned) ...[
|
||||
if (!notification.isRead) ...[
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
const Icon(
|
||||
Icons.circle_rounded,
|
||||
color: Colors.black,
|
||||
size: 10,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
],
|
||||
] else ...[
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
Transform.rotate(
|
||||
angle: 0.5,
|
||||
child: Icon(
|
||||
notification.isRead
|
||||
? Icons.push_pin_outlined
|
||||
: Icons.push_pin,
|
||||
color: Colors.black,
|
||||
size: 30,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
],
|
||||
Text(
|
||||
notification.title,
|
||||
style: notification.isRead
|
||||
? theme.textTheme.bodyMedium
|
||||
: theme.textTheme.bodyLarge,
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
dateTimePushed,
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _navigateToNotificationDetail(
|
||||
|
@ -274,7 +317,6 @@ Future<void> _navigateToNotificationDetail(
|
|||
NotificationTranslations notificationTranslations,
|
||||
NotificationStyle style,
|
||||
) async {
|
||||
await markNotificationAsRead(notificationService, notification);
|
||||
if (context.mounted) {
|
||||
await Navigator.push(
|
||||
context,
|
||||
|
@ -287,6 +329,7 @@ Future<void> _navigateToNotificationDetail(
|
|||
),
|
||||
);
|
||||
}
|
||||
await markNotificationAsRead(notificationService, notification);
|
||||
}
|
||||
|
||||
Future<void> dismissNotification(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "../flutter_notification_center.dart";
|
||||
import "package:flutter_notification_center/flutter_notification_center.dart";
|
||||
import "package:intl/intl.dart";
|
||||
|
||||
/// A page displaying the details of a notification.
|
||||
|
@ -27,43 +27,58 @@ class NotificationDetailPage extends StatelessWidget {
|
|||
final NotificationTranslations translations;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
translations.appBarTitle,
|
||||
style: notificationStyle.appTitleTextStyle,
|
||||
),
|
||||
centerTitle: true,
|
||||
Widget build(BuildContext context) {
|
||||
var theme = Theme.of(context);
|
||||
return Scaffold(
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
appBar: AppBar(
|
||||
backgroundColor: theme.appBarTheme.backgroundColor,
|
||||
title: Text(
|
||||
translations.appBarTitle,
|
||||
style: theme.appBarTheme.titleTextStyle,
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
notification.title,
|
||||
style: notificationStyle.titleTextStyle ?? const TextStyle(),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
notification.body,
|
||||
style:
|
||||
notificationStyle.subtitleTextStyle ?? const TextStyle(),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'${translations.datePrefix} ${DateFormat('yyyy-MM-dd HH:mm').format(
|
||||
notification.dateTimePushed ?? DateTime.now(),
|
||||
)}',
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
iconTheme: theme.appBarTheme.iconTheme ??
|
||||
const IconThemeData(color: Colors.white),
|
||||
centerTitle: true,
|
||||
leading: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.arrow_back_ios,
|
||||
),
|
||||
),
|
||||
);
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
notification.title,
|
||||
style: notificationStyle.titleTextStyle ?? const TextStyle(),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
notification.body,
|
||||
style: notificationStyle.subtitleTextStyle ?? const TextStyle(),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"${translations.datePrefix}"
|
||||
' ${DateFormat('yyyy-MM-dd HH:mm').format(
|
||||
notification.dateTimePushed ?? DateTime.now(),
|
||||
)}',
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_notification_center/flutter_notification_center.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_notification_center/flutter_notification_center.dart";
|
||||
import "package:intl/intl.dart";
|
||||
|
||||
class NotificationDialog extends StatelessWidget {
|
||||
const NotificationDialog({
|
||||
required this.title,
|
||||
required this.body,
|
||||
required this.translations,
|
||||
super.key,
|
||||
this.datetimePublished,
|
||||
});
|
||||
final String title;
|
||||
final String body;
|
||||
final DateTime? datetimePublished;
|
||||
final NotificationTranslations translations;
|
||||
|
||||
const NotificationDialog({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.body,
|
||||
required this.translations,
|
||||
this.datetimePublished,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String formattedDateTime = datetimePublished != null
|
||||
? DateFormat('dd MMM HH:mm').format(datetimePublished!)
|
||||
var formattedDateTime = datetimePublished != null
|
||||
? DateFormat("dd MMM HH:mm").format(datetimePublished!)
|
||||
: translations.notAvailable;
|
||||
|
||||
return AlertDialog(
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_notification_center/flutter_notification_center.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_notification_center/flutter_notification_center.dart";
|
||||
import "package:intl/intl.dart";
|
||||
|
||||
class NotificationSnackbar extends SnackBar {
|
||||
NotificationSnackbar({
|
||||
super.key,
|
||||
required String title,
|
||||
required String body,
|
||||
required NotificationTranslations translations,
|
||||
required VoidCallback onDismiss,
|
||||
super.key,
|
||||
DateTime? datetimePublished,
|
||||
}) : super(
|
||||
content: Column(
|
||||
|
@ -33,7 +33,7 @@ class NotificationSnackbar extends SnackBar {
|
|||
const SizedBox(height: 4),
|
||||
Text(
|
||||
datetimePublished != null
|
||||
? DateFormat('dd MMM HH:mm').format(datetimePublished)
|
||||
? DateFormat("dd-MM-yyyy HH:mm").format(datetimePublished)
|
||||
: translations.notAvailable,
|
||||
style: const TextStyle(
|
||||
fontSize: 12.0,
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
// Define a PopupHandler class to handle notification popups
|
||||
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";
|
||||
|
||||
class PopupHandler {
|
||||
final BuildContext context;
|
||||
final NotificationConfig config;
|
||||
|
||||
PopupHandler({
|
||||
required this.context,
|
||||
required this.config,
|
||||
});
|
||||
final BuildContext context;
|
||||
final NotificationConfig config;
|
||||
|
||||
void handleNotificationPopup(NotificationModel notification) {
|
||||
Future<void> handleNotificationPopup(NotificationModel notification) async {
|
||||
if (!config.enableNotificationPopups) return;
|
||||
|
||||
if (config.showAsSnackBar) {
|
||||
|
@ -31,7 +30,7 @@ class PopupHandler {
|
|||
} else {
|
||||
if (ModalRoute.of(context)?.isCurrent != true) return;
|
||||
|
||||
showDialog(
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) => NotificationDialog(
|
||||
translations: config.translations,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import "dart:async";
|
||||
|
||||
import "package:flutter/material.dart";
|
||||
import "../models/notification.dart";
|
||||
import "package:flutter_notification_center/src/models/notification.dart";
|
||||
|
||||
/// An abstract class representing a service for managing notifications.
|
||||
abstract class NotificationService with ChangeNotifier {
|
||||
|
@ -26,8 +26,10 @@ abstract class NotificationService with ChangeNotifier {
|
|||
List<NotificationModel> listOfPlannedNotifications;
|
||||
|
||||
/// Pushes a notification to the service.
|
||||
Future pushNotification(NotificationModel notification,
|
||||
[Function(NotificationModel model)? onNewNotification]);
|
||||
Future pushNotification(
|
||||
NotificationModel notification, [
|
||||
Function(NotificationModel model)? onNewNotification,
|
||||
]);
|
||||
|
||||
/// Retrieves the list of active notifications.
|
||||
Future<List<NotificationModel>> getActiveNotifications();
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
name: flutter_notification_center
|
||||
description: "A Flutter package for displaying notifications in a notification center."
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.4.1
|
||||
publish_to: "none"
|
||||
version: 2.0.0
|
||||
|
||||
environment:
|
||||
sdk: '>=3.3.2 <4.0.0'
|
||||
sdk: ">=3.3.2 <4.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
|
@ -15,11 +14,11 @@ dependencies:
|
|||
flutter_animated_widgets:
|
||||
git:
|
||||
url: https://github.com/Iconica-Development/flutter_animated_widgets
|
||||
ref: 0.2.0
|
||||
ref: 0.3.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
sdk: flutter
|
||||
flutter_iconica_analysis:
|
||||
git:
|
||||
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
void main() {
|
||||
test('test', () {
|
||||
test("test", () {
|
||||
expect(true, true);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,28 +1,9 @@
|
|||
# This file configures the analyzer, which statically analyzes Dart code to
|
||||
# check for errors, warnings, and lints.
|
||||
#
|
||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||
# invoked from the command line by running `flutter analyze`.
|
||||
include: package:flutter_iconica_analysis/analysis_options.yaml
|
||||
|
||||
# The following line activates a set of recommended lints for Flutter apps,
|
||||
# packages, and plugins designed to encourage good coding practices.
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
# Possible to overwrite the rules from the package
|
||||
|
||||
analyzer:
|
||||
exclude:
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||
# included above or to enable additional rules. A list of all available lints
|
||||
# and their documentation is published at https://dart.dev/lints.
|
||||
#
|
||||
# Instead of disabling a lint rule for the entire project in the
|
||||
# section below, it can also be suppressed for a single line of code
|
||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||
# producing the lint.
|
||||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
|
|
|
@ -1,14 +1,25 @@
|
|||
import 'dart:async';
|
||||
import "dart:async";
|
||||
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_notification_center/flutter_notification_center.dart';
|
||||
import "package:cloud_firestore/cloud_firestore.dart";
|
||||
import "package:firebase_auth/firebase_auth.dart";
|
||||
import "package:firebase_core/firebase_core.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_notification_center/flutter_notification_center.dart";
|
||||
|
||||
class FirebaseNotificationService
|
||||
with ChangeNotifier
|
||||
implements NotificationService {
|
||||
FirebaseNotificationService({
|
||||
required this.newNotificationCallback,
|
||||
this.firebaseApp,
|
||||
this.activeNotificationsCollection = "active_notifications",
|
||||
this.plannedNotificationsCollection = "planned_notifications",
|
||||
this.listOfActiveNotifications = const [],
|
||||
this.listOfPlannedNotifications = const [],
|
||||
}) {
|
||||
_firebaseApp = firebaseApp ?? Firebase.app();
|
||||
unawaited(_startTimer());
|
||||
}
|
||||
final Function(NotificationModel) newNotificationCallback;
|
||||
final FirebaseApp? firebaseApp;
|
||||
final String activeNotificationsCollection;
|
||||
|
@ -23,33 +34,23 @@ class FirebaseNotificationService
|
|||
// ignore: unused_field
|
||||
late Timer _timer;
|
||||
|
||||
FirebaseNotificationService({
|
||||
required this.newNotificationCallback,
|
||||
this.firebaseApp,
|
||||
this.activeNotificationsCollection = 'active_notifications',
|
||||
this.plannedNotificationsCollection = 'planned_notifications',
|
||||
this.listOfActiveNotifications = const [],
|
||||
this.listOfPlannedNotifications = const [],
|
||||
}) {
|
||||
_firebaseApp = firebaseApp ?? Firebase.app();
|
||||
_startTimer();
|
||||
}
|
||||
|
||||
void _startTimer() {
|
||||
_timer = Timer.periodic(const Duration(seconds: 15), (timer) {
|
||||
debugPrint('Checking for scheduled notifications');
|
||||
checkForScheduledNotifications();
|
||||
Future<void> _startTimer() async {
|
||||
_timer = Timer.periodic(const Duration(seconds: 15), (timer) async {
|
||||
debugPrint("Checking for scheduled notifications");
|
||||
await checkForScheduledNotifications();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> pushNotification(NotificationModel notification,
|
||||
[Function(NotificationModel model)? onNewNotification]) async {
|
||||
Future<void> pushNotification(
|
||||
NotificationModel notification, [
|
||||
Function(NotificationModel model)? onNewNotification,
|
||||
]) async {
|
||||
try {
|
||||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
||||
|
||||
if (userId == null) {
|
||||
debugPrint('User is not authenticated');
|
||||
debugPrint("User is not authenticated");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -59,9 +60,9 @@ class FirebaseNotificationService
|
|||
.doc(userId)
|
||||
.collection(activeNotificationsCollection);
|
||||
|
||||
DateTime currentDateTime = DateTime.now();
|
||||
var currentDateTime = DateTime.now();
|
||||
notification.dateTimePushed = currentDateTime;
|
||||
Map<String, dynamic> notificationMap = notification.toMap();
|
||||
var notificationMap = notification.toMap();
|
||||
await notifications.doc(notification.id).set(notificationMap);
|
||||
|
||||
listOfActiveNotifications = [...listOfActiveNotifications, notification];
|
||||
|
@ -74,8 +75,8 @@ class FirebaseNotificationService
|
|||
}
|
||||
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
debugPrint('Error creating document: $e');
|
||||
} on Exception catch (e) {
|
||||
debugPrint("Error creating document: $e");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,7 +86,7 @@ class FirebaseNotificationService
|
|||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
||||
|
||||
if (userId == null) {
|
||||
debugPrint('User is not authenticated');
|
||||
debugPrint("User is not authenticated");
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -95,12 +96,11 @@ class FirebaseNotificationService
|
|||
.doc(userId)
|
||||
.collection(activeNotificationsCollection);
|
||||
|
||||
QuerySnapshot querySnapshot = await activeNotificationsResult.get();
|
||||
var querySnapshot = await activeNotificationsResult.get();
|
||||
|
||||
List<NotificationModel> activeNotifications =
|
||||
querySnapshot.docs.map((doc) {
|
||||
Map<String, dynamic> data = doc.data() as Map<String, dynamic>;
|
||||
data['id'] = doc.id;
|
||||
var activeNotifications = querySnapshot.docs.map((doc) {
|
||||
var data = doc.data()! as Map<String, dynamic>;
|
||||
data["id"] = doc.id;
|
||||
return NotificationModel.fromJson(data);
|
||||
}).toList();
|
||||
|
||||
|
@ -114,19 +114,22 @@ class FirebaseNotificationService
|
|||
.sort((a, b) => b.dateTimePushed!.compareTo(a.dateTimePushed!));
|
||||
|
||||
listOfActiveNotifications.insertAll(
|
||||
0, activeNotifications.where((element) => element.isPinned));
|
||||
0,
|
||||
activeNotifications.where((element) => element.isPinned),
|
||||
);
|
||||
|
||||
notifyListeners();
|
||||
return listOfActiveNotifications;
|
||||
} catch (e) {
|
||||
debugPrint('Error getting active notifications: $e');
|
||||
} on Exception catch (e) {
|
||||
debugPrint("Error getting active notifications: $e");
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> createRecurringNotification(
|
||||
NotificationModel notification) async {
|
||||
NotificationModel notification,
|
||||
) async {
|
||||
if (notification.recurring) {
|
||||
switch (notification.occuringInterval) {
|
||||
case OcurringInterval.daily:
|
||||
|
@ -147,18 +150,19 @@ class FirebaseNotificationService
|
|||
break;
|
||||
case null:
|
||||
}
|
||||
createScheduledNotification(notification);
|
||||
await createScheduledNotification(notification);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> createScheduledNotification(
|
||||
NotificationModel notification) async {
|
||||
NotificationModel notification,
|
||||
) async {
|
||||
try {
|
||||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
||||
|
||||
if (userId == null) {
|
||||
debugPrint('User is not authenticated');
|
||||
debugPrint("User is not authenticated");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -168,21 +172,22 @@ class FirebaseNotificationService
|
|||
.doc(userId)
|
||||
.collection(plannedNotificationsCollection);
|
||||
|
||||
Map<String, dynamic> notificationMap = notification.toMap();
|
||||
var notificationMap = notification.toMap();
|
||||
await plannedNotifications.doc(notification.id).set(notificationMap);
|
||||
} catch (e) {
|
||||
debugPrint('Error creating document: $e');
|
||||
} on Exception catch (e) {
|
||||
debugPrint("Error creating document: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deletePlannedNotification(
|
||||
NotificationModel notificationModel) async {
|
||||
NotificationModel notificationModel,
|
||||
) async {
|
||||
try {
|
||||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
||||
|
||||
if (userId == null) {
|
||||
debugPrint('User is not authenticated');
|
||||
debugPrint("User is not authenticated");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -202,24 +207,26 @@ class FirebaseNotificationService
|
|||
.get();
|
||||
|
||||
if (querySnapshot.docs.isEmpty) {
|
||||
debugPrint('The collection is now empty');
|
||||
debugPrint("The collection is now empty");
|
||||
} else {
|
||||
debugPrint(
|
||||
'Deleted planned notification with title: ${notificationModel.title}');
|
||||
"Deleted planned notification with title: ${notificationModel.title}",
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Error deleting document: $e');
|
||||
} on Exception catch (e) {
|
||||
debugPrint("Error deleting document: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> dismissActiveNotification(
|
||||
NotificationModel notificationModel) async {
|
||||
NotificationModel notificationModel,
|
||||
) async {
|
||||
try {
|
||||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
||||
|
||||
if (userId == null) {
|
||||
debugPrint('User is not authenticated');
|
||||
debugPrint("User is not authenticated");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -233,19 +240,20 @@ class FirebaseNotificationService
|
|||
listOfActiveNotifications
|
||||
.removeAt(listOfActiveNotifications.indexOf(notificationModel));
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
debugPrint('Error deleting document: $e');
|
||||
} on Exception catch (e) {
|
||||
debugPrint("Error deleting document: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> pinActiveNotification(
|
||||
NotificationModel notificationModel) async {
|
||||
NotificationModel notificationModel,
|
||||
) async {
|
||||
try {
|
||||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
||||
|
||||
if (userId == null) {
|
||||
debugPrint('User is not authenticated');
|
||||
debugPrint("User is not authenticated");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -255,7 +263,7 @@ class FirebaseNotificationService
|
|||
.doc(userId)
|
||||
.collection(activeNotificationsCollection)
|
||||
.doc(notificationModel.id);
|
||||
await documentReference.update({'isPinned': true});
|
||||
await documentReference.update({"isPinned": true});
|
||||
notificationModel.isPinned = true;
|
||||
|
||||
listOfActiveNotifications
|
||||
|
@ -263,19 +271,20 @@ class FirebaseNotificationService
|
|||
listOfActiveNotifications.insert(0, notificationModel);
|
||||
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
debugPrint('Error updating document: $e');
|
||||
} on Exception catch (e) {
|
||||
debugPrint("Error updating document: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> unPinActiveNotification(
|
||||
NotificationModel notificationModel) async {
|
||||
NotificationModel notificationModel,
|
||||
) async {
|
||||
try {
|
||||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
||||
|
||||
if (userId == null) {
|
||||
debugPrint('User is not authenticated');
|
||||
debugPrint("User is not authenticated");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -285,7 +294,7 @@ class FirebaseNotificationService
|
|||
.doc(userId)
|
||||
.collection(activeNotificationsCollection)
|
||||
.doc(notificationModel.id);
|
||||
await documentReference.update({'isPinned': false});
|
||||
await documentReference.update({"isPinned": false});
|
||||
notificationModel.isPinned = false;
|
||||
|
||||
listOfActiveNotifications
|
||||
|
@ -294,19 +303,20 @@ class FirebaseNotificationService
|
|||
listOfActiveNotifications.add(notificationModel);
|
||||
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
debugPrint('Error updating document: $e');
|
||||
} on Exception catch (e) {
|
||||
debugPrint("Error updating document: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> markNotificationAsRead(
|
||||
NotificationModel notificationModel) async {
|
||||
NotificationModel notificationModel,
|
||||
) async {
|
||||
try {
|
||||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
||||
|
||||
if (userId == null) {
|
||||
debugPrint('User is not authenticated');
|
||||
debugPrint("User is not authenticated");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -316,22 +326,22 @@ class FirebaseNotificationService
|
|||
.doc(userId)
|
||||
.collection(activeNotificationsCollection)
|
||||
.doc(notificationModel.id);
|
||||
await documentReference.update({'isRead': true});
|
||||
await documentReference.update({"isRead": true});
|
||||
notificationModel.isRead = true;
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
debugPrint('Error updating document: $e');
|
||||
} on Exception catch (e) {
|
||||
debugPrint("Error updating document: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> checkForScheduledNotifications() async {
|
||||
DateTime currentTime = DateTime.now();
|
||||
var currentTime = DateTime.now();
|
||||
try {
|
||||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
||||
|
||||
if (userId == null) {
|
||||
debugPrint('User is not authenticated');
|
||||
debugPrint("User is not authenticated");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -341,20 +351,19 @@ class FirebaseNotificationService
|
|||
.doc(userId)
|
||||
.collection(plannedNotificationsCollection);
|
||||
|
||||
QuerySnapshot querySnapshot = await plannedNotificationsResult.get();
|
||||
var querySnapshot = await plannedNotificationsResult.get();
|
||||
|
||||
if (querySnapshot.docs.isEmpty) {
|
||||
debugPrint('No scheduled notifications to be pushed');
|
||||
debugPrint("No scheduled notifications to be pushed");
|
||||
return;
|
||||
}
|
||||
|
||||
List<NotificationModel> plannedNotifications =
|
||||
querySnapshot.docs.map((doc) {
|
||||
Map<String, dynamic> data = doc.data() as Map<String, dynamic>;
|
||||
var plannedNotifications = querySnapshot.docs.map((doc) {
|
||||
var data = doc.data()! as Map<String, dynamic>;
|
||||
return NotificationModel.fromJson(data);
|
||||
}).toList();
|
||||
|
||||
for (NotificationModel notification in plannedNotifications) {
|
||||
for (var notification in plannedNotifications) {
|
||||
if (notification.scheduledFor!.isBefore(currentTime) ||
|
||||
notification.scheduledFor!.isAtSameMomentAs(currentTime)) {
|
||||
await pushNotification(notification, newNotificationCallback);
|
||||
|
@ -363,7 +372,7 @@ class FirebaseNotificationService
|
|||
|
||||
//Plan new recurring notification instance
|
||||
if (notification.recurring) {
|
||||
NotificationModel newNotification = NotificationModel(
|
||||
var newNotification = NotificationModel(
|
||||
id: UniqueKey().toString(),
|
||||
title: notification.title,
|
||||
body: notification.body,
|
||||
|
@ -375,8 +384,8 @@ class FirebaseNotificationService
|
|||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Error getting planned notifications: $e');
|
||||
} on Exception catch (e) {
|
||||
debugPrint("Error getting planned notifications: $e");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -386,7 +395,7 @@ class FirebaseNotificationService
|
|||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
||||
|
||||
if (userId == null) {
|
||||
debugPrint('User is not authenticated');
|
||||
debugPrint("User is not authenticated");
|
||||
yield 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
name: flutter_notification_center_firebase
|
||||
description: "A new Flutter project."
|
||||
publish_to: "none"
|
||||
|
||||
version: 1.4.1
|
||||
version: 2.0.0
|
||||
|
||||
environment:
|
||||
sdk: ">=2.18.0 <3.0.0"
|
||||
|
@ -10,7 +9,7 @@ environment:
|
|||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
intl: any
|
||||
intl: any
|
||||
|
||||
# Firebase
|
||||
cloud_firestore: ^4.16.0
|
||||
|
@ -22,15 +21,16 @@ dependencies:
|
|||
flutter_notification_center:
|
||||
git:
|
||||
url: https://github.com/Iconica-Development/flutter_notification_center
|
||||
ref: 1.4.0
|
||||
ref: 2.0.0
|
||||
path: packages/flutter_notification_center
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
flutter_lints: ^2.0.0
|
||||
flutter_iconica_analysis:
|
||||
git:
|
||||
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
||||
ref: 7.0.0
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
|
|
Loading…
Reference in a new issue