Local notifications are a great way to engage users with timely and relevant information without requiring an internet connection. In Flutter, you can implement local notifications easily using packages like flutter_local_notifications. This article provides a comprehensive guide to implementing local notifications for reminders and alerts in Flutter.
What are Local Notifications?
Local notifications are messages that pop up on a user’s device without needing a server or internet connection. These notifications can be scheduled to appear at specific times, making them ideal for reminders, alerts, and other time-sensitive information.
Why Use Local Notifications?
- Engage Users: Provide timely reminders and alerts.
- Offline Functionality: Work without an internet connection.
- Improved User Experience: Deliver relevant information at the right time.
Prerequisites
Before you start, make sure you have Flutter installed and set up correctly. You also need to add the flutter_local_notifications package to your pubspec.yaml file.
Step 1: Add Dependencies
Add the flutter_local_notifications and rxdart packages to your pubspec.yaml file:
dependencies:
flutter:
sdk: flutter
flutter_local_notifications: ^16.3.2
rxdart: ^0.27.5
Then, run flutter pub get to install the dependencies.
Step 2: Set Up FlutterLocalNotificationsPlugin
In your main.dart file, initialize the FlutterLocalNotificationsPlugin:
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:rxdart/rxdart.dart';
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
final BehaviorSubject<ReceivedNotification> didReceiveLocalNotificationSubject =
BehaviorSubject<ReceivedNotification>();
final BehaviorSubject<String?> selectNotificationSubject =
BehaviorSubject<String?>();
class ReceivedNotification {
ReceivedNotification({
required this.id,
required this.title,
required this.body,
required this.payload,
});
final int id;
final String? title;
final String? body;
final String? payload;
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('app_icon');
final InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
);
await flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onDidReceiveNotificationResponse: (NotificationResponse notificationResponse) {
selectNotificationSubject.add(notificationResponse.payload);
},
onDidReceiveBackgroundNotificationResponse: notificationTapBackground,
);
runApp(MyApp());
}
@pragma('vm:entry-point')
void notificationTapBackground(NotificationResponse notificationResponse) {
// handle action
print('notification(${notificationResponse.id}) action tapped: '
'${notificationResponse.actionId} with'
' payload: ${notificationResponse.payload}');
if (notificationResponse.input?.isNotEmpty ?? false) {
print(
' notification action tapped with input: ${notificationResponse.input}');
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
void initState() {
super.initState();
selectNotificationSubject.stream.listen((String? payload) {
if (payload != null) {
print('Notification payload: $payload');
}
});
}
@override
void dispose() {
didReceiveLocalNotificationSubject.close();
selectNotificationSubject.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Local Notifications Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () {
_showNotification();
},
child: Text('Show Notification'),
),
ElevatedButton(
onPressed: () {
_scheduleNotification();
},
child: Text('Schedule Notification'),
),
],
),
),
);
}
Future<void> _showNotification() async {
const AndroidNotificationDetails androidNotificationDetails =
AndroidNotificationDetails(
'channelId',
'channelName',
channelDescription: 'channelDescription',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker');
const NotificationDetails notificationDetails = NotificationDetails(
android: androidNotificationDetails,
);
await flutterLocalNotificationsPlugin.show(
0,
'Test Title',
'Test Body',
notificationDetails,
payload: 'item x',
);
}
Future<void> _scheduleNotification() async {
final DateTime now = DateTime.now();
final DateTime scheduledDate = now.add(Duration(seconds: 5));
const AndroidNotificationDetails androidNotificationDetails =
AndroidNotificationDetails(
'scheduledChannelId',
'scheduledChannelName',
channelDescription: 'scheduledChannelDescription',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker',
);
const NotificationDetails notificationDetails = NotificationDetails(
android: androidNotificationDetails,
);
await flutterLocalNotificationsPlugin.schedule(
1,
'Scheduled Title',
'Scheduled Body',
scheduledDate,
notificationDetails,
payload: 'item y',
androidAllowWhileIdle: true,
);
}
}
Replace 'app_icon' with the name of your app icon (without extension) that you’ve placed in the android/app/src/main/res/drawable folder.
Step 3: Display a Simple Notification
Add the following method to the _MyHomePageState class to display a simple notification:
Future<void> _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 notificationDetails = NotificationDetails(
android: androidNotificationDetails,
);
await flutterLocalNotificationsPlugin.show(
0,
'Plain Title',
'Plain Body',
notificationDetails,
payload: 'item x',
);
}
Now, call this method from a button to show the notification:
ElevatedButton(
onPressed: () {
_showNotification();
},
child: Text('Show Notification'),
),
Step 4: Schedule a Notification
To schedule a notification, add the following method to the _MyHomePageState class:
Future<void> _scheduleNotification() async {
final DateTime now = DateTime.now();
final DateTime scheduledDate = now.add(Duration(seconds: 5));
const AndroidNotificationDetails androidNotificationDetails =
AndroidNotificationDetails(
'channelId',
'Channel Name',
channelDescription: 'Channel Description',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker',
);
const NotificationDetails notificationDetails = NotificationDetails(
android: androidNotificationDetails,
);
await flutterLocalNotificationsPlugin.schedule(
1,
'Scheduled Title',
'Scheduled Body',
scheduledDate,
notificationDetails,
payload: 'item y',
androidAllowWhileIdle: true,
);
}
Add a button to trigger the scheduled notification:
ElevatedButton(
onPressed: () {
_scheduleNotification();
},
child: Text('Schedule Notification'),
),
This will schedule a notification to appear 5 seconds after the button is pressed.
Handling Notification Interactions
To handle when a user taps on a notification, you can use the selectNotificationSubject. Add the following code in your initState method:
@override
void initState() {
super.initState();
selectNotificationSubject.stream.listen((String? payload) {
if (payload != null) {
print('Notification payload: $payload');
// Add navigation logic here
}
});
}
You can navigate to a specific screen or perform any other action based on the payload.
Customizing Notifications
The flutter_local_notifications package provides many options for customizing your notifications. Here are a few examples:
Setting Notification Icons
To set a custom icon for your notification, make sure the icon file (e.g., app_icon.png) is placed in the android/app/src/main/res/drawable folder. Then, reference it in the AndroidInitializationSettings:
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('app_icon');
Adding Actions to Notifications
You can add actions to your notifications, such as buttons to perform specific tasks:
const AndroidNotificationDetails androidNotificationDetails =
AndroidNotificationDetails(
'channelId',
'Channel Name',
channelDescription: 'Channel Description',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker',
actions: <AndroidNotificationAction>[
AndroidNotificationAction('id_1', 'Action 1'),
AndroidNotificationAction('id_2', 'Action 2'),
],
);
Error Handling and Troubleshooting
If you encounter any issues with local notifications, check the following:
- Permissions: Ensure the necessary permissions are granted (especially on Android 13 and above).
- Initialization: Make sure the
FlutterLocalNotificationsPluginis initialized correctly. - Channel IDs: Use unique and descriptive channel IDs.
- Android Icon: Verify that the app icon is correctly placed and referenced.
Complete Example
Here is the complete code for implementing local notifications in Flutter:
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:rxdart/rxdart.dart';
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
final BehaviorSubject<ReceivedNotification> didReceiveLocalNotificationSubject =
BehaviorSubject<ReceivedNotification>();
final BehaviorSubject<String?> selectNotificationSubject =
BehaviorSubject<String?>();
class ReceivedNotification {
ReceivedNotification({
required this.id,
required this.title,
required this.body,
required this.payload,
});
final int id;
final String? title;
final String? body;
final String? payload;
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('app_icon');
final InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
);
await flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onDidReceiveNotificationResponse: (NotificationResponse notificationResponse) {
selectNotificationSubject.add(notificationResponse.payload);
},
onDidReceiveBackgroundNotificationResponse: notificationTapBackground,
);
runApp(MyApp());
}
@pragma('vm:entry-point')
void notificationTapBackground(NotificationResponse notificationResponse) {
// handle action
print('notification(${notificationResponse.id}) action tapped: '
'${notificationResponse.actionId} with'
' payload: ${notificationResponse.payload}');
if (notificationResponse.input?.isNotEmpty ?? false) {
print(
' notification action tapped with input: ${notificationResponse.input}');
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
void initState() {
super.initState();
selectNotificationSubject.stream.listen((String? payload) {
if (payload != null) {
print('Notification payload: $payload');
// Add navigation logic here
}
});
}
@override
void dispose() {
didReceiveLocalNotificationSubject.close();
selectNotificationSubject.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Local Notifications Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () {
_showNotification();
},
child: Text('Show Notification'),
),
ElevatedButton(
onPressed: () {
_scheduleNotification();
},
child: Text('Schedule Notification'),
),
],
),
),
);
}
Future<void> _showNotification() async {
const AndroidNotificationDetails androidNotificationDetails =
AndroidNotificationDetails(
'channelId',
'channelName',
channelDescription: 'channelDescription',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker');
const NotificationDetails notificationDetails = NotificationDetails(
android: androidNotificationDetails,
);
await flutterLocalNotificationsPlugin.show(
0,
'Test Title',
'Test Body',
notificationDetails,
payload: 'item x',
);
}
Future<void> _scheduleNotification() async {
final DateTime now = DateTime.now();
final DateTime scheduledDate = now.add(Duration(seconds: 5));
const AndroidNotificationDetails androidNotificationDetails =
AndroidNotificationDetails(
'scheduledChannelId',
'scheduledChannelName',
channelDescription: 'scheduledChannelDescription',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker',
);
const NotificationDetails notificationDetails = NotificationDetails(
android: androidNotificationDetails,
);
await flutterLocalNotificationsPlugin.schedule(
1,
'Scheduled Title',
'Scheduled Body',
scheduledDate,
notificationDetails,
payload: 'item y',
androidAllowWhileIdle: true,
);
}
}
Conclusion
Local notifications are an effective way to enhance user engagement in Flutter applications. By using the flutter_local_notifications package, you can easily implement and customize notifications for various purposes, such as reminders and alerts. Make sure to handle notification interactions properly to provide a seamless user experience. By following this guide, you can create compelling and useful local notifications in your Flutter apps.