Why I got broadcast received for message rather than recieving a message on device with flutter? - flutter

I'm on creating a flutter application with Firebase Cloud Messaging and cloud-functions I'm trying to send notification via FCM with flutter But all what I goit is broadcast received for message in the terminal. and Exception from a finished function: Error: tokens must be a non-empty array in cloud-functions debug
this is my code :
Future<void> configure() async {
await _firebaseMessaging.getToken().then((token) async {
print("FCM: $token");
await _firestore
.collection("flutterTokens")
.doc()
.set({'token': token});
});
}
Future init() async {
final settings = await _requestPermission();
//await _getToken();
await configure();
//await _getToken();
// _registerForegroundMessageHandler();
}
Future<void> _messageHandler(RemoteMessage message) async {
print(
'background message ${message.notification!.body} + ${message.notification!.title}');
}
in the initState():
await _msgService.init();
FirebaseMessaging.onBackgroundMessage(_messageHandler);

Related

Flutter - an async function returns before really finishing?

I have a function scanAndConnect() that should scan for BLE devices and connect to the device with the specified service ID. This function should be async and should return Future.
The problem is that scanAndConnect() prints 99999 and returns without waiting for flutterReactiveBle.statusStream.listen() to finish although I use await before it.
Future scanAndConnect(Uuid serviceId, Uuid charctId) async {
StreamSubscription<BleStatus>? bleStatusStreamSubscription;
StreamSubscription<DiscoveredDevice>? deviceStreamSubscription;
Stream<DiscoveredDevice> stream;
bleStatusStreamSubscription =
await flutterReactiveBle.statusStream.listen((bleStatus) async {
print("new listen ${bleStatus.toString()}");
if (bleStatus == BleStatus.ready) {
await bleStatusStreamSubscription!.cancel();
connectionStatus = BLEConnectionStatus.Connecting;
stream = await flutterReactiveBle.scanForDevices(
withServices: [serviceId],
scanMode: ScanMode.lowLatency,
);
}
});
print("9999999");
}
....
Future connectToDevice() async {
await ble.scanAndConnect(BLE_SERVICE_UUID, BLE_CHAR_UUID)
print("Statement after await in main");
setState(() {
loading = false;
print("Changing state to ${loading.toString()}");
});
}
This is the output I get in Xcode:
flutter: 9999999
flutter: Statement after await in main
flutter: Changing state to false
flutter: new listen BleStatus.unknown
flutter: new listen BleStatus.ready
How can I make scanAndConnect doesn't return before really finishing?
According to the documentation, FlutterReactiveBle.scanForDevices() returns a Stream, not a Future, so await will not work here. You can use
await for
listen()
await stream.first()
to wait for data from a Stream.

Can't Read Data of Notification Message - Flutter

I used postman for Creating a Notification.Also I used Firebase Cloud Messaging as backend.
All works, even Notification pops in the android. But I can't read the data in my debug console ,
the Following errors are shown in console:
D/FLTFireMsgReceiver(23051): broadcast received for message
W/FLTFireMsgService(23051): A background message could not be handled in Dart as no onBackgroundMessage handler has been registered.
W/FirebaseMessaging(23051): Missing Default Notification Channel metadata in AndroidManifest. Default value will be used.
The Codes of the Push Notification class:
import 'dart:io';
import 'package:cholachol_drive/globalvariables.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
class PushNotificationService {
// final FirebaseMessaging fcm = FirebaseMessaging.instance;
Future initialize(context) async {
FirebaseMessaging.onMessage.listen((event) {
(Map<String, dynamic> message) async {
// retrieveRideRequestInfo(getRideRequestId(message), context);
print("onMessage: $message");
if (Platform.isAndroid) {
String rideID = message["data"]['ride_id'];
if (rideID != null) {
print("Fucked");
} else {
print('ride_id: $rideID');
}
}
};
});
FirebaseMessaging.onMessageOpenedApp.listen((event) {
(Map<String, dynamic> message) async {
// retrieveRideRequestInfo(getRideRequestId(message), context);
print("onLaunch: $message");
if (Platform.isAndroid) {
String rideID = message["data"]['ride_id'];
if (rideID != null) {
print("Fucked");
} else {
print('ride_id: $rideID');
}
}
};
});
}
Future<String> getToken() async {
String token = await FirebaseMessaging.instance.getToken();
print("token: $token");
DatabaseReference tokerRef = FirebaseDatabase.instance
.ref()
.child("drivers/${currentFirebaseUser.uid}/token");
FirebaseMessaging.instance.subscribeToTopic('alldrivers');
FirebaseMessaging.instance.subscribeToTopic('allusers');
}
}
It seems you don't have onBackgroundMessage implemented in your app. You need to use onBackgroundMessage in order to handle background messages. onMessage will only handle foreground messages.

