Contents
Outline
In this blog post, I will introduce how to use flutter_local_notifications to send push notifications at a specific date time from the user’s own device on Flutter.
- flutter_local_notifications: https://pub.dev/packages/flutter_local_notifications
You can see full source code of this blog post on the link below.
- GitHub: https://github.com/dev-yakuza/study-flutter/tree/main/packages/flutter_local_notifications_example
Create Flutter project
To check how to use flutter_local_notifications on Flutter, execute the following command to create a new Flutter project.
flutter create flutter_local_notifications_example
Install flutter_local_notifications
To use flutter_local_notifications
, we need to execute the following command to install flutter_local_notifications
and additional required packages.
flutter pub add flutter_local_notifications flutter_native_timezone flutter_app_badger
- flutter_native_timezone: Pacakge to show the message at the specific date time.
- flutter_app_badger: Pacakge to initialize the app icon badge.
Next, let’s see how to use flutter_local_notifications.
flutter_local_notifications Android configuration
To use flutter_local_notifications
on Android, open the android/app/build.gradle
file and modify it like the following.
...
android {
compileSdkVersion 33
compileOptions {
...
coreLibraryDesugaringEnabled true
}
defaultConfig {
...
multiDexEnabled true
}
...
}
dependencies {
...
implementation "androidx.multidex:multidex:2.0.1"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
}
Example code
To see how to use flutter_local_notifications
, open the ./lib/main.dart
file and modify it like the below.
import 'package:flutter/material.dart';
import 'package:flutter_app_badger/flutter_app_badger.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_native_timezone/flutter_native_timezone.dart';
import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
@override
void initState() {
super.initState();
WidgetsBinding.instance!.addObserver(this);
_init();
}
@override
void dispose() {
WidgetsBinding.instance!.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
FlutterAppBadger.removeBadge();
}
}
Future<void> _init() async {
await _configureLocalTimeZone();
await _initializeNotification();
}
Future<void> _configureLocalTimeZone() async {
tz.initializeTimeZones();
final String? timeZoneName = await FlutterNativeTimezone.getLocalTimezone();
tz.setLocalLocation(tz.getLocation(timeZoneName!));
}
Future<void> _initializeNotification() async {
const DarwinInitializationSettings initializationSettingsIOS =
DarwinInitializationSettings(
requestAlertPermission: false,
requestBadgePermission: false,
requestSoundPermission: false,
);
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('ic_notification');
const InitializationSettings initializationSettings =
InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
);
await _flutterLocalNotificationsPlugin.initialize(initializationSettings);
}
Future<void> _cancelNotification() async {
await _flutterLocalNotificationsPlugin.cancelAll();
}
Future<void> _requestPermissions() async {
await _flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: true,
badge: true,
sound: true,
);
}
Future<void> _registerMessage({
required int hour,
required int minutes,
required message,
}) async {
final tz.TZDateTime now = tz.TZDateTime.now(tz.local);
tz.TZDateTime scheduledDate = tz.TZDateTime(
tz.local,
now.year,
now.month,
now.day,
hour,
minutes,
);
await _flutterLocalNotificationsPlugin.zonedSchedule(
0,
'flutter_local_notifications',
message,
scheduledDate,
NotificationDetails(
android: AndroidNotificationDetails(
'channel id',
'channel name',
importance: Importance.max,
priority: Priority.high,
ongoing: true,
styleInformation: BigTextStyleInformation(message),
icon: 'ic_notification',
),
iOS: const DarwinNotificationDetails(
badgeNumber: 1,
),
),
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
matchDateTimeComponents: DateTimeComponents.time,
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Local Notifications'),
),
body: Center(
child: ElevatedButton(
onPressed: () async {
await _cancelNotification();
await _requestPermissions();
final tz.TZDateTime now = tz.TZDateTime.now(tz.local);
await _registerMessage(
hour: now.hour,
minutes: now.minute + 1,
message: 'Hello, world!',
);
},
child: const Text('Show Notification'),
),
),
);
}
}
Analyze code
To check how to use flutter_local_notifications
, let’s see the example code one by one.
Initialize badge
The flutter_local_notifications
package doens’ provide the app icon badge initialization code. So, we need to use the FlutterAppBader
package to initialize the app icon badge when the app becomes the Foreground
state.
...
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
...
@override
void initState() {
super.initState();
WidgetsBinding.instance!.addObserver(this);
...
}
@override
void dispose() {
WidgetsBinding.instance!.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
FlutterAppBadger.removeBadge();
}
}
...
}
Initialize flutter_local_notification
To use flutter_local_notification
to send messages at the specific date time, we need to initialize the flutter_local_notifications
package like the below.
...
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
@override
void initState() {
super.initState();
...
_init();
}
...
Future<void> _init() async {
await _configureLocalTimeZone();
await _initializeNotification();
}
Future<void> _configureLocalTimeZone() async {
tz.initializeTimeZones();
final String? timeZoneName = await FlutterNativeTimezone.getLocalTimezone();
tz.setLocalLocation(tz.getLocation(timeZoneName!));
}
Future<void> _initializeNotification() async {
const DarwinInitializationSettings initializationSettingsIOS =
DarwinInitializationSettings(
requestAlertPermission: false,
requestBadgePermission: false,
requestSoundPermission: false,
);
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('ic_notification');
const InitializationSettings initializationSettings =
InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
);
await _flutterLocalNotificationsPlugin.initialize(initializationSettings);
}
...
}
We need to use the following code to register the current device time.
...
Future<void> _configureLocalTimeZone() async {
tz.initializeTimeZones();
final String? timeZoneName = await FlutterNativeTimezone.getLocalTimezone();
tz.setLocalLocation(tz.getLocation(timeZoneName!));
}
...
Also, we need to initialize iOS
with the message permission request like the below. When iOS
is initialized, not to show the permmission request alert, I set false
to all options.
For Android
, I set ic_notification
to the app push icon. The icon should be copied to the ./android/app/src/main/res/drawable*
folders.
...
Future<void> _initializeNotification() async {
const DarwinInitializationSettings initializationSettingsIOS =
DarwinInitializationSettings(
requestAlertPermission: false,
requestBadgePermission: false,
requestSoundPermission: false,
);
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('ic_notification');
const InitializationSettings initializationSettings =
InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
);
await _flutterLocalNotificationsPlugin.initialize(initializationSettings);
}
Cancel registered messages
When a new message is registered, I just cancel all messages that are registered before by using the cancelAll()
method.
...
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
...
Future<void> _cancelNotification() async {
await _flutterLocalNotificationsPlugin.cancelAll();
}
...
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Local Notifications'),
),
body: Center(
child: ElevatedButton(
onPressed: () async {
await _cancelNotification();
...
},
child: const Text('Show Notification'),
),
),
);
}
}
Request permission
Before a message is registered, we need to get a permission about sending message from the user on iOS
. The following code doesn’t request again when the user decides the permission.
...
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
...
Future<void> _requestPermissions() async {
await _flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: true,
badge: true,
sound: true,
);
}
...
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Local Notifications'),
),
body: Center(
child: ElevatedButton(
onPressed: () async {
...
await _requestPermissions();
...
},
child: const Text('Show Notification'),
),
),
);
}
}
Register message
Lastly, I register a message to be shown at one minute later than the current time. This message will be sent at the same time of every date.
...
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
...
Future<void> _registerMessage({
required int hour,
required int minutes,
required message,
}) async {
final tz.TZDateTime now = tz.TZDateTime.now(tz.local);
tz.TZDateTime scheduledDate = tz.TZDateTime(
tz.local,
now.year,
now.month,
now.day,
hour,
minutes,
);
await _flutterLocalNotificationsPlugin.zonedSchedule(
0,
'flutter_local_notifications',
message,
scheduledDate,
NotificationDetails(
android: AndroidNotificationDetails(
'channel id',
'channel name',
importance: Importance.max,
priority: Priority.high,
ongoing: true,
styleInformation: BigTextStyleInformation(message),
icon: 'ic_notification',
),
iOS: const DarwinNotificationDetails(
badgeNumber: 1,
),
),
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
matchDateTimeComponents: DateTimeComponents.time,
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Local Notifications'),
),
body: Center(
child: ElevatedButton(
onPressed: () async {
...
final tz.TZDateTime now = tz.TZDateTime.now(tz.local);
await _registerMessage(
hour: now.hour,
minutes: now.minute + 1,
message: 'Hello, world!',
);
},
child: const Text('Show Notification'),
),
),
);
}
}
If you set the same ID on zonedSchedule
, the duplicate message won’t be shown when the same ID message is shown currently.
...
await _flutterLocalNotificationsPlugin.zonedSchedule(
0,
...
);
...
If you set true
to ongoing
on AndroidNotificationDetails
, the message can be disappeared when the app is in the foreground.
await _flutterLocalNotificationsPlugin.zonedSchedule(
...
NotificationDetails(
android: AndroidNotificationDetails(
...
ongoing: true,
...
),
...
),
...
);
Check
Let’s see flutter_local_notifications
is working well. When you execute the simulater, you can see the screen like the below.
Now, when you press the Show Notification
button, you can see the permission request dialog like the below.
Press Allow
in here to receive notifications. After then, make the app to be in Background
and wait 1 minute. You can see the message like the below.
And then, when you start the app again, you can see the app icon badge is disappeared well.
Completed
Done! we’ve seen how to use flutter_local_notifications
to show a messsage at the specific time from the user’s own device. Now, try this to develop an app to send notifications reqularly!
Was my blog helpful? Please leave a comment at the bottom. it will be a great help to me!
App promotion
Deku
.Deku
created the applications with Flutter.If you have interested, please try to download them for free.