Flutter Developer Bootcamp is now 70% OFF!

black friday sale!

12  comments

Notifications are an essential component of many apps. Local notifications  in particular have many use cases, though most commonly they're used to create scheduled reminders. There is an endless variety of apps you can make with this as a core or an additional feature.

Notifications are platform specific and require us to tap into native code to create them. Fortunately, we don’t have to become native developers to implement them into Flutter apps. Thanks to the powerful Awesome Notifications plugin, we can create notifications for iOS and Android with minimal effort!

The Finished App

In this tutorial, we are going to build a mock plant care app. The finished app will be able to display two types of notifications with a variety of customizations. We'll also cover several differences between iOS and Android notifications so that you can comfortably create platform specific experiences. You can view the final project demonstrations for both platforms in the videos below.

When the app runs for the first time our permission will be requested for the app to display notifications on the device. We will only see that prompt if the permission isn't already granted.

The home page will have three buttons, two of which will display different kinds of notifications. The third “Cancel” button will cancel any scheduled notifications we create. This will mainly be helpful  when testing our app, but can also be used as a feature in a production application.

Plant Food Notification

Complex topics such as state management are best understood on real-ish projects. No, we're not going to build yet another counter app. Instead, we're going to create a weather app with master and detail screens, a fake repository to get the weather data from and the app will also have visually appealing error handling.

Water Notification

The second button will create a scheduled notification reminding the user to water their plant weekly. When you tap on the button, it will bring up a dialog to pick the day of the week and then a time picker. This allows you to select when you want the notification to be displayed. At the scheduled time a notification with a title, a body, an emoji and an action button will appear. This notification will also produce a custom sound on both platforms and will be non-dismissible on Android.

Getting Started

In this tutorial, we are going to dive deep into creating local notifications with Flutter. We'll be using the Awesome Notifications plugin throughout, which makes it incredibly easy to set everything up.

This plugin has a myriad of options and can even be integrated with a push notification service such as Firebase Cloud Messaging. As of now, there's excellent support for iOS and Android and that's what our app will be built for. The plugin seems to be in active development and the creator is aiming to have support for desktop and the web as well. So hopefully, we'll be able to cover those in the future.

We will be using a starter project in this tutorial that will contain our app UI, helpful utility functions, and assets that we'll use throughout. You can go ahead and grab the starter project from the link below if you would like to follow along.

Let’s get started by adding the Awesome Notifications plugin to our pubspec.yaml file. At the time of writing this tutorial the version is 0.0.6+10 and it was migrated to null-safety. The plugin is being actively developed though, so be mindful of any potential breaking changes with future updates.

pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  awesome_notifications: ^0.0.6+10

Starter Project Overview

Before we start coding let's familiarize ourselves with the starter project.

Dependencies and Assets

If you look at the pubspec.yaml file, you will see that we already have a few dependencies other than the typical ones added. We are depending on the flutter_launcher_icons package to create our custom app icons for iOS and Android and the font_awesome_flutter package for displaying some specific, decorative icons in the UI.

In the project folder, we also have an assets folder that holds several images. The images are either already used in the starter project or will be used later on when we are setting up the notifications.

If you go inside the iOS>Runner folder you will find an aiff file that we will use as the custom sound for one of our notifications on iOS. You can find an m4a file with the same sound for Android inside the android>app>src>main>res>raw folder.

If you are using native resources for things like notification icons and sounds be sure to prefix your file names with "res_". This will protect them from being minified on Android. More details on this here.

We will also have custom icons displayed in our notifications. You can find the icons that will be used in iOS inside the iOS>Runner>Assets.xcassets>AppIcon.appiconset folder. The icon we will use for Android notifications is located inside the android>app>src>main>res>drawable folder.

If you try running the starter project on an iOS simulator and get an error saying that the custom notification sound file is missing, you can resolve this in the following manner. First, go to the ios>Runner folder and copy the res_custom_notification.aiff file to another location. Then, go to the ios folder and open the runner.xcodeproj file in Xcode. You should see that in Xcode the sound file name appears in red. Delete that file and then paste the one you copied earlier in its place. Now the file name should no longer be red and everything should be working.

