Flutter FCM problem: notification not appearing - flutter

I'm trying to use Firebase Cloud Messaging to set up push notifications for my app. I'm trying to start of simple by manually sending a notification payload and have it handled as a background notification in the android app. However, no notification appears in the emulator.
This is my current main.dart code:
import 'package:flutter/material.dart';
import 'package:redue/Screens/Welcome%20Screen/WelcomeScreen.dart';
import 'pages/menu.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
import 'pages/settings.dart';
import 'package:location/location.dart';
import 'pages/settings.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print("Handling a background message: ${message.messageId}");
}
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
Location location = new Location();
bool _serviceEnabled;
PermissionStatus _permissionGranted;
_serviceEnabled = await location.serviceEnabled();
if (!_serviceEnabled) {
_serviceEnabled = await location.requestService();
if (!_serviceEnabled) {
return null;
}
}
_permissionGranted = await location.hasPermission();
if (_permissionGranted == PermissionStatus.denied) {
_permissionGranted = await location.requestPermission();
if (_permissionGranted != PermissionStatus.granted) {
return null;
}
}
_firebaseMessaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
FirebaseMessaging messaging = FirebaseMessaging.instance;
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
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}');
}
});
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// This widget is the root of your application.
#override
void initState() {
super.initState();
currentTheme.addListener(() {
setState(() {});
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "ReDue",
debugShowCheckedModeBanner: false,
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
home: WelcomeScreen(),
);
}
}
I'm also using cloud firestore in my app as well as google services (why I ask for the location). I should also note that when I open the app in the emulator I have not recieved a notification permission method and in the firebase console when I publish the notification it says the notification has zero sends.

Related

how to disable push notifications by condition in Flutter?

