Implementing Local Notifications Using Packages Like flutter_local_notifications in Flutter

Local notifications are a vital feature for mobile applications, providing users with timely updates and reminders without requiring an internet connection. In Flutter, implementing local notifications is made easy with packages like flutter_local_notifications. This comprehensive guide will walk you through setting up and using the flutter_local_notifications package to implement local notifications in your Flutter app.

What are Local Notifications?

Local notifications are notifications that are scheduled and delivered by the operating system on the user’s device. They are typically used for reminders, alerts, and confirmations. Unlike push notifications, local notifications don’t require a server or internet connectivity.

Why Use Local Notifications?

  • Timely Reminders: Keep users informed with timely reminders.
  • Enhanced User Engagement: Drive user engagement through relevant alerts.
  • Offline Functionality: Deliver notifications without needing an internet connection.
  • Customization: Offer customizable notification content and behavior.

How to Implement Local Notifications Using flutter_local_notifications in Flutter

Step 1: Add the flutter_local_notifications Package

First, add the flutter_local_notifications package to your pubspec.yaml file:

dependencies:
  flutter_local_notifications: ^16.3.0  # Use the latest version

Run flutter pub get to install the package.

Step 2: Configure the Plugin for Android

For Android, you need to configure the plugin in your AndroidManifest.xml file. Add the following to the <manifest> tag:

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

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    <uses-permission android:name="android.permission.VIBRATE"/>

    <application
        android:label="Your App Name"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">

        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">

            <meta-data
                android:name="io.flutter.embedding.android.NormalTheme"
                android:resource="@style/NormalTheme"
            />

            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver"
            android:enabled="true"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
                <action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
                </intent-filter>
        </receiver>

        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>
</manifest>

Ensure you replace your_package_name with your app’s package name.

Step 3: Configure the Plugin for iOS

For iOS, you need to request permission from the user to send notifications. Add the following to your AppDelegate.swift or AppDelegate.m:

AppDelegate.swift:

import UIKit
import Flutter
import UserNotifications

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate

    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

Additionally, add the necessary permissions to your Info.plist file:

<key>NSUserActivityUsageDescription</key>
<string>This app needs to access your activity.</string>
<key>UIBackgroundModes</key>
<array>
 <string>fetch</string>
 <string>remote-notification</string>
</array>

Step 4: Initialize the Plugin

Initialize the flutter_local_notifications plugin in your Flutter app. This is typically done in the main() function or in the initState() method of your main widget.

import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';

final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
    FlutterLocalNotificationsPlugin();

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  const AndroidInitializationSettings initializationSettingsAndroid =
      AndroidInitializationSettings('app_icon'); // Replace 'app_icon' with the name of your app's icon in the 'drawable' folder

  final DarwinInitializationSettings initializationSettingsDarwin =
      DarwinInitializationSettings(
          onDidReceiveLocalNotification: (id, title, body, payload) => null);

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

  await flutterLocalNotificationsPlugin.initialize(initializationSettings,
      onDidReceiveNotificationResponse: (NotificationResponse response) => {});

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Local Notifications Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Local Notifications Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              child: Text('Show Notification'),
              onPressed: () {
                _showNotification();
              },
            ),
            ElevatedButton(
              child: Text('Schedule Notification'),
              onPressed: () {
                _scheduleNotification();
              },
            ),
          ],
        ),
      ),
    );
  }

  Future<void> _showNotification() async {
    const AndroidNotificationDetails androidNotificationDetails =
        AndroidNotificationDetails(
            'channel_id', 'channel_name',
            channelDescription: 'channel_description',
            importance: Importance.max,
            priority: Priority.high,
            ticker: 'ticker');

    const NotificationDetails platformChannelSpecifics =
        NotificationDetails(android: androidNotificationDetails);

    await flutterLocalNotificationsPlugin.show(
        0,
        'Test Title',
        'Test Body',
        platformChannelSpecifics,
        payload: 'item x');
  }

  Future<void> _scheduleNotification() async {
    const AndroidNotificationDetails androidNotificationDetails =
        AndroidNotificationDetails('channel_id', 'channel_name',
            channelDescription: 'channel_description',
            importance: Importance.max,
            priority: Priority.high,
            ticker: 'ticker');

    const NotificationDetails platformChannelSpecifics =
        NotificationDetails(android: androidNotificationDetails);

    await flutterLocalNotificationsPlugin.schedule(
        1,
        'Scheduled Title',
        'Scheduled Body',
        DateTime.now().add(const Duration(seconds: 5)),
        platformChannelSpecifics,
        payload: 'item x');
  }
}