Flutter Unhandled exception: ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized

I am trying to create an Isolate un Flutter and then use this isolate to fetch some data from Firebase Realtime Database.
I am creating de Isolate in a file called home.dart (not main) and here is my code for that file. I have a class to create the Isolate and the function for the Isolate to execute. Inside this function I am trying to fetch the data.
void elIsolate(SendPort sPort) async {
print("Fetching data");
final databaseReference = FirebaseDatabase.instance.reference().child("categories");
DataSnapshot info;
/*databaseReference.once().then((DataSnapshot snapshot) {
info = snapshot;
print(info.value);
});*/
print("new isolate created");
IsolateChannel channel = IsolateChannel.connectSend(sPort);
channel.stream.listen((data) {
print('newIsolate received : $data');
});
channel.sink.add("hi");
}
class _MyHomePageState extends State<MyHomePage> {
List list = [];
void initState(){
WidgetsFlutterBinding.ensureInitialized();
super.initState();
print("Init state");
loadIsolate();
}
Future loadIsolate() async {
await Firebase.initializeApp();
print("Load isolate");
ReceivePort rPort = ReceivePort();
IsolateChannel channel = IsolateChannel.connectReceive(rPort);
channel.stream.listen((data) {
print('rootIsolate received : $data');
channel.sink.add('How are you');
});
await Isolate.spawn(elIsolate, rPort.sendPort);
/*await Isolate.spawn(getAllWorkers, receivePort.sendPort);
receivePort.listen((message) {
print(message);
});*/
}
}
Then I have my main.dart. I added this line inside the main function: WidgetsFlutterBinding.ensureInitialized();
Here is my code
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
bool resp;
await SharedPreferences.getInstance().then((prefs) {
resp = prefs.getBool('isUser');
if (resp == null) {
FirebaseAuth _auth = FirebaseAuth.instance;
resp = (_auth.currentUser != null);
prefs.setBool('isUser', resp);
}
});
runApp(MyApp(user: resp));
}
flutter_isolate: ^2.0.2
onPressed: () {
FlutterIsolate.spawn(_isolateEntrypoint, "");
}
// A "top level" function (i.e. not inside a class or make it static)
_isolateEntrypoint(String foo) {
WidgetsFlutterBinding.ensureInitialized();
...
}
Make sure that authorization and initialization were made on the same main thread (top level or static).
Now this FlutterEngine will be able to communicate with Firebase Realtime Database but the main FlutterEngine won't. In practice, depending on the app, an app may want to communicate with Realtime Database from either engine (or both). In background apps, more likely from here rather than the main isolate, but again that depends on the app.

Flutter how to access provider.of(context) in FCM backgroundHandler static method?