I have an application, I want to send notifications only to authorized users,
authorization occurs by token if the token is empty, then the user is not authorized, for authorization and storing the token I use the shared_pref package. How can I disable notifications if the user is not logged in?
Token validation is only done in MyAppState notifications I need to disable in the same place
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await Permission.notification.request();
if(Constants.USER_TOKEN.isEmpty) {
FirebaseMessaging.instance.setAutoInitEnabled(false);
}
RemoteMessage? initialMessage =
await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) {
if (navKey.currentState != null) {
if(Constants.USER_TOKEN.isNotEmpty) {
navKey.currentState!.push(MaterialPageRoute(
builder: (context) => NotificationScreen()));
}
print('Message');
}
}
await _messaging.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification!.android;
if (notification != null && android != null) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channelDescription: channel.description,
icon: '#mipmap/ic_launcher',
),
),
);
}
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage? message) {
print("On message opened app: ${message?.data}}");
if (navKey.currentState != null) {
if(Constants.USER_TOKEN.isNotEmpty) {
navKey.currentState!.push(MaterialPageRoute(
builder: (context) => const NotificationScreen()));
}
}
});
var initializationSettingsAndroid =
const AndroidInitializationSettings('#drawable/ic_notification');
var initializationSettingsIOS = const DarwinInitializationSettings();
var initializationSettings = InitializationSettings(
android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
await _messaging.requestPermission();
String? token = await _messaging.getToken();
if (token != null) {
print("FIREBASE TOKEN $token");
} else {
print("CANNOT TAKE FIREBASE TOKEN");
}
if (Platform.isIOS) {
var APNS = await _messaging.getAPNSToken();
print('APNS: $APNS');
}
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
getToken() async {
SharedPreferences pref = await SharedPreferences.getInstance();
setState(() {
Constants.USER_TOKEN = pref.getString('login') ?? "";
});
}
#override
void didChangeDependencies() async {
super.didChangeDependencies();
await getToken();
}
Use deleteToken which invalidates the current token and getToken again to get a new one when enabling.
getToken() async {
SharedPreferences pref = await SharedPreferences.getInstance();
setState(() {
Constants.USER_TOKEN = pref.getString('login') ?? "";
});
if(Constants.USER_TOKEN.isEmpty()){
// delete firebase token
}else{
// get firebase token
}
}

Firebase Push Notification is Not working Properly

i have implemented firebase push notification in my project with payload json,
I have done all the configurations and now when i click on the notification, it works sometimes perfectly but sometimes the redirection just doesn't work, let's say if i click the notification 10 times, it redirects to the desired page for 4 times and rest 6 times, its taking me to home screen. I am sharing my code below.
Main File
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
DartPluginRegistrant.ensureInitialized();
await Firebase.initializeApp();
await SharedPref.initialize();
initilizeCamera();
initializeDb();
FlutterError.onError = (error) {
FirebaseCrashlytics.instance.recordFlutterFatalError(error);
};
PlatformDispatcher.instance.onError = (error, stack) {
FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
return true;
};
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
.then((_) {
runApp(
const ProviderScope(child: MyApp()),
);
});
await pushNotificationToken();
/// Get initial message
await FirebaseMessaging.instance.getInitialMessage();
notificationActions(
action: (RemoteMessage? message) {
if (kDebugMode) {
if (message?.notification != null) {
}
}
if (message != null) {
notificationAction(message: message);
}
},
localNotification: true,
localNotificationAction: (payload) {
if (kDebugMode) {
}
notificationAction(payload: payload);
});
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
systemNavigationBarColor: Colors.white,
statusBarColor: Colors.white,
));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return KeyboardDismissOnTap(
child: GetMaterialApp(
theme: appThemeData,
debugShowCheckedModeBanner: false,
title: 'XYZ',
// Start the app with the "/" named route. In this case, the app starts
// on the FirstScreen widget.
initialRoute: '/',
initialBinding:XYZBindings(),
routes: {
// When navigating to the "/" route, build the FirstScreen widget.
'/': (context) => const XYZScreen(),
}),
);
}
}
Firebase Notification Code
/// Firebase messaging instance
FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
/// Get firebase notification instance
FirebaseMessaging get firebaseMessagingInstance => _firebaseMessaging;
/// Firebase push notification token
Future<String?> pushNotificationToken() async =>
_firebaseMessaging.getToken().then((value) {
Log.w('FCM TOKEN :: $value');
return value;
});
/// When user clicks on the notification
void notificationActions({
#required Function(RemoteMessage?)? action,
bool localNotification = false,
Function(Map<String, dynamic>)? localNotificationAction,
}) {
/// Make sure localNotification is Not Null
if (localNotification) {
assert(localNotificationAction != null);
}
if (Platform.isIOS) {
_requestPermissions();
}
if (localNotification) {
FlutterLocalNotificationHelper().initializeSettings(
actionCallback: localNotificationAction,
);
}
/// Get initial message
_firebaseMessaging.getInitialMessage().then((RemoteMessage? message) {
action!(message);
});
/// On Message
FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
if (localNotification) {
await FlutterLocalNotificationHelper().showNotificationWithDefaultSound(
title: message.notification?.title,
body: message.notification?.body,
payload: jsonEncode(message.data),
);
}
});
/// On Message open app
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
action!(message);
});
}
/// Request permission for iOS
void _requestPermissions() {
_firebaseMessaging.requestPermission(
provisional: true,
);
}
Local Notification Page
/// Helper class for local notifications
class FlutterLocalNotificationHelper {
/// Constructor
factory FlutterLocalNotificationHelper() => _instance;
FlutterLocalNotificationHelper._();
/// Flutter LocalNotification Helper instance
static final FlutterLocalNotificationHelper _instance =
FlutterLocalNotificationHelper._();
/// Flutter LocalNotification for iOS Category
static const String darwinNotificationCategory = 'plainCategory';
/// Notification plugin
late FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin;
/// Flutter LocalNotifications Plugin
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
/// Request local notification
Future<void> requestLocalNotification() async {
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.requestPermission();
}
/// local notification plugin callback
late Function(Map<String, dynamic>) _localNotificationCallback;
/// Initialize local notifications
Future<void> initializeSettings({
#required Function(Map<String, dynamic>)? actionCallback,
}) async {
if (actionCallback != null) {
_localNotificationCallback = actionCallback;
}
/// initialize settings for => Android
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('#mipmap/ic_launcher');
/// initialize settings for => iOS
const DarwinInitializationSettings initializationSettingsIOS =
DarwinInitializationSettings(
requestSoundPermission: false,
requestBadgePermission: false,
requestAlertPermission: false,
);
/// Initialize settings for both => Android / iOS
const InitializationSettings initializationSettings =
InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
);
/// Flutter local notification plugin
_flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
/// Local notification plugin initialize
await _flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onDidReceiveNotificationResponse: (NotificationResponse response) {
if (response.payload != null) {
_localNotificationCallback(json.decode(response.payload!));
}
},
);
/// On notification click
final NotificationAppLaunchDetails? notificationAppLaunchDetails =
await _flutterLocalNotificationsPlugin
.getNotificationAppLaunchDetails();
if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
await onSelectNotification(
notificationAppLaunchDetails?.notificationResponse.toString(),
);
}
}
/// WHEN USER CLICKS TO NOTIFICATION
Future<void> onSelectNotification(String? payload) async {
if (payload != null) {
final Map<String, dynamic> payloadJson =
jsonDecode(payload) as Map<String, dynamic>;
_localNotificationCallback(payloadJson);
}
}
/// SHOW LOCAL NOTIFICATION
/// 'your channel id', 'your channel name', IS NOT NEEDED
Future<void> showNotificationWithDefaultSound({
#required String? title,
#required String? body,
#required String? payload,
}) async {
/// Android notification details
const AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails(
'your channel id',
'your channel name',
importance: Importance.max,
priority: Priority.high,
color: AppColors.red,
icon: '#mipmap/ic_launcher',
);
/// iOS Notification details
const DarwinNotificationDetails iOSPlatformChannelSpecifics =
DarwinNotificationDetails(
categoryIdentifier: darwinNotificationCategory,
presentSound: true,
presentAlert: true,
badgeNumber: 0,
);
/// Notification platforms details
const NotificationDetails platformChannelSpecifics = NotificationDetails(
android: androidPlatformChannelSpecifics,
iOS: iOSPlatformChannelSpecifics,
);
/// Show notification
await _flutterLocalNotificationsPlugin.show(
0,
title,
body,
platformChannelSpecifics,
payload: payload,
);
}
}
Notifications Action page
void notificationAction(
{RemoteMessage? message, Map<String, dynamic>? payload}) {
The redirection is written here.
}
please let me know that why sometimes the redirection works and sometimes not.

