I have 2 projects in my firebase : testProject and mainProject (let's call them like that)
So I implemented the firebase messaging using this tutorial and it works with the json from testProject but after I change to mainProject json, my emulator doesn't receive the notifications. I think this is really weird because in both projects I added the app and I only changed the json.
Do you know why this happens?
this is my code:
class MyAppRC extends StatelessWidget {
static final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
#override
Widget build(BuildContext context) {
final pushNotificationService = PushNotificationService(_firebaseMessaging);
pushNotificationService.initialise();
return MaterialApp(),
home: MyApp(),
);
}
}
Push_notification_service
class PushNotificationService {
final FirebaseMessaging _fcm;
PushNotificationService(this._fcm);
Future initialise() async {
if (Platform.isIOS) {
_fcm.requestNotificationPermissions(IosNotificationSettings());
}
String token = await _fcm.getToken();
debugPrint("FirebaseMessaging token: $token");
_fcm.configure(
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
},
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
},
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
},
);
}
}
Ps. for more details please ask.
Related
Push Notification code.
I don't understand how to implement the new method of implementing a notification in flutter using firebase
import 'dart:io' show Platform;
import 'package:google_maps_flutter/google_maps_flutter.dart';
class PushNotificationService
{
final firebaseMessaging = FirebaseMessaging.instance;
Future initialize(context) async
{
firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
retrieveRideRequestInfo(getRideRequestId(message), context);
},
onLaunch: (Map<String, dynamic> message) async {
retrieveRideRequestInfo(getRideRequestId(message), context);
},
onResume: (Map<String, dynamic> message) async {
retrieveRideRequestInfo(getRideRequestId(message), context);
},
);
}
Seems like you are using the old method. Here is the updated version. Link for your references
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
RemoteNotification notification = message.notification;
showNotification(notification);});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print("onMessageOpenedApp: $message");});
FirebaseMessaging.onBackgroundMessage((RemoteMessage message) {
print("onBackgroundMessage: $message");});
Future initialize(context) async {
FirebaseMessaging.instance.getNotificationSettings(
FirebaseMessaging.onMessage.listen((Map<String, dynamic> message)) async {
retrieveRideRequestInfo(getRideRequestId(message), context);
},
onLaunch: (Map<String, dynamic> message) async {
retrieveRideRequestInfo(getRideRequestId(message), context);
},
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
getRideRequestId(message);
retrieveRideRequestInfo(getRideRequestId(message), context);
},
);
}
Instead of on Message use :
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
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}');
}
});
for background message
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// If you're going to use other Firebase services in the background, such as Firestore,
// make sure you call `initializeApp` before using other Firebase services.
await Firebase.initializeApp();
print("Handling a background message: ${message.messageId}");
}
void main() {
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
Note background message is handled in separate isolate if you are using firebase you have to initialise it first to use it.
Reference:
Foreground:https://firebase.flutter.dev/docs/messaging/usage#foreground-messages
Background:https://firebase.flutter.dev/docs/messaging/usage#background-messages
In initState() of the _PushMessagingExampleState class in the example app for Flutter firebase_messaging 6.0.13, _firebaseMessaging.configure is called first, followed by _firebaseMessaging.requestNotificationPermissions.
However, in the firebase_messaging 6.0.13 API reference for the FirebaseMessaging class, the first thing it states is:
Your app should call requestNotificationPermissions first and then
register handlers for incoming messages with configure.
Which is correct?
I'm guessing the API reference is correct and the example app could be improved?
Technically it doesn't matter but I have it set up as request permissions first, then configure. As an example of a working app:
configureFcm() {
if (!isConfigured) {
if (Platform.isIOS) {
_fcm.onIosSettingsRegistered.listen((IosNotificationSettings data) {
});
_fcm.requestNotificationPermissions(IosNotificationSettings(sound: true, badge: true, alert: true));
}
_fcm.configure(
// in foreground
onMessage: (Map<String, dynamic> message) async {
_handleNotification(message);
},
// onBackgroundMessage: (Map<String, dynamic> message) async {
// print('on background message $message');
// },
// in background
onResume: (Map<String, dynamic> message) async {
_handleNotification(message);
},
// terminated
onLaunch: (Map<String, dynamic> message) async {
_handleNotification(message);
},
);
isConfigured = true;
}
}
I am using firebase cloud messaging to push notification on my app that contains a chat page.
I defined my firebase push functions on my main.dart as below:
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
//_showItemDialog(message);
},
onBackgroundMessage: myBackgroundMessageHandler,
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
//_navigateToItemDetail(message);
},
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
//_navigateToItemDetail(message);
},
);
When chat widget opened and i receive a push notification, my OnMessage method is reached normally.
The question is: what is the best way to refresh my chat page considering that the opened page is not the same one where the reached OnMessage function is declared?
I have used following peice of code for different question on StackOverflow. But the problem there is entirely different from yours, so pasting relevant code.
You can use BLOC here. FCM/NotificationService will send notifications to BLOC/NotificationsBloc and all the widgets that need notifications can subscribe for the notifications. Sample implementation
BLOC
import 'package:rxdart/rxdart.dart';
class LocalNotification {
final String type;
final Map data;
LocalNotification(this.type, this.data);
}
class NotificationsBloc {
NotificationsBloc._internal();
static final NotificationsBloc instance = NotificationsBloc._internal();
final BehaviorSubject<LocalNotification> _notificationsStreamController = BehaviorSubject<LocalNotification>();
Stream<LocalNotification> get notificationsStream {
return _notificationsStreamController;
}
void newNotification(LocalNotification notification) {
_notificationsStreamController.sink.add(notification);
}
void dispose() {
_notificationsStreamController?.close();
}
}
FCM Listener (NotificationService)
import 'package:firebase_messaging/firebase_messaging.dart';
import 'notifications_bloc.dart';
class LocalNotificationService {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
bool _started = false;
LocalNotificationService._internal();
static final LocalNotificationService instance = LocalNotificationService._internal();
// ********************************************************* //
// YOU HAVE TO CALL THIS FROM SOMEWHERE (May be main widget)
// ********************************************************* //
void start() {
if (!_started) {
_start();
_started = true;
_refreshToken();
}
}
void _refreshToken() {
_firebaseMessaging.getToken().then(_tokenRefresh, onError: _tokenRefreshFailure);
}
void _start() {
_firebaseMessaging.requestNotificationPermissions();
_firebaseMessaging.onTokenRefresh.listen(_tokenRefresh, onError: _tokenRefreshFailure);
_firebaseMessaging.configure(
onMessage: _onMessage,
onLaunch: _onLaunch,
onResume: _onResume,
);
}
void _tokenRefresh(String newToken) async {
print(" New FCM Token $newToken");
}
void _tokenRefreshFailure(error) {
print("FCM token refresh failed with error $error");
}
Future<void> _onMessage(Map<String, dynamic> message) async {
print("onMessage $message");
if (message['notification'] != null) {
final notification = LocalNotification("notification", message['notification'] as Map);
NotificationsBloc.instance.newNotification(notification);
return null;
}
if (message['data'] != null) {
final notification = LocalNotification("data", message['data'] as Map);
NotificationsBloc.instance.newNotification(notification);
return null;
}
}
Future<void> _onLaunch(Map<String, dynamic> message) {
print("onLaunch $message");
return null;
}
Future<void> _onResume(Map<String, dynamic> message) {
print("onResume $message");
return null;
}
}
Finally in your Widget
Stream<LocalNotification> _notificationsStream;
#override
void initState() {
super.initState();
_notificationsStream = NotificationsBloc.instance.notificationsStream;
_notificationsStream.listen((notification) {
// TODO: Implement your logic here
print('Notification: $notification');
});
}
#override
void dispose() {
super.dispose();
}
Hope this is what you are looking for.
I have implemented the flutter onBackgroundMessage, that gets triggered when the device receives a Firebase Cloud Messaging data message; i should open a popup, but in this event handler I have no context object. What's the correct way to achieve this ?
I created a class with static methods:
class FirebaseMessagingHandler {
final FirebaseMessaging firebaseMessaging = FirebaseMessaging();
final _bloc = AppModule.to.getBloc<FirebaseMessagingHandlerBloc>();
void setListeners() {
if (Platform.isIOS) _iOSPermission();
getToken();
refreshToken();
}
void getToken() {
firebaseMessaging.getToken().then((token) {
_bloc.saveToken(token);
print('DeviceToken = $token');
});
}
void _iOSPermission() {
firebaseMessaging.configure();
firebaseMessaging.requestNotificationPermissions(IosNotificationSettings(sound: true, badge: true, alert: true));
firebaseMessaging.onIosSettingsRegistered.listen((IosNotificationSettings settings) {
});
}
void refreshToken() {
firebaseMessaging.onTokenRefresh.listen((token) {
_bloc.refreshToken(token);
});
}
void showDialog(BuildContext context, Map<String, dynamic> message) {
// data
}
void showErrorDialog(BuildContext context, dynamic error) {
// data
}
void redirectToPage(BuildContext context, Map<String, dynamic> message) {
// data
}
}
And in my homePage (a page that will always be called when open my app) I call the configure:
class _HomePageState extends State<HomePage> {
final _fcm = FirebaseMessagingHandler();
#override
void initState() {
super.initState();
firebaseCloudMessagingListeners();
}
void firebaseCloudMessagingListeners() {
_fcm.firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
try {
_fcm.showDialog(context, message);
} catch (e) {
_fcm.showErrorDialog(context, e);
}
},
onLaunch: (Map<String, dynamic> message) async {
try {
_fcm.redirectToPage(context, message);
} catch (e) {
_fcm.showErrorDialog(context, e);
}
},
onResume: (Map<String, dynamic> message) async {
try {
_fcm.redirectToPage(context, message);
} catch (e) {
_fcm.showErrorDialog(context, e);
}
},
);
}
}
If you want to display a pop-up within the app, then you don't need onBackgroundMessage - that is only for processing data when a message is received in the background. There is no way to launch the app at the moment the message is received.
However, if a user taps on the notification, the app will launch, and either the onResume or onLaunch callbacks will be called.
You can notify the relevant screen to show a pop up when this happens.
Here's a simple implementation:
In firebase_notification_receiver.dart:
import 'dart:async';
import 'package:firebase_messaging/firebase_messaging.dart';
class NotificationEvent {
final Map<String, dynamic> content;
/// whether the notification was delivered while the app was in the foreground
final bool inApp;
NotificationEvent({this.content, this.inApp = false});
}
class FirebaseNotificationReceiver extends NotificationReceiver {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
StreamController<NotificationEvent> _controller = StreamController<NotificationEvent>.broadcast();
StreamSubscription _streamSubscription;
Function(NotificationEvent) _listener;
init{
// add the rest of the code to initialise firebase here
_firebaseMessaging.configure(
/// Fires when App was in foreground when receiving the notification
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
_controller.sink.add(NotificationEvent(content: message, inApp: true));
},
/// Fires when App was in background when receiving the notification and user has tapped on it
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
_controller.sink.add(NotificationEvent(content: message));
}
/// Fires when App was closed when receiving the notification and user has tapped on it
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
_controller.sink.add(NotificationEvent(content: message));
},
);
_streamSubscription =
_controller.stream.listen(_onStreamEvent, onError: (e) {
print("Notification Stream error $e");
});
}
setListener(Function(NotificationEvent) onData) {
this._listener = onData;
}
}
In main.dart:
// imports go here
void main(){
final notificationReceiver = NotificationReceiver.firebase();
runApp(
MultiProvider(
providers: [
Provider<NotificationReceiver>(
builder: (_) => notificationReceiver),
// more providers go here
],
child: App(), // Your custom app class
),
);
}
In notification_listenable.dart:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class NotificationListenable extends StatefulWidget {
final Widget child;
final Function(NotificationEvent) onData;
const NotificationListenable({#required this.child, this.onData});
#override
_NotificationListenableState createState() => _NotificationListenableState();
}
class _NotificationListenableState extends State<NotificationListenable> {
#override
Widget build(BuildContext context) {
Provider.of<NotificationReceiver>(context).setListener(widget.onData);
return widget.child;
}
}
In my_screen.dart:
/// add your imports here
class MyScreen extends StatefulWidget {
#override
HomePageState createState() => HomePageState();
}
class MyScreenState extends State<MyScreen> {
final _scaffoldKey = GlobalKey<ScaffoldState>();
void _onNotification(NotificationEvent n) {
(_scaffoldKey.currentState)?.showSnackBar(
SnackBar(
duration: Duration(seconds: 2),
content: Text("I am a pop up"),
),
),
}
#override
Widget build(BuildContext context) {
return NotificationListenable(
child: YourCustomScreenContent(),
onData: _onNotification,
);
}