I am working on work manager to fetxh api data and initiate a notification in flutter,
//this is the name given to the background fetch
const simplePeriodicTask = "simplePeriodicTask";
Workmanager workmanager = Workmanager();
// flutter local notification setup
Future initializeWorkManagerAndPushNotification() async {
await workmanager.initialize(
callbackDispatcher,
isInDebugMode: false,
); //to true if still in testing lev turn it to false whenever you are launching the app
await workmanager.registerPeriodicTask(
"1", simplePeriodicTask,
existingWorkPolicy: ExistingWorkPolicy.replace,
frequency: Duration(minutes: 1), //when should it check the link
initialDelay:
Duration(seconds: 5), //duration before showing the notification
constraints: Constraints(
networkType: NetworkType.connected,
),
);
}
void callbackDispatcher() async {
workmanager.executeTask((task, inputData) async {
print('Ruuning - callbackDispatcher');
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
var androidSettings = AndroidInitializationSettings('#mipmap/ic_launcher');
var iOSSettings = IOSInitializationSettings();
var initSetttings =
InitializationSettings(android: androidSettings, iOS: iOSSettings);
//
// TODO: Permission
//
await flutterLocalNotificationsPlugin.initialize(initSetttings);
String message =
await WebServiceController.getInstance.getPushNotificationMessage();
print("here 1 ================");
// print(response);
// var convert = json.decode(response.body);
// if (convert['status'] == true) {
// showNotification(convert['msg'], flutterLocalNotificationsPlugin);
// } else {
// print("no messgae");
// }
print("messgae");
print(message);
await showNotification(message, flutterLocalNotificationsPlugin);
return Future.value(true);
});
}
Future showNotification(payloadmessage, flutterLocalNotificationsPlugin) async {
print("showing notification");
var androidDetails = AndroidNotificationDetails(
'channel id', 'channel NAME', 'CHANNEL DESCRIPTION',
priority: Priority.high, importance: Importance.max);
var iOSDetails = IOSNotificationDetails();
var platform = NotificationDetails(android: androidDetails, iOS: iOSDetails);
await flutterLocalNotificationsPlugin.show(
0, 'Message - Virtual intelligent solution', '$payloadmessage', platform,
payload: 'OnClick Payload -VIS \n $payloadmessage');
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
await initializeWorkManagerAndPushNotification();
runApp(MyApp());
}
this function callbackDispatcher() function is used to Initialize Notification and fetch api data from below method.
Future<String> getPushNotificationMessage() async {
print("getting - getPushNotificationMessage");
try {
var response = await _dio.get(APIURLConstants.pushNotification);
print(response);
print("here================ ${response.data}");
var convert = json.decode(response.data);
return convert['message'].toString() ?? "";
} on DioError catch (error) {
print("Dio Error ${error.message}");
} catch (error) {
print("Error - ${error.toString()}");
}
}
as per the below output , It seems that only print(response); of getPushNotificationMessage() is executed after that below code is not executing,
Performing hot restart...
Restarted application in 2,266ms.
W/WM-WorkSpec(31092): Interval duration lesser than minimum allowed value; Changed to 900000
I/flutter (31092): getting - getPushNotificationMessage
I/flutter (31092): {"message":"Hi There I am notification","seen":0,"notification_id":"111"}
I/WM-WorkerWrapper(31092): Worker result SUCCESS for Work [ id=6c935b3b-bfe4-47fa-b30a-0965299f5224, tags={ be.tramckrijte.workmanager.BackgroundWorker } ]
I founded the issue, to make the code execute, I needed to hot restart the application to execute code from
main() method
Related
Service works properly on Android 9 and above facing issues on devices 8<=
Notification disappears in some duration and location services stops
I have also disabled battery optimization but it did not work
Is there anyway to perform background services on android 8 and below
My Code
#pragma('vm:entry-point')
Future<void> onStart(ServiceInstance service) async {
// Only available for flutter 3.0.0 and later
DartPluginRegistrant.ensureInitialized();
final DatabaseService databaseService = DatabaseService();
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
if (service is AndroidServiceInstance) {
service.on('setAsForeground').listen((event) {
service.setAsForegroundService();
});
service.on('setAsBackground').listen((event) {
service.setAsBackgroundService();
});
}
service.on('stopService').listen((event) {
service.stopSelf();
});
final stream = Geolocator.getPositionStream(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.high,
distanceFilter: 0,
timeLimit: Duration(seconds: 10)
),
).listen((position) async {
if (kDebugMode) {
print('Position : $position');
}
flutterLocalNotificationsPlugin.show(
notificationId,
'COOL SERVICE',
position.latitude.toString() + position.longitude.toString() + DateTime.now().toString(),
const NotificationDetails(
android: AndroidNotificationDetails(
notificationChannelId,
'MY FOREGROUND SERVICE',
icon: 'ic_bg_service_small',
ongoing: true,
),
),
);
var now = DateTime.now();
//Month is capital MM to distinct 'minute and month'
//HH means 24 hour format
var formatter = DateFormat('yyyy-MM-dd HH:mm:ss');
var route = MapRoute(createdAt:formatter.format(now),latitude: position.latitude, longitude: position.longitude);
try {
await databaseService.insertBreed(route);
} catch (e) {
if (kDebugMode) {
print(e);
}
}});
}
Thankyou
I found the solution to make Background services work on Android Devices less than 9. I had to disable AllBatteryOptimzation for current app.
Do this
DisableBatteryOptimization.showDisableAllOptimizationsSettings('App Process', 'Enable App Battery Usage', 'Battery Optimization', 'Enable process');
Instead of this
DisableBatteryOptimization.showDisableBatteryOptimizationSettings();
I'm creating a chat app (kind of WhatsApp-like messaging) using Flutter.
First, the notifications mechanism is working as intended, whenever I send a message from 1 device to another device, the notification would pop up.
I created a local_notification_service.dart to handle the foreground notification & sending a not
import 'dart:math';
import 'package:get/get.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class LocalNotificationService extends GetConnect {
String serverKey ='xxxxxxxxxxxxxxxxxxxx'
static final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
static void initialize() {
const InitializationSettings initializationSettings = InitializationSettings(android: AndroidInitializationSettings("#mipmap/ic_launcher"));
_flutterLocalNotificationsPlugin.initialize(initializationSettings);
}
static void display(RemoteMessage message) async {
try {
print("Display notification");
// int id = DateTime.now().microsecondsSinceEpoch ~/1000000;
Random random = Random();
int id = random.nextInt(1000);
const NotificationDetails notificationDetails = NotificationDetails(
android: AndroidNotificationDetails(
"mychanel",
"my chanel",
importance: Importance.max,
priority: Priority.high,
));
print("my id is ${id.toString()}");
await _flutterLocalNotificationsPlugin.show(
id,
message.notification!.title,
message.notification!.body,
notificationDetails,
);
} on Exception catch (e) {
print('Error>>>$e');
}
}
Future<void> sendNotification({
String? title,
String? message,
String? token,
String? uniqueId,
String? action,
String? channelId,
String? channelName,
String? channelDesc,
}) async {
final data = {
"click_action": "FLUTTER_NOTIFICATION_CLICK",
"action": action,
"uniqueId": uniqueId,
"message": message,
"channelId": channelId ?? 'my channel id',
"channelName": channelName ?? 'my channel Name',
"channelDesc": channelDesc ?? 'my channel description',
};
try {
final response = await post(
'https://fcm.googleapis.com/fcm/send',
{
'notification': {'title': title, 'body': message},
'priority': 'high',
'data': data,
'to': '$token',
'direct_boot_ok': true,
},
headers: {
'Content-Type': 'application/json',
'Authorization': 'key=$serverKey',
},
);
print('response body : ${response.body}');
} catch (e) {}
}
}
Then, I'm trying to validate the users in my flutter application whenever they receive FCM notification, here's the logic that I want to create:
If the user is not logged in, then the device could not receive the notification
If the user is logged in, but the specific user is not eligible to receive the message (in case there are some users with the same FCM token / device registered ) then the device could not receive the notification. I would want to solve this after the point number 1 is succeeded
Here's my main.dart file
void main() async {
await GetStorage.init();
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
GlobalController globalC = Get.put(GlobalController());
AuthController authC = Get.put(AuthController());
ErrorController errorC = Get.put(ErrorController());
ConnectivityResult connectivityResult = ConnectivityResult.none;
final Connectivity connectivity = Connectivity();
connectivityResult = await connectivity.checkConnectivity();
if (connectivityResult == ConnectivityResult.wifi || connectivityResult == ConnectivityResult.mobile) {
// Start FCM
final fcmToken = await FirebaseMessaging.instance.getToken();
globalC.fcmToken.value = fcmToken ?? ''; //set global fcm Token
final FirebaseMessaging fcmInstance = FirebaseMessaging.instance;
NotificationSettings settings = await fcmInstance.requestPermission(
alert: true,
announcement: true,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
/* Handle message when in foreground */
FirebaseMessaging.onMessage.listen((event) {
if (globalC.isAuthenticated.isTrue) {
LocalNotificationService.display(event); //display notification
}
});
/* Handle message when in background */
if (globalC.isAuthenticated.isTrue) {
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
}
fcmInstance.onTokenRefresh.listen((fcmToken) {
// Note: This callback is fired at each app startup and whenever a new
// token is generated.
}).onError((err) {
// Error getting token.
});
// End FCM
}
FirebaseAnalytics analytics = FirebaseAnalytics.instance;
runApp(MyApp());
}
as you can see, I'm trying to filter the non logged in user when in the foreground using the FirebaseMessaging.onMessage.listen with the globalC.isAuthenticated.isTrue validation. And it works (because the default of globalC.isAuthenticated is false whenever user is not logged in)
But for the FirebaseMessaging.onBackgroundMessage function does not seems to work with the validation. I've tried to search for the solution in the documentations, youtube but i couldn't find it till this question is made.
How can I make this kind of validation for background message?
Sorry for this newbie question, any help would be greatly appreciated.
Thank you .
so i'm creating an app like facebook,instagram ....
When i was in systeme notifications step, i managed to display notifications when the App state is Open or paused but not when the app is dettached (closed), i'm no longer able to recieve new local notifications.
i tried many packages like flutter_background_service and workmanager, i'm working with flutter_background_service now in debug mode all was good, but when i build the apk it's not working in background.
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
AwesomeNotifications().initialize('resource://drawable/appicon', [
NotificationChannel(
channelKey: 'basic_channel',
channelName: 'basic Notifications',
channelDescription: "channelDescription",
importance: NotificationImportance.High,
enableVibration: true,
channelShowBadge: true,
)
]);
await Firebase.initializeApp(
name: 'firebase',
options: const FirebaseOptions(//firebaseconfig),
);
await initializeService();
runApp(MyApp());
}
Future<void> initializeService() async {
final service = FlutterBackgroundService();
await service.configure(
androidConfiguration: AndroidConfiguration(
// this will be executed when app is in foreground or background in separated isolate
// auto start service
autoStart: true,
onStart: onStart,
isForegroundMode: false,
),
iosConfiguration: IosConfiguration(
// auto start service
autoStart: true,
// this will be executed when app is in foreground in separated isolate
onForeground: onStart,
// you have to enable background fetch capability on xcode project
onBackground: onIosBackground,
),
);
service.startService();
}
// to ensure this is executed
// run app from xcode, then from xcode menu, select Simulate Background Fetch
bool onIosBackground(ServiceInstance service) {
WidgetsFlutterBinding.ensureInitialized();
print('FLUTTER BACKGROUND FETCH');
return true;
}
void onStart(ServiceInstance service) async {
DartPluginRegistrant.ensureInitialized();
if (service is AndroidServiceInstance) {
service.on('setAsForeground').listen((event) {
service.setAsForegroundService();
});
service.on('setAsBackground').listen((event) {
service.setAsBackgroundService();
});
}
service.on('stopService').listen((event) {
service.stopSelf();
});
// bring to foreground
Timer.periodic(const Duration(seconds: 1), (timer) async {
/// you can see this log in logcat
print('FLUTTER BACKGROUND SERVICE is running : ${DateTime.now()}');
//////////////////////////////////////////////////////////////////////////////////////////
I want somthing like this: keep 2 getxcontroller working even if the app detached
//////////////////////////////////////////////////////////////////////////////////////////
await Firebase.initializeApp(
name: 'firebase',
options: const FirebaseOptions(//firebaseconfig),
).whenComplete(() {
final notifc = Get.put(Notificationsc());
final msgl = Get.put(MessageslistsController());
});
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
if (service is AndroidServiceInstance) {
service.setForegroundNotificationInfo(
title: "My App Service",
content: "Updated at ${DateTime.now()}",
);
}
// test using external plugin
final deviceInfo = DeviceInfoPlugin();
String? device;
if (Platform.isAndroid) {
final androidInfo = await deviceInfo.androidInfo;
device = androidInfo.model;
}
if (Platform.isIOS) {
final iosInfo = await deviceInfo.iosInfo;
device = iosInfo.model;
}
service.invoke(
'update',
{
"current_date": DateTime.now().toIso8601String(),
"device": device,
},
);
});
}
I've configured my flutter app in order to receive push notifications, when I try from FCM it works properly and I obtain Heads up notification when the app is in foreground/background/closed.
I configured also the channel and all works.
The problem is when I want to send an automatic notification by using of Google Cloud Function, I never receive the heads up notificationn.
below my code:
Main.Dart
void main() async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(backgroundHandler);
runApp(...) }
...
#override
void initState() {
LocalNotificationService.initialize(changeMessage);
/// gives you the message on which user taps and it oened app from terminated state
FirebaseMessaging.instance.getInitialMessage().then((event) {
setState(() {
widget.message = event!.data["route"];
});
});
//foreground
FirebaseMessaging.onMessage.listen((event) {
print(event.notification?.body);
print(event.notification?.title);
LocalNotificationService.display(event);
});
// app in background but opened and user taps on notification
FirebaseMessaging.onMessageOpenedApp.listen((event) {
setState(() {
widget.message = event!.data["route"];
});
/*final routeFromMessage = event.data["route"];
print(routeFromMessage);
Navigator.of(context).pushNamed(routeFromMessage);*/
});
}
in Manifest I have:
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value = "myChannel"
/>
I created Service
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class LocalNotificationService{
static final FlutterLocalNotificationsPlugin _notificationsPlugin= FlutterLocalNotificationsPlugin();
static void initialize(Function updateRoute){
final InitializationSettings initializationSettings =
InitializationSettings(android: AndroidInitializationSettings("#mipmap/ic_launcher"));
_notificationsPlugin.initialize(initializationSettings,onSelectNotification: (String? route) async{
updateRoute(route);
});
}
static void display(RemoteMessage remoteMessage) async{
try {
final id = DateTime.now().millisecondsSinceEpoch ~/1000;
final NotificationDetails notificationDetails = NotificationDetails(
android: AndroidNotificationDetails(
"myChannel",
"myChannel channel",
importance: Importance.max,
priority: Priority.high
)
);
await _notificationsPlugin.show(
id, remoteMessage.notification!.title, remoteMessage.notification!.body, notificationDetails,payload: remoteMessage.data["route"]);
} on Exception catch (e) {
print(e);
}
}
}
and my Cloud Function:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp(functions.config().functions);
exports.messageTrigger = functions.firestore.document('events/{likedId}').onCreate(async (snap,context) => {
console.log('----------------start function--------------------')
const doc = snap.data()
console.log(doc)
const idFrom = doc.idFrom //chi mette like
const idTo = doc.idTo //creatore evento
const activity = doc.activity
console.log(`Recupero il TO`)
const res = await admin
.firestore()
.collection('users')
.where('uid', '==', idTo)
.get()
.then(querySnapshot => {
querySnapshot.forEach(userTo => {
console.log(`Found user to: "${userTo.data().surname}"`)
console.log(`Recupero il FROM`)
if (userTo.data().pushToken) {
// Get info user from (sent)
admin
.firestore()
.collection('users')
.where('uid', '==', idFrom)
.get()
.then(querySnapshot2 => {
querySnapshot2.forEach(userFrom => {
console.log(`Found user from: ${userFrom.data().name}`)
const payload = {
notification: {
title: ...,
body: ...,
channel_id: "myChannel",
},
data: {route: ...}
}
// Let push to the target device
admin
.messaging()
.sendToDevice(userTo.data().pushToken, payload)
.then(response => {
console.log('Successfully sent message:', response)
})
.catch(error => {
console.log('Error sending message:', error)
})
})
})
} else {
console.log('Can not find pushToken target user')
}
})
})
return null
});
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
// functions.logger.info("Hello logs!", {structuredData: true});
// response.send("Hello from Firebase!");
// });
How I can solve,
thanks
For future readers I solve it by using of android_channel_id: "myChannel" instead of channel_id: "myChannel" in my cloud function
I would like to ask if there's a http get in flutter local notifications? My aim here is when the flutter notification will show, a http get request will be triggered
This my http request for my api:
final String url = 'http://192.168.43.45:8000/api';//url in my request
List data;
Future<String> getData() async {
var response =await http.get(
Uri.encodeFull(url),
headers:{"Accept":"application/json"}
);//get data and decode it to json
}
This code will initialize the notifications:
initializeNotifications() async {
var initializationSettingsAndroid =
AndroidInitializationSettings('#mipmap/launcher_icon');//icon will display when notification appears
var initializationSettingsIOS = IOSInitializationSettings();
var initializationSettings = InitializationSettings(
initializationSettingsAndroid, initializationSettingsIOS);
await flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: onSelectNotification);
}
Future onSelectNotification(String payload) async {
if (payload != null) {
debugPrint('notification payload: ' + payload);
}
await Navigator.push(
context,
new MaterialPageRoute(builder: (context) => HomePage()),
);
}
Code for the notification when click it will redirect to homePage:
Future<void> scheduleNotification(Medicine medicine) async {
var hour = int.parse(medicine.startTime[0] + medicine.startTime[1]);
var ogValue = hour;
var minute = int.parse(medicine.startTime[2] + medicine.startTime[3]);
var androidPlatformChannelSpecifics = AndroidNotificationDetails(
'repeatDailyAtTime channel id',
'repeatDailyAtTime channel name',
'repeatDailyAtTime description',
importance: Importance.Max,
ledColor: Color(0xFF3EB16F),
ledOffMs: 1000,
ledOnMs: 1000,
enableLights: true,
);
//notification details
var iOSPlatformChannelSpecifics = IOSNotificationDetails();
var platformChannelSpecifics = NotificationDetails(
androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
//this code will show the whats the notification must appear
await flutterLocalNotificationsPlugin.showDailyAtTime(
int.parse(medicine.notificationIDs[i]),
'Mediminder: ${medicine.medicineName}',
medicine.medicineType.toString() !=
MedicineType.None.toString()
? 'It is time to take your Medicine, according to schedule'
: 'It is time to take your medicine, according to schedule',
Time(hour, minute,),
platformChannelSpecifics);
hour = ogValue;
}
//await flutterLocalNotificationsPlugin.cancelAll();//cancel the flutter notifications
}
}
it seems that you are using flutter_local_notifications, looking at their documentation i don't think it's possible to you to handle the onReceive notification, this is done internally by the package.
But maybe you could implement your own receiver class extending BroadCastReceiver and listen for the same action that the packages send, from there you'll be able to send HTTP requests.
Take a look at this question, maybe it helps.