Utilities and Widgets

In the lib folder, we have a widgets.dart file that has some of the widgets used across the pages of our app. This file contains widgets that display the title for the app bar as well as an image of a plant on the home page and the stats page. It also contains the buttons for the home page and mock plant stats for the plant stats page.

In the starter project you will also find a utilities.dart file that holds a couple of helpful functions and a custom class we will use for the creation of notifications. The createUniqueId function returns an integer we will use to create unique ids. The pickSchedule function allows us to pick a time and a day of the week that we will use to create scheduled notifications. This function returns a NotificationWeekAndTime object that holds the information we need to set a notification schedule.

App Pages and main.dart File

The final pieces of our starter app that we will go over are the main.darthome_page.dart, and plant_stats_page.dart files. The main.dart file simply holds the main function and an AppWidget that returns a MaterialApp. The MaterialApp has some theme styling and the HomePage inside of its home parameter.

The home_page.dart file has a stateful HomePage widget. There, we have an AppBar with a title and an IconButton that navigates the user to the PlantsStatsPage. In the body, we have a centered Column that holds the PlantImage and the HomePageButtons widgets. We will populate the three "onPressed" parameters for the HomePageButtons later in this tutorial.

The plant_stats_page.dart has some basic UI elements that are there for visual purposes only. We won’t really be working with this page and will only use it for routing when we tap on a notification.

Initialization & Notification Channels

Before we create any notifications, we need to initialize the Awesome Notifications plugin, pass in the location of our custom Android icon, and create at least one NotificationChannel object.

Initialization & Custom Notification Icon

To initialize the plugin, we need to call the initialize method on the AwesomeNotifications object. This call will be made inside the main function. We also need to provide values to the required positional parameters of this method. For the first one, we'll provide the path to where our custom Android notification icon is located. This icon will be used for all Android notifications. For iOS, one of the icons mentioned in the Dependencies and Assets section of the Starter Project Overview will be shown.

Android supports only very simple PNG images for the notification icons. To create your own custom icons with ease you can use an Android icon generator. Click here to check out the one used to create the icon for this project.

So let's get what we just discussed implemented now and then we'll move on to creating notification channels.

main.dart

void main() {
 AwesomeNotifications().initialize(
  'resource://drawable/res_notification_app_icon', 
  );
 runApp(AppWidget());
}

Notification Channels

The NotificationChannel constructor has several parameters you can provide values to. The NotificationChannel object holds information about the notification channel itself, such as the channelKey and channelName. It also holds some of the default configurations for the notifications that will be assigned to it.

Not all configurations will work on every platform or device, so you have to keep that in mind when creating both your channels and notifications. For example you can set the ledColor parameter on a channel, but not all devices will have an LED to display that functionality.

In native terms, notification channels became a thing when they were released in Android Oreo as a way to declutter the notification drawer. To this day, you can go to the notification settings of any app in Android and manage the settings and permissions for individual notification channels. In iOS however, there are no channels, so you won’t have these options. You can see how the notification settings look on each device in the image below.

Even though you won’t see the separate channels in your iPhone’s notification settings, the configurations you set on a channel will still take effect. For example, let’s say you created a channel for reminder notifications. On that channel, you specified a path to a custom sound you want played when a reminder notification is displayed. On an iPhone, even though you won't have the channel separation like on an Android phone, you'll still hear the custom sound when a notification is displayed through the reminder channel. 

Creating a Basic Notification Channel

In the finished app, we will have two notification channels, but for now let’s just create one - the basic notification channel. We'll create this channel, put it inside a List and pass it to the second positional parameter of the initialize method.

To configure the basic channel, we need to create a NotificationChannel object. For that object we need to specify the channelKey and channelName. The channelKey will be used to connect our notifications to the specific channels. The channelName will be displayed in Android's app’s notification settings. For this channel we will also specify the defaultColor which will determine the color of our Android notification icon.