Remember to replace 'app_icon' with the name of your app icon in the drawable folder. Also, adjust the channel_id, channel_name, and channel_description to match your app’s requirements.

Step 5: Request iOS Permissions

For iOS, you need to request permission from the user to send notifications. Add the following code to your initState() method in the MyHomePage widget:

  @override
  void initState() {
    super.initState();
    _requestPermissions();
  }

  void _requestPermissions() async {
    if (Platform.isIOS || Platform.isMacOS) {
      await flutterLocalNotificationsPlugin
          .resolvePlatformSpecificImplementation<
              IOSFlutterLocalNotificationsPlugin>()
          ?.requestPermissions(
            alert: true,
            badge: true,
            sound: true,
          );
      await flutterLocalNotificationsPlugin
          .resolvePlatformSpecificImplementation<
              MacOSFlutterLocalNotificationsPlugin>()
          ?.requestPermissions(
            alert: true,
            badge: true,
            sound: true,
          );
    }
  }

Step 6: Show a Simple Notification

Create a function to display a simple notification when a button is pressed.

  Future<void> _showNotification() async {
    const AndroidNotificationDetails androidNotificationDetails =
        AndroidNotificationDetails(
            'channel_id', 'channel_name',
            channelDescription: 'channel_description',
            importance: Importance.max,
            priority: Priority.high,
            ticker: 'ticker');

    const NotificationDetails platformChannelSpecifics =
        NotificationDetails(android: androidNotificationDetails);

    await flutterLocalNotificationsPlugin.show(
        0,
        'Test Title',
        'Test Body',
        platformChannelSpecifics,
        payload: 'item x');
  }

Step 7: Schedule a Notification

You can also schedule notifications to appear at a specific time.

  Future<void> _scheduleNotification() async {
    const AndroidNotificationDetails androidNotificationDetails =
        AndroidNotificationDetails('channel_id', 'channel_name',
            channelDescription: 'channel_description',
            importance: Importance.max,
            priority: Priority.high,
            ticker: 'ticker');

    const NotificationDetails platformChannelSpecifics =
        NotificationDetails(android: androidNotificationDetails);

    await flutterLocalNotificationsPlugin.schedule(
        1,
        'Scheduled Title',
        'Scheduled Body',
        DateTime.now().add(const Duration(seconds: 5)),
        platformChannelSpecifics,
        payload: 'item x');
  }

Step 8: Handle Notification Taps

To handle when a user taps on a notification, you can use the onDidReceiveNotificationResponse callback during initialization.

  await flutterLocalNotificationsPlugin.initialize(initializationSettings,
      onDidReceiveNotificationResponse: (NotificationResponse response) {
    print('Notification payload: ${response.payload}');
  });

This will print the payload of the notification when it is tapped, allowing you to navigate to the relevant section in your app.

Best Practices

  • Use Meaningful Titles and Body: Notifications should be clear and informative.
  • Handle User Preferences: Allow users to customize notification settings.
  • Respect Notification Frequency: Avoid bombarding users with too many notifications.
  • Test on Both Android and iOS: Ensure notifications work as expected on different platforms.

Conclusion

Implementing local notifications in Flutter using the flutter_local_notifications package enhances your app’s ability to engage users with timely and relevant information. By following this guide, you can seamlessly integrate local notifications into your Flutter application, creating a more interactive and user-friendly experience.