online software programming courses

Flutter local notification explained for ios and android

First part of this tutorial

1. iOS Settings

2. Work on initialization

3. For older version of iOS

4. Request Permisions for iOS

5. Scheduled Notification

6. Immediate Notification

7. Android Settings

8. BehaviorSubject

9. Configure Local Time Zone

10. Navigate to different route

11. Passing payload

 

The big picture

 

1. iOS Settings

In your project folder go to your ios folder and find AppDelegate.Swift

and add a code like above the picture. Add the code below

   if #available(iOS 10.0, *) {
        UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
    }

With the above code you are done with basic iOS settings.

2. Work on Initialization

First you need to instantiate the plugin like below 

  FlutterLocalNotificationsPlugin
  flutterLocalNotificationsPlugin =
      FlutterLocalNotificationsPlugin(); 

then we will use flutterLocalNotificationsPlugin for IOSInitializationSettings, AndroidInitializationSettings and passing a callback function to onSelectNotification.

First take a look at an important callback function for iOS below

onDidReceiveLocalNotification

3. For older version of iOS

  Future onDidReceiveLocalNotification(
      int id, String title, String body, String payload) async {
    // display a dialog with the notification details, tap ok to go to another page
    showDialog(
      //context: context,
      builder: (BuildContext context) => CupertinoAlertDialog(
        title: Text(title),
        content: Text(body),
        actions: [
          CupertinoDialogAction(
            isDefaultAction: true,
            child: Text('Ok'),
            onPressed: () async {
              Navigator.of(context, rootNavigator: true).pop();
              await Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => SecondScreen(payload),
                ),
              );
            },
          )
        ],
      ),
    );
  }

Must be called from initillization functions  ios. But this is only necessary if your ios version older than 10. 

For iOS you can call it from IOSInitializationSettings() 
Use it like below

 final IOSInitializationSettings initializationSettingsIOS =
     IOSInitializationSettings(
         requestSoundPermission: false,
         requestBadgePermission: false,
         requestAlertPermission: false,
         onDidReceiveLocalNotification: onDidReceiveLocalNotification);