We also want to make sure that when our notification is displayed, it peeks out from the top of the screen. This is a default behavior on iOS, but for Android we need to specify the notification importance. To do this, we will add NotificationImportance.High to the importance parameter. Lastly, we want to enable notification badges for this channel. So we'll set the channelShowBadge to true.

main.dart

AwesomeNotifications().initialize(
  'resource://drawable/res_notification_app_icon',
  [
    NotificationChannel(
      channelKey: 'basic_channel',
      channelName: 'Basic Notifications',
      defaultColor: Colors.teal,
      importance: NotificationImportance.High,
      channelShowBadge: true,  
    ),
  ],
);

Requesting Notification Permission

Before we create and send out notifications, we need to set up a way to obtain the permission from the user to do so. On Android notifications are allowed by default, but on iOS, you must always get the user’s permission before you can send any out. No matter the platform, we are going to check if notifications are allowed for our app and if they aren't then we will request the user’s permission.

First, let's override the initState method inside our _HomePageState. There, we will call the isNotificationAllowed method on the AwesomeNotifications object to get a Future<bool> back. Then we will add a callback using .then that will check if the returned value is false. If the returned value is indeed false, we will display an elegant AlertDialog to get the permission. After the permission is granted, we will close the dialog.

home_page.dart

class _HomePageState extends State<HomePage> {
  @override
  void initState() {
    super.initState();
    AwesomeNotifications().isNotificationAllowed().then(
      (isAllowed) {
        if (!isAllowed) {
          showDialog(
            context: context,
            builder: (context) => AlertDialog(
              title: Text('Allow Notifications'),
              content: Text('Our app would like to send you notifications'),
              actions: [
                TextButton(
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  child: Text(
                    'Don\'t Allow',
                    style: TextStyle(color: Colors.grey, fontSize: 18),
                  ),
                ),
                TextButton(
                  onPressed: () => AwesomeNotifications()
                      .requestPermissionToSendNotifications()
                      .then((_) => Navigator.pop(context)),
                  child: Text(
                    'Allow',
                    style: TextStyle(
                      color: Colors.teal,
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ),
              ],
            ),
          );
        }
      },
    );
  }

If you run the app now on an iOS simulator, you should see the dialog pop right up. When you tap “Allow” a native dialog will be displayed so you can grant the permission. If you run the app on Android, you won’t see anything since notifications are allowed by default. You can test to see if the dialog will appear in the case where notifications are disabled. To do that, go to the notification settings for the app and disable all notifications. Then you can restart your app, and the dialog should appear on Android as well. Now, when you tap "Allow" you will be taken to the notification settings for the app where you can enable them again.

Creating a Notification on the Basic Channel

Hurray, it’s finally time to create our first notification! The first notification will be assigned to the basic channel we added earlier. It'll notify the imaginary user of our mock plant care app that they need to buy some plant food.

First, let’s create a new file in our lib folder. We are going to call it notifications.dart and here we will create functions that will display all of our notifications. This way, we won’t have to clutter up our home_page.dart so much.

Let’s create an async function that will have a return type of Future<void> and call it createPlantFoodNotification. We will then call the createNotification method on the AwesomeNotifications object and await that call. The createNotification method has a required content parameter where we will provide a NotificationContent object. We will specify the id, channelKey, title, body, bigPicture and notificationLayout for this object.

Even though the id and channelKey parameters say they're optional, they really aren’t, because if you don’t provide them, the notification won’t be created. Furthermore, if you don’t add at least a title or a body, the notification will be create, but never displayed.

notifications.dart

Future<void> createPlantFoodNotification() async {
  await AwesomeNotifications().createNotification(
    content: NotificationContent(
      id: createUniqueId(),
      channelKey: 'basic_channel',
      title:
          '${Emojis.money_money_bag + Emojis.plant_cactus} Buy Plant Food!!!',
      body: 'Florist at 123 Main St. has 2 in stock.',
      bigPicture: 'asset://assets/notification_map.png',
      notificationLayout: NotificationLayout.BigPicture,
    ),
  );
}

If you want to be able to display multiple instances of a specific notification, then you must provide a unique id. If you provide a hard coded integer instead, then only one notification of said type will be displayed at a time . To create a unique id, we are using a helper createUniqueId function from our utilities.dart file and passing it to the id parameter of the NotificationContent object. For channelKey we provide a String of  ‘basic_channel', because we want this notification to be displayed on the basic channel. To spruce up our title, we also added some emojis. Awesome Notifications plugin comes with a handy prepackaged Emojis class you can use to easily add some emojis where you see fit.

As per the Awesome Notifications documentation some emojis may not work. Make sure to test all emojis if using! Also, some of the emoji names are incorrectly spelled for ex. water_droplet is spelled wheater_droplet. So, just be aware of all this if you decide to use the Emojis class. 

The body of our notification has text telling the user where to buy some plant food. We are also providing an image of a map to the bigPicture parameter to be displayed in the notification. The path you provide needs to specify the image source. We are using an image from our project assets folder, so we prefixed our String path accordingly.

You can provide images to the bigPicture parameter using your Flutter project assets, through the network, device file storage and a drawable native resource. To find out how to correctly configure the path for different sources you can check out the plugin example project

Lastly, to make sure the image displays correctly, we specified the notificationLayout. Here we can choose from several layouts and in our case, we are using NotificationLayout.BigPicture.

Now let’s go back to the home_page.dart file. Go to the bottom of the page to the HomePageButtons widget and replace the empty function for the onPressedOne parameter with the createPlatFoodNotification function.

home_page.dart


...
HomePageButtons(
   onPressedOne: createPlantFoodNotification,
   onPressedTwo: () async {},
   onPressedThree: () async {},
   ),
...

If we save everything now and go to the app, tapping on the “Plant Food” button should almost instantly bring up the notification. If everything is working correctly, when the notification is in open view, you'll see all of the visual components we specified. Take a look at the image below to see how it looks on iOS and Android.

Notification Streams

When using the Awesome Notifications plugin, you can listen to several notification streams. You can then respond to the data coming through these streams in ways you see fit. In this tutorial, we are going to listen to just a couple of the available streams, but we'll first get a brief understanding of all of them.

Types of Streams

createdStream

A createdStream transports ReceivedNotification objects when notifications are created. In other words, if you press the “Plant Food” button a notification will be created and you can listen to the createdStream to get the ReceivedNotification object and do something with it. The ReceivedNotification object holds all kinds of information about your notification.

displayedStream

A displayedStream transports ReceivedNotification objects when notifications are displayed to the user. 

dismissedStream

Unlike the previous two streams we discussed, the dismissedStream transports ReceivedAction objects. That is because dismissing a notification is considered an action. You still have access to all of the notification details, but in addition to that, you can see data about the action taken. For example, you are able to get the information about when the notification was dismissed through the dismissedDate field.

actionStream

Just like the dismissedStream, the actionStream transports ReceivedAction objects. In this stream, you receive objects when the user taps on the notification to open it or taps on any action buttons if present.

It is important to note that streams will fire at different times depending on the device and the state of your app. You can find out more about this here.

Listening to Streams in Our App

In our app, we are going to be listening to the createdStream and the actionStream. Let’s first set up the listener for the createdStream. We are going to add it to the initState of our _HomePageState below where we check for notification permissions.

home_page.dart


AwesomeNotifications().createdStream.listen((notification) {
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(
      content: Text('Notification Created on ${notification.channelKey}'),
    ),
  );
});

As you can see, here we are using the notification variable which holds the ReceivedNotification object to get the channelKey and displaying it in the SnackBar. We'll see the SnackBar every time a notification is created and the message will let us know what channel it belongs to. This will be particularly useful when we create scheduled notifications. It will help us see a notification was actually created, instead of waiting until the scheduled time. We can also test the app now to see if this is working using the “Plant Food” notification.

Now let’s create the listener for the actionStream. Here we are going to set up routing to the PlantStatsPage. We will be routed there if any action is taken on the displayed notification.

home_page.dart


AwesomeNotifications().actionStream.listen((notification) {
 Navigator.pushAndRemoveUntil(
      context,
      MaterialPageRoute(
        builder: (_) => PlantStatsPage(),
      ),
      (route) => route.isFirst);
 });
}

If you are running this app on iOS, you may have noticed that the red badge number is increasing with every notification that you display. Android has notification badges too, but they work a bit differently.

Instead of displaying the number of unread notifications, a dot is displayed over the app icon to notify the user of any active notifications. You can also long press on the app icon to see what those notifications are. When you open or dismiss all of the active notifications, the badge disappears.

Unlike on Android, when we open the notification or dismiss it on iOS, the badge number doesn’t go down. That is why we need to manually set up the logic to make sure this number doesn’t grow out of proportion.

home_page.dart


AwesomeNotifications().actionStream.listen((notification) {
  if (notification.channelKey == 'basic_channel' && Platform.isIOS) {
    AwesomeNotifications().getGlobalBadgeCounter().then(
          (value) =>
              AwesomeNotifications().setGlobalBadgeCounter(value - 1),
        );
  }
  Navigator.pushAndRemoveUntil(
      context,
      MaterialPageRoute(
        builder: (_) => PlantStatsPage(),
      ),
      (route) => route.isFirst);
 });

In a production app, you can have a more elaborate configuration for this and can also include a similar thing for when notifications are dismissed. For this example app though, we're simply getting the global badge counter and then decrementing the value we get by one. The badge value will decrement only if the user is on an iOS device and only if an action is taken on a basic channel notification. Don’t forget to import dart:io to be able to use Platform.isIOS.

Disposing of Streams

Don’t forget to dispose of any streams that you set up listeners for. To dispose of a stream, you need to add AwesomeNotifications().[insert stream name]Sink.close() to the dispose method. For our app, we are overriding the dispose method of the _HomePageState and disposing of the two streams we created. You can add this right above the build method.

home_page.dart


@override
void dispose() {
  AwesomeNotifications().actionSink.close();
  AwesomeNotifications().createdSink.close();
  super.dispose();
}

Creating Scheduled Notifications

We are now going to work on creating scheduled notifications. Realistically, most local notifications in a production app would be scheduled, so this part is rather exciting!

Scheduled Notifications Channel

First, we are going to create a scheduled notifications channel. Head on over to main.dart and add another NotificationChannel to the list with a channelKey of ‘scheduled_channel’ and a channel name of ‘Scheduled Notifications’. Let’s also add the Colors.teal for the Android icon, just like we did in the basic channel.

main.dart


...
NotificationChannel(
  channelKey: 'scheduled_channel',
  channelName: 'Scheduled Notifications',
  defaultColor: Colors.teal,
   ),
 ],
);

