Implementing Local Notifications in Flutter

Local notifications are a great way to engage users even when they are not actively using your Flutter app. They can be used to remind users of upcoming events, provide updates, or encourage them to return to your app. Implementing local notifications in Flutter involves using plugins and writing specific platform-dependent code.

What are Local Notifications?

Local notifications are messages that your app can display to users when the app is not in the foreground. Unlike push notifications, local notifications are scheduled and delivered by the app itself, without needing a server.

Why Use Local Notifications?

  • User Engagement: Keep users informed and engaged with your app.
  • Reminders: Remind users of tasks, events, or appointments.
  • Offline Functionality: Notifications can be scheduled even without an internet connection.

How to Implement Local Notifications in Flutter

Implementing local notifications in Flutter involves using the flutter_local_notifications plugin.

Step 1: Add the flutter_local_notifications Dependency

Add the flutter_local_notifications package to your pubspec.yaml file:

dependencies:
  flutter_local_notifications: ^16.3.0

Then, run flutter pub get to install the dependency.

Step 2: Initialize the Plugin

Initialize the plugin in your main.dart file. This includes setting up the necessary initialization settings for both Android and iOS.

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

final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
    FlutterLocalNotificationsPlugin();

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

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

  const DarwinInitializationSettings initializationSettingsIOS =
      DarwinInitializationSettings(
    requestAlertPermission: true,
    requestBadgePermission: true,
    requestSoundPermission: true,
  );

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

  await flutterLocalNotificationsPlugin.initialize(initializationSettings);

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Local Notifications Example'),
        ),
        body: NotificationScreen(),
      ),
    );
  }
}

Note: Ensure that the app_icon.png (or the name of your app icon) exists in the android/app/src/main/res/drawable directory for Android.

Step 3: Request Permissions (iOS)

For iOS, you need to request permissions to send notifications. You can do this when initializing the plugin.

// Already included in the initialization above
const DarwinInitializationSettings initializationSettingsIOS =
  DarwinInitializationSettings(
    requestAlertPermission: true,
    requestBadgePermission: true,
    requestSoundPermission: true,
);

Step 4: Display a Simple Notification

Create a function to show a simple local notification. You can customize the title, body, and other parameters.

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

class NotificationScreen extends StatelessWidget {
  Future showNotification() async {
    const AndroidNotificationDetails androidNotificationDetails =
        AndroidNotificationDetails(
            'your_channel_id', 'your_channel_name',
            channelDescription: 'your_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);
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: ElevatedButton(
        child: Text('Show Notification'),
        onPressed: showNotification,
      ),
    );
  }
}

Step 5: Schedule a Notification

You can schedule a notification to be displayed at a specific time in the future.

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

class NotificationScreen extends StatefulWidget {
  @override
  _NotificationScreenState createState() => _NotificationScreenState();
}

class _NotificationScreenState extends State {
  @override
  void initState() {
    super.initState();
    _configureLocalTimeZone();
  }

  Future _configureLocalTimeZone() async {
    tz.initializeTimeZones();
    final String? timeZoneName = await FlutterNativeTimezone.getLocalTimezone();
    tz.setLocalLocation(tz.getLocation(timeZoneName!));
  }

  Future scheduleNotification() async {
    await flutterLocalNotificationsPlugin.zonedSchedule(
        0,
        'Scheduled Title',
        'Scheduled Body',
        tz.TZDateTime.now(tz.local).add(const Duration(seconds: 5)),
        const NotificationDetails(
            android: AndroidNotificationDetails(
                'your_channel_id', 'your_channel_name',
                channelDescription: 'your_channel_description')),
        androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
        uiLocalNotificationDateInterpretation:
            UILocalNotificationDateInterpretation.absoluteTime,
        matchDateTimeComponents: DateTimeComponents.time);
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: ElevatedButton(
        child: Text('Schedule Notification'),
        onPressed: scheduleNotification,
      ),
    );
  }
}

In this example:

  • We initialize the timezone using the timezone and flutter_native_timezone packages.
  • The zonedSchedule method schedules a notification for 5 seconds in the future.
  • You also need to add the timezone and flutter_native_timezone dependencies to your pubspec.yaml:
dependencies:
  timezone: ^0.9.2
  flutter_native_timezone: ^2.0.0

Step 6: Handle Notification Taps

To handle when a user taps on a notification, you need to set up a callback function.

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  const AndroidInitializationSettings initializationSettingsAndroid =
      AndroidInitializationSettings('app_icon');

  const DarwinInitializationSettings initializationSettingsIOS =
      DarwinInitializationSettings(
    requestAlertPermission: true,
    requestBadgePermission: true,
    requestSoundPermission: true,
    onDidReceiveLocalNotification:
        (int id, String? title, String? body, String? payload) async {
      // Handle notification tap on iOS
    },
  );

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

  await flutterLocalNotificationsPlugin.initialize(
    initializationSettings,
    onDidReceiveNotificationResponse:
        (NotificationResponse notificationResponse) async {
      // Handle notification tap
      print('Notification payload: ${notificationResponse.payload}');
    },
  );

  runApp(MyApp());
}

You can set a payload when showing the notification to pass custom data to the callback function.

Platform-Specific Setup

Android

  • Ensure you have an icon file (app_icon.png) in the android/app/src/main/res/drawable directory.
  • Add the following meta-data to your AndroidManifest.xml file within the <application> tag:
<meta-data
    android:name="com.google.firebase.messaging.default_notification_icon"
    android:resource="@drawable/app_icon" />

iOS

  • Request permissions for notifications.
  • Ensure that background modes are enabled for remote notifications in the Xcode project settings (if needed).

Conclusion

Local notifications in Flutter can greatly enhance user engagement by providing timely reminders and updates. Using the flutter_local_notifications plugin, you can easily implement and customize notifications for both Android and iOS platforms. Remember to handle permissions, set up appropriate initialization, and manage notification taps to create a seamless user experience.