My App does the following: It runs a background Task using Flutter Workmanager which checks some values and then it throws a Notification via Flutter Local Notification. In the initialize method from FlutterLocalNotifications Plugin, i can specify a inline fuction, which should navigate to a page. Since i dont have a Builder context, i must use a Navigator Key with OnGenerateRoute to forward the user to a site. However, this doesn`t work and i don´t know why. I know that this code is useful when the app gotkilled.
Example Code
final NotificationAppLaunchDetails? notificationAppLaunchDetails =
await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
String initialRoute = HomePage.routeName;
if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
selectedNotificationPayload = notificationAppLaunchDetails!.payload;
initialRoute = SecondPage.routeName;
}
But what to do when the app is still alive? My Project code is listed below.
Main.Dart
void main() {
WidgetsFlutterBinding.ensureInitialized();
Workmanager().initialize(callbackDispatcher, isInDebugMode: true);
Workmanager().registerPeriodicTask("1", "test",frequency: Duration(minutes: 15));
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
State createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: "/",
navigatorKey: NavigationService.navigatorKey,
onGenerateRoute: RouteGenerator.generateRoute
);
}
}
RouteGenerator.dart
class RouteGenerator {
static Route<dynamic> generateRoute(RouteSettings settings) {
final args = settings.arguments;
switch(settings.name) {
case '/first':
return MaterialPageRoute(builder: (_) => Page1(title: "First"));
case '/second':
return MaterialPageRoute(builder: (_) => Page2(title: "Second"));
case '/third':
return MaterialPageRoute(builder: (_) => Page3(title: "Third"));
case '/fourth':
return MaterialPageRoute(builder: (_) => Page4(title: "Fourth"));
}
return MaterialPageRoute(builder: (_) => Page0(title: "Root!"));
}
}
class NavigationService {
static final GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
static Future<dynamic> navigateTo(String routeName) {
return navigatorKey.currentState!.pushNamed(routeName);
}
}
service.dart
class DevHttpOverrides extends HttpOverrides {
#override
HttpClient createHttpClient(SecurityContext? context) {
return super.createHttpClient(context)
..badCertificateCallback = (X509Certificate cert, String host, int port) => true;
}
}
void callbackDispatcher() {
Workmanager().executeTask((task, inputData) async{
HttpOverrides.global = new DevHttpOverrides();
var url = 'https://172.16.0.100/handler.php?page=settings';
http.Response response = await http.get(Uri.parse(url));
List<dynamic> list = jsonDecode(response.body);
SharedPreferences prefs = await SharedPreferences.getInstance();
var usage = "Beides";
var checkValue = "temp_out";
var borderValueString = "14.9";
var checktype = "Grenzwert überschreiten";
var borderValueDouble;
var message = "";
if(usage != "Nur Home Widgets" && checkValue != "" && borderValueString != "" && checktype != "")
{
var value = list[0][checkValue];
if (double.tryParse(borderValueString) != null && double.tryParse(value) != null)
{
borderValueDouble = double.parse(borderValueString);
value = double.parse(value);
}
if (checktype == "Grenzwert unterschreiten")
{
if (borderValueDouble is double)
{
if (value <= borderValueDouble)
{
message = "Grenzwert unterschritten";
}
}
}
else if (checktype == "Grenzwert überschreiten")
{
if (borderValueDouble is double)
{
if (value >= borderValueDouble)
{
message = "Grenzwert überschritten";
}
}
}
else if (checktype == "Entspricht Grenzwert")
{
if (borderValueDouble == value)
{
message = "Grenzwert erreicht";
}
}
}
if(message != "")
{
FlutterLocalNotificationsPlugin flip = new FlutterLocalNotificationsPlugin();
var android = new AndroidInitializationSettings('#mipmap/ic_launcher');
var ios = new IOSInitializationSettings();
var settings = new InitializationSettings(android: android, iOS: ios);
flip.initialize(settings, onSelectNotification: (String? payload) async {
await NavigationService.navigatorKey.currentState!.push(MaterialPageRoute(builder: (context) => Page4(title: "Hello")));
});
var androidPlatformChannelSpecifics = new AndroidNotificationDetails(
'1',
'weatherstation',
'Notify when values change',
importance: Importance.max,
priority: Priority.high
);
var iOSPlatformChannelSpecifics = new IOSNotificationDetails();
var platformChannelSpecifics = new NotificationDetails(
android: androidPlatformChannelSpecifics,
iOS: iOSPlatformChannelSpecifics);
await flip.show(0, message,
'App öffnen für weitere Details',
platformChannelSpecifics, payload: 'Default_Sound'
);
}
return Future.value(true);
});
}
Did you find any solution for it? I'm also working on an Android application that update user data in firestore database (at interval of 15min) and send user its notification (both task happens in background using flutter workmanager_plugin). When user taps on the notification he should be navigated to the route which shows latest data from the database.
First 2 background tasks are happening successfully but when the notification is clicked nothing is happening. I'm also using GlobalKey key to get MaterialApp widget's context, so that routing Route can be pushed.
It seems like onSelectNotification property for FlutterLocalNotificationsPlugin.initialize() method don't work for workmanager plugin. I have also added a print statement inside it, but nothing get displayed in console. I thought maybe my Globalkey has some fault but when I tried it for navigating pages in non background task it was happening succesfully, similarly onSelectNotification was working perfectely for non-workmanager task.
void callbackDispatcher() {
Workmanager().executeTask((task, data) async {
if(task=='showNotification'){
FlutterLocalNotificationsPlugin notifPlugin =
FlutterLocalNotificationsPlugin();
NotificationDetails notificationDetails = NotificationDetails(
android: AndroidNotificationDetails(
'main_channel',
'Main Channel',
'Main Notification Channel',
importance: Importance.max,
priority: Priority.high,
),
);
await notifPlugin.initialize(
InitializationSettings(
android: AndroidInitializationSettings('ic_launcher')),
onSelectNotification: (String? payload) async {
print('Inside on select Route Navigator. Route= $payload');
switch (payload!) {
case 'Home':
// navigatorKey is GlobalKey
navigatorKey.currentState!
.push(MaterialPageRoute(builder: (context) => Home()));
break;
case 'Auth':
navigatorKey.currentState!
.push(MaterialPageRoute(builder: (context) => Auth()));
break;
case 'Details':
navigatorKey.currentState!
.push(MaterialPageRoute(builder: (context) => UserDetails()));
break;
}
});
await notifPlugin.show(id, title, body, notificationDetails, payload:payload);
}
return Future.value(true);
}
}
When notification is clicked. Following message should print on console: "Inside on select Route Navigator. Route= Details" and he should be navigated on the UserDetails page but nothing seems to be happening.
I solved it by reacting to two different events.
If the App starts, i check if the app was launched by a notification. This can be done in createState or initState in main.dart. This code is useful for that.
final NotificationAppLaunchDetails? notificationAppLaunchDetails =
await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
String initialRoute = HomePage.routeName;
if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
selectedNotificationPayload = notificationAppLaunchDetails!.payload;
initialRoute = SecondPage.routeName;
}
If the app is in background and a notification launches the app again, you must use Widgets Binding Observer and react to the App Resume event. There is an article at Medium which has example code for this case. Have a look at it here.
There is one drawback when reacting to App Resume Event and using the Flutter Local notifications Plugin. The aforementioned code always delivers true once triggered by an notification, even if the app entered background state again and was resumed manually by an user. This means code for changing a page will always be called, even
if you did not click an notification. Therefore, I´m using a boolean variable to trigger the App State Resume code once. Obviously, if you enter the app via notification ,the app gets resumed and you get a second notification, the code for changing a page will not be executed. It´s a workaround, but for my case, it´s good enough.
no, I am not asking about Firebase Cloud Messaging notification here, but purely using local notification package from here flutter local notification
I want if my user click the notification then it will be directed to a certain page. how to do that ?
currently my code is like this
class LocalNotificationService {
late FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin;
LocalNotificationService._privateConstructor() {
_init();
}
static final LocalNotificationService _instance = LocalNotificationService._privateConstructor();
static LocalNotificationService get instance => _instance; // singleton access
void _init() {
_flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
_initializePlatformSpecifics();
}
Future<void> _initializePlatformSpecifics() async {
var androidInitializationSettings = AndroidInitializationSettings("app_notification_icon");
var iOSInitializationSettings = IOSInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
onDidReceiveLocalNotification: (id, title, body, payload) async {
// do something if notification is clicked
},
);
final initializationSettings = InitializationSettings(
android: androidInitializationSettings,
iOS: iOSInitializationSettings,
);
await _flutterLocalNotificationsPlugin.initialize(initializationSettings);
}
void showSimpleNotification({
required String title,
required String body,
required NotificationType notificationType,
}) async {
var android = _setAndroidNotificationDetails(notificationType);
var ios = IOSNotificationDetails();
var notificationDetails = new NotificationDetails(android: android, iOS: ios);
await _flutterLocalNotificationsPlugin.show(
1, // only need to show one notification in the notification tray, so it is ok if we use the Notification ID
title,
body,
notificationDetails,
);
}
}
First thing you need to create a GlobalKey for navigation like the following:
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
and inside MaterialApp, set the navigatorKey property to be our created key like the following property
navigatorKey: navigatorKey
Now you can use the navigation methods from inside the current state of key, example:
navigatorKey.currentState!.push()
i recommend reading plugin documentation
as per documentation
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('app_icon');
final IOSInitializationSettings initializationSettingsIOS =
IOSInitializationSettings(
requestSoundPermission: false,
requestBadgePermission: false,
requestAlertPermission: false,
onDidReceiveLocalNotification: onDidReceiveLocalNotification,
);
final MacOSInitializationSettings initializationSettingsMacOS =
MacOSInitializationSettings(
requestAlertPermission: false,
requestBadgePermission: false,
requestSoundPermission: false);
final InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
macOS: initializationSettingsMacOS);
await flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: onSelectNotification);
you can pass onSelectNotification function which will be called whenever user click on notification
from this function u can redirection to particular screen example as below
Future selectNotification(String payload) async {
if (payload != null) {
debugPrint('notification payload: $payload');
}
await Navigator.push(
context,
MaterialPageRoute<void>(builder: (context) =>
SecondScreen(payload)),);
}
This is first time I am testing FCM with Flutter. I checked some of the SO questions and documents from GitHub.
I am able to send the notifications and they are getting delivered when the app is not running.
If app is running or in the background then messages are not visible.
I have added the code in main.dart file but not sure this is the correct way or not.
Edit:
This is for onResume:
{notification: {}, data: {badge: 1, collapse_key: com.HT, google.original_priority: high, google.sent_time: 1623238, google.delivered_priority: high, sound: default, google.ttl: 2419200, from: 71374876, body: Body, title: Title, click_action: FLUTTER_NOTIFICATION_CLICK, google.message_id: 0:50a56}}
In the Below code, i am trying to use local notifications with FCM.
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
new FlutterLocalNotificationsPlugin();
#override
void initState() {
var initializationSettingsAndroid =
new AndroidInitializationSettings('#mipmap/ic_launcher');
var initializationSettingsIOS = new IOSInitializationSettings();
var initializationSettings = new InitializationSettings(
android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: onSelectNotification);
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
showNotification(
message['notification']['title'], message['notification']['body']);
print("onMessage: $message");
},
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
// Navigator.pushNamed(context, '/notify');
ExtendedNavigator.of(context).push(
Routes.bookingQRScan,
);
},
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
},
);
}
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: AnimatedSplashScreen(), //SplashScreen()
builder: ExtendedNavigator.builder<a.Router>(router: a.Router()),
);
}
Future onSelectNotification(String payload) async {
showDialog(
context: context,
builder: (_) {
return new AlertDialog(
title: Text("PayLoad"),
content: Text("Payload : $payload"),
);
},
);
}
void showNotification(String title, String body) async {
await _demoNotification(title, body);
}
Future<void> _demoNotification(String title, String body) async {
var androidPlatformChannelSpecifics = AndroidNotificationDetails(
'channel_ID', 'channel name', 'channel description',
importance: Importance.max,
playSound: false, //true,
//sound: 'sound',
showProgress: true,
priority: Priority.high,
ticker: 'test ticker');
//var iOSChannelSpecifics = IOSNotificationDetails();
var platformChannelSpecifics =
NotificationDetails(android: androidPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin
.show(0, title, body, platformChannelSpecifics, payload: 'test');
}
}
Error
This is when my app is running on foreground. E/FlutterFcmService(14434): Fatal: failed to find callback
W/FirebaseMessaging(14434): Missing Default Notification Channel metadata in AndroidManifest. Default value will be used.
W/ConnectionTracker(14434): Exception thrown while unbinding
W/ConnectionTracker(14434): java.lang.IllegalArgumentException: Service not registered: lu#fb04880
Notification is visible in notification center. Now i am clicking on it and app get terminated.
and new instance of app is running and below is the return code. I/flutter (14434): onResume: {notification: {}, data: {badge: 1, collapse_key: com.HT, google.original_priority: high, google.sent_time: 1607733798, google.delivered_priority: high, sound: default, google.ttl: 2419200, from: 774876, body: Body, title: Title, click_action: FLUTTER_NOTIFICATION_CLICK, google.message_id: 0:1607573733816296%850a56}}
E/FlutterFcmService(14434): Fatal: failed to find callback
W/ConnectionTracker(14434): Exception thrown while unbinding
Edit 2:
I did more digging and come up with the below code.
final FirebaseMessaging _fcm = FirebaseMessaging();
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
var initializationSettingsAndroid;
var initializationSettingsIOS;
var initializationSettings;
void _showNotification() async {
//await _buildNotification();
}
Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) {
if (message.containsKey('data')) {
// Handle data message
final dynamic data = message['data'];
}
if (message.containsKey('notification')) {
// Handle notification message
final dynamic notification = message['notification'];
}
// Or do other work.
}
Future<void> _createNotificationChannel(
String id, String name, String description) async {
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
var androidNotificationChannel = AndroidNotificationChannel(
id,
name,
description,
importance: Importance.max,
playSound: true,
// sound: RawResourceAndroidNotificationSound('not_kiddin'),
enableVibration: true,
);
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(androidNotificationChannel);
}
Future<void> _buildNotification(String title, String body) async {
var androidPlatformChannelSpecifics = AndroidNotificationDetails(
'my_channel', 'Channel Name', 'Channel Description.',
importance: Importance.max,
priority: Priority.high,
// playSound: true,
enableVibration: true,
// sound: RawResourceAndroidNotificationSound('not_kiddin'),
ticker: 'noorderlicht');
//var iOSChannelSpecifics = IOSNotificationDetails();
var platformChannelSpecifics =
NotificationDetails(android: androidPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin
.show(0, title, body, platformChannelSpecifics, payload: 'payload');
}
#override
void initState() {
super.initState();
initializationSettingsAndroid =
AndroidInitializationSettings('#mipmap/ic_launcher');
initializationSettingsIOS = IOSInitializationSettings(
onDidReceiveLocalNotification: onDidReceiveLocalNotification);
initializationSettings =
InitializationSettings(android: initializationSettingsAndroid);
// initializationSettingsAndroid, initializationSettingsIOS);
_fcm.requestNotificationPermissions();
_fcm.configure(
onMessage: (Map<String, dynamic> message) async {
print(message);
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: onSelectNotification);
//_showNotification();
Map.from(message).map((key, value) {
print(key);
print(value);
print(value['title']);
_buildNotification(value['title'], value['body']);
});
},
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
},
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
print(message['data']['title']);
//AlertDialog(title: message['data']['title']);
ExtendedNavigator.of(context).push(
Routes.bookingQRScan,
);
//_showNotification();
},
);
}
Future onDidReceiveLocalNotification(
int id, String title, String body, String payload) async {
// display a dialog with the notification details, tap ok to go to another page
showDialog(
context: context,
builder: (BuildContext context) => CupertinoAlertDialog(
title: Text(title),
content: Text(body),
actions: [
CupertinoDialogAction(
isDefaultAction: true,
child: Text('Ok'),
onPressed: () {},
)
],
),
);
}
Future onSelectNotification(String payload) async {
if (payload != null) {
debugPrint('Notification payload: $payload');
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: AnimatedSplashScreen(), //SplashScreen()
builder: ExtendedNavigator.builder<a.Router>(router: a.Router()),
);
}
With above code i can see notifications in notification bar but onresume section i want to redirection that is not working. Not sure why.
Also I want to show alert box in onmeesage and onresume events.
Your payload must be correct, notification and data object inside the payload must contain the title and body key. you will get title and body null when your app is closed in notification key in that situation you should have title and body in the side data key.
{notification: {title: title, body: test}, data: {notification_type: Welcome, body: body, badge: 1, sound: , title: farhana mam, click_action: FLUTTER_NOTIFICATION_CLICK, message: H R U, category_id: 2, product_id: 1, img_url: }}
and don't put title and body null
void showNotification(Map<String, dynamic> msg) async {
//{notification: {title: title, body: test}, data: {notification_type: Welcome, body: body, badge: 1, sound: , title: farhana mam, click_action: FLUTTER_NOTIFICATION_CLICK, message: H R U, category_id: 2, product_id: 1, img_url: }}
print(msg);
print(msg['data']['title']);
var title = msg['data']['title'];
var msge = msg['data']['body'];
var android = new AndroidNotificationDetails(
'channel id', 'channel NAME', 'CHANNEL DESCRIPTION',
priority: Priority.High, importance: Importance.Max);
var iOS = new IOSNotificationDetails();
var platform = new NotificationDetails(android, iOS);
await flutterLocalNotificationsPlugin.show(0, title, msge, platform,
payload: msge);
}
for redirection
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
var android = new AndroidInitializationSettings('#mipmap/ic_launcher');
var iOS = new IOSInitializationSettings();
var initSetttings = new InitializationSettings(android, iOS);
flutterLocalNotificationsPlugin.initialize(initSetttings, onSelectNotification: onSelectNotification);
firebaseCloudMessaging_Listeners();
Future onSelectNotification(String payload) async {
if (payload != null) {
debugPrint('notification payload:------ ${payload}');
await Navigator.push(
context,
new MaterialPageRoute(builder: (context) => NotificationListing()),
).then((value) {});
}
}
in 'onSelectNotification' you can pass your condition in string parama and you can redirect
(optional, but recommended) If want to be notified in your app (via onResume and onLaunch, see below) when the user clicks on a notification in the system tray include the following intent-filter within the tag of your android/app/src/main/AndroidManifest.xml:
Looking at your (last edited) code above, I think first you have to make sure, whether localnotifications is used or the default fcm one. Since your myBackgroundMessageHandler does not do anything, I assume the latter one. Try replacing the title temporarily with a fixed string (e.g. "this is a local one") to make sure.
Secondly, myBackgroundMessageHandler will only be called for data messages. If you use the payload you wrote in the beginning, you should be fine. Anyway make sure to not put title, body, style-information etc directly in the payload. If you need it, put it in the data node.
This is the code I am using:
calling the notificationService init() method in main.dart
notification-service.dart
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:app/models/data-notification.dart';
import 'dart:async';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart';
import 'dart:io';
FlutterLocalNotificationsPlugin notificationsPlugin =
FlutterLocalNotificationsPlugin();
//Function to handle Notification data in background.
Future<dynamic> backgroundMessageHandler(Map<String, dynamic> message) {
print("FCM backgroundMessageHandler $message");
showNotification(DataNotification.fromPushMessage(message['data']));
return Future<void>.value();
}
//Function to handle Notification Click.
Future<void> onSelectNotification(String payload) {
print("FCM onSelectNotification");
return Future<void>.value();
}
//Function to Parse and Show Notification when app is in foreground
Future<dynamic> onMessage(Map<String, dynamic> message) {
print("FCM onMessage $message");
showNotification(DataNotification.fromPushMessage(message['data']));
return Future<void>.value();
}
//Function to Handle notification click if app is in background
Future<dynamic> onResume(Map<String, dynamic> message) {
print("FCM onResume $message");
return Future<void>.value();
}
//Function to Handle notification click if app is not in foreground neither in background
Future<dynamic> onLaunch(Map<String, dynamic> message) {
print("FCM onLaunch $message");
return Future<void>.value();
}
void showNotification(DataNotification notification) async {
final AndroidNotificationDetails androidPlatformChannelSpecifics =
await getAndroidNotificationDetails(notification);
final NotificationDetails platformChannelSpecifics =
NotificationDetails(android: androidPlatformChannelSpecifics);
await notificationsPlugin.show(
0,
notification.title,
notification.body,
platformChannelSpecifics,
);
}
Future<AndroidNotificationDetails> getAndroidNotificationDetails(
DataNotification notification) async {
switch (notification.notificationType) {
case NotificationType.NEW_INVITATION:
case NotificationType.NEW_MEMBERSHIP:
case NotificationType.NEW_ADMIN_ROLE:
case NotificationType.MEMBERSHIP_BLOCKED:
case NotificationType.MEMBERSHIP_REMOVED:
case NotificationType.NEW_MEMBERSHIP_REQUEST:
return AndroidNotificationDetails(
'organization',
'Organization management',
'Notifications regarding your organizations and memberships.',
importance: Importance.max,
priority: Priority.high,
showWhen: false,
category: "Organization",
icon: 'my_app_icon_simple',
largeIcon: DrawableResourceAndroidBitmap('my_app_icon'),
styleInformation: await getBigPictureStyle(notification),
sound: RawResourceAndroidNotificationSound('slow_spring_board'));
case NotificationType.NONE:
default:
return AndroidNotificationDetails('general', 'General notifications',
'General notifications that are not sorted to any specific topics.',
importance: Importance.max,
priority: Priority.high,
showWhen: false,
category: "General",
icon: 'my_app_icon_simple',
largeIcon: DrawableResourceAndroidBitmap('my_app_icon'),
styleInformation: await getBigPictureStyle(notification),
sound: RawResourceAndroidNotificationSound('slow_spring_board'));
}
}
Future<BigPictureStyleInformation> getBigPictureStyle(
DataNotification notification) async {
if (notification.imageUrl != null) {
print("downloading");
final String bigPicturePath =
await _downloadAndSaveFile(notification.imageUrl, 'bigPicture');
return BigPictureStyleInformation(FilePathAndroidBitmap(bigPicturePath),
hideExpandedLargeIcon: true,
contentTitle: notification.title,
htmlFormatContentTitle: false,
summaryText: notification.body,
htmlFormatSummaryText: false);
} else {
print("NOT downloading");
return null;
}
}
Future<String> _downloadAndSaveFile(String url, String fileName) async {
final Directory directory = await getApplicationDocumentsDirectory();
final String filePath = '${directory.path}/$fileName';
final http.Response response = await http.get(url);
final File file = File(filePath);
await file.writeAsBytes(response.bodyBytes);
return filePath;
}
class NotificationService {
FirebaseMessaging _fcm = FirebaseMessaging();
void init() async {
final AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('app_icon');
final IOSInitializationSettings initializationSettingsIOS =
IOSInitializationSettings();
final InitializationSettings initializationSettings =
InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
);
await notificationsPlugin.initialize(initializationSettings,
onSelectNotification: (value) => onSelectNotification(value));
_fcm.configure(
onMessage: onMessage,
onBackgroundMessage: backgroundMessageHandler,
onLaunch: onLaunch,
onResume: onResume,
);
}
}
data-notification.dart
import 'package:enum_to_string/enum_to_string.dart';
class DataNotification {
final String id;
final String title;
final String body;
final NotificationType notificationType;
final String imageUrl;
final dynamic data;
final DateTime readAt;
final DateTime createdAt;
final DateTime updatedAt;
DataNotification({
this.id,
this.title,
this.body,
this.notificationType,
this.imageUrl,
this.data,
this.readAt,
this.createdAt,
this.updatedAt,
});
factory DataNotification.fromPushMessage(dynamic data) {
return DataNotification(
id: data['id'],
title: data['title'],
body: data['body'],
notificationType: EnumToString.fromString(
NotificationType.values, data['notification_type']),
imageUrl: data['image_url'] ?? null,
data: data,
readAt: null,
createdAt: null,
updatedAt: null,
);
}
}
enum NotificationType {
NONE,
NEW_INVITATION,
NEW_MEMBERSHIP,
NEW_ADMIN_ROLE,
MEMBERSHIP_BLOCKED,
MEMBERSHIP_REMOVED,
NEW_MEMBERSHIP_REQUEST
}
You can ignore the DataNotification model part, and parse the notification yourself, I just used it for some additional interactions with in the backend.
This works well for me, however, if you want to show an alert for "onSelectNotification" or alike, you need to find a way to get the context there. Not (yet) sure, how to do that.
EDIT:
You can call it like this in main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
NotificationService().init();
runApp(
MyApp()
);
}
Be aware that there is currently an issue with backgroundmessaging and hot-reloading: https://github.com/FirebaseExtended/flutterfire/issues/4316
I have use flutter_local_notifications, push notification is successfully receiving and when i click on it, i'm trying to redirect to specific screen, but it always redirect to home screen.
Below is my code.
#override
void initState() {
// TODO: implement initState
super.initState();
// initialise the plugin. app_icon needs to be a added as a drawable resource to the Android head project
var initializationSettingsAndroid =
new AndroidInitializationSettings('app_icon');
var initializationSettingsIOS = new IOSInitializationSettings();
var initializationSettings = new InitializationSettings(
initializationSettingsAndroid, initializationSettingsIOS);
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: onSelectNotification);
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) {
print('on message $message');
var data = message['data'] as Map;
var msg1 = data['message'] as String;
var response = json.decode(msg1) as Map;
String str_title = response['title'] as String;
String str_body = response['body'] as String;
_showNotification(str_title, str_body, msg1);
},
onResume: (Map<String, dynamic> message) {
print('on resume $message');
},
onLaunch: (Map<String, dynamic> message) {
print('on launch $message');
}
_showNotification function code
void _showNotification(String title, String body, String pay_load) async {
var androidPlatformChannelSpecifics = new AndroidNotificationDetails(
AppConstant.notify_channel_id,
AppConstant.notify_channel_name,
AppConstant.notify_channel_desc,
importance: Importance.Max,
priority: Priority.High);
var iOSPlatformChannelSpecifics = new IOSNotificationDetails();
var platformChannelSpecifics = new NotificationDetails(
androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin
.show(0, title, body, platformChannelSpecifics, payload: pay_load);
}
onSelectNotification function code
Future onSelectNotification(String payload) async {
if (payload != null) {
debugPrint('notification payload: ' + payload);
}
// here set and put condition for property id
var response = json.decode(payload) as Map;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
PropertyDetailScreen()),
);
PropertyDetailScreen is the screen where i want to redirect when click on notification, but it is always redirect to home screen, So please guide me where i am wrong, or why my code its not working.
Try by using GlobalKey as following
Declare it inside
class _MyAppState extends State<MyApp> {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey(debugLabel: "MainNavigator");
}
Assign it in MaterialApp
MaterialApp(
navigatorKey: navigatorKey,
supportedLocales: [
Locale('en', 'US'),
Locale('ar', ''),
Locale('it'),
Locale('fr'),
Locale('es'),
Locale('de'),
Locale('pt'),
],
);
Use it as navigatorKey.currentState.push(MaterialPageRoute(builder: (context) => YourClassName()));