On Android, we can make notifications non-dismissible. That means a user can’t just swipe them away and has to tap on them (i.e. take an action) to make them disappear. To do that, we can set the locked parameter to true.

We also want to make sure that the notifications peek out from the top of the screen on Android. Just like in the basic channel, we are going to do this by setting the importance to high.

Lastly, we want to add a custom sound to this notification type. To do so, we need to provide the path to our custom sound file in the native resources. If you want to know where the sound files need to be stored for this to work correctly, you can refer to the Starter Project Overview section of this tutorial.

Android supports a variety of file formats for custom notification sounds. For iOS, however, you must make sure your sound is in either the aiff, wav, or caf format.

main.dart


...
NotificationChannel(
  channelKey: 'scheduled_channel',
  channelName: 'Scheduled Notifications',
  defaultColor: Colors.teal,
  locked: true,
  importance: NotificationImportance.High,
  soundSource: 'resource://raw/res_custom_notification'),
 ],
);

Scheduled Notifications

Now we can go to notifications.dart and create an async function called createWaterReminderNotification that returns a Future<void> and requires that our custom NotificationWeekAndTime object is passed into its only parameter. Similarly to how we created the “Plant Notification” we need to call and await the createNotification method and provide a configured NotificationContent object to its content parameter.

notifications.dart


