Mobile applications often need to communicate important information to users, even when the app isn’t actively running. Local notifications are an excellent way to achieve this, allowing you to display messages, reminders, or alerts directly on the user’s device. This article will explore how to implement local notifications in a Flutter application using the powerful awesome_notifications
package.
We’ll discuss why you’d want to use local notifications, their importance in applications, and then provide a step-by-step guide on creating, scheduling, and canceling them. We’ll also walk through setting up a Flutter project and installing the necessary dependencies, with in-depth explanations of each code block.
Table of Contents
Why Use Local Notifications?
Local notifications play a vital role in enhancing user engagement and providing a seamless user experience. Here are some key reasons for incorporating local notifications in your Flutter application:
-
Improved User Engagement: Notifications help keep users engaged with your app by providing timely and relevant information, updates, or reminders. For instance, a fitness app might send a notification reminding a user to log their daily workout.
-
Retaining User Attention: In a crowded app landscape, notifications serve as a means to capture and retain user attention, ensuring they don’t forget about your app. They act as gentle nudges to bring users back into the application.
-
Enhanced User Experience: Local notifications can enhance the overall user experience by providing real-time updates, alerts, or personalized messages without requiring the user to open the app. Think of a weather app sending an alert about an incoming storm.
-
Task Reminders: Applications often need to remind users about specific tasks, events, or deadlines. Local notifications are an effective way to alert users about such events, like a to-do list app reminding you of an overdue task.
Prerequisites
Before we begin, ensure you have the following installed on your system:
-
Flutter SDK: Make sure you have Flutter installed and configured correctly. You can follow the official Flutter installation guide for your operating system.
-
Dart SDK: Dart comes bundled with Flutter, so if you have Flutter installed, you’re good to go.
-
An IDE: Visual Studio Code or Android Studio with the Flutter and Dart plugins are highly recommended for a smooth development experience.
It’ll also be helpful to have basic familiarity with Flutter widgets, state management (especially StatefulWidget
), and asynchronous programming (async
/await
).
Project Example
In this project, we’re going to build a Flutter application for iOS and Android that incorporates local notifications. You’ll learn how to schedule, cancel, and reduce the notification count on iOS, as well as how to trigger actions when a notification is opened.
Set Up the Flutter Project
Let’s start by creating a Flutter project. Open your terminal or command prompt and run the following commands:
flutter create app_notifications
<span class="hljs-built_in">cd</span> app_notifications
-
flutter create app_notifications
: This command creates a new Flutter project namedapp_notifications
. -
cd app_notifications
: This command navigates you into the newly created project directory.
Configure Project Dependencies
Now, we need to add the necessary packages to our project. Open the pubspec.yaml
file located at the root of your project and add the following dependencies under the dependencies
section:
<span class="hljs-attr">dependencies:</span>
<span class="hljs-attr">flutter:</span>
<span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
<span class="hljs-attr">flutter_launcher_icons:</span> <span class="hljs-string">^0.13.1</span>
<span class="hljs-attr">awesome_notifications:</span> <span class="hljs-string">^0.9.2</span>
<span class="hljs-attr">cool_alert:</span> <span class="hljs-string">^2.0.1</span>
<span class="hljs-attr">awesome_notifications_core:</span> <span class="hljs-string">^0.9.1</span>
Explanation of dependencies:
-
flutter_launcher_icons
: This package allows you to easily update your Flutter app’s launcher icon for both Android and iOS. It’s a handy utility for branding your application. -
awesome_notifications
: This is the primary package we’ll be using for handling local notifications. It provides a comprehensive set of features for creating, scheduling, and managing notifications. -
cool_alert
: This package provides beautiful and customizable alert dialogs, which we’ll use for user feedback in our application. -
awesome_notifications_core
: This package contains the core functionalities ofawesome_notifications
and is often a dependency of the mainawesome_notifications
package itself. Including it explicitly ensures all necessary components are available.
Next, still in your pubspec.yaml
file, configure flutter_launcher_icons
by adding the following code below the dev_dependencies
section:
<span class="hljs-attr">flutter_icons:</span>
<span class="hljs-attr">android:</span> <span class="hljs-string">"launcher_icon"</span>
<span class="hljs-attr">ios:</span> <span class="hljs-literal">true</span>
<span class="hljs-attr">remove_alpha_ios:</span> <span class="hljs-literal">true</span>
<span class="hljs-attr">image_path:</span> <span class="hljs-string">"assets/imgs/icon.png"</span>
<span class="hljs-attr">adaptive_icon_background:</span> <span class="hljs-string">"#6C63FF"</span>
<span class="hljs-attr">adaptive_icon_foreground:</span> <span class="hljs-string">"assets/imgs/icon.png"</span>
What’s going on in the flutter_icons
configuration:
-
android: "launcher_icon"
: Specifies that the Android launcher icon should be generated. -
ios: true
: Enables icon generation for iOS. -
remove_alpha_ios: true
: Removes the alpha channel from iOS icons, which is often a requirement for App Store submission. -
image_path: "assets/imgs/icon.png"
: Points to the source image file for your app icon. We’ll create this path in the next step. -
adaptive_icon_background: "#6C63FF"
: Sets the background color for Android adaptive icons. This color (#6C63FF
) is a shade of purple, which we’ll also define as ourprimaryColor
. -
adaptive_icon_foreground: "assets/imgs/icon.png"
: Sets the foreground image for Android adaptive icons.
Add Project Assets
Applications often need static assets like images. Add the following to your pubspec.yaml
file to declare your asset folder:
<span class="hljs-attr">assets:</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">assets/imgs/</span>
This tells Flutter where to find your image assets. Now, create a folder named assets
at the root of your project, and inside it, create another folder named imgs
. Place your image files (icon.png
, cancel.png
, eco.png
, eco_large.png
, network.png
, res_notification_icon.png
, rocket.png
, stats.png
) into this imgs
folder.
After modifying pubspec.yaml
and adding your assets, run the following commands in your terminal to apply the changes and generate the launcher icons:
flutter pub get
flutter pub run flutter_launcher_icons
-
flutter pub get
: This command fetches all the newly added dependencies and updates your project. -
flutter pub run flutter_launcher_icons
: This command executes theflutter_launcher_icons
package to generate your app icons based on the configuration you provided.
Define App Constants
It’s good practice to centralize frequently used strings and keys. Inside your lib
directory, create a folder named constants
. Inside this folder, create a file named app_strings.dart
and add the following code:
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppStrings</span> </span>{
<span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-built_in">String</span> BASIC_CHANNEL_KEY = <span class="hljs-string">'basic_channel'</span>;
<span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-built_in">String</span> BASIC_CHANNEL_NAME = <span class="hljs-string">'Basic Notifications'</span>;
<span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-built_in">String</span> BASIC_CHANNEL_DESCRIPTION = <span class="hljs-string">'This channel is for basic notification'</span>;
<span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-built_in">String</span> SCHEDULE_CHANNEL_KEY = <span class="hljs-string">'schedule_channel'</span>;
<span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-built_in">String</span> SCHEDULE_CHANNEL_NAME = <span class="hljs-string">'Schedule Notifications'</span>;
<span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-built_in">String</span> SCHEDULE_CHANNEL_DESCRIPTION = <span class="hljs-string">'This channel is for schedule notification'</span>;
<span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-built_in">String</span> DEFAULT_ICON = <span class="hljs-string">'asset://assets/imgs/icon.png'</span>;
<span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-built_in">String</span> SCHEDULED_NOTIFICATION_BUTTON1_KEY = <span class="hljs-string">'button_one'</span>;
<span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-built_in">String</span> SCHEDULED_NOTIFICATION_BUTTON2_KEY = <span class="hljs-string">'button_two'</span>;
}
What’s going on in AppStrings
:
This file contains string constants that will be used throughout the application. These constants provide a single source of truth for values like:
-
Notification Channel Keys, Names, and Descriptions: These are essential for categorizing and managing notifications on Android. Each notification must belong to a channel.
-
DEFAULT_ICON
: A reference to our default notification icon. -
SCHEDULED_NOTIFICATION_BUTTON1_KEY
andSCHEDULED_NOTIFICATION_BUTTON2_KEY
: These keys will be used to identify actions triggered by buttons within scheduled notifications.
Define App Colors
For consistent theming, define your application’s color palette in one place. Inside the constants
folder, create a file named colors.dart
and add the following code:
<span class="hljs-keyword">import</span> <span class="hljs-string">'dart:ui'</span>;
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppColor</span> </span>{
<span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> primaryColor = Color(<span class="hljs-number">0XFF6C63FF</span>);
<span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> secondaryColor = Color(<span class="hljs-number">0XFFF96685</span>);
}
This file defines color constants that you can use for consistent theming in your application. primaryColor
and secondaryColor
will be used across various UI elements to maintain a cohesive design.
Implement Notification Utilities
This is where the core logic for handling notifications resides. Create a folder inside lib
called utilities
. Inside this folder, create a file named notification_util.dart
and add the following code:
<span class="hljs-keyword">import</span> <span class="hljs-string">'dart:io'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:app_notifications/utilities/create_uid.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:awesome_notifications/awesome_notifications.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../constants/app_strings.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../constants/colors.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../main.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../pages/stats_page.dart'</span>;
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NotificationUtil</span> </span>{
<span class="hljs-keyword">final</span> AwesomeNotifications awesomeNotifications;
NotificationUtil({<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.awesomeNotifications});
<span class="hljs-comment">/// <span class="markdown">Creates a basic notification that appears immediately.</span></span>
Future<<span class="hljs-keyword">void</span>> createBasicNotification({
<span class="hljs-keyword">required</span> <span class="hljs-built_in">int</span> id,
<span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> channelKey,
<span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> title,
<span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> body,
<span class="hljs-built_in">String</span> bigPicture = AppStrings.DEFAULT_ICON,
NotificationLayout layout = NotificationLayout.BigPicture,
}) <span class="hljs-keyword">async</span> {
awesomeNotifications.createNotification(
content: NotificationContent(
id: id,
channelKey: channelKey,
title: title,
body: body,
bigPicture: bigPicture,
notificationLayout: layout,
),
);
}
<span class="hljs-comment">/// <span class="markdown">Creates a scheduled notification that will appear at a specific time and can repeat.</span></span>
Future<<span class="hljs-keyword">void</span>> createScheduledNotification({
<span class="hljs-keyword">required</span> <span class="hljs-built_in">int</span> id,
<span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> channelKey,
<span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> title,
<span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> body,
<span class="hljs-built_in">String</span> bigPicture = AppStrings.DEFAULT_ICON,
NotificationLayout layout = NotificationLayout.BigPicture,
<span class="hljs-keyword">required</span> NotificationCalendar notificationCalendar,
}) <span class="hljs-keyword">async</span> {
awesomeNotifications.createNotification(
content: NotificationContent(
id: id,
channelKey: channelKey,
title: title,
body: body,
bigPicture: bigPicture,
notificationLayout: layout,
),
actionButtons: [
NotificationActionButton(
key: AppStrings.SCHEDULED_NOTIFICATION_BUTTON1_KEY,
label: <span class="hljs-string">'Mark Done'</span>,
),
NotificationActionButton(
key: AppStrings.SCHEDULED_NOTIFICATION_BUTTON2_KEY,
label: <span class="hljs-string">'Clear'</span>,
),
],
schedule: NotificationCalendar(
weekday: notificationCalendar.weekday,
hour: notificationCalendar.hour,
minute: notificationCalendar.minute,
repeats: <span class="hljs-keyword">true</span>, <span class="hljs-comment">// This notification will repeat every week on the specified day and time.</span>
),
);
}
<span class="hljs-comment">/// <span class="markdown">Cancels all currently scheduled notifications.</span></span>
<span class="hljs-keyword">void</span> cancelAllScheduledNotifications({<span class="hljs-keyword">required</span> BuildContext context}){
awesomeNotifications.cancelAllSchedules().then((value) => {
ScaffoldMessenger.of(context).showSnackBar(
<span class="hljs-keyword">const</span> SnackBar(
content: Text(<span class="hljs-string">'Cancelled all scheduled notifications'</span>),
backgroundColor: AppColor.primaryColor,
),
)
});
}
<span class="hljs-comment">/// <span class="markdown">Requests permission from the user to send notifications. This is crucial for Android 13+ and iOS.</span></span>
<span class="hljs-keyword">void</span> requestPermissionToSendNotifications({<span class="hljs-keyword">required</span> BuildContext context}) {
AwesomeNotifications().requestPermissionToSendNotifications().then((value) {
<span class="hljs-comment">// After requesting permission, pop the dialog that prompted the user.</span>
Navigator.of(context).pop();
});
}
<span class="hljs-comment">/// <span class="markdown">Static methods for handling notification lifecycle events.</span></span>
<span class="hljs-comment">/// <span class="markdown">These methods are marked with <span class="hljs-code">`@pragma("vm:entry-point")`</span> to ensure they are accessible</span></span>
<span class="hljs-comment">/// <span class="markdown">even when the application is running in the background or killed.</span></span>
<span class="hljs-comment">/// <span class="markdown">Use this method to detect when a new notification or a schedule is created.</span></span>
<span class="hljs-meta">@pragma</span>(<span class="hljs-string">"vm:entry-point"</span>)
<span class="hljs-keyword">static</span> Future<<span class="hljs-keyword">void</span>> onNotificationCreatedMethod(
ReceivedNotification receivedNotification, BuildContext context) <span class="hljs-keyword">async</span> {
<span class="hljs-comment">// Show a SnackBar to indicate that a notification has been created.</span>
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
<span class="hljs-string">'Notification created <span class="hljs-subst">${receivedNotification.channelKey}</span>'</span>,
),
backgroundColor: AppColor.primaryColor,
),
);
}
<span class="hljs-comment">/// <span class="markdown">Use this method to detect every time that a new notification is displayed.</span></span>
<span class="hljs-meta">@pragma</span>(<span class="hljs-string">"vm:entry-point"</span>)
<span class="hljs-keyword">static</span> Future<<span class="hljs-keyword">void</span>> onNotificationDisplayedMethod(
ReceivedNotification receivedNotification) <span class="hljs-keyword">async</span> {
<span class="hljs-comment">// Your code to handle a notification being displayed can go here.</span>
<span class="hljs-comment">// For example, you might log the event or update a UI element.</span>
}
<span class="hljs-comment">/// <span class="markdown">Use this method to detect if the user dismissed a notification.</span></span>
<span class="hljs-meta">@pragma</span>(<span class="hljs-string">"vm:entry-point"</span>)
<span class="hljs-keyword">static</span> Future<<span class="hljs-keyword">void</span>> onDismissActionReceivedMethod(
ReceivedAction receivedAction) <span class="hljs-keyword">async</span> {
<span class="hljs-comment">// Your code to handle a notification being dismissed can go here.</span>
<span class="hljs-comment">// This is useful for tracking user interaction or cleaning up resources.</span>
}
<span class="hljs-comment">/// <span class="markdown">Use this method to detect when the user taps on a notification or an action button within it.</span></span>
<span class="hljs-meta">@pragma</span>(<span class="hljs-string">"vm:entry-point"</span>)
<span class="hljs-keyword">static</span> Future<<span class="hljs-keyword">void</span>> onActionReceivedMethod(
ReceivedAction receivedAction) <span class="hljs-keyword">async</span> {
<span class="hljs-comment">// Reducing icon badge count on iOS when a basic notification is tapped/acted upon.</span>
<span class="hljs-comment">// This is important for maintaining accurate badge counts.</span>
<span class="hljs-keyword">if</span> (receivedAction.channelKey == AppStrings.BASIC_CHANNEL_KEY &&
Platform.isIOS) {
AwesomeNotifications().getGlobalBadgeCounter().then((value) {
AwesomeNotifications().setGlobalBadgeCounter(value - <span class="hljs-number">1</span>);
});
}
<span class="hljs-comment">// Navigating to the StatsPage when any notification action is received.</span>
<span class="hljs-comment">// The `navigatorKey` from `MyApp` is used to navigate from anywhere in the app.</span>
MyApp.navigatorKey.currentState?.pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => <span class="hljs-keyword">const</span> StatsPage(),
),
(route) => route.isFirst);
}
}
What’s going on in NotificationUtil
:
This class is the heart of our notification logic. It encapsulates methods for:
-
createBasicNotification
: This function creates a simple, immediate notification. It takes anid
,channelKey
,title
, andbody
. ThebigPicture
andlayout
parameters allow for rich notification content. -
createScheduledNotification
: This powerful function allows you to schedule notifications to appear at a specific date and time. It includesactionButtons
(like “Mark Done” or “Clear”) that users can interact with directly from the notification, and aNotificationCalendar
for precise scheduling withrepeats: true
to make it a weekly recurring notification. -
cancelAllScheduledNotifications
: A utility to cancel all notifications that have been scheduled. It also displays aSnackBar
for user feedback. -
requestPermissionToSendNotifications
: This method handles the crucial step of asking the user for permission to send notifications. This is a system-level prompt on both Android (especially Android 13+) and iOS. -
Listener Methods (
onNotificationCreatedMethod
,onNotificationDisplayedMethod
,onDismissActionReceivedMethod
,onActionReceivedMethod
): Thesestatic
methods are callbacks thatawesome_notifications
invokes at different stages of a notification’s lifecycle. They are marked with@pragma("vm:entry-point")
to ensure they can execute even when the app is in the background or completely closed.-
onNotificationCreatedMethod
: Triggered when a new notification is created. -
onNotificationDisplayedMethod
: Triggered when a notification is actually displayed to the user. -
onDismissActionReceivedMethod
: Triggered when a user dismisses a notification. -
onActionReceivedMethod
: This is a very important one. It’s triggered when the user taps on the notification itself or any of its action buttons. In our implementation, it handles:-
iOS Badge Count Reduction: For basic notifications on iOS, it decrements the app icon’s badge counter, providing a more accurate unread count.
-
Navigation: It navigates the user to the
StatsPage
regardless of which notification action was received. This demonstrates how you can direct users to specific parts of your app based on their notification interaction.
-
-
Generate Unique IDs
Every notification needs a unique identifier. Inside the utilities
folder, create a file named create_uid.dart
and add the following code:
<span class="hljs-built_in">int</span> createUniqueId() {
<span class="hljs-keyword">return</span> <span class="hljs-built_in">DateTime</span>.now().millisecondsSinceEpoch.remainder(<span class="hljs-number">100000</span>);
}
createUniqueId
is a simple function that generates a unique integer ID by taking the current timestamp in milliseconds and getting its remainder when divided by 100000. This ensures a reasonably unique ID for each notification without creating excessively large numbers.
Create Reusable UI Components
To maintain a clean and modular codebase, we’ll create several reusable UI components. Create a folder named components
inside lib
. Inside this folder, create the following files: custom_alert_dialog.dart
, custom_rich_text.dart
, custom_elevated_button.dart
, stats_container.dart
, and k_cool_alert.dart
.
custom_alert_dialog.dart
:
<span class="hljs-keyword">import</span> <span class="hljs-string">'dart:io'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/cupertino.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../constants/colors.dart'</span>;
Future<<span class="hljs-keyword">void</span>> customAlertDialog({
<span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> title,
<span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> content,
<span class="hljs-keyword">required</span> BuildContext context,
<span class="hljs-keyword">required</span> <span class="hljs-built_in">Function</span> action,
<span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> button1Title,
<span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> button2Title,
}) {
<span class="hljs-keyword">return</span> showDialog(
context: context,
builder: (context) =>
<span class="hljs-comment">// FOR iOS</span>
Platform.isIOS
? CupertinoAlertDialog(
title: Text(
title,
style: <span class="hljs-keyword">const</span> TextStyle(
fontWeight: FontWeight.w700,
color: Colors.black,
fontSize: <span class="hljs-number">16</span>,
),
),
content: Text(content),
actions: [
Padding(
padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">8.0</span>),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
padding: <span class="hljs-keyword">const</span> EdgeInsets.symmetric(horizontal: <span class="hljs-number">5</span>),
backgroundColor: AppColor.secondaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(<span class="hljs-number">10</span>),
),
),
onPressed: () => action(),
child: Text(
button1Title,
style: <span class="hljs-keyword">const</span> TextStyle(
color: Colors.white,
fontWeight: FontWeight.normal,
),
),
),
),
Padding(
padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">8.0</span>),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
padding: <span class="hljs-keyword">const</span> EdgeInsets.symmetric(horizontal: <span class="hljs-number">5</span>),
backgroundColor: AppColor.secondaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(<span class="hljs-number">10</span>),
),
),
onPressed: () => Navigator.of(context).pop(),
child: Text(
button2Title,
style: <span class="hljs-keyword">const</span> TextStyle(
color: Colors.white,
fontWeight: FontWeight.normal,
),
),
),
),
],
)
<span class="hljs-comment">// FOR Android</span>
: AlertDialog(
title: Text(
title,
style: <span class="hljs-keyword">const</span> TextStyle(
fontWeight: FontWeight.w700,
color: Colors.black,
fontSize: <span class="hljs-number">16</span>,
),
),
content: Text(content),
actions: [
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.secondaryColor,
padding: <span class="hljs-keyword">const</span> EdgeInsets.symmetric(horizontal: <span class="hljs-number">5</span>),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(<span class="hljs-number">10</span>),
),
),
onPressed: () => action(),
child: Text(
button1Title,
style: <span class="hljs-keyword">const</span> TextStyle(
color: Colors.white,
fontWeight: FontWeight.normal,
),
),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.secondaryColor,
padding: <span class="hljs-keyword">const</span> EdgeInsets.symmetric(horizontal: <span class="hljs-number">5</span>),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(<span class="hljs-number">10</span>),
),
),
onPressed: () => Navigator.of(context).pop(),
child: Text(
button2Title,
style: <span class="hljs-keyword">const</span> TextStyle(
color: Colors.white,
fontWeight: FontWeight.normal,
),
),
),
],
),
);
}
The customAlertDialog
function provides a customizable alert dialog that adapts its appearance based on the platform. It uses CupertinoAlertDialog
for iOS to provide a native look and feel, and AlertDialog
for Android.
This ensures a consistent user experience across different devices. It takes a title
, content
, context
, an action
function for the primary button, and titles for both buttons.
custom_rich_text.dart
:
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CustomRichText</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
<span class="hljs-keyword">const</span> CustomRichText({
Key? key,
<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.title,
<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.content,
}) : <span class="hljs-keyword">super</span>(key: key);
<span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> title;
<span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> content;
<span class="hljs-meta">@override</span>
Widget build(BuildContext context) {
<span class="hljs-keyword">return</span> RichText(
text: TextSpan(
text: title,
style: TextStyle(
color: Colors.grey.shade800,
fontWeight: FontWeight.w800,
),
children: [
TextSpan(
text: content,
style: <span class="hljs-keyword">const</span> TextStyle(
color: Colors.grey,
),
),
],
),
);
}
}
The CustomRichText
widget is a simple RichText
component designed to display a title
and content
with different text styles. The title
is bold and dark grey, while the content
is a lighter grey, making it ideal for displaying labels and their corresponding values.
custom_elevated_button.dart
:
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../constants/colors.dart'</span>;
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CustomElevatedButton</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
<span class="hljs-keyword">const</span> CustomElevatedButton({
Key? key,
<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.function,
<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.title,
<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.icon,
}) : <span class="hljs-keyword">super</span>(key: key);
<span class="hljs-keyword">final</span> <span class="hljs-built_in">Function</span> function;
<span class="hljs-keyword">final</span> IconData icon;
<span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> title;
<span class="hljs-meta">@override</span>
Widget build(BuildContext context) {
<span class="hljs-keyword">return</span> ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.secondaryColor,
),
onPressed: () => function(),
icon: Icon(
icon,
color: Colors.white,
),
label: Text(
title,
style: <span class="hljs-keyword">const</span> TextStyle(
color: Colors.white,
),
),
);
}
}
The CustomElevatedButton
widget is a reusable ElevatedButton
with an icon and a text label. It takes a function
to execute when pressed, an icon
, and a title
. It uses our secondaryColor
for its background, ensuring a consistent look and feel for primary actions.
k_cool_alert.dart
:
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:cool_alert/cool_alert.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../constants/colors.dart'</span>;
Future kCoolAlert({
<span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> message,
<span class="hljs-keyword">required</span> BuildContext context,
<span class="hljs-keyword">required</span> CoolAlertType alert,
<span class="hljs-built_in">bool</span> barrierDismissible = <span class="hljs-keyword">true</span>,
<span class="hljs-built_in">String</span> confirmBtnText = <span class="hljs-string">'Ok'</span>,
}) {
<span class="hljs-keyword">return</span> CoolAlert.<span class="hljs-keyword">show</span>(
backgroundColor: AppColor.primaryColor,
confirmBtnColor: AppColor.secondaryColor,
context: context,
type: alert,
text: message,
barrierDismissible: barrierDismissible,
confirmBtnText: confirmBtnText,
);
}
The kCoolAlert
function leverages the cool_alert
package to display aesthetically pleasing alert dialogs. It allows you to specify the message
, context
, alert type
(for example, success, error, warning), whether it’s barrierDismissible
, and the confirmBtnText
. It uses our primaryColor
and secondaryColor
for styling.
stats_container.dart
:
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../constants/colors.dart'</span>;
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StatsContainer</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
StatsContainer({
Key? key,
<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.icon,
<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.stat,
<span class="hljs-keyword">this</span>.iconColor = Colors.orange,
}) : <span class="hljs-keyword">super</span>(key: key);
Color iconColor;
<span class="hljs-keyword">final</span> IconData icon;
<span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> stat;
<span class="hljs-meta">@override</span>
Widget build(BuildContext context) {
<span class="hljs-keyword">return</span> Container(
height: <span class="hljs-number">40</span>,
width: <span class="hljs-number">110</span>,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(<span class="hljs-number">20</span>),
color: AppColor.secondaryColor,
),
child: Center(
child: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
spacing: <span class="hljs-number">6</span>,
children: [
Icon(
icon,
color: iconColor,
),
Text(
stat,
style: <span class="hljs-keyword">const</span> TextStyle(
color: Colors.white,
fontSize: <span class="hljs-number">18</span>,
fontWeight: FontWeight.w700,
),
)
],
),
),
);
}
}
The StatsContainer
widget is a simple container designed to display an icon and a numeric statistic. It features a rounded background using AppColor.secondaryColor
and provides a visually appealing way to present key metrics, as we’ll see on the StatsPage
.
Build Application Pages
Now, let’s create the main screens of our application. Create a new folder inside lib
called pages
. Inside this folder, create two files: home_page.dart
and stats_page.dart
.
home_page.dart
:
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:app_notifications/pages/stats_page.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:awesome_notifications/awesome_notifications.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/cupertino.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../components/custom_elevated_button.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../components/custom_alert_dialog.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../components/custom_rich_text.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../constants/app_strings.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../constants/colors.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../utilities/create_uid.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../utilities/notification_util.dart'</span>;
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HomePage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
<span class="hljs-keyword">const</span> HomePage({Key? key}) : <span class="hljs-keyword">super</span>(key: key);
<span class="hljs-meta">@override</span>
State<HomePage> createState() => _HomePageState();
}
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_HomePageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span><<span class="hljs-title">HomePage</span>> </span>{
<span class="hljs-built_in">String</span> selectedNotificationDay = <span class="hljs-string">''</span>;
<span class="hljs-built_in">int</span> selectedDayOfTheWeek = <span class="hljs-number">0</span>;
TimeOfDay selectedTime = TimeOfDay.now();
<span class="hljs-built_in">bool</span> isTimeSelected = <span class="hljs-keyword">false</span>;
<span class="hljs-keyword">late</span> NotificationUtil notificationUtil;
<span class="hljs-comment">// list of notification days</span>
<span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span><<span class="hljs-built_in">String</span>> notificationDays = [
<span class="hljs-string">'Mon'</span>,
<span class="hljs-string">'Tue'</span>,
<span class="hljs-string">'Wed'</span>,
<span class="hljs-string">'Thur'</span>,
<span class="hljs-string">'Fri'</span>,
<span class="hljs-string">'Sat'</span>,
<span class="hljs-string">'Sun'</span>,
];
<span class="hljs-comment">// Function to create a basic notification</span>
<span class="hljs-keyword">void</span> createBasicNotification() {
notificationUtil.createBasicNotification(
id: createUniqueId(), <span class="hljs-comment">// Get a unique ID for this notification</span>
channelKey: AppStrings.BASIC_CHANNEL_KEY,
title: <span class="hljs-string">'<span class="hljs-subst">${Emojis.clothing_backpack + Emojis.transport_air_airplane}</span> Network Call'</span>,
body:
<span class="hljs-string">'Lorem ipsum dolor sit amet consectetur adipisicing elit. Maxime mollitia,molestiae quas vel sint commodi repudiandae consequuntur'</span>,
bigPicture: <span class="hljs-string">'asset://assets/imgs/eco_large.png'</span>, <span class="hljs-comment">// Display a large image</span>
);
}
<span class="hljs-comment">// Function to trigger cancellation of all scheduled notifications</span>
<span class="hljs-keyword">void</span> triggerCancelNotification() {
notificationUtil.cancelAllScheduledNotifications(context: context);
}
<span class="hljs-comment">// Function to initiate the scheduling process by showing a day selection dialog</span>
<span class="hljs-keyword">void</span> triggerScheduleNotification() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Show Notification Every'</span>),
content: Wrap(
spacing: <span class="hljs-number">3.0</span>,
runSpacing: <span class="hljs-number">8.0</span>,
children: notificationDays
.asMap()
.entries
.map(
(day) => ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.secondaryColor),
onPressed: () {
<span class="hljs-built_in">int</span> index = day.key;
setState(() {
selectedNotificationDay = day.value;
selectedDayOfTheWeek = index + <span class="hljs-number">1</span>; <span class="hljs-comment">// Weekday is 1-indexed (Sunday is 1, Monday is 2, etc.)</span>
});
Navigator.of(context).pop(); <span class="hljs-comment">// Close day selection dialog</span>
pickTime(); <span class="hljs-comment">// Then, prompt for time selection</span>
},
child: Text(
day.value,
style: <span class="hljs-keyword">const</span> TextStyle(
color: Colors.white,
),
),
),
)
.toList(),
),
),
);
}
<span class="hljs-comment">// Function to create the actual scheduled notification after day and time are selected</span>
<span class="hljs-keyword">void</span> createScheduleNotification() {
notificationUtil.createScheduledNotification(
id: createUniqueId(),
channelKey: AppStrings.SCHEDULE_CHANNEL_KEY,
title: <span class="hljs-string">'<span class="hljs-subst">${Emojis.time_alarm_clock}</span> Check your rocket!'</span>,
body:
<span class="hljs-string">'Lorem ipsum dolor sit amet consectetur adipisicing elit. Maxime mollitia,molestiae quas vel sint commodi repudiandae consequuntur'</span>,
layout: NotificationLayout.Default,
notificationCalendar: NotificationCalendar(
hour: selectedTime.hour,
minute: selectedTime.minute,
weekday: selectedDayOfTheWeek, <span class="hljs-comment">// Use the selected day of the week</span>
),
);
}
<span class="hljs-comment">// Function to show a time picker dialog</span>
Future<TimeOfDay?> pickTime() <span class="hljs-keyword">async</span> {
TimeOfDay? pickedTime = <span class="hljs-keyword">await</span> showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
);
<span class="hljs-keyword">if</span> (pickedTime != <span class="hljs-keyword">null</span>) {
setState(() {
selectedTime = pickedTime;
isTimeSelected = <span class="hljs-keyword">true</span>;
});
createScheduleNotification(); <span class="hljs-comment">// Once time is picked, create the notification</span>
}
<span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
}
<span class="hljs-comment">// Function to request notification permissions</span>
<span class="hljs-keyword">void</span> requestPermission() {
notificationUtil.requestPermissionToSendNotifications(context: context);
}
<span class="hljs-meta">@override</span>
<span class="hljs-keyword">void</span> initState() {
<span class="hljs-keyword">super</span>.initState();
<span class="hljs-comment">// Check notification permission and prompt if not allowed</span>
AwesomeNotifications().isNotificationAllowed().then((isAllowed) {
<span class="hljs-keyword">if</span> (!isAllowed) {
customAlertDialog(
title: <span class="hljs-string">'Allow notifications'</span>,
content: <span class="hljs-string">'Rocket App needs access to notifications to send you timely updates and reminders.'</span>,
context: context,
action: requestPermission,
button1Title: <span class="hljs-string">'Allow'</span>,
button2Title: <span class="hljs-string">'Don't Allow'</span>,
);
}
});
<span class="hljs-comment">// Initialize NotificationUtil with an instance of AwesomeNotifications</span>
notificationUtil = NotificationUtil(
awesomeNotifications: AwesomeNotifications(),
);
<span class="hljs-comment">// Set up listeners for various notification events</span>
AwesomeNotifications().setListeners(
onNotificationCreatedMethod: (notification) <span class="hljs-keyword">async</span> =>
NotificationUtil.onNotificationCreatedMethod(notification, context),
onActionReceivedMethod: NotificationUtil.onActionReceivedMethod,
onDismissActionReceivedMethod: (ReceivedAction receivedAction) =>
NotificationUtil.onDismissActionReceivedMethod(receivedAction),
onNotificationDisplayedMethod: (ReceivedNotification receivedNotification) =>
NotificationUtil.onNotificationDisplayedMethod(receivedNotification),
);
}
<span class="hljs-meta">@override</span>
<span class="hljs-keyword">void</span> dispose() {
<span class="hljs-comment">// Dispose of AwesomeNotifications resources when the widget is removed</span>
AwesomeNotifications().dispose();
<span class="hljs-keyword">super</span>.dispose();
}
<span class="hljs-meta">@override</span>
Widget build(BuildContext context) {
<span class="hljs-keyword">return</span> Scaffold(
appBar: AppBar(
backgroundColor: AppColor.primaryColor,
title: <span class="hljs-keyword">const</span> Wrap(
spacing: <span class="hljs-number">8</span>,
children: [
Icon(
CupertinoIcons.rocket,
color: Colors.white,
),
Text(
<span class="hljs-string">'Rockets'</span>,
style: TextStyle(
color: Colors.white,
),
),
],
),
actions: [
IconButton(
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => <span class="hljs-keyword">const</span> StatsPage(),
),
),
icon: <span class="hljs-keyword">const</span> Icon(
CupertinoIcons.chart_bar_square,
color: Colors.white,
),
)
],
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
<span class="hljs-comment">// Display selected day and time if a schedule is picked</span>
<span class="hljs-keyword">if</span> (isTimeSelected) ...[
CustomRichText(
title: <span class="hljs-string">'Selected Day: '</span>,
content: selectedNotificationDay,
),
<span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">10</span>),
CustomRichText(
title: <span class="hljs-string">'Selected Time: '</span>,
content: selectedTime.format(context),
),
<span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">10</span>),
],
Image.asset(<span class="hljs-string">'assets/imgs/rocket.png'</span>),
<span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">20</span>),
<span class="hljs-comment">// Buttons for various notification actions</span>
CustomElevatedButton(
function: createBasicNotification,
title: <span class="hljs-string">'Show Basic Notification'</span>,
icon: Icons.notifications,
),
<span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">20</span>),
CustomElevatedButton(
function: triggerScheduleNotification,
title: <span class="hljs-string">'Schedule Notification'</span>,
icon: Icons.schedule,
),
<span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">20</span>),
CustomElevatedButton(
function: triggerCancelNotification,
title: <span class="hljs-string">'Cancel All Scheduled Notifications'</span>,
icon: Icons.cancel,
),
],
),
);
}
}
The HomePage
is the main interactive screen of our application.
-
State Variables: manages the
selectedNotificationDay
,selectedDayOfTheWeek
,selectedTime
, andisTimeSelected
to handle the scheduling process. -
notificationDays
List: A simple list of strings representing days of the week, used for the schedule selection dialog. -
createBasicNotification()
: This function is triggered by a button press and callsnotificationUtil.createBasicNotification
to display an immediate notification with an image. -
triggerCancelNotification()
: CallsnotificationUtil.cancelAllScheduledNotifications
to clear any pending scheduled notifications. -
triggerScheduleNotification()
: This function first presents anAlertDialog
where the user can select a day of the week for the scheduled notification. Once a day is selected, it callspickTime()
. -
createScheduleNotification()
: After the user selects both a day and time, this function is called to create the recurring scheduled notification usingnotificationUtil.createScheduledNotification
. -
pickTime()
: Uses Flutter’sshowTimePicker
to allow the user to select a specific time for the scheduled notification. -
requestPermission()
: A simple wrapper to callnotificationUtil.requestPermissionToSendNotifications
. -
initState()
: This crucial method is called once when the widget is inserted into the widget tree.-
It first checks if notification permissions are granted using
AwesomeNotifications().isNotificationAllowed()
. If not, it displays acustomAlertDialog
prompting the user for permission. -
It initializes
notificationUtil
to interact with our notification helper class. -
Crucially, it sets up the
awesome_notifications
listeners (setListeners
). These listeners connect the global static methods inNotificationUtil
to the various notification events (creation, display, dismissal, and action received). This ensures our app can react to notification interactions even when it’s not actively in the foreground.
-
-
dispose()
: This method is called when the widget is removed from the widget tree. It callsAwesomeNotifications().dispose()
to release any resources held by the notification package, which is good practice. -
build()
Method: This describes the UI of the home page, including the app bar, arocket.png
image, and threeCustomElevatedButton
widgets that trigger the different notification functionalities. It also conditionally displays the selected day and time if a scheduled notification has been initiated.
stats_page.dart
:
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../components/stats_container.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../constants/colors.dart'</span>;
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StatsPage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
<span class="hljs-keyword">const</span> StatsPage({Key? key}) : <span class="hljs-keyword">super</span>(key: key);
<span class="hljs-meta">@override</span>
Widget build(BuildContext context) {
<span class="hljs-keyword">return</span> Scaffold(
appBar: AppBar(
backgroundColor: AppColor.primaryColor,
title: <span class="hljs-keyword">const</span> Wrap(
spacing: <span class="hljs-number">8</span>,
children: [
Icon(
Icons.analytics,
color: Colors.white,
),
Text(
<span class="hljs-string">'Stats'</span>,
style: TextStyle(
color: Colors.white,
),
),
],
),
),
body: Center(
child: Padding(
padding: <span class="hljs-keyword">const</span> EdgeInsets.symmetric(horizontal: <span class="hljs-number">20</span>),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
<span class="hljs-keyword">const</span> StatsContainer(
icon: Icons.notifications,
stat: <span class="hljs-string">'10'</span>, <span class="hljs-comment">// Dummy data for demonstration</span>
),
<span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">20</span>),
<span class="hljs-keyword">const</span> StatsContainer(
icon: Icons.schedule,
stat: <span class="hljs-string">'5'</span>, <span class="hljs-comment">// Dummy data for demonstration</span>
),
<span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">20</span>),
<span class="hljs-keyword">const</span> StatsContainer(
icon: Icons.cancel,
stat: <span class="hljs-string">'2'</span>, <span class="hljs-comment">// Dummy data for demonstration</span>
),
],
),
),
),
);
}
}
The StatsPage
is a simple screen designed to display some hypothetical statistics related to notifications.
-
It features an
AppBar
with a title and an analytics icon. -
The
body
consists of aCenter
widget containing aColumn
of threeStatsContainer
widgets. -
Each
StatsContainer
displays a dummy number for “notifications,” “scheduled notifications,” and “canceled notifications.” This page serves as a placeholder to demonstrate navigation after a notification action, and in a real application, these numbers would be dynamic.
Initialize and Run the Application
Finally, let’s set up the main entry point of our Flutter application. Open main.dart
and replace its content with the following code:
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:awesome_notifications/awesome_notifications.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'pages/home_page.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'constants/app_strings.dart'</span>; <span class="hljs-comment">// Import AppStrings for channel keys</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'constants/colors.dart'</span>; <span class="hljs-comment">// Import AppColor for default channel color</span>
<span class="hljs-keyword">void</span> main() <span class="hljs-keyword">async</span> {
WidgetsFlutterBinding.ensureInitialized(); <span class="hljs-comment">// Ensure Flutter binding is initialized</span>
<span class="hljs-comment">// Initialize Awesome Notifications with notification channels</span>
AwesomeNotifications().initialize(
<span class="hljs-string">'resource://drawable/app_icon'</span>, <span class="hljs-comment">// The icon to display for notifications.</span>
<span class="hljs-comment">// For Android, this usually points to a drawable resource.</span>
<span class="hljs-comment">// 'resource://drawable/res_notification_icon' is another common path.</span>
<span class="hljs-comment">// If your icon isn't showing, try experimenting with this path.</span>
[
<span class="hljs-comment">// Notification channel for basic notifications</span>
NotificationChannel(
key: AppStrings.BASIC_CHANNEL_KEY,
name: AppStrings.BASIC_CHANNEL_NAME,
channelDescription: AppStrings.BASIC_CHANNEL_DESCRIPTION,
defaultColor: AppColor.primaryColor, <span class="hljs-comment">// Default color for notifications in this channel</span>
importance: NotificationImportance.High, <span class="hljs-comment">// High importance notifications make sound and appear on screen</span>
defaultRingtoneType: DefaultRingtoneType.Notification, <span class="hljs-comment">// Use the default notification sound</span>
),
<span class="hljs-comment">// Notification channel for scheduled notifications</span>
NotificationChannel(
key: AppStrings.SCHEDULE_CHANNEL_KEY,
name: AppStrings.SCHEDULE_CHANNEL_NAME,
channelDescription: AppStrings.SCHEDULE_CHANNEL_DESCRIPTION,
defaultColor: AppColor.primaryColor,
importance: NotificationImportance.High,
defaultRingtoneType: DefaultRingtoneType.Notification,
),
],
<span class="hljs-comment">// Optional: set this to true if you want to debug Awesome Notifications</span>
debug: <span class="hljs-keyword">false</span>,
);
runApp(MyApp());
}
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
<span class="hljs-comment">// GlobalKey is used to access the NavigatorState from anywhere in the application</span>
<span class="hljs-comment">// This is crucial for navigating from background notification actions.</span>
<span class="hljs-keyword">static</span> GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
<span class="hljs-meta">@override</span>
Widget build(BuildContext context) {
<span class="hljs-keyword">return</span> MaterialApp(
navigatorKey: navigatorKey, <span class="hljs-comment">// Assign the global key to the MaterialApp</span>
title: <span class="hljs-string">'Rockets'</span>,
theme: ThemeData(
primarySwatch: MaterialColor(
AppColor.primaryColor.value, <span class="hljs-comment">// Convert Color to MaterialColor for primary swatch</span>
<<span class="hljs-built_in">int</span>, Color>{
<span class="hljs-number">50</span>: AppColor.primaryColor.withOpacity(<span class="hljs-number">0.1</span>),
<span class="hljs-number">100</span>: AppColor.primaryColor.withOpacity(<span class="hljs-number">0.2</span>),
<span class="hljs-number">200</span>: AppColor.primaryColor.withOpacity(<span class="hljs-number">0.3</span>),
<span class="hljs-number">300</span>: AppColor.primaryColor.withOpacity(<span class="hljs-number">0.4</span>),
<span class="hljs-number">400</span>: AppColor.primaryColor.withOpacity(<span class="hljs-number">0.5</span>),
<span class="hljs-number">500</span>: AppColor.primaryColor.withOpacity(<span class="hljs-number">0.6</span>),
<span class="hljs-number">600</span>: AppColor.primaryColor.withOpacity(<span class="hljs-number">0.7</span>),
<span class="hljs-number">700</span>: AppColor.primaryColor.withOpacity(<span class="hljs-number">0.8</span>),
<span class="hljs-number">800</span>: AppColor.primaryColor.withOpacity(<span class="hljs-number">0.9</span>),
<span class="hljs-number">900</span>: AppColor.primaryColor.withOpacity(<span class="hljs-number">1.0</span>),
},
),
),
home: <span class="hljs-keyword">const</span> HomePage(),
);
}
}
The main.dart
file is the entry point of your Flutter application.
-
main()
Function:-
WidgetsFlutterBinding.ensureInitialized();
: This line is vital to ensure that the Flutter widget binding is initialized beforeAwesomeNotifications().initialize()
is called. This prevents potential errors, especially when dealing with platform channels. -
AwesomeNotifications().initialize(...)
: This is whereawesome_notifications
is set up.-
The first argument (
'resource://drawable/app_icon'
) specifies the default icon for notifications. This path points to a drawable resource on Android. -
The second argument is a list of
NotificationChannel
objects. Notification channels are mandatory for Android 8.0 (API level 26) and above. They allow users to control notification settings (sound, vibration, importance) on a per-channel basis. We define two channels: one for basic notifications and another for scheduled notifications, each with itskey
,name
,channelDescription
,defaultColor
,importance
, anddefaultRingtoneType
. -
debug: false
: Set totrue
during development to see more detailed logs fromawesome_notifications
.
-
-
-
MyApp
Class:-
static GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
: This is a crucial line. AGlobalKey
assigned toMaterialApp
‘snavigatorKey
allows us to access theNavigatorState
from anywhere in the application, even from static methods likeNotificationUtil.onActionReceivedMethod
. This enables us to perform navigation (for example, toStatsPage
) when a notification is tapped, regardless of the current screen. -
The
MaterialApp
widget sets up the basic structure of our app, including the title, theme (using ourAppColor.primaryColor
), and setsHomePage
as the initial screen. ThenavigatorKey
is assigned here so it can be accessed globally.
-
Save all files and run the application from your terminal:
flutter run
This command will launch the application on your connected device or emulator, and you can start triggering basic and scheduled notifications!
Some screenshots:
To explore more examples and get detailed information about the awesome_notifications
package, you can refer to the official documentation and GitHub repository:
-
Official Documentation: awesome_notifications on pub.dev: This is the official package page on pub.dev. You can find documentation, examples, and version history here.
-
GitHub Repository: awesome_notifications on GitHub: Visit the GitHub repository to access the source code, issues, discussions, and more. It’s a great resource to explore the inner workings of the package.
Reading through the documentation and checking out the official repository can provide additional insights, usage scenarios, and updates related to the awesome_notifications
package. It’s always beneficial to refer to the official sources for the latest and most comprehensive information.
Conclusion
Implementing local notifications in a Flutter application is essential for providing users with timely information and reminders. The awesome_notifications
package simplifies the process of creating, scheduling, and handling notifications significantly.
By following the detailed steps outlined in this article, and understanding the purpose of each code segment, you can effectively enhance user engagement and provide a better overall experience for your Flutter application users.
Source: freeCodeCamp Programming Tutorials: Python, JavaScript, Git & MoreÂ