Native Notifications in Compose & Kotlin Multiplatform: A Comprehensive How-To
Native notifications are crucial if you want to have a significant impact on your application’s UI using Compose & Kotlin Multiplatform. In this article, we are going to discuss native notifications for each platform using Compose & Kotlin Multiplatform. So, without further ado, let’s get into it.
Alert-KMP Library:
If you don’t want to implement notification using expect and actual then you can use the Alert-KMP Library. It contains alot of features that we didn’t discuss here. Features are like custom Notifications on Web, Desktop, Android & iOS.
Expect / Actual :
The Expect and Actual mechanism is used for displaying native functionality on each platform using Kotlin Multiplatform and Compose Multiplatform. We need to create an expect
function inside the App.kt
.
// Path: composeApp/src/commonMain/kotlin/org/company/app/App.kt
@Composable
internal expect fun Notify(message: String)
I just wanted to display native notifications like toast on Android and similar ones on other platforms, which is why I’m just using the message as a parameter. You can define more parameters according to your own needs.
Android Module Code:
In the Android Module, You need to simply provide this code as reference:
// Path: composeApp/src/androidMain/kotlin/org/company/app/App.android.kt
@Composable
internal actual fun Notify(message: String) {
Toast.makeText(
LocalContext.current, message, Toast.LENGTH_SHORT
).show()
}
You can also write the notification code this way.
iOS Module Code:
In the iOS Module, you simply need to use this code as a referece:
// Path: composeApp/src/iosMain/kotlin/org/company/app/App.ios.kt
@Composable
internal actual fun Notify(message: String) {
val viewController = UIApplication.sharedApplication.keyWindow?.rootViewController?.modalViewController
val alertController = UIAlertController.alertControllerWithTitle(
title = UIDevice.currentDevice.systemName,
message = message,
preferredStyle = UIAlertControllerStyle.MAX_VALUE
)
alertController.addAction(
UIAlertAction.actionWithTitle(
"OK",
style = UIAlertControllerStyle.MAX_VALUE,
handler = null
)
)
viewController?.presentViewController(alertController, animated = true, completion = null)
}
JvmMain Module Code:
For the Jvm Module, You can simply use this code for displaying the native notifications on Windows, MacOS & Linux as well.
// Path: composeApp/src/jvmMain/kotlin/org/company/app/App.jvm.kt
@Composable
internal actual fun Notify(message: String) {
if (SystemTray.isSupported()) {
val tray = SystemTray.getSystemTray()
val image = Toolkit.getDefaultToolkit().createImage("logo.webp")
val trayIcon = TrayIcon(image, "Desktop Notification")
tray.add(trayIcon)
trayIcon.displayMessage("Desktop Notification", message, TrayIcon.MessageType.INFO)
} else {
// Fallback for systems that don't support SystemTray
JOptionPane.showMessageDialog(
null,
message,
"Desktop Notification",
JOptionPane.INFORMATION_MESSAGE
)
}
}
JsMain Module Code:
For this jsMain, you can simply use the Alerts:
// Not Recomended.....
// Path: composeApp/src/jsMain/kotlin/org/company/app/App.js.kt
@Composable
internal actual fun Notify(message: String) {
window.alert(message)
}
But the alerts are not the ideal way to display the notifications. So, We will use Native Notifications on the Web as well. For that we need to get the permission from the user as well.
// Recomended Way.....
// Path: composeApp/src/jsMain/kotlin/org/company/app/App.js.kt
import kotlinx.browser.window
import org.w3c.notifications.Notification
import org.w3c.notifications.NotificationOptions
import org.w3c.notifications.NotificationPermission
@Composable
internal actual fun Notify(message: String) {
window.alert(message)
if (!js("typeof Notification !== 'undefined'").unsafeCast<Boolean>()) {
window.alert(message)
return
}
when (Notification.permission) {
NotificationPermission.GRANTED -> {
showNotification(message)
}
NotificationPermission.DENIED -> {
window.alert(message)
}
NotificationPermission.DEFAULT -> {
Notification.requestPermission().then { permission ->
if (permission == NotificationPermission.GRANTED) {
showNotification(message)
} else {
window.alert(message)
}
}
}
}
}
private fun showNotification(message: String) {
val options = NotificationOptions(
body = message
)
Notification("New Notification", options)
}
Demo:
Conclusion:
In this article, we’ve explored how to implement native notifications across multiple platforms using Compose and Kotlin Multiplatform. By leveraging the expect/actual mechanism, we can provide platform-specific implementations that enhance the user experience. Whether it’s a toast on Android, an alert on iOS, a system tray notification on JVM, or a web notification on JS, Kotlin Multiplatform allows us to maintain a consistent codebase while delivering native functionality. This approach not only simplifies the development process but also ensures that our applications feel right at home on any platform.
By following the steps outlined in this guide, you can effectively integrate native notifications into your Compose Multiplatform projects, providing a seamless and engaging experience for your users.
Let’s Connect :