Future<void> createWaterReminderNotification(
    NotificationWeekAndTime notificationSchedule) async {
  await AwesomeNotifications().createNotification(
    content: NotificationContent(
      id: createUniqueId(),
      channelKey: 'scheduled_channel',
      title: '${Emojis.wheater_droplet} Add some water to your plant!',
      body: 'Water your plant regularly to keep it healthy.',
      notificationLayout: NotificationLayout.Default,
    ),
  );
}

So far so good, but we still need to set up the scheduling. The createNotification method has some additional parameters we haven’t discussed yet. The ones we are interested in are actionButtons and schedule.

The actionButtons parameter accepts a List of NotificationActionButton objects. NotificationActionButton constructor has several parameters, but in this app we are just going to specify the key and label. The key will be the identifier for the action button. This key can be used to perform certain actions inside the actionStream listener. The label will be the button text displayed in our notification. Go ahead and add the following code below the NotificationContent.

notifications.dart


...
actionButtons: [
 NotificationActionButton(
  key: 'MARK_DONE',
  label: 'Mark Done',
  )
],
...

We want our water notifications to be repeated weekly on the day and time selected by the user. Our createWaterReminderNotification function accepts a custom NotificationWeekAndTime object that has the information we need. This object will be created after a user selects what day of the week they want to be reminded on and at what time. It contains an int that represents the day of the week and a TimeOfDay object for the selected time. You can view the custom class and the function we will use to gather the user input inside the utilities.dart file.

