Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      Error’d: Pickup Sticklers

      September 27, 2025

      From Prompt To Partner: Designing Your Custom AI Assistant

      September 27, 2025

      Microsoft unveils reimagined Marketplace for cloud solutions, AI apps, and more

      September 27, 2025

      Design Dialects: Breaking the Rules, Not the System

      September 27, 2025

      Building personal apps with open source and AI

      September 12, 2025

      What Can We Actually Do With corner-shape?

      September 12, 2025

      Craft, Clarity, and Care: The Story and Work of Mengchu Yao

      September 12, 2025

      Cailabs secures €57M to accelerate growth and industrial scale-up

      September 12, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      Using phpinfo() to Debug Common and Not-so-Common PHP Errors and Warnings

      September 28, 2025
      Recent

      Using phpinfo() to Debug Common and Not-so-Common PHP Errors and Warnings

      September 28, 2025

      Mastering PHP File Uploads: A Guide to php.ini Settings and Code Examples

      September 28, 2025

      The first browser with JavaScript landed 30 years ago

      September 27, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured
      Recent
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»Development»How to Use Local Notifications in Flutter – A Tutorial for Beginners

    How to Use Local Notifications in Flutter – A Tutorial for Beginners

    June 27, 2025

    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?

    • Prerequisites

    • Project Example

      • Set Up the Flutter Project

      • Configure Project Dependencies

      • Add Project Assets

      • Define App Constants

      • Define App Colors

      • Implement Notification Utilities

      • Generate Unique IDs

      • Create Reusable UI Components

      • Build Application Pages

      • Initialize and Run the Application

    • Conclusion

    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 named app_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 of awesome_notifications and is often a dependency of the main awesome_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 our primaryColor.

    • 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 the flutter_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 and SCHEDULED_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 an id, channelKey, title, and body. The bigPicture and layout parameters allow for rich notification content.

    • createScheduledNotification: This powerful function allows you to schedule notifications to appear at a specific date and time. It includes actionButtons (like “Mark Done” or “Clear”) that users can interact with directly from the notification, and a NotificationCalendar for precise scheduling with repeats: true to make it a weekly recurring notification.

    • cancelAllScheduledNotifications: A utility to cancel all notifications that have been scheduled. It also displays a SnackBar 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): These static methods are callbacks that awesome_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, and isTimeSelected 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 calls notificationUtil.createBasicNotification to display an immediate notification with an image.

    • triggerCancelNotification(): Calls notificationUtil.cancelAllScheduledNotifications to clear any pending scheduled notifications.

    • triggerScheduleNotification(): This function first presents an AlertDialog where the user can select a day of the week for the scheduled notification. Once a day is selected, it calls pickTime().

    • createScheduleNotification(): After the user selects both a day and time, this function is called to create the recurring scheduled notification using notificationUtil.createScheduledNotification.

    • pickTime(): Uses Flutter’s showTimePicker to allow the user to select a specific time for the scheduled notification.

    • requestPermission(): A simple wrapper to call notificationUtil.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 a customAlertDialog 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 in NotificationUtil 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 calls AwesomeNotifications().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, a rocket.png image, and three CustomElevatedButton 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 a Center widget containing a Column of three StatsContainer 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 before AwesomeNotifications().initialize() is called. This prevents potential errors, especially when dealing with platform channels.

      • AwesomeNotifications().initialize(...): This is where awesome_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 its key, name, channelDescription, defaultColor, importance, and defaultRingtoneType.

        • debug: false: Set to true during development to see more detailed logs from awesome_notifications.

    • MyApp Class:

      • static GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();: This is a crucial line. A GlobalKey assigned to MaterialApp‘s navigatorKey allows us to access the NavigatorState from anywhere in the application, even from static methods like NotificationUtil.onActionReceivedMethod. This enables us to perform navigation (for example, to StatsPage) 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 our AppColor.primaryColor), and sets HomePage as the initial screen. The navigatorKey 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:

    b8462f6c-2c2e-4461-ad16-45c0d6256257

    9fea68c2-fed5-4198-9430-fe22ead6e1ad

    45b5212d-df1f-4e6b-85d3-dbd1ea3d6345

    e636c37c-0cad-4364-bda9-0cd650f137ae

    95c7f91a-3700-4cc5-a531-15a5bd4227d4

    9c7f9dc5-177f-4948-b5c8-296b6ce535a3

    64a7400c-d114-4067-bf84-aedc60f896b4

    f2aee4c0-848f-448e-bfd8-9cceb5731d8a

    d9513419-f350-4ffc-81c2-8b798f42a209

    1962fd89-86d5-4d14-a38d-d632597f8a62

    2e0bff86-2c09-411f-8d46-5105d8ca0f46

    e5b378e5-4285-4028-9d45-48ac18627dba

    bcb69623-d46f-40f9-ad24-c4d53f6025fa

    4320b0b9-06f5-4830-8e18-d747f2238c10

    91566242-aca7-4b92-8297-809226078506

    1d55d56f-b0e3-4692-8618-7fb9f5554194

    8ee4c840-d16f-4b6e-a106-12ada5721cb2

    79ce425e-4f29-4877-97b1-c9d0452913c7

    0c7f15e8-4891-4cea-a16f-e9a5557190dc

    fcacf44b-a0eb-489e-8d05-0a76dc01973c

    To explore more examples and get detailed information about the awesome_notifications package, you can refer to the official documentation and GitHub repository:

    1. 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.

    2. 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 

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleUsing generative AI to help robots jump higher and land safely
    Next Article Rust VS Go VS TypeScript – which back end language is for you? With Tai Groot [Podcast #176]

    Related Posts

    Development

    Using phpinfo() to Debug Common and Not-so-Common PHP Errors and Warnings

    September 28, 2025
    Development

    Mastering PHP File Uploads: A Guide to php.ini Settings and Code Examples

    September 28, 2025
    Leave A Reply Cancel Reply

    For security, use of Google's reCAPTCHA service is required which is subject to the Google Privacy Policy and Terms of Use.

    Continue Reading

    New Linux Vulnerabilities

    Security

    Zimbra Collaboration Server GraphQL Vulnerability Exposes Sensitive User Data

    Security

    Convert Any Value to Collections with Laravel’s Collection::wrap Method

    Development

    Perplexity’s Comet AI browser could expose your data to attackers – here’s how

    News & Updates

    Highlights

    Web Development

    Live Shopping Explained: Trends, Growth & Brand Impact

    June 23, 2025

    In 2025, the retail and e-commerce landscape is seeing its next big evolution: Live Shopping.…

    laravelcm/laravel-subscriptions

    April 4, 2025

    Kodeco Podcast: Kotlin Symbol Processing – Podcast V2, S3 E4 [FREE]

    May 8, 2025

    ConnectWise Hit by Cyberattack; Nation-State Actor Suspected in Targeted Breach

    May 30, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.