Flutter firebase messaging GetInitialMessage blocking app from starting

i am creating a streaming app that include some kind of notifications using fcm.
i want to collect the initial fcm message if the app is killed, but this is blocking my app from starting.
this is called in the MyApp widget, in the iniState.
this code include my main function and my application
my code:
Future<void> setup() async {
await FlutterNotificationChannel.registerNotificationChannel(
description: 'General Notification Channel',
id: 'message',
importance: NotificationImportance.IMPORTANCE_HIGH,
name: 'general',
allowBubbles: true,
enableVibration: true,
enableSound: true,
showBadge: true,
);
await FlutterNotificationChannel.registerNotificationChannel(
description: 'Promotional Messages from the app developer',
id: 'link',
importance: NotificationImportance.IMPORTANCE_HIGH,
name: 'promotional messages',
allowBubbles: true,
enableVibration: true,
enableSound: true,
showBadge: true,
);
await FlutterNotificationChannel.registerNotificationChannel(
description: 'Live Events notifications',
id: 'live',
importance: NotificationImportance.IMPORTANCE_HIGH,
name: 'Live Events',
allowBubbles: true,
enableVibration: true,
enableSound: true,
showBadge: true,
);
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
await Settings.init();
if (Settings.getValue<bool>("push", true)) {
await FirebaseMessaging.instance.subscribeToTopic('global');
} else {}
}
Future<void> main() async {
IsolateNameServer.registerPortWithName(
backgroundMessageport.sendPort,
backgroundMessageIsolateName,
);
backgroundMessageport.listen(backgroundMessagePortHandler);
setup();
final initFuture = MobileAds.instance.initialize();
final adState = AdState(initFuture);
if (await SimpleConnectionChecker.isConnectedToInternet()) {
isConnected = true;
final response = await http.get(Uri.parse('myurl'));
switch (response.statusCode) {
case 200:
_data = json.decode(response.body);
channel = response.body.jsonList((e) => Channel.fromJson(e));
break;
default:
{
showToast('error: ${response.body.toString()}');
}
}
} else {
isConnected = false;
}
runApp(Provider.value(
value: adState,
builder: (context, child) => MyApp(),
));
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [
SystemUiOverlay.bottom,
],
);
getInitialFCMNotification();
}
Future<void> getInitialFCMNotification() async {
RemoteMessage? message =
await FirebaseMessaging.instance.getInitialMessage();
if (message != null) {
showToast('got notification!');
}
}
}
runing this code will keep my app stuck, ui is not rendered.

Send push notifications in different languages using FCM

