I am still in the process of doing the above task(Send Firebase Notifications in Flutter App based on User role type,), Notification should send when user press the Report Button.
Error is
lib/screens/report.dart:96:33: Error: The method ‘sendNotification’ isn’t defined for the class ‘FirebaseMessaging’.
- ‘FirebaseMessaging’ is from ‘package:firebase_messaging/firebase_messaging.dart’ (‘../../AppData/Local/Pub/Cache/hosted/pub.dev/firebase_messaging-14.6.2/lib/firebase_messaging.dart’).
Try correcting the name to the name of an existing method, or defining a method named ‘sendNotification’.
return _firebaseMessaging.sendNotification(
^^^^^^^^^^^^^^^^
report.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:intl/intl.dart';
import 'package:leishsys/models/case.dart';
import '../collection.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:location/location.dart';
import 'package:geocoding/geocoding.dart' as geocoding;
import 'package:firebase_auth/firebase_auth.dart' as auth;
import 'dart:io';
import 'package:image_picker/image_picker.dart';
import 'package:firebase_storage/firebase_storage.dart' as storage;
import 'body_parts_screen.dart';
import 'diagnsotic_screen.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
class ReportScreen extends StatefulWidget {
const ReportScreen({Key? key}) : super(key: key);
static const routeName = "/report";
@override
State<ReportScreen> createState() => _ReportScreenState();
}
class _ReportScreenState extends State<ReportScreen> {
int _sum = 0;
late Case _case;
final _usernameRpt = TextEditingController();
String _reporterDesignation = "";
final _otherRpt = TextEditingController();
DateTime _reportDate = DateTime.now();
final _ptName = TextEditingController();
final _ptAge = TextEditingController();
final _ptNic = TextEditingController();
String _ptSex = "";
bool _presumptiveCase = false;
final _ptAddress = TextEditingController();
final _ptResidentialArea = TextEditingController();
LatLng? _selectedLocation;
final _ptMoh = TextEditingController();
final _ptPhi = TextEditingController();
final _ptGn = TextEditingController();
final _ptOccupation = TextEditingController();
final _ptOccupationalArea = TextEditingController();
final _ptTravelHistory = TextEditingController();
final _ptPhone = TextEditingController();
bool _confirmedCase = false;
String _confirmedMethod = "";
late File _lesionImage;
bool _uploadingImage = false;
late List<String> _lesionAreas;
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
@override
void initState() {
super.initState();
_setInitialUsername();
_configureFirebaseMessaging();
}
void _configureFirebaseMessaging() {
// Request permission for receiving notifications
FirebaseMessaging.instance.requestPermission();
// Configure notification behavior when the app is in the foreground
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
// Handle the received notification message
print('Foreground Notification: ${message.notification?.body}');
});
// Configure notification behavior when the app is in the background
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
// Handle the tapped notification message
print('Background Notification: ${message.notification?.body}');
});
}
Future<void> _sendNotificationToReviewersAndAdmins() async {
// Retrieve all users with role "reviewer" or "admin"
final snapshot = await FirebaseFirestore.instance
.collection('users')
.where('role', whereIn: ['reviewer', 'admin'])
.get();
// Extract the FCM tokens of the users
final fcmTokens = snapshot.docs.map((doc) => doc['fcmToken']).toList();
// Send the notification
await Future.wait(fcmTokens.map((token) {
return _firebaseMessaging.sendNotification(
RemoteMessage(
data: {
'click_action': 'FLUTTER_NOTIFICATION_CLICK',
'id': '1',
'status': 'done',
},
notification: RemoteNotification(
title: 'New Report Submitted',
body: 'A new report has been submitted.',
),
token: token,
),
);
}));
print('Notification sent to reviewers and admins.');
}
Future<void> _setInitialUsername() async {
final user = auth.FirebaseAuth.instance.currentUser;
if (user != null) {
setState(() {
_usernameRpt.text = user.email ?? '';
});
}
}
//there's some code
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.green,
title: const Text("Report a Case"),
),
body: SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(),
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: "Reporter's Username *"),
controller: _usernameRpt,
keyboardType: TextInputType.text,
enabled: false,
),
),
// there's code here
Padding(
padding: const EdgeInsets.all(8.0),
child: IgnorePointer(
ignoring: _disabled,
child: Opacity(
opacity: _disabled ? 0.5 : 1.0,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
),
onPressed: () {
_sendNotificationToReviewersAndAdmins();
showDialog(
context: context,
builder: (BuildContext context) {
return FutureBuilder(
future: _reportCase(_case),
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return const AlertDialog(
title: Text('Submitting Report'),
content: LinearProgressIndicator(
backgroundColor: Colors.green,
),
);
} else {
if (snapshot.hasData &&
snapshot.data == true) {
return AlertDialog(
title: const Text('Report Submitted'),
content: const Text(
'Your report has been submitted successfully.'),
actions: [
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
Navigator.of(context).pop();
},
child: const Text('OK'),
),
],
);
} else {
return AlertDialog(
title: const Text(
'Report Not Submitted'),
content: const Text(
'There was an error submitting your report.'),
actions: [
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(Colors.green),
),
child: const Text('OK'),
),
],
);
}
}
},
);
},
);
setState(() {
_case = Case.report(
usernameRpt: _usernameRpt.text,
reporterDesignation:
_reporterDesignation.toString(),
otherRpt: _otherRpt.text,
reportDate: _reportDate,
ptName: _ptName.text,
ptNic: _ptNic.text,
ptAge: int.parse(_ptAge.text),
ptSex: _ptSex.toString(),
lesionAreas:_lesionAreas,
presumptiveCase: _presumptiveCase,
ptAddress: _ptAddress.text,
ptResidentialArea: _ptResidentialArea.text,
ptMoh: _ptMoh.text,
ptPhi: _ptPhi.text,
ptGn: _ptGn.text,
ptOccupation: _ptOccupation.text,
ptOccupationalArea: _ptOccupationalArea.text,
ptTravelHistory: _ptTravelHistory.text,
ptPhone: int.parse(_ptPhone.text),
confirmedCase: _confirmedCase,
confirmedMethod: _confirmedMethod.toString(),
);
_pressedReport = true;
});
},
child: const Text("Report"),
),
),
),
)
],
)),
),
),
);
}
Future<bool> _reportCase(Case kase) async {
//case cannot be used. So I used kase
final db = FirebaseFirestore.instance;
try {
final latitude = _selectedLocation?.latitude;
final longitude = _selectedLocation?.longitude;
kase.latitude = latitude;
kase.longitude = longitude;
final downloadURL = await _uploadImage();
kase.lesionImageUrl = downloadURL;
if (downloadURL != null) {
kase.lesionImageUrl = downloadURL;
} else {
// Handle error when image upload fails
return false;
}
await db
.collection(Collection.cases)
.withConverter(
fromFirestore: Case.fromFirestore,
toFirestore: (value, options) {
return kase.toFirestore();
},
)
.doc(kase.id)
.set(kase);
} catch (e) {
print(e.toString());
return false;
} finally {
await Future.delayed(const Duration(milliseconds: 1000));
}
return true;
}
}
//some code here
main.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:leishsys/screens/login_screen.dart';
import 'package:leishsys/screens/report.dart';
import 'package:leishsys/screens/reported_cases.dart';
import 'package:leishsys/screens/reviewer_home.dart';
import 'package:leishsys/screens/superadmin.dart';
import 'auth/auth_functions.dart';
import 'screens/home/home.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(Main());
}
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print('Handling a background message: ${message.messageId}');
}
class Main extends StatelessWidget {
Main({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(primaryColor: CupertinoColors.activeGreen),
title: "LeishSys",
home: MainScreen(),
routes: {
Home.routeName: (context) => Home(),
ReportScreen.routeName: (context) => ReportScreen(),
CasesScreen.routeName: (context) => CasesScreen(),
}
);
}
}
class MainScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data != null) {
UserHelper.saveUser(snapshot.data!,'','','');
return StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection("users")
.doc(snapshot.data?.uid)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasData && snapshot.data != null) {
final userDoc = snapshot.data!;
final user = userDoc.data();
if (user != null && user is Map<String, dynamic>) {
if (user['role'] == 'reviewer') {
return AdminHomePage();
} else if(user['role'] == 'admin'){
return SuperAdminHomePage();
}
else {
return Home();
}
} else {
return const Material(
child: Center(
child: Text('Error retrieving user data'),
),
);
}
} else {
return const Material(
child: Center(
child: CircularProgressIndicator(),
),
);
}
},
);
}
return LoginPage();
});
}
}
auth_functions.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:package_info_plus/package_info_plus.dart';
class AuthHelper {
static final FirebaseAuth _auth = FirebaseAuth.instance;
static Future<User?> signInWithEmail({
required String email,
required String password,
}) async {
try {
final res = await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
final User? user = res.user;
return user;
} catch (e) {
throw e;
}
}
static Future<User?> signupWithEmail({
required String email,
required String password,
required String name,
required String designation,
required String telephone,
}) async {
try {
final res = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
final User? user = res.user;
if (user != null) {
await UserHelper.saveUser(user, name, designation, telephone);
}
return user;
} catch (e) {
throw e;
}
}
static Future<void> logOut() async {
await _auth.signOut();
}
}
class UserHelper {
static final FirebaseFirestore _db = FirebaseFirestore.instance;
static Future<void> saveUser(
User user,
String name,
String designation,
String telephone,
) async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
int buildNumber = int.parse(packageInfo.buildNumber);
Map<String, dynamic> userData = {
"name": name,
"designation": designation,
"telephone": telephone,
"email": user.email,
"last_login": user.metadata.lastSignInTime?.millisecondsSinceEpoch,
"created_at": user.metadata.creationTime?.millisecondsSinceEpoch,
"role": "user",
"build_number": buildNumber,
};
final userRef = _db.collection("users").doc(user.uid);
final userDoc = await userRef.get();
if (userDoc.exists) {
final userData = userDoc.data();
if (userData != null && userData.containsKey('role')) {
userData['role'] = userData['role'];
} else {
userData?['role'] = 'user';
}
await userRef.update(userData as Map<Object, Object?>); // Explicit cast
} else {
await userRef.set(userData);
}
await _saveDevice(user);
}
static Future<void> _saveDevice(User user) async {
DeviceInfoPlugin devicePlugin = DeviceInfoPlugin();
String? deviceId;
late Map<String, dynamic> deviceData;
if (Platform.isAndroid) {
final deviceInfo = await devicePlugin.androidInfo;
deviceData = {
"os_version": deviceInfo.version.sdkInt.toString(),
"platform": 'android',
"model": deviceInfo.model,
"device": deviceInfo.device,
};
}
if (Platform.isIOS) {
final deviceInfo = await devicePlugin.iosInfo;
deviceId = deviceInfo.identifierForVendor;
deviceData = {
"os_version": deviceInfo.systemVersion,
"device": deviceInfo.name,
"model": deviceInfo.utsname.machine,
"platform": 'ios',
};
}
final nowMS = DateTime.now().toUtc().millisecondsSinceEpoch;
final deviceRef = _db
.collection("users")
.doc(user.uid)
.collection("devices")
.doc(deviceId);
if ((await deviceRef.get()).exists) {
await deviceRef.update({
"updated_at": nowMS,
"uninstalled": false,
});
} else {
await deviceRef.set({
"updated_at": nowMS,
"uninstalled": false,
"id": deviceId,
"created_at": nowMS,
"device_info": deviceData,
});
}
}
}
How to do this avoiding errors?
2
Answers
I implemented by this letting the users subscribing to a topic.
Subscription handled by this,
Firebase Cloud Messages can only send so-called downstream messages (messages to a device) from a trusted environment, such as your development machine, a server that you control, or something like Cloud Functions/Cloud Run. It does not support sending messages from one device directly to another device.
For an example of how to send messages through FCM with Cloud Functions, see the use-case in the documentation of notifying the user when something interesting happens.
Also see: