I am facing issue received notifications when app terminated/Remove from recent apps from background.
I have complete all the setup required for android app in build.gradle files both app or project level. I am able to receive push notification when app is open or when app is in the recent apps.
Library versions
firebase_messaging: ^11.2.0
firebase_core: ^1.10.0
flutter_local_notifications: ^9.1.4
here is my code.
await Firebase.initializeApp();
FirebaseMessaging messaging = FirebaseMessaging.instance;
messaging.getToken().then((value) {
print('firebase token =$value');
});
FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
//print(event.notification!.body);
RemoteNotification? notification = message.notification;
if (notification != null) {
print("Notification received when app in foreground");
}
});
FirebaseMessaging.onMessageOpenedApp.listen((message) {
print('Message clicked!');
});
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
BackgroundMessage handler code is below
Future<void> _messageHandler(RemoteMessage message) async {
await Firebase.initializeApp();
RemoteNotification? notification = message.notification;
if (notification != null) {
print("Notification received when app in background");
}
}
Below is my complete code of main.dart file
Future<void> _messageHandler(RemoteMessage message) async {
await Firebase.initializeApp();
RemoteNotification? notification = message.notification;
if (notification != null) {
print("Notification received when app in background");
}
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(_messageHandler);
runApp(MyApp());
}
class MyApp extends StatefulWidget {
createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool isUserLoggedIn = true;
bool isLogoutAlertShown = false;
final materialAppKey = GlobalKey();
late FirebaseMessaging messaging;
#override
void initState() {
super.initState();
setUpNotification();
}
setUpNotification() async {
messaging = FirebaseMessaging.instance;
messaging.getToken().then((value) {
print('firebase token =$value');
//sendTokenToServer(value);
Pref.setFcmToken(token: '$value');
});
FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
//print(event.notification!.body);
RemoteNotification? notification = message.notification;
if (notification != null) {
print("Notification received when app in foreground");
}
});
FirebaseMessaging.onMessageOpenedApp.listen((message) {
print('Message clicked!');
});
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
}
#override
Widget build(BuildContext context) {
return _materialApp();
}
Widget _materialApp() {
return FutureBuilder(
future: _loginState(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return MaterialApp(
debugShowCheckedModeBanner: false,
key: materialAppKey,
darkTheme: AppTheme.lightTheme,
theme: AppTheme.lightTheme,
home: isUserLoggedIn == true ?
BottomNavigationContainer() : LoginOptions(),
);
} else {
return Container(color: Colors.white);
}
});
}
Future<void> _loginState() async {
final token = await Pref.getToken();
isUserLoggedIn = token.length > 0 ? true : false;
}
}
Suggest me what I am missing or doing wrong.
Thank to all for your response.
I Found the solution that why my background handler not working because I am running my app in Debugging mode. As per the FlutterFire Cloud messaging Doc background method not work in Debugging mode if you kill your app.
You can test your background handler method by run your application in debug and then background it by switching apps so it's no longer in the foreground.
If You want to check After app terminate/kill from recent task then Run app in Release Mode. It Works fine
Again thanks to All.
Related
I have create a custom notification using flutter_local_notification package , when I
send notification using FCM, two notifications are visible in both foreground and background state of application. When I click on FCM generated notification its works as usual , but while clicking on custom background notification my app start but am not able to run my logics, so please help me in handling the background custom notification
On clicking background custom notification it shows below info
Attempted to start a duplicate background isolate. Returning...
class NotificationService {
static final NotificationService _notificationService =
NotificationService._private();
NotificationService._private();
factory NotificationService() => _notificationService;
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
final AndroidNotificationChannel _androidChannel =
const AndroidNotificationChannel(
'high_importance_channel', // id
'Notifications', // title
description: 'important notification channel', // description
importance: Importance.high,
playSound: true);
final AndroidInitializationSettings _initializationSettingsAndroid =
const AndroidInitializationSettings(
'app_icon',
);
///This functions initializes Flutterlocalnotificationplugin with required settings and channels
initialize() async {
AndroidFlutterLocalNotificationsPlugin? resp =
flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>();
await resp!.createNotificationChannel(_androidChannel);
resp.requestPermission();
var initializationSettings =
InitializationSettings(android: _initializationSettingsAndroid);
await flutterLocalNotificationsPlugin.initialize(initializationSettings,
onDidReceiveNotificationResponse: _handleForegroundNotification,
onDidReceiveBackgroundNotificationResponse:
_handleBackgroundNotification);
}
/// This function create the custom notifcation.
/// before calling this function , firebaseLocalNotification instance should be initialized
showNotification(RemoteMessage message) {
var initializationSettings =
InitializationSettings(android: _initializationSettingsAndroid);
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onDidReceiveNotificationResponse: _handleForegroundNotification,
onDidReceiveBackgroundNotificationResponse:
_handleBackgroundNotification);
flutterLocalNotificationsPlugin.show(
1123,
message.data['title'],
message.data['body'],
const NotificationDetails(
android: AndroidNotificationDetails(
'1123',
'channel.name',
channelDescription: 'channel.description',
color: Colors.blue,
playSound: true,
),
));
}
}
///This functions handle background notification press and performs given instructions
#pragma('vm:entry-point')
_handleBackgroundNotification(NotificationResponse response) async {
await Future.delayed(Duration(seconds: 3));
Fluttertoast.showToast(
msg: "backgrrrround call nice no",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0);
}
///This functions handel foregroud notification press and performs given instruction
_handleForegroundNotification(NotificationResponse response) async {
await Future.delayed(Duration(seconds: 3));
Fluttertoast.showToast(
msg: "Forerground call nice no",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0);
}
and another class for managing FCM
class FCMService {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
String? fcmToken;
///This function initialized FCM and start stream of token,foregroundmessage,backgroundmessage
initialize() async {
fcmToken = await FirebaseMessaging.instance.getToken();
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
debugPrint(fcmToken);
_listenToTokenStream();
_listenToForegroundMessage();
}
// /Handle background but not teminated app notification press
_listenToOnMessageOpenedApp() {
//when
FirebaseMessaging.onMessageOpenedApp.listen((message) {
print("FCM>>>>>>>> backgroud");
});
}
/// Request permission for higher sdk version
_requestNotificationPermission() async {
NotificationSettings settings = await _firebaseMessaging.requestPermission(
alert: true,
sound: true,
);
debugPrint('User granted permission: ${settings.authorizationStatus}');
}
/// listen to foreground message recieved from FCM
_listenToForegroundMessage() {
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
NotificationService().showNotification(message);
});
}
///Listen to streamm of token when token expires helps us to updat it
_listenToTokenStream() {
FirebaseMessaging.instance.onTokenRefresh.listen((token) {
fcmToken = token;
debugPrint(fcmToken);
}).onError((e) {
debugPrint("token refresh failed");
});
}
}
///listen background message recieve from FCM , DO NOT: perform heavy task in background
///otherwise your process may get killed
Future<void> fcmBackgroundMessageHandler(RemoteMessage message) async {
await Firebase.initializeApp();
// await NotificationService().initialize();
NotificationService().showNotification(message);
}
main method :
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FCMService().initialize();
NotificationService().initialize();
FirebaseMessaging.onBackgroundMessage(fcmBackgroundMessageHandler);
runApp(const MyApp());
}
you should listen to background messages on FCM initialization But you are doing it on main. SO remove that and add the option in FCM initialization.
I have updated your code with the required updates.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FCMService().initialize();
NotificationService().initialize();
runApp(const MyApp());
}
/// fcm background message handler
Future<void> fcmBackgroundMessageHandler(RemoteMessage message) async {
print(message.notification!.title);
// NotificationService().showNotification(message);
}
class FCMService {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
String? fcmToken;
///This function initialized FCM and start stream of token,foregroundmessage,backgroundmessage
initialize() async {
fcmToken = await FirebaseMessaging.instance.getToken();
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
debugPrint(fcmToken);
_listenToTokenStream();
_listenToForegroundMessage();
_listenToBackgroundMessage();
}
/// handle push notification events on background
void handlePushNotificationBackgroundEvents() {
FirebaseMessaging.onBackgroundMessage(fcmBackgroundMessageHandler);
}
// /Handle background but not teminated app notification press
_listenToOnMessageOpenedApp() {
//when
FirebaseMessaging.onMessageOpenedApp.listen((message) {
print("FCM>>>>>>>> backgroud");
});
}
/// Request permission for higher sdk version
_requestNotificationPermission() async {
NotificationSettings settings = await _firebaseMessaging.requestPermission(
alert: true,
sound: true,
);
debugPrint('User granted permission: ${settings.authorizationStatus}');
}
/// listen to foreground message recieved from FCM
_listenToForegroundMessage() {
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
NotificationService().showNotification(message);
});
}
///Listen to streamm of token when token expires helps us to updat it
_listenToTokenStream() {
FirebaseMessaging.instance.onTokenRefresh.listen((token) {
fcmToken = token;
debugPrint(fcmToken);
}).onError((e) {
debugPrint("token refresh failed");
});
}
}
I'm using local_auth for user verification.
The root widget of my app is a stateless widget.
The bug:
The authentication screen pops up as usual. If the fingerprint (or pin) matches, the user can then access the app. However, if the back button is pressed (while the authentication screen is still up), the authentication window vanishes and the user can access the app without authenticating.
I'm using Flutter-2.5.3 and local_auth-1.1.8.
This is the main.dart:
//other imports here
import 'package:local_auth/local_auth.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
var localAuth = LocalAuthentication();
SharedPreferences prefs = await SharedPreferences.getInstance();
if (prefs.getBool('auth') == true) {
await localAuth.authenticate(
localizedReason: 'Authenticate to access Notes',
useErrorDialogs: true,
stickyAuth: true,);
}
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]).then((_) {
runApp(ProviderScope(child: MyApp()));
});
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
//returns my widget
}
}
I tried moving the runApp block under a conditional, such that the main root window gets called only when the authentication was successful. However the result remained the same.
This was what I did:
if (prefs.getBool('auth') == true) {
var authenticate = await localAuth.authenticate(
localizedReason: 'Authenticate to access Notes',
useErrorDialogs: true,
stickyAuth: true,);
if (authenticate == true) {
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]).then((_) {
runApp(ProviderScope(child: MyApp()));
});
}
}
This is what worked for me:
Changing MyApp to a StatefulWidget.
Adding an awaited function that attempts to authenticate the user before the user can access the widget (that is, the build function).
Modifying the code:
//other imports here
import 'package:local_auth/local_auth.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
//removing the authentication block from the main method
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]).then((_) {
runApp(ProviderScope(child: MyApp()));
});
}
class MyApp extends StatefulWidget { //changing MyApp to StatefulWidget
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
_authenticate(); //the function that handles authentication
}
void _authenticate() async {
var localAuth = LocalAuthentication();
SharedPreferences prefs = await SharedPreferences.getInstance();
if (prefs.getBool('auth') == true) {
var authenticate = await localAuth.authenticate(
localizedReason: 'Authenticate to access Notes',
useErrorDialogs: true,
//Not using stickyAuth because: https://github.com/flutter/flutter/issues/83773
// stickyAuth: true,
);
if (authenticate != true)
exit(0); //exiting the app if the authentication failed
}
}
#override
Widget build(BuildContext context) {
//returns my widget
}
}
I want to open the appropriate window depending on whether the user is authenticated or not.
But now an error appears for a second
[core/no-app] No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp()
and then the map does not open even though the user is authenticated.
"start" it is a variable that is initialRoute
Here is my code.
void main() => runApp(App());
String start = "";
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
Firebase.initializeApp().whenComplete((){
FirebaseAuth.instance
.authStateChanges()
.listen((User user) {
if (user == null) {
print('User is currently signed out!');
start = '/';
} else {
print('User is signed in!');
start = '/map/${FirebaseAuth.instance.currentUser.uid}';
}
});
});
WidgetsFlutterBinding.ensureInitialized();
return FutureBuilder(
builder: (context, snapshot) {
return MyApp();
}
);
}
}
Initialize your default firebase app like below;
Future<void> initializeDefault() async {
FirebaseApp app = await Firebase.initializeApp();
assert(app != null);
setState(() {
loading = false;
}
);
print('Initialized default app $app');
}
#override
void initState() {
initializeDefault();
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(statusBarColor: Colors.transparent));
super.initState();
}
I have built my own application in flutter and I have implemented Local notifications and also FirebaseMessaging:
final FirebaseMessaging firebaseMessaging = FirebaseMessaging();
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
#override
void initState() {
super.initState();
registerNotification();
configLocalNotification();
firebaseMessaging.requestNotificationPermissions();
}
void registerNotification() {
firebaseMessaging.requestNotificationPermissions();
firebaseMessaging.configure(onMessage: (Map<String, dynamic> message) {
print('onMessage: $message');
Platform.isAndroid
? showNotification(message['notification'])
: showNotification(message['aps']['alert']);
return ;
}, onResume: (Map<String, dynamic> message) {
print('onResume: $message');
return Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NotificationsScreen()));
}, onLaunch: (Map<String, dynamic> message) {
print('onLaunch: $message');
return;
});
firebaseMessaging.getToken().then((token) {
print('token: $token');
FirebaseFirestore.instance
.collection('Consultant')
.doc(firebaseUser.uid)
.update({'deviceToken': token});
}).catchError((err) {
//Fluttertoast.showToast(msg: err.message.toString());
});
}
Future selectNotification(String payload) async {
if (payload != null) {
debugPrint('notification payload: $payload');
}
await Navigator.push(
context,
MaterialPageRoute<void>(builder: (context) => NotificationsScreen(payload: payload,)),
);
}
void showNotification(message) async {
var androidPlatformChannelSpecifics = new AndroidNotificationDetails(
Platform.isAndroid
? 'it.wytex.vibeland_pro_app'
: 'it.wytex.vibeland_pro_app',
'Vibeland',
'Vibeland',
playSound: true,
enableVibration: true,
importance: Importance.max,
priority: Priority.high,
ongoing: true,
);
var iOSPlatformChannelSpecifics = new IOSNotificationDetails();
var platformChannelSpecifics = new NotificationDetails(
android: androidPlatformChannelSpecifics, iOS: iOSPlatformChannelSpecifics);
print(message);
print(message['body'].toString());
print(json.encode(message));
await flutterLocalNotificationsPlugin.show(0, message['title'].toString(),
message['body'].toString(), platformChannelSpecifics,
payload: json.encode(message));
await flutterLocalNotificationsPlugin.show(
0, '📩 Hai ricevuto un messaggio 📩 ', 'Controlla subito le Tue notifiche 🔔🔔', platformChannelSpecifics,
payload: 'item x',
);
}
void configLocalNotification() {
var initializationSettingsAndroid =
new AndroidInitializationSettings('vibeland_pro');
var initializationSettingsIOS = new IOSInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
);
var initializationSettings = new InitializationSettings(
android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
flutterLocalNotificationsPlugin.initialize(initializationSettings);
}
I wrote a function into index.js to push also firestore notification when user creates order etc..
Everything works fine, I got Localnotifications when all is open and FCM notifications when app is in background, the problem is when the app sleep...
after some minutes the app goes in sleep mode and to start it again you need to restart the application which keep my user Logged:
MultiProvider(
providers: [
Provider<AuthenticationProvider>(
create: (_) => AuthenticationProvider(FirebaseAuth.instance),
),
StreamProvider(
create: (context) => context.read<AuthenticationProvider>().authState,
)
],
child: MaterialApp(
theme: ThemeData(
pageTransitionsTheme: PageTransitionsTheme(
builders: {
TargetPlatform.android: CupertinoPageTransitionsBuilder(
),
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
}
)
),
debugShowCheckedModeBanner: false,
title: 'Vibeland Admin Authentication',
home: Authenticate(),
),
);
}
}
class Authenticate extends StatelessWidget {
#override
Widget build(BuildContext context) {
final firebaseUser = context.watch<User>();
if (firebaseUser != null) {
return StartScreenLogged();
}
return StartScreen();
}
}
but if I dont run again the app I dont get the notification because the app sleep.
what we need to do in this case?
I am trying to send push notifications to the users of my app using FirebaseMessaging ^8.0.0-dev.14 but I am having some trouble. I am able to receive the notifications on IOS but nothing shows up on my Android emulator. I have listeners set up on init but they only see the notification on IOS devices. Has anyone had a similar issue or know how to get the notifications to show on Android? Here is my code:
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart'
as locNots;
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:url_launcher/url_launcher.dart';
import '../widgets/mentorActionsButton.dart';
import '../widgets/list_of_events.dart';
import 'userPage.dart';
import 'loginPage.dart';
class MentorMainPage extends StatefulWidget {
#override
_MentorMainPageState createState() => _MentorMainPageState();
}
class _MentorMainPageState extends State<MentorMainPage> {
#override
void initState() {
super.initState();
final fbm = FirebaseMessaging.instance;
// IOS Configurations
fbm.setForegroundNotificationPresentationOptions(
alert: true, badge: true, sound: true);
fbm.requestPermission();
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('IOS Listener');
print('Got a message whilst in the foreground!');
print('Message data: ${message.data}');
if (message.notification != null) {
print('Message also contained a notification: ${message.notification}');
}
});
//Android Configurations
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
'This channel is used for important notifications.', // description
importance: Importance.max,
);
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Android Listener');
RemoteNotification notification = message.notification;
AndroidNotification android = message.notification?.android;
print('Android Notification:');
if (notification != null && android != null) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id, channel.name, channel.description,
icon: android?.smallIcon,
showWhen: false,
importance: Importance.max,
priority: locNots.Priority.high),
));
}
});
}
var userLoggedIn = true;
#override
Widget build(BuildContext context) {
Code for screen view.....
}
add these before calling onMessage()
await Firebase.initializeApp(); // necessary for initializing firebase
FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage message) {
print('message recived');
});
try making main function async and adding the previous lines there