To create the repeating notification, we are going to pass in a NotificationCalendar object to the schedule parameter. First, we want to set the repeats parameter of the NotificationCalendar constructor to true so we keep seeing this notification every week.

Now we can specify the time and day of the week. We know that the notification should be displayed on a specific day of the week at a specific hour and minute. So what we need to do is take the corresponding values from our notificationSchedule variable and populate the weekday, hour and minute parameters with them. We must also set the second and millisecond to zero. If we don't do that, the notification will keep showing up until the minute on the clock changes from the scheduled one. Here is how everything should look now.

notifications.dart


Future<void> createWaterReminderNotification(
    NotificationWeekAndTime notificationSchedule) async {
  await AwesomeNotifications().createNotification(
    content: NotificationContent(
      id: createUniqueId(),
      channelKey: 'scheduled_channel',
      title: '${Emojis.wheater_droplet} Add some water to your plant!',
      body: 'Water your plant regularly to keep it healthy.',
      notificationLayout: NotificationLayout.Default,
    ),
    actionButtons: [
      NotificationActionButton(
        key: 'MARK_DONE',
        label: 'Mark Done',
      )
    ],
    schedule: NotificationCalendar(
      weekday: notificationSchedule.dayOfTheWeek,
      hour: notificationSchedule.timeOfDay.hour,
      minute: notificationSchedule.timeOfDay.minute,
      second: 0,
      millisecond: 0,
    ),
  );
}

Now we have the function needed to create our notification, but we still need to provide the schedule details to it. To do this, let’s go to the home_page.dart file. There, inside the body of the function in the onPressedTwo parameter of the HomePageButtons widget we are going to add the following. 

home_page.dart


...
onPressedTwo: () async {
  NotificationWeekAndTime? pickedSchedule =
      await pickSchedule(context);

  if (pickedSchedule != null) {
    createWaterReminderNotification(pickedSchedule);
  }
},
...

Here we are using our handy pickSchedule function from the utilities.dart file to bring up a dialog where we can select the day of the week and a time picker to select the time. This function returns a NotificationWeekAndTime object with the selected values. We are storing this object inside the pickedSchedule variable. Then we are checking to make sure the value of that variable isn't null and calling the createWaterReminderNotification function while providing our pickedSchedule variable to it.

Cancelling Scheduled Notifications