And then for android add the below code

 final Android InitializationSettings initializationSettingsAndroid =
     Android InitializationSettings("appicon);

The above code with make sure it will run on Android version. For android version we must mention the app icon name. The app icon name could be anything you like. But there must be an image for that name.

 

And then call IOSInitializationSettings from InitializationSettings like below

 final InitializationSettings initializationSettings =
        InitializationSettings(
       iOS: initializationSettingsIOS,
       android:initializationSettingsAndroid,
    );

 

and then call the "initialize" function from  flutterLocalNotificationsPlugin like below

 await flutterLocalNotificationsPlugin.initialize(
        initializationSettings,
        onSelectNotification: selectNotification);

Do remember that, selectNotification function is a callback function which means it will get called later. Once again callback functions are functions that get called later. When somethigns happens later.  In our case selectNotification will be called like say 5 seconds or 5 minutes later.

Now let's see the complate initialization function  of flutterLocalNotificationsPlugin

  FlutterLocalNotificationsPlugin
  flutterLocalNotificationsPlugin =
      FlutterLocalNotificationsPlugin(); //

  initializeNotification() async {
    //tz.initializeTimeZones();
 final IOSInitializationSettings initializationSettingsIOS =
     IOSInitializationSettings(
         requestSoundPermission: false,
         requestBadgePermission: false,
         requestAlertPermission: false,
         onDidReceiveLocalNotification: onDidReceiveLocalNotification
     );


 final Android InitializationSettings initializationSettingsAndroid =
     Android InitializationSettings("appicon);

    final InitializationSettings initializationSettings =
        InitializationSettings(
       iOS: initializationSettingsIOS,
       android:initializationSettingsAndroid,
    );
    await flutterLocalNotificationsPlugin.initialize(
        initializationSettings,
        onSelectNotification: selectNotification);

  }

 

Before we start to use the notification we must get permission from users in iOS. Use the code below to get permission

4. Request Permisions for iOS

  void requestIOSPermissions() {
    flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
        IOSFlutterLocalNotificationsPlugin>()
        ?.requestPermissions(
      alert: true,
      badge: true,
      sound: true,
    );
  }

Call the requestIOSPermissions() inside your initialize method.

 

Now take a look at selectNotification callback fuction for onSelectNotification. We need to implement it inside initialization settings. With this function we would be able to go to another page once the notification arrives by tapping on the notification tab. 

We implement it like below

For on tap onSelectNotification

  Future selectNotification(String payload) async {
    if (payload != null) {
      print('notification payload: $payload');
    } else {
      print("Notification Done");
    }
     Get.to(()=>SecondScreen(payload));
  }

As you can see it takes us to new page. Awesome. Here we use GetX package for navigation. With GetX you don't need to pass the context. It becomes much easier. 

This callback function also takes the payload (the infomation you want to send after on tap to a new route or page).

We are done with settings. Now we can create another function for showing notification on the screen when the time arraive. 

♠ Sqflite step by step ♠

5. Scheduled Notification

   scheduledNotification() async {
     await flutterLocalNotificationsPlugin.zonedSchedule(
         0,
         'scheduled title',
         'theme changes 5 seconds ago',
         tz.TZDateTime.now(tz.local).add(const Duration(seconds: 5)),
         const NotificationDetails(
             android: AndroidNotificationDetails('your channel id',
                 'your channel name', 'your channel description')),
         androidAllowWhileIdle: true,
         uiLocalNotificationDateInterpretation:
             UILocalNotificationDateInterpretation.absoluteTime);

   }

The above function is the most basic scheduled notification which implements flutterLocalNotificationsPlugin.zonedSchedule

Here we get a notification after 5 seconds once the app has been initialized. You need to call scheduledNotification() from somewhere in your app.

The line about time  TZDateTime is quiet important.

tz.TZDateTime.now(tz.local).add(const Duration(days:0, minutes: 0, seconds: 5)),

You can only pass constants to it's duration parameters. So we passed 5(a constant) seconds to it. If you try to send a variable you will get error like

Error: Not a constant expression.

This also means that, if you want to pass a time from somewhere else, it won't work. You will get the above error.

Of course, you can change the Duration function and send dynamic time.

Here you can add a property to make it periodically or in fixed schedule notification.

matchDateTimeComponents: DateTimeComponents.time

This property makes sure that, the notification would pop up periodically or in a fixed time manner or daily.

Now instead of .time you can use .DayOfWeekAndTime. This would show the notification every week on cerntain time and day.

matchDateTimeComponents: DateTimeComponents.DayOfWeekAndTime

 

6. Immediate Notification 

Instant or immediate notification could be match together on when you tap on a button. Just call the below function from the onTap() event on your button.

  displayNotification({required String title, required String body}) async {
    print("doing test");
    var androidPlatformChannelSpecifics = new AndroidNotificationDetails(
        'your channel id', 'your channel name', 'your channel description',
        importance: Importance.max, priority: Priority.high);
    var iOSPlatformChannelSpecifics = new IOSNotificationDetails();
    var platformChannelSpecifics = new NotificationDetails(
        android: androidPlatformChannelSpecifics, iOS: iOSPlatformChannelSpecifics);
    await flutterLocalNotificationsPlugin.show(
      0,
      'You change your theme',
      'You changed your theme back !',
      platformChannelSpecifics,
      payload: 'It could be anything you pass',
    );
  }

The above code will also repsone and navigate to a different page, when you tap on the notification.

Call this function from your initialize function. Take a look at the initialize function now

 initializeNotification() async {
 final IOSInitializationSettings initializationSettingsIOS =
     IOSInitializationSettings(
         requestSoundPermission: false,
         requestBadgePermission: false,
         requestAlertPermission: false,
         onDidReceiveLocalNotification: onDidReceiveLocalNotification
     );

    final InitializationSettings initializationSettings =
        InitializationSettings(
       iOS: initializationSettingsIOS,
    );

    await flutterLocalNotificationsPlugin.initialize(
        initializationSettings,
        onSelectNotification: selectNotification
    );
  }

 

7. Android Settings

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.flutterappnoti">

    <!-- The INTERNET permission is required for development. Specifically,
         flutter needs it to communicate with the running application
         to allow setting breakpoints, to provide hot reload, etc.
    -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

 

And then app icon for android. Without setting app icons you will get crash

Set it inside the drawable folder

Warnings for Android users

You should also make sure that, you installed java and jdk on your machines to run android emulator. If your java and jdk environment paths are not defined, you won't be able to run android emulator.

Second part of the tutorial

 

8. BehaviorSubject

BehaviorSubject is a controller(Stream), you can add items to it and emit items from it to a listener. So all it does is, wait for events(like notifications) to happen and capture the events and forward them to a listener.

BehaviourSubject is important if you want to click on the the notification and go to another page or to a route with payload.

Once you click on the notificatoin tab, BehaviorSubject will capture the lastest(this) notification and it's related info like payload string (the info you want to send with the notifications).

You can create an instance like below

  final BehaviorSubject<String> selectNotificationSubject =
  BehaviorSubject<String>();

Now selectNotificationSubject will have some properties that you can use like

selectNotificationSubject.add(payload);
selectNotificationSubject.stream.listen(()=>);

So selectNotificationSubject.add should be called inside onSelectNotification callback. We know that, onSelectNotification gets called duration the initialization of notifications.

 onSelectNotification: (String payload) async {
          if (payload != null) {
            debugPrint('notification payload: ' + payload);
          }
          selectNotificationSubject.add(payload);
});

Now with this, payload would be added for the next route. And listener will receive this event for executing later (based on your notification time).

And later BehaviourSubject selectNotificationSubject instance would send the added payload to the listener.

So if you want to tap on notification message and go to another page, you must implement BehaviorSubject. And the process is first add the items to this on onSelectNotififcation callback and then call for listening from the first initialization.

9. Configure Local Time Zone

You need to configure local time zone if you want to trigger a notification on certain time later. For this you must access the local time zone from native layer.

flutter_native_timezone: ^1.0.10

You need to install this package and you need to import the three packages

import 'package:flutter_native_timezone/flutter_native_timezone.dart';
import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz;

Then in your initialization you put like below

tz.initializeTimeZones();
final String timeZoneName = await FlutterNativeTimezone.getLocalTimezone();
tz.setLocalLocation(tz.getLocation(timeZoneName));

The first line does the necessary settings for the time zone and second line get the time zone from the native layer of the device and third line sets the time.

10. Navigate to different route

Now you will see how to navigate to different routes. You can use selectNotification call back to do that. In our case the body is selectNotification. We can do a conditional check to navigate to different routes

   Future selectNotification(String payload) async {
    if (payload != null) {
      //selectedNotificationPayload = "The best";
      //selectNotificationSubject.add(payload);
      print('notification payload: $payload');
    } else {
      print("Notification Done");
    }

   if(payload=="Theme Changed"){
      //going nowhere
      }else{
        Get.to(()=>SecondScreen(payload));
      }
  }

11. Pass payload

Payload is always gotten from the functions you call for notification. In our case, we are calling displayNotification and scheduledNotification from our front end fluter code.

They must carry and pass payload from flutter caller to callee. See the callee

  scheduledNotification(int hour, int minutes,Task task) async {
    await flutterLocalNotificationsPlugin.zonedSchedule(
      task.id,task.title,task.note,
      ...........................................................
      ..........................................................
      matchDateTimeComponents: DateTimeComponents.time,
      payload: "${task.title}|"+"${task.note}|"+"${task.startTime}|",
    );
}

And the other one

  displayNotification({@required String title, @required String body}) async {
    ....................................................................
   ....................................................................
    await flutterLocalNotificationsPlugin.show(
      0,
      'You change your theme',
      'You changed your theme back !',
      platformChannelSpecifics,

      payload: title,
    );
  }

So you see how we pass the paylaod. 

Our callback function onSelectNotification also gets the same payload. So you can get them in this callback function and send anywhere you want to navigate for.

   Future selectNotification(String payload) async {
    if (payload != null) {
      print('notification payload: $payload');
    } else {
      print("No plaload");
    }

      if(payload=="Theme Changed"){
      }else{
       Get.to(()=>SecondScreen(payload));
      }
  }

This is how you can pass payload to a different page or route