Opening a popup in a flutter Flutter when onBackgroundMessage gets executed - flutter

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,
);
}

Related

how to mock firebase_messaging in flutter?

Hello im trying to mock firebase messaging to get token but when i try to test i get some error,can someone help me to solve this error. This error occur only in testing and not in my emulator or mobile phone. Here is my setupFirebaseAuthMocks. Thank you
my test
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
}
void main() {
setupFirebaseAuthMocks();
late ProviderContainer container;
group('AuthenticationControllerTest -', () {
setUpAll(() async {
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
registerThirdPartyServices();
});
tearDown(() {
unregisterThirdPartyServices();
//container.dispose();
});
});
Error
MissingPluginException(No implementation found for method Messaging#getToken on channel plugins.flutter.io/firebase_messaging)
here is the method im trying to call
Future<Result<Failure, bool>> registerUserFirebaseToken() async {
try {
log.i('Registering Firebase');
final fireBaseMessaging = FirebaseMessaging.instance;
final token = await fireBaseMessaging.getToken();
log.v('Firebase token: $token');
await api.post(
link: '${env.getValue(kAuthUrl)}users/auth/firebase',
body: {'token': token},
hasHeader: true,
);
return const Success(true);
} catch (e) {
return Error(Failure(message: 'Firebase registration went wrong, Please try again!', content: e.toString()));
}
}
For those having the same issue, there is an example of a Mock on the official firebase messaging Github
Depending on your Mockito's version, you may have to update this code a little bit.
Here is the Mock file I'm using with Mockito v5.3.2
// ignore_for_file: require_trailing_commas
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart';
import 'package:firebase_messaging_platform_interface/firebase_messaging_platform_interface.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
typedef Callback = Function(MethodCall call);
final MockFirebaseMessaging kMockMessagingPlatform = MockFirebaseMessaging();
Future<T> neverEndingFuture<T>() async {
// ignore: literal_only_boolean_expressions
while (true) {
await Future.delayed(const Duration(minutes: 5));
}
}
void setupFirebaseMessagingMocks() {
TestWidgetsFlutterBinding.ensureInitialized();
setupFirebaseCoreMocks();
// Mock Platform Interface Methods
// ignore: invalid_use_of_protected_member
when(kMockMessagingPlatform.delegateFor(app: anyNamed('app')))
.thenReturn(kMockMessagingPlatform);
// ignore: invalid_use_of_protected_member
when(kMockMessagingPlatform.setInitialValues(
isAutoInitEnabled: anyNamed('isAutoInitEnabled'),
)).thenReturn(kMockMessagingPlatform);
}
// Platform Interface Mock Classes
// FirebaseMessagingPlatform Mock
class MockFirebaseMessaging extends Mock
with MockPlatformInterfaceMixin
implements FirebaseMessagingPlatform {
MockFirebaseMessaging() {
TestFirebaseMessagingPlatform();
}
#override
bool get isAutoInitEnabled {
return super.noSuchMethod(Invocation.getter(#isAutoInitEnabled),
returnValue: true, returnValueForMissingStub: true) as bool;
}
#override
FirebaseMessagingPlatform delegateFor({FirebaseApp? app}) {
return super.noSuchMethod(
Invocation.method(#delegateFor, [], {#app: app}),
returnValue: TestFirebaseMessagingPlatform(),
returnValueForMissingStub: TestFirebaseMessagingPlatform(),
) as FirebaseMessagingPlatform;
}
#override
FirebaseMessagingPlatform setInitialValues({bool? isAutoInitEnabled}) {
return super.noSuchMethod(
Invocation.method(
#setInitialValues, [], {#isAutoInitEnabled: isAutoInitEnabled}),
returnValue: TestFirebaseMessagingPlatform(),
returnValueForMissingStub: TestFirebaseMessagingPlatform(),
) as FirebaseMessagingPlatform;
}
#override
Future<RemoteMessage?> getInitialMessage() {
return super.noSuchMethod(Invocation.method(#getInitialMessage, []),
returnValue: neverEndingFuture<RemoteMessage>(),
returnValueForMissingStub: neverEndingFuture<RemoteMessage>())
as Future<RemoteMessage?>;
}
#override
Future<void> deleteToken() {
return super.noSuchMethod(Invocation.method(#deleteToken, []),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as Future<void>;
}
#override
Future<String?> getAPNSToken() {
return super.noSuchMethod(Invocation.method(#getAPNSToken, []),
returnValue: Future<String>.value(''),
returnValueForMissingStub: Future<String>.value('')) as Future<String?>;
}
#override
Future<String> getToken({String? vapidKey}) {
return super.noSuchMethod(
Invocation.method(#getToken, [], {#vapidKey: vapidKey}),
returnValue: Future<String>.value(''),
returnValueForMissingStub: Future<String>.value('')) as Future<String>;
}
#override
Future<void> setAutoInitEnabled(bool? enabled) {
return super.noSuchMethod(Invocation.method(#setAutoInitEnabled, [enabled]),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as Future<void>;
}
#override
Stream<String> get onTokenRefresh {
return super.noSuchMethod(
Invocation.getter(#onTokenRefresh),
returnValue: const Stream<String>.empty(),
returnValueForMissingStub: const Stream<String>.empty(),
) as Stream<String>;
}
#override
Future<NotificationSettings> requestPermission(
{bool? alert = true,
bool? announcement = false,
bool? badge = true,
bool? carPlay = false,
bool? criticalAlert = false,
bool? provisional = false,
bool? sound = true}) {
return super.noSuchMethod(
Invocation.method(#requestPermission, [], {
#alert: alert,
#announcement: announcement,
#badge: badge,
#carPlay: carPlay,
#criticalAlert: criticalAlert,
#provisional: provisional,
#sound: sound
}),
returnValue: neverEndingFuture<NotificationSettings>(),
returnValueForMissingStub:
neverEndingFuture<NotificationSettings>())
as Future<NotificationSettings>;
}
#override
Future<void> subscribeToTopic(String? topic) {
return super.noSuchMethod(Invocation.method(#subscribeToTopic, [topic]),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as Future<void>;
}
#override
Future<void> unsubscribeFromTopic(String? topic) {
return super.noSuchMethod(Invocation.method(#unsubscribeFromTopic, [topic]),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as Future<void>;
}
}
class TestFirebaseMessagingPlatform extends FirebaseMessagingPlatform {
TestFirebaseMessagingPlatform() : super();
}
and here is the unit test itself
void main() {
setupFirebaseMessagingMocks();
setUpAll(() async {
await Firebase.initializeApp();
FirebaseMessagingPlatform.instance = kMockMessagingPlatform;
});
test('An example of test', () {
//...
when(kMockMessagingPlatform.getToken(vapidKey: anyNamed('vapidKey')))
.thenAnswer(
(_) => Future.value('DEVICE_ID'),
);
//...
});
}

Succeeding Bloc are not working after latest Bloc migration

I am using a MultiBlocProvider which is working for all Bloc before I migrate it to v8.0.1. Now, only the first Bloc (SignInBloc) is working.
This is on my main.dart
return MultiBlocProvider(
providers: [
BlocProvider<SignInBloc>(
create: (context) => SignInBloc(
authenticationRepository: authenticationRepository,
userDataRepository: userDataRepository,
),
),
BlocProvider<SignUpBloc>(
create: (context) => SignUpBloc(
authenticationRepository: authenticationRepository,
userDataRepository: userDataRepository,
),
),
Edit: here is my SignInBloc
SignInBloc(
{required this.authenticationRepository,
required this.userDataRepository})
: super(SignInInitialState()) {
on<CheckIfSignedInEvent>(mapCheckIfSignedInEventToState);
}
Future<void> mapCheckIfSignedInEventToState(
CheckIfSignedInEvent event,
Emitter<SignInState> emit,
) async {
try {
bool isSignedIn = await authenticationRepository.checkIfSignedIn();
if (isSignedIn) {
emit(CheckIfSignedInEventCompletedState(true));
} else {
emit(CheckIfSignedInEventCompletedState(false));
}
} catch (e) {
print(e);
emit(CheckIfSignedInEventFailedState());
}
}
I am not sure what to show but here is my SignUpBloc which is similar to my SignInBloc
SignUpBloc(
{required this.authenticationRepository,
required this.userDataRepository})
: super(SignUpInitialState()) {
on<SignUpWithGoogle>(mapSignUpWithGoogleEventToState);
}
Stream<SignUpState> mapSignUpWithGoogleEventToState(
SignUpWithGoogle event,
Emitter<SignUpState> emit,
) async* {
emit(SignUpInProgressState());
try {
User? checkUser = await authenticationRepository.checkIfUserExists();
if (checkUser != null) {
emit(SignUpWithGoogleInitialExistState());
} else {
bool checkDup =
await authenticationRepository.checkIfUserDup(event.name);
if (checkDup == true) {
emit(SignUpWithNameExistState());
} else {
User firebaseUser = await authenticationRepository.signUpWithGoogle();
emit(SignUpWithGoogleInitialCompletedState(firebaseUser));
}
}
} catch (e) {
print(e);
emit(SignUpWithGoogleInitialFailedState());
}
}
My main.dart will call the splash screen which has the declaration of the bloc
late SignInBloc signInBloc;
late SignUpBloc signupBloc;
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
signInBloc = BlocProvider.of<SignInBloc>(context);
signupBloc = BlocProvider.of<SignUpBloc>(context);
What I tried to do it to put alot of Print statement in order to check which part is getting called but I don't get why the SignUpBloc is not getting called anymore. Please help. Thanks!
Edit: I tried to debug.
This will trigger my SignInBloc. I'm able to listen to my SignInBloc.
signInBloc.add(CheckIfSignedInEvent());
This should trigger my SignUpBloc. But it doesn't do anything similar to my SignInBloc.
signupBloc.add(SignUpWithGoogle(name: selectedName));
Here's both of my events for comparison:
class CheckIfSignedInEvent extends SignInEvent {
#override
String toString() => 'CheckIfSignedInEvent';
}
class SignUpWithGoogle extends SignUpEvent {
final String name;
SignUpWithGoogle({required this.name});
#override
String toString() => 'SignUpWithGoogleEvent';
}
This is the part where I listen to the states which is both in my splash screen. Only signInBloc is able to listen.
signupBloc.stream.listen((state) {
print('BLOC: signupBloc splash screen init : $state');
});
signInBloc.stream.listen((state) {
print('BLOC: signinBloc splash screen init : $state');
});
It turns out that changing the Stream to Future will fix my issue. async* should also be changed to async
Future<void> mapSignUpWithGoogleEventToState(
SignUpWithGoogle event,
Emitter<SignUpState> emit,
) async {
emit(SignUpInProgressState());
try {
User? checkUser = await authenticationRepository.checkIfUserExists();
if (checkUser != null) {
emit(SignUpWithGoogleInitialExistState());
} else {
bool checkDup =
await authenticationRepository.checkIfUserDup(event.name);
if (checkDup == true) {
emit(SignUpWithNameExistState());
} else {
User firebaseUser = await authenticationRepository.signUpWithGoogle();
emit(SignUpWithGoogleInitialCompletedState(firebaseUser));
}
}
} catch (e) {
print(e);
emit(SignUpWithGoogleInitialFailedState());
}
}

flutter firebase cloud messaging

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.

Flutter Pusher Websocket package not working

I have a backend Laravel application that uses Pusher for notifications. I would like to show notifications in my Flutter app (both iOS and Android). I found that https://pub.dev/packages/pusher_websocket_flutter/ package has the best score, but I can't get it to work. I've followed this tutorial, and I get no errors (whatever I put for my APP_KEY, which must be wrong), but I never get anything shown.
Has anyone managed to get this working, or should I switch to firebase?
This is my pusher_service.dart:
import 'package:flutter/services.dart';
import 'package:pusher_websocket_flutter/pusher.dart';
import 'dart:async';
class PusherService {
Event lastEvent;
String lastConnectionState;
Channel channel;
StreamController<String> _eventData = StreamController<String>();
Sink get _inEventData => _eventData.sink;
Stream get eventStream => _eventData.stream;
Future<void> initPusher() async {
try {
await Pusher.init('XXX', PusherOptions(cluster: 'XX'), enableLogging: true);
print("Pusher initialized");
}
on PlatformException catch (e) {
print(e.message);
}
}
void connectPusher() {
Pusher.connect(
onConnectionStateChange: (ConnectionStateChange connectionState) async {
lastConnectionState = connectionState.currentState;
print("Pusher connected");
}, onError: (ConnectionError e) {
print("Error: ${e.message}");
});
}
Future<void> subscribePusher(String channelName) async {
channel = await Pusher.subscribe(channelName);
print("Pusher subscribed to channel");
}
void unSubscribePusher(String channelName) {
Pusher.unsubscribe(channelName);
}
void bindEvent(String eventName) {
channel.bind(eventName, (last) {
final String data = last.data;
_inEventData.add(data);
});
print("Pusher data binded");
}
void unbindEvent(String eventName) {
channel.unbind(eventName);
_eventData.close();
}
Future<void> firePusher(String channelName, String eventName) async {
await initPusher();
connectPusher();
await subscribePusher(channelName);
bindEvent(eventName);
}
}
My pusher_test.dart:
import 'package:flutter/material.dart';
import 'package:chalet/services/pusher_service.dart';
import 'package:pusher/pusher.dart';
import 'dart:async';
class PusherTest extends StatefulWidget {
#override
_PusherTestState createState() => _PusherTestState();
}
class _PusherTestState extends State<PusherTest> {
PusherService pusherService = PusherService();
#override
void initState() {
pusherService = PusherService();
pusherService.firePusher('public', 'create');
testPusher();
super.initState();
}
#override
void dispose() {
pusherService.unbindEvent('create');
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: StreamBuilder(
stream: pusherService.eventStream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
return Container(
child: Text(snapshot.data),
);
},
),
),
);
}
}
I've checked and my snapshot.connectionState is always waiting.
Try this:
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'package:pusher_client/pusher_client.dart';
//instantiate Pusher Class
class PusherController {
static final PusherController _pusherController =
PusherController._internal();
factory PusherController() {
return _pusherController;
}
PusherController._internal();
PusherClient pusher;
Channel channel;
StreamController<String> _eventData = StreamController<String>.broadcast();
Sink get _inEventData => _eventData.sink;
Stream get eventStream => _eventData.stream;
String channelName = "";
String prevChannelName = "";
String eventName = "";
void initPusher() {
PusherOptions options = PusherOptions(
cluster: "eu",
);
pusher = new PusherClient("key", options,
autoConnect: true, enableLogging: true);
}
void setChannelName(String name) {
channelName = name;
print("channelName: ${channelName}");
}
void setEventName(String name) {
eventName = name;
print("eventName: ${eventName}");
}
void subscribePusher() {
channel = pusher.subscribe(channelName);
pusher.onConnectionStateChange((state) {
log("previousState: ${state.previousState}, currentState: ${state.currentState}");
});
pusher.onConnectionError((error) {
log("error: ${error.message}");
});
//Bind to listen for events called and sent to channel
channel.bind(eventName, (PusherEvent event) {
print("xxxxxxxxx From pusher xxxxxxxxx");
print('xxxxx This is Event name - $eventName xxxx');
print('xxxxx This is Event gotten - ${event.data} xxx');
_inEventData.add(event.data);
prevChannelName = eventName;
});
}
void connectPusher() {
pusher.connect();
}
void disconnectPusher() async {
await channel.unbind(eventName);
await pusher.disconnect();
}
}
Then use streamBuilder and stream from evenStream.

Firebase OnMessage function refreshes widget

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.