Before we go ahead and test out everything we just did, we need to set up a way to cancel scheduled notifications. As we are building and testing our the app, we don't want those schedules to stick around indefinitely. In a production app scenario, you also want to give your users the ability to cancel the scheduled reminders. All we need to do for our project is add this simple function to the notifications.dart file.

notifications.dart


Future<void> cancelScheduledNotifications() async {
  await AwesomeNotifications().cancelAllSchedules();
}

Now go back to the home_page.dart file and pass this function into the onPressedThree parameter of the HomePageButtons widget.

home_page.dart


...
onPressedThree: cancelScheduledNotifications,
...

Amazing! Now run the app and tap on the “Water” button. Then select whatever day of the week it is for you. After you selected the weekday, you should see the time picker. The clock is set up to display your current time plus one minute. So you can go ahead and just click OK here (unless you’d like to wait longer). While you wait, you should already see the SnackBar letting you know that a notification was created on the scheduled channel. When the time changes the next minute, you should see the notification. You should also hear the custom notification sound we provided. The action button we set up looks different on iOS and Android  — you can see how both look in the image below.

Conclusion

Congratulations, you made it through the entire tutorial! Hopefully you learned a whole lot and won’t forget to water your plants ever again.

There are so many options in the Awesome Notifications plugin that, unfortunately, we couldn’t cover them all in this tutorial. That is why I encourage you to add the dependency to one of your Flutter projects now and play around with as many options as possible. Before you know it, you will become an absolute pro at creating local notifications in your Flutter apps.

About the author 

Ashley Novik

Ashley is a Flutter developer and tutor at Reso Coder with a passion for tech and an infinite drive to learn and teach others 😄. On her days off she enjoys exploring nature and powering through off-road trails on her mountain bike 🚵‍♀️.

You may also like

  • excellent tutorial. I saw you didn’t use Firebase. Would these notifications only be local? Or could you notify with an update over the internet?

    • Hi Adriano,

      Thank you for the positive feedback!

      This tutorial is only about local notifications. There is a plan to make a tutorial about push notifications using Firebase Cloud Messaging in the near future. In the meantime you can also check out the official Awesome Notifications example project and documentation where you can find examples of using Firebase Cloud Messaging.

  • I feel so much in love with your tutorial because you go the extra mile to explain comprehensively concepts(highlighted in red and blue background) that will be useful for the reader.

    Your tutorials are exceptionally wonderful. Weldone man!

  • Hi Ashley,
    First off, thanks very much for this awesome tutorial, that’s very much appreciated.
    However, I have a question. I have followed your tutorial to the letter but when I click on the notification bubble that appears after it has been programmed, I have a “bad state : stream had already been listened to ” error that shows up. Any idea why ?

    Thanks again!

    • Hi Tom,

      Thank you for the kind words!

      I’ve spent many hours with this package, testing it in various ways, but never got the error you are mentioning. Perhaps there is something you overlooked?

      I suggest you try grabbing the finished project from GitHub (you can find the link at the beginning of the Getting Started section of this article). Then you can run that finished project to see if you still get the same error. If the error isn’t there you can try comparing it to your code to see if you can pinpoint where something might have gone wrong.

      Hope this helps, let me know if you’re still facing this issue.

      Cheers!

  • Hey Ashley. Thank you so much for this amazing tutorial!

    I was wondering if you have any tips on using crontabSchedule with createNotificationFromJsonData(). I can’t seem to get it to work on my app.

    I have this map (the crontab is supposed to notify every second), but it only notifies once when the app runs:
    {
    “content”: {
    “id”: 500,
    “channelKey”: “channel”,
    “title”: “Title”,
    “body”: “Body”,
    },
    “schedule”: {
    “crontabSchedule”: “0/1 * * ? * * *”
    }
    };

    Thanks,
    Amr

  • Hey Ashley. Thank you for this amazing tutorial.
    I just want to know how to pass the payload using this package?
    Also if I have a model class object then how can I pass it to the payload.
    Any help would be appreciated.
    Thank you.

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
    >