Flutter Local Notification iOS settings has been changed a lot. Now instead of IOSInitializationSettings, they call it DarwinInitializationSettings.
The below code is plugin greater than 9.1.5
flutter_local_notifications: ^9.1.5
If you version is less than or equal 9.1.5, the older code work, but if its the latest one it won't work. For the latest version use the below code.
Otherwise you will get below error
FlutterLocalNotificationsPlugin().initialize(initializationSettings, onSelectNotification: (String? payload) async. it says the named parameter 'onSelectNotification' isn't defined
The complete code below
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_native_timezone/flutter_native_timezone.dart';
import 'package:http/http.dart' as http;
import 'package:image/image.dart' as image;
import 'package:path_provider/path_provider.dart';
import 'package:rxdart/subjects.dart';
import 'package:timezone/data/latest_all.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
int id = 0;
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
/// Streams are created so that app can respond to notification-related events
/// since the plugin is initialised in the `main` function
final BehaviorSubject<ReceivedNotification> didReceiveLocalNotificationSubject =
BehaviorSubject<ReceivedNotification>();
final BehaviorSubject<String?> selectNotificationSubject =
BehaviorSubject<String?>();
const MethodChannel platform =
MethodChannel('dexterx.dev/flutter_local_notifications_example');
const String portName = 'notification_send_port';
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;
}
String? selectedNotificationPayload;
/// A notification action which triggers a url launch event
const String urlLaunchActionId = 'id_1';
/// A notification action which triggers a App navigation event
const String navigationActionId = 'id_3';
/// Defines a iOS/MacOS notification category for text input actions.
const String darwinNotificationCategoryText = 'textCategory';
/// Defines a iOS/MacOS notification category for plain actions.
const String darwinNotificationCategoryPlain = 'plainCategory';
@pragma('vm:entry-point')
void notificationTapBackground(NotificationResponse notificationResponse) {
// ignore: avoid_print
print('notification(${notificationResponse.id}) action tapped: '
'${notificationResponse.actionId} with'
' payload: ${notificationResponse.payload}');
if (notificationResponse.input?.isNotEmpty ?? false) {
// ignore: avoid_print
print(
'notification action tapped with input: ${notificationResponse.input}');
}
}
Future<void> main() async {
// needed if you intend to initialize in the `main` function
WidgetsFlutterBinding.ensureInitialized();
await _configureLocalTimeZone();
final NotificationAppLaunchDetails? notificationAppLaunchDetails = await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
String initialRoute = HomePage.routeName;
if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
selectedNotificationPayload =
notificationAppLaunchDetails!.notificationResponse?.payload;
initialRoute = SecondPage.routeName;
}
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('app_icon');
/// Note: permissions aren't requested here just to demonstrate that can be
/// done later
final DarwinInitializationSettings initializationSettingsDarwin =
DarwinInitializationSettings(
requestAlertPermission: false,
requestBadgePermission: false,
requestSoundPermission: false,
onDidReceiveLocalNotification:
(int id, String? title, String? body, String? payload) async {
didReceiveLocalNotificationSubject.add(
ReceivedNotification(
id: id,
title: title,
body: body,
payload: payload,
),
);
},
// notificationCategories: darwinNotificationCategories,
);
final InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsDarwin,
);
await flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onDidReceiveNotificationResponse:
(NotificationResponse notificationResponse) {
switch (notificationResponse.notificationResponseType) {
case NotificationResponseType.selectedNotification:
selectNotificationSubject.add(notificationResponse.payload);
break;
case NotificationResponseType.selectedNotificationAction:
if (notificationResponse.actionId == navigationActionId) {
selectNotificationSubject.add(notificationResponse.payload);
}
break;
}
},
onDidReceiveBackgroundNotificationResponse: notificationTapBackground,
);
runApp(
MaterialApp(
initialRoute: initialRoute,
routes: <String, WidgetBuilder>{
HomePage.routeName: (_) => HomePage(notificationAppLaunchDetails),
SecondPage.routeName: (_) => SecondPage(selectedNotificationPayload)
},
),
);
}
Future<void> _configureLocalTimeZone() async {
if (kIsWeb || Platform.isLinux) {
return;
}
tz.initializeTimeZones();
final String? timeZoneName = await FlutterNativeTimezone.getLocalTimezone();
tz.setLocalLocation(tz.getLocation(timeZoneName!));
}
class PaddedElevatedButton extends StatelessWidget {
const PaddedElevatedButton({
required this.buttonText,
required this.onPressed,
Key? key,
}) : super(key: key);
final String buttonText;
final VoidCallback onPressed;
@override
Widget build(BuildContext context) => Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 8),
child: ElevatedButton(
onPressed: onPressed,
child: Text(buttonText),
),
);
}
class HomePage extends StatefulWidget {
const HomePage(
this.notificationAppLaunchDetails, {
Key? key,
}) : super(key: key);
static const String routeName = '/';
final NotificationAppLaunchDetails? notificationAppLaunchDetails;
bool get didNotificationLaunchApp =>
notificationAppLaunchDetails?.didNotificationLaunchApp ?? false;
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool _notificationsEnabled = false;
@override
void initState() {
super.initState();
_isAndroidPermissionGranted();
_requestPermissions();
_configureDidReceiveLocalNotificationSubject();
_configureSelectNotificationSubject();
}
Future<void> _isAndroidPermissionGranted() async {
if (Platform.isAndroid) {
final bool granted = await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.areNotificationsEnabled() ??
false;
setState(() {
_notificationsEnabled = granted;
});
}
}
Future<void> _requestPermissions() async {
if (Platform.isIOS || Platform.isMacOS) {
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: true,
badge: true,
sound: true,
critical: true,
);
} else if (Platform.isAndroid) {
final AndroidFlutterLocalNotificationsPlugin? androidImplementation =
flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>();
final bool? granted = await androidImplementation?.requestPermission();
setState(() {
_notificationsEnabled = granted ?? false;
});
}
}
void _configureDidReceiveLocalNotificationSubject() {
didReceiveLocalNotificationSubject.stream
.listen((ReceivedNotification receivedNotification) async {
await showDialog(
context: context,
builder: (BuildContext context) => CupertinoAlertDialog(
title: receivedNotification.title != null
? Text(receivedNotification.title!)
: null,
content: receivedNotification.body != null
? Text(receivedNotification.body!)
: null,
actions: <Widget>[
CupertinoDialogAction(
isDefaultAction: true,
onPressed: () async {
Navigator.of(context, rootNavigator: true).pop();
await Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context) =>
SecondPage(receivedNotification.payload),
),
);
},
child: const Text('Ok'),
)
],
),
);
});
}
void _configureSelectNotificationSubject() {
selectNotificationSubject.stream.listen((String? payload) async {
await Navigator.of(context).push(MaterialPageRoute<void>(
builder: (BuildContext context) => SecondPage(payload),
));
});
}
@override
void dispose() {
didReceiveLocalNotificationSubject.close();
selectNotificationSubject.close();
super.dispose();
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8),
child: Center(
child: Column(
children: <Widget>[
if (widget.didNotificationLaunchApp) ...<Widget>[
const Text('Launch notification details'),
_InfoValueString(
title: 'Notification id',
value: widget.notificationAppLaunchDetails!
.notificationResponse?.id),
_InfoValueString(
title: 'Action id',
value: widget.notificationAppLaunchDetails!
.notificationResponse?.actionId),
_InfoValueString(
title: 'Input',
value: widget.notificationAppLaunchDetails!
.notificationResponse?.input),
_InfoValueString(
title: 'Payload:',
value: widget.notificationAppLaunchDetails!
.notificationResponse?.payload,
),
],
if (kIsWeb || !Platform.isLinux) ...<Widget>[
PaddedElevatedButton(
buttonText:
'Schedule notification to appear in 5 seconds '
'based on local time zone',
onPressed: () async {
await _zonedScheduleNotification();
},
),
PaddedElevatedButton(
buttonText: 'Repeat notification every minute',
onPressed: () async {
await _repeatNotification();
},
),
PaddedElevatedButton(
buttonText: 'Check pending notifications',
onPressed: () async {
await _checkPendingNotificationRequests();
},
),
],
PaddedElevatedButton(
buttonText:
'Schedule monthly Monday 10:00:00 am notification in '
'your local time zone',
onPressed: () async {
await _scheduleMonthlyMondayTenAMNotification();
},
),
PaddedElevatedButton(
buttonText:
'Schedule yearly Monday 10:00:00 am notification in '
'your local time zone',
onPressed: () async {
await _scheduleYearlyMondayTenAMNotification();
},
),
PaddedElevatedButton(
buttonText: 'Cancel latest notification',
onPressed: () async {
await _cancelNotification();
},
),
PaddedElevatedButton(
buttonText: 'Cancel all notifications',
onPressed: () async {
await _cancelAllNotifications();
},
),
const Divider(),
if (Platform.isAndroid) ...<Widget>[
const Text(
'Android-specific examples',
style: TextStyle(fontWeight: FontWeight.bold),
),
Text('notifications enabled: $_notificationsEnabled'),
PaddedElevatedButton(
buttonText:
'Show notification that times out after 3 seconds',
onPressed: () async {
await _showTimeoutNotification();
},
),
PaddedElevatedButton(
buttonText: 'Show insistent notification',
onPressed: () async {
await _showInsistentNotification();
},
),
],
],
),
),
),
),
);
Future<void> _cancelNotification() async {
await flutterLocalNotificationsPlugin.cancel(--id);
}
Future<void>
_zonedScheduleNotification() async {
await flutterLocalNotificationsPlugin.zonedSchedule(
0,
'scheduled title',
'scheduled body',
tz.TZDateTime.now(tz.local).add(const Duration(seconds: 2)),
const NotificationDetails(
android: AndroidNotificationDetails(
'your channel id', 'your channel name',
channelDescription: 'your channel description')),
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime);
}
Future<void> _showTimeoutNotification() async {
const AndroidNotificationDetails androidNotificationDetails =
AndroidNotificationDetails('silent channel id', 'silent channel name',
channelDescription: 'silent channel description',
timeoutAfter: 3000,
styleInformation: DefaultStyleInformation(true, true));
const NotificationDetails notificationDetails =
NotificationDetails(android: androidNotificationDetails);
await flutterLocalNotificationsPlugin.show(id++, 'timeout notification',
'Times out after 3 seconds', notificationDetails);
}
Future<void> _showInsistentNotification() async {
// This value is from: https://developer.android.com/reference/android/app/Notification.html#FLAG_INSISTENT
const int insistentFlag = 4;
final AndroidNotificationDetails androidNotificationDetails =
AndroidNotificationDetails('your channel id', 'your channel name',
channelDescription: 'your channel description',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker',
additionalFlags: Int32List.fromList(<int>[insistentFlag]));
final NotificationDetails notificationDetails =
NotificationDetails(android: androidNotificationDetails);
await flutterLocalNotificationsPlugin.show(
id++, 'insistent title', 'insistent body', notificationDetails,
payload: 'item x');
}
Future<void> _checkPendingNotificationRequests() async {
final List<PendingNotificationRequest> pendingNotificationRequests =
await flutterLocalNotificationsPlugin.pendingNotificationRequests();
return showDialog<void>(
context: context,
builder: (BuildContext context) => AlertDialog(
content:
Text('${pendingNotificationRequests.length} pending notification '
'requests'),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('OK'),
),
],
),
);
}
Future<void> _cancelAllNotifications() async {
await flutterLocalNotificationsPlugin.cancelAll();
}
Future<void> _repeatNotification() async {
const AndroidNotificationDetails androidNotificationDetails =
AndroidNotificationDetails(
'repeating channel id', 'repeating channel name',
channelDescription: 'repeating description');
const NotificationDetails notificationDetails =
NotificationDetails(android: androidNotificationDetails);
await flutterLocalNotificationsPlugin.periodicallyShow(
id++,
'repeating title',
'repeating body',
RepeatInterval.everyMinute,
notificationDetails,
androidAllowWhileIdle: true);
}
Future<void> _scheduleMonthlyMondayTenAMNotification() async {
await flutterLocalNotificationsPlugin.zonedSchedule(
0,
'monthly scheduled notification title',
'monthly scheduled notification body',
_nextInstanceOfMondayTenAM(),
const NotificationDetails(
android: AndroidNotificationDetails('monthly notification channel id',
'monthly notification channel name',
channelDescription: 'monthly notificationdescription'),
),
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
matchDateTimeComponents: DateTimeComponents.dayOfMonthAndTime);
}
Future<void> _scheduleYearlyMondayTenAMNotification() async {
await flutterLocalNotificationsPlugin.zonedSchedule(
0,
'yearly scheduled notification title',
'yearly scheduled notification body',
_nextInstanceOfMondayTenAM(),
const NotificationDetails(
android: AndroidNotificationDetails('yearly notification channel id',
'yearly notification channel name',
channelDescription: 'yearly notification description'),
),
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
matchDateTimeComponents: DateTimeComponents.dateAndTime);
}
tz.TZDateTime _nextInstanceOfTenAM() {
final tz.TZDateTime now = tz.TZDateTime.now(tz.local);
tz.TZDateTime scheduledDate =
tz.TZDateTime(tz.local, now.year, now.month, now.day, 10);
if (scheduledDate.isBefore(now)) {
scheduledDate = scheduledDate.add(const Duration(days: 1));
}
return scheduledDate;
}
tz.TZDateTime _nextInstanceOfMondayTenAM() {
tz.TZDateTime scheduledDate = _nextInstanceOfTenAM();
while (scheduledDate.weekday != DateTime.monday) {
scheduledDate = scheduledDate.add(const Duration(days: 1));
}
return scheduledDate;
}
}
class SecondPage extends StatefulWidget {
const SecondPage(
this.payload, {
Key? key,
}) : super(key: key);
static const String routeName = '/secondPage';
final String? payload;
@override
State<StatefulWidget> createState() => SecondPageState();
}
class SecondPageState extends State<SecondPage> {
String? _payload;
@override
void initState() {
super.initState();
_payload = widget.payload;
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: const Text('Second Screen'),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('payload ${_payload ?? ''}'),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Go back!'),
),
],
),
),
);
}
class _InfoValueString extends StatelessWidget {
const _InfoValueString({
required this.title,
required this.value,
Key? key,
}) : super(key: key);
final String title;
final Object? value;
@override
Widget build(BuildContext context) => Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 8),
child: Text.rich(
TextSpan(
children: <InlineSpan>[
TextSpan(
text: '$title ',
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
TextSpan(
text: '$value',
)
],
),
),
);
}
If you get error onSelectNotifcation does not work then downgrade your FlutterLocalNotification version. Do it around 9.1.15