import 'dart:async'; import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_timezone/flutter_timezone.dart'; import 'package:timezone/data/latest_all.dart' as tz; import 'package:timezone/timezone.dart' as tz; class NotificationAPI { static int id = 0; static final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); static final StreamController didReceiveNotificationStream = StreamController.broadcast(); final StreamController selectNotificationStream = StreamController.broadcast(); static Future init() async {} } /// 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 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 /// Only be used in IOS? final StreamController didReceiveNotificationStream = StreamController.broadcast(); final StreamController selectNotificationStream = StreamController.broadcast(); const MethodChannel platform = MethodChannel('dexterx.dev/flutter_local_notifications_example'); const String portName = 'notification_send_port'; /// 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. 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; late final NotificationAppLaunchDetails? notificationAppLaunchDetails; /// 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'; /// Defines a iOS/MacOS notification category for plain actions. 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) { 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 _configureLocalTimeZone() async { if (kIsWeb || Platform.isLinux) { return; } tz.initializeTimeZones(); final String timeZoneName = await FlutterTimezone.getLocalTimezone(); tz.setLocalLocation(tz.getLocation(timeZoneName)); } Future 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'; 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 darwinNotificationCategories = [ DarwinNotificationCategory( darwinNotificationCategoryText, actions: [ DarwinNotificationAction.text( 'text_1', 'action 1', buttonTitle: 'send', placeholder: 'Placeholder', ), ], ), DarwinNotificationCategory( darwinNotificationCategoryPlain, actions: [ DarwinNotificationAction.plain('id_1', 'Action 1'), DarwinNotificationAction.plain( 'id_2', 'Action 2 (destructive)', options: { DarwinNotificationActionOption.destructive, }, ), DarwinNotificationAction.plain( navigationActionId, 'Action 3 (foreground)', options: { DarwinNotificationActionOption.foreground, }, ), DarwinNotificationAction.plain( 'id_4', 'Action 4 (auth required)', options: { DarwinNotificationActionOption.authenticationRequired, }, ), ], options: { 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, ); }