I have successfully setup background notifications and tested it using postman and all is good.
Now I need to access Provider.of(context) in my backgroundHandler which must be a static method where there is no context.
All I need to do is to perform an action according to the data in the background notification.
Here is my code for initializing FCM (I do it in Splash screen)
Future<void> initFcm() async {
_firebaseMessaging.configure(
onBackgroundMessage: myBackgroundMessageHandler,
onMessage: (msg) async {
print('this is ONMESSAGE $msg');
},
onLaunch: (msg) async {
print('ON LAUNCH');
},
onResume: (msg) async {
print('ON RESUME');
},
);
// For testing purposes print the Firebase Messaging token
deviceToken = await _firebaseMessaging.getToken();
print("FirebaseMessaging token: $deviceToken");
}
static Future<dynamic> myBackgroundMessageHandler(
Map<String, dynamic> message) async {
print(message);
if (message.containsKey('data')) {
// Handle data message
final dynamic data = message['data'];
final orderId = data['order_id'];
//THE PROBLEM IS THAT HERE I DON'T HAVE CONTEXT (coz static method)
Provider.of<Orders>(context, listen: false).setBackgroundStatus(orderId);
}
if (message.containsKey('notification')) {
// Handle notification message
final dynamic notification = message['notification'];
}
// Or do other work.
}
My question is how can I handle tasks in provider in my backgroundHandler which is a static method that doesn't have context?

Save and Retrieve notification with shared preferences list flutter

I have implemented Firebase Cloud Messaging in my flutter application. Everything works well but I want to store all list of messages locally with shared preferences and retrieve them in another screen. All my logic does not work well as I can't save the messages when the onMessage function is called.
PushNotificationService
class PushNotificationService {
final FirebaseMessaging _fcm = FirebaseMessaging();
List<String> titles;
List<String> msgs;
Future initialise() async {
notiList = List<NotiMessage>();
if (Platform.isIOS) {
// request permissions if we're on android
_fcm.requestNotificationPermissions(IosNotificationSettings());
_fcm.configure();
// For testing purposes print the Firebase Messaging token
String token = await _fcm.getToken();
print("FirebaseMessaging token: $token");
} else{
String token = await _fcm.getToken();
print("FirebaseMessaging token: $token");
}
_fcm.configure(
// Called when the app is in the foreground and we receive a push notification
onMessage: (Map<String, dynamic> message) async {
print('onMessage: $message');
//add list of messages to shared preferences
_setMessage(message);
},
// Called when the app has been closed comlpetely and it's opened
// from the push notification.
onLaunch: (Map<String, dynamic> message) async {
print('onLaunch: $message');
_serialiseAndNavigate(message);
//add list of messages to shared preferences
_setMessage(message);
},
// Called when the app is in the background and it's opened
// from the push notification.
onResume: (Map<String, dynamic> message) async {
print('onResume: $message');
_serialiseAndNavigate(message);
//add list of messages to shared preferences
_setMessage(message);
},
);
}
void _serialiseAndNavigate(Map<String, dynamic> message) {
var notificationData = message['data'];
var view = notificationData['view'];
if (view != null) {
// Navigate to desired page
if (view == 'create_post') {
}
}
}
_setMessage(Map<String, dynamic> message) {
//add list of messages to shared preferences
final notification = message['notification'];
final data = message['data'];
final String title = notification['title'];
final String body = notification['body'];
String mMessage = data['message'];
//add to list
titles.add(title);
msgs.add(mMessage);
//save to shared preferences (does not work)
storeTitles(titles);
storeMsgs(msgs);
print("Title: $title, body: $body, message: $mMessage");
}
void storeTitles(List<String> list) async{
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setStringList("notiTitles", list);
//list returns null
}
void storeMsgs(List<String> list) async{
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setStringList("notiMsgs", list);
}
Future<List<String>> getTitles(List<String> list) async{
SharedPreferences prefs = await SharedPreferences.getInstance();
list = prefs.getStringList("notiTitles");
return prefs.getStringList("notiTitles");
}
Future<List<String>> getMsgs(List<String> list) async{
SharedPreferences prefs = await SharedPreferences.getInstance();
list = prefs.getStringList("notiMsgs");
return prefs.getStringList("notiMsgs");
}
}
Whats the best way to achieve this. I want to save the messages persistently and call them in another screen. Please help me.
The code on saving the List on shared_preferences seems to be ok. The issue might be on how the data is fetched. If you're storing critical data, I suggest to better use something like provider instead. The shared_preferences plugin is unable to guarantee that writes will be persisted to disk after returning as mentioned in its docs.