I've added FCM in my Flutter app and it works, but I can't find a way to send notifications in different languages.
I saw that you can set the conditions for the language on the Firebase console when sending a new notification, but I'm not quite sure how it works. I would like to know if I need to add something to my code to make it work.
Here's the code I have:
push_notification_service.dart
import 'package:firebase_messaging/firebase_messaging.dart';
enum AppState {
foreground,
background,
terminated,
}
class PushNotificationsManager {
PushNotificationsManager._();
factory PushNotificationsManager() => _instance;
static final PushNotificationsManager _instance = PushNotificationsManager._();
Future<void> init() async {
await _setFCMToken();
_configure();
}
_setFCMToken() async {
FirebaseMessaging messaging = FirebaseMessaging.instance;
NotificationSettings settings = await messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
String? token = await messaging.getToken();
print('FirebaseMessaging token: $token');
}
}
void _configure() async {
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
_showForegroundNotificationInAndroid(message);
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
_handleNotification(message: message.data, appState: AppState.foreground);
});
RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) {
_handleNotification(message: initialMessage.data, appState: AppState.terminated);
}
}
void _showForegroundNotificationInAndroid(RemoteMessage message) async {}
void _handleNotification({
Map<String, dynamic>? message,
AppState? appState,
}) async {
print('PushNotificationsManager: _handleNotification ${message.toString()} ${appState.toString()}');
}
}
and in main.dart I have:
initState(){
super.initState();
PushNotificationsManager().init();
}
Do I need to use flutter_analytics package?

An eexception occurs when using flutter_downloader package

I'm trying to use flutter_downloader package to download some files (images/pdf). There is a listView with ListTiles each containing a button to start downloading when clicked but this error occurs when scrolling the list view.
[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: 'package:flutter_downloader/src/downloader.dart': Failed assertion: line 30 pos 12: '!_initialized': FlutterDownloader.initialize() must be called only once!
//my code is like this:
import 'dart:io';
import 'dart:isolate';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
class DownloadFile extends StatefulWidget {
DownloadFile({this.downloadUrl});
final String downloadUrl;
#override
_DownloadFileState createState() => _DownloadFileState();
}
class _DownloadFileState extends State<DownloadFile> {
String downloadId;
String _localPath;
ReceivePort _port = ReceivePort();
#override
void initState(){
super.initState();
_init();
}
Future<void> _init() async {
await FlutterDownloader.initialize();
IsolateNameServer.registerPortWithName(
_port.sendPort, 'downloader_send_port');
_port.listen((dynamic data) {
String id = data[0];
DownloadTaskStatus status = data[1];
int progress = data[2];
print("status: $status");
print("progress: $progress");
print("id == downloadId: ${id == downloadId}");
});
FlutterDownloader.registerCallback(downloadCallback);
_localPath = (await _findLocalPath()) + '/Download';
final savedDir = Directory(_localPath);
bool hasExisted = await savedDir.exists();
if (!hasExisted) {
savedDir.create();
}
}
static void downloadCallback(String id, DownloadTaskStatus status, int progress) {
print(
'Background Isolate Callback: task ($id) is in status ($status) and process ($progress)');
final SendPort send =
IsolateNameServer.lookupPortByName('downloader_send_port');
send.send([id, status, progress]);
}
Future<String> _findLocalPath() async {
final directory = await getExternalStorageDirectory();
return directory.path;
}
Future<bool> _checkPermission() async {
if (Theme.of(context).platform == TargetPlatform.android) {
PermissionStatus permission = await PermissionHandler()
.checkPermissionStatus(PermissionGroup.storage);
if (permission != PermissionStatus.granted) {
Map<PermissionGroup, PermissionStatus> permissions =
await PermissionHandler()
.requestPermissions([PermissionGroup.storage]);
if (permissions[PermissionGroup.storage] == PermissionStatus.granted) {
return true;
}
} else {
return true;
}
} else {
return true;
}
return false;
}
//----------------------------------------------------------------
#override
void dispose() {
super.dispose();
}
//---------------------------------------------------------------
#override
Widget build(BuildContext context) {
return FlatButton(
onPressed: () async {
if (await _checkPermission()) {
final taskId = await FlutterDownloader.enqueue(
url: widget.downloadUrl,
savedDir: _localPath,
showNotification:
true, // show download progress in status bar (for Android)
openFileFromNotification:
true, // click on notification to open downloaded file (for Android)
);
downloadId = taskId;
}
},
child: Text('Downloa File',style: TextStyle(color: Colors.teal),)
);
}
}
According to the Usage section in the flutter_downloader package and the error you are getting, you must call the FlutterDownloader.initialize not more than once.
You can do that in the main method of your application, just like so:
WidgetsFlutterBinding.ensureInitialized();
await FlutterDownloader.initialize();