This article is originally from my blog post.
Critical Alerts: Never Miss Important Notifications
Table of contents
Open Table of contents
Intro
Critical Alerts are a unique type of notification designed to bypass Do Not Disturb, silent, and focus modes. This ensures you receive crucial updates and alerts, no matter the current mode of your phone.
IOS
Implementing critical alerts on iOS is simple because it’s a built-in feature, introduced in iOS 12 (September 2018). However, It needs to get approval from Apple before using it.
To start, must request permission for Critical Alerts
through Apple’s developer portal. You can submit your request here: Critical Alerts Entitlement Request.
After submitting the request, Apple will approve it in a few days. Once approved, we can add critical alerts to the iOS app.
Setup Notification Entitlements
Here is the details guide on how to set up critical alerts entitlements.
iOS: How Do You Implement Critical Alerts for Your App When You Don’t Have an Entitlements File?
Requesting permission for critical notification
In your Flutter app, request permission for critical notifications by using the flutter_critical_alert_permission_ios
package:
if (Platform.isIOS) {
FlutterCriticalAlertPermissionIos.requestCriticalAlertPermission();
}
Once you completes these steps, The app will show a critical alert prompt, allowing users to enable critical alerts on their devices.
Voilà, you are now ready to receive critical alert notifications on the iOS system.
Android
Unlike iOS, Android does not natively support critical alerts. However, you can implement a workaround by utilizing the Do Not Disturb (DND)
permissions and manipulating the device’s notification settings.
1. Request Do Not Disturb Permission
To modify Do Not Disturb (DND) settings on Android, you need to request permission using the DoNotDisturbPlugin
static Future<void> requestDoNotDisturbPermission(
BuildContext context) async {
final dndPlugin = DoNotDisturbPlugin();
bool hasAccess = await dndPlugin.isNotificationPolicyAccessGranted();
if (!hasAccess) {
showDialog(
context: context,
builder: (BuildContext context) {
return CupertinoAlertDialog(
title: const Text('Allow this app to access Do Not Disturb'),
content: const Text(
'This will allow the app to manage Do Not Disturb settings'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text("Cancel"),
),
TextButton(
onPressed: () async {
await dndPlugin.openNotificationPolicyAccessSettings();
Navigator.of(context).pop();
},
child: const Text("Open Settings"),
),
],
);
},
);
}
}
This code essentially checks whether the permission has already been granted. If the permission has not been granted, it displays a dialog prompting the user to open the settings and enable the Do Not Disturb
access permission, as shown below.
2. Manage Critical Notifications
In your notification handling, check if the notification is critical by checking a flag, and then manage DND permissions accordingly. Here’s how you can handle it:
if (message.data["critical"] == "true") {
if (Platform.isAndroid) {
CriticalNotificationManager().manageCriticalNotificationAccess();
}
}
And then manageCriticalNotificationAccess
method ensures that the critical notification bypasses any DND or silent settings.
3. Overwrite DND mode.
If the app has access to modify DND settings, you can enable or disable interruptions:
bool hasAccess = await dndPlugin.isNotificationPolicyAccessGranted();
if (!hasAccess) {
} else {
final isDndEnabled = await dndPlugin.isDndEnabled();
if (isDndEnabled) {
await dndPlugin.setInterruptionFilter(InterruptionFilter.all);
Future.delayed(const Duration(seconds: 5), () async {
await dndPlugin.setInterruptionFilter(InterruptionFilter.none);
});
return;
} else {
if (Platform.isAndroid) {
Sound().temporarilySwitchToNormalMode();
}
}
}
This code checks if the device is in DND mode. If it is, it temporarily disables DND by setting the interruption filter to “all,” allowing critical notifications. After a few seconds, it restores the previous mode. If DND is not enabled, it checks for silent mode and overrides it if necessary.
void temporarilySwitchToNormalMode() async {
RingerModeStatus ringerStatus = await SoundMode.ringerModeStatus;
if (ringerStatus == RingerModeStatus.silent) {
await SoundMode.setSoundMode(RingerModeStatus.normal);
Future.delayed(const Duration(seconds: 5), () async {
await SoundMode.setSoundMode(ringerStatus);
});
}
}
This function checks the current ringer mode of a device. If the device is in silent mode
, it temporarily switches the mode to normal
for 5 seconds and then reverts it back to the original silent mode
.
Now your app is ready for getting critical notifications.
Use this Python script to trigger critical messages through FCM.
import firebase_admin
from firebase_admin import credentials, messaging
from decouple import config
service_account_path = "/Users/sohag/Desktop/critical-alert-firebase-adminsdk.json"
cred = credentials.Certificate(service_account_path)
firebase_admin.initialize_app(cred)
def send_notification_to_all(message_title, message_body):
# Create a message to send to all users
message = messaging.Message(
notification=messaging.Notification(),
topic='all',
data={
"title": message_title,
"body": message_body,
"critical": "true"
},
apns=messaging.APNSConfig(
payload=messaging.APNSPayload(
aps=messaging.Aps(
alert=messaging.ApsAlert(
title=message_title,
body=message_body
),
badge=1,
sound=messaging.CriticalSound(
name="critical_alert.wav",
critical=1,
volume=1.0
)
)
)
),
)
# Send the notification
try:
response = messaging.send(message)
print(f'[NOTIFICATION-SENT]: Successfully sent notification to all users:', response)
except Exception as e:
print('Failed to send notification:', e)
if __name__ == "__main__":
# Notification details
message_title = "Critical Alert"
message_body = "This is a critical alert message for all users."
# Sending notification to all users
send_notification_to_all(message_title, message_body)
Here are a few things to keep in mind while triggering the script:
- The default Notification object leaves it empty.
- Put the title and body in the data object.
- Define a critical notification by using the flag
critical = true
on Android andcritical = 1
on iOS.
Here’s the full demo code. You can check it out. Thanks.
References
Thanks for reading. If you have any questions or suggestions, feel free to reach out to me on LinkedIn. I’d love to hear from you.