2023-06-21 17:44:28 +08:00
|
|
|
import 'dart:async';
|
2023-09-09 16:48:47 +08:00
|
|
|
import 'dart:io';
|
2023-06-21 17:44:28 +08:00
|
|
|
|
2023-09-09 16:48:47 +08:00
|
|
|
import 'package:flutter/cupertino.dart';
|
|
|
|
import 'package:flutter/foundation.dart';
|
2023-06-21 17:44:28 +08:00
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
2023-09-09 16:48:47 +08:00
|
|
|
import 'package:flutter_timezone/flutter_timezone.dart';
|
2023-06-21 17:44:28 +08:00
|
|
|
|
2023-09-09 16:48:47 +08:00
|
|
|
import 'package:timezone/data/latest_all.dart' as tz;
|
|
|
|
import 'package:timezone/timezone.dart' as tz;
|
|
|
|
|
|
|
|
/// Before release build of app, read [Release build configuration]
|
|
|
|
/// https://pub-web.flutter-io.cn/packages/flutter_local_notifications
|
|
|
|
|
|
|
|
int id = 0;
|
|
|
|
|
|
|
|
/// Init notification plugin
|
2023-06-21 17:44:28 +08:00
|
|
|
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
|
2023-09-09 16:48:47 +08:00
|
|
|
/// Only be used in IOS?
|
|
|
|
final StreamController<ReceivedNotification?> didReceiveNotificationStream =
|
2023-06-21 17:44:28 +08:00
|
|
|
StreamController<ReceivedNotification>.broadcast();
|
|
|
|
|
|
|
|
final StreamController<String?> selectNotificationStream =
|
|
|
|
StreamController<String?>.broadcast();
|
|
|
|
|
|
|
|
const MethodChannel platform =
|
|
|
|
MethodChannel('dexterx.dev/flutter_local_notifications_example');
|
|
|
|
|
|
|
|
const String portName = 'notification_send_port';
|
|
|
|
|
2023-09-09 16:48:47 +08:00
|
|
|
/// Here, the first argument is the id of notification and is common to all
|
|
|
|
/// methods that would result in a notification being shown. This is typically
|
|
|
|
/// set a unique value per notification as using the same id multiple times
|
|
|
|
/// would result in a notification being updated/overwritten.
|
2023-06-21 17:44:28 +08:00
|
|
|
class ReceivedNotification {
|
|
|
|
final int id;
|
|
|
|
final String? title;
|
|
|
|
final String? body;
|
|
|
|
final String? payload;
|
|
|
|
|
|
|
|
const ReceivedNotification({
|
|
|
|
required this.id,
|
|
|
|
required this.title,
|
|
|
|
required this.body,
|
|
|
|
required this.payload,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
String? selectedNotificationPayload;
|
|
|
|
|
2023-09-09 16:48:47 +08:00
|
|
|
late final NotificationAppLaunchDetails? notificationAppLaunchDetails;
|
|
|
|
|
2023-06-21 17:44:28 +08:00
|
|
|
/// A notification action which triggers a url launch event
|
|
|
|
const String urlLanunchActionId = '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';
|
|
|
|
|
2023-09-09 16:48:47 +08:00
|
|
|
/// Defines a iOS/MacOS notification category for plain actions.
|
2023-06-21 17:44:28 +08:00
|
|
|
const String darwinNotificationCategoryPlain = 'plainCategory';
|
|
|
|
|
|
|
|
@pragma('vm:entry-point')
|
|
|
|
void notificationTapBackground(NotificationResponse notificationResponse) {
|
|
|
|
print('notification(${notificationResponse.id}) action tapped: '
|
|
|
|
'${notificationResponse.actionId} with'
|
|
|
|
' payload: ${notificationResponse.payload}');
|
|
|
|
|
|
|
|
if (notificationResponse.input?.isNotEmpty ?? false) {
|
2023-09-09 16:48:47 +08:00
|
|
|
print(
|
|
|
|
'notification action tapped with input: ${notificationResponse.input}');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Scheduling notifications now requires developers to specify a date and
|
|
|
|
/// time relative to a specific time zone.
|
|
|
|
/// This is to solve issues with daylight saving time that existed in the
|
|
|
|
/// schedule method that is now deprecated.
|
|
|
|
Future<void> _configureLocalTimeZone() async {
|
|
|
|
if (kIsWeb || Platform.isLinux) {
|
|
|
|
return;
|
2023-06-21 17:44:28 +08:00
|
|
|
}
|
2023-09-09 16:48:47 +08:00
|
|
|
|
|
|
|
tz.initializeTimeZones();
|
|
|
|
final String timeZoneName = await FlutterTimezone.getLocalTimezone();
|
|
|
|
tz.setLocalLocation(tz.getLocation(timeZoneName));
|
2023-06-21 17:44:28 +08:00
|
|
|
}
|
|
|
|
|
2023-09-09 16:48:47 +08:00
|
|
|
Future<void> initNotification() async {
|
|
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
|
|
|
|
|
|
await _configureLocalTimeZone();
|
|
|
|
|
|
|
|
// not null if platform is Linux
|
|
|
|
notificationAppLaunchDetails = !kIsWeb && Platform.isLinux
|
|
|
|
? null
|
|
|
|
// Use the getNotificationAppLaunchDetails method when the app starts
|
|
|
|
// if you need to handle when a notification triggering the launch for
|
|
|
|
// an app e.g. change the home route of the app for deep-linking.
|
|
|
|
: await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
|
|
|
|
|
|
|
|
String initRouteName = 'Home';
|
2023-06-21 17:44:28 +08:00
|
|
|
|
2023-09-09 16:48:47 +08:00
|
|
|
if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
|
|
|
|
// If the app was launched via notification
|
|
|
|
// (e.g. taps a notification out of the app).
|
|
|
|
|
|
|
|
// route change depends on what type message is. Such as receiving a chat
|
|
|
|
// message, this route should change to message screen, and receiving a apply
|
|
|
|
// request shuold change to apply screen
|
|
|
|
initRouteName = 'Message'; // TODO: Set the route according to message type
|
|
|
|
}
|
|
|
|
|
|
|
|
const AndroidInitializationSettings androidInitializationSettings =
|
|
|
|
AndroidInitializationSettings('app_icon');
|
|
|
|
|
|
|
|
final List<DarwinNotificationCategory> darwinNotificationCategories =
|
|
|
|
<DarwinNotificationCategory>[
|
|
|
|
DarwinNotificationCategory(
|
|
|
|
darwinNotificationCategoryText,
|
|
|
|
actions: <DarwinNotificationAction>[
|
|
|
|
DarwinNotificationAction.text(
|
|
|
|
'text_1',
|
|
|
|
'action 1',
|
|
|
|
buttonTitle: 'send',
|
|
|
|
placeholder: 'Placeholder',
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
DarwinNotificationCategory(
|
|
|
|
darwinNotificationCategoryPlain,
|
|
|
|
actions: <DarwinNotificationAction>[
|
|
|
|
DarwinNotificationAction.plain('id_1', 'Action 1'),
|
|
|
|
DarwinNotificationAction.plain(
|
|
|
|
'id_2',
|
|
|
|
'Action 2 (destructive)',
|
|
|
|
options: <DarwinNotificationActionOption>{
|
|
|
|
DarwinNotificationActionOption.destructive,
|
|
|
|
},
|
|
|
|
),
|
|
|
|
DarwinNotificationAction.plain(
|
|
|
|
navigationActionId,
|
|
|
|
'Action 3 (foreground)',
|
|
|
|
options: <DarwinNotificationActionOption>{
|
|
|
|
DarwinNotificationActionOption.foreground,
|
|
|
|
},
|
|
|
|
),
|
|
|
|
DarwinNotificationAction.plain(
|
|
|
|
'id_4',
|
|
|
|
'Action 4 (auth required)',
|
|
|
|
options: <DarwinNotificationActionOption>{
|
|
|
|
DarwinNotificationActionOption.authenticationRequired,
|
|
|
|
},
|
|
|
|
),
|
|
|
|
],
|
|
|
|
options: <DarwinNotificationCategoryOption>{
|
|
|
|
DarwinNotificationCategoryOption.hiddenPreviewShowTitle,
|
|
|
|
},
|
|
|
|
),
|
|
|
|
];
|
|
|
|
|
|
|
|
/// Note: permissions aren't requested here just to demonstrate that can be
|
|
|
|
/// done later
|
|
|
|
final DarwinInitializationSettings darwinInitializationSettings =
|
|
|
|
DarwinInitializationSettings(
|
|
|
|
requestAlertPermission: false,
|
|
|
|
requestBadgePermission: false,
|
|
|
|
requestSoundPermission: false,
|
|
|
|
onDidReceiveLocalNotification: (
|
|
|
|
int id,
|
|
|
|
String? title,
|
|
|
|
String? body,
|
|
|
|
String? payload,
|
|
|
|
) async {
|
|
|
|
didReceiveNotificationStream.add(
|
|
|
|
ReceivedNotification(
|
|
|
|
id: id,
|
|
|
|
title: title,
|
|
|
|
body: body,
|
|
|
|
payload: payload,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
notificationCategories: darwinNotificationCategories,
|
|
|
|
);
|
|
|
|
|
|
|
|
final InitializationSettings initializationSettings = InitializationSettings(
|
|
|
|
android: androidInitializationSettings,
|
|
|
|
iOS: darwinInitializationSettings,
|
|
|
|
);
|
|
|
|
|
|
|
|
await flutterLocalNotificationsPlugin.initialize(
|
|
|
|
initializationSettings,
|
|
|
|
// This callback fire when a notification has tapped
|
|
|
|
onDidReceiveNotificationResponse: (NotificationResponse notificationRes) {
|
|
|
|
switch (notificationRes.notificationResponseType) {
|
|
|
|
case NotificationResponseType.selectedNotification:
|
|
|
|
selectNotificationStream.add(notificationRes.payload);
|
|
|
|
break;
|
|
|
|
case NotificationResponseType.selectedNotificationAction:
|
|
|
|
if (notificationRes.actionId == navigationActionId) {
|
|
|
|
selectNotificationStream.add(notificationRes.payload);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
onDidReceiveBackgroundNotificationResponse: notificationTapBackground,
|
|
|
|
);
|
|
|
|
}
|