Header Ads Widget

Responsive Advertisement

Compose Multiplatform is a powerful framework that allows developers to create cross-platform applications with shared UI logic. However, platform-specific features, like notifications or toast messages, often require a custom approach. This blog explores the concept of bridging in Compose Multiplatform and provides detailed examples of implementing toast notifications on Android, iOS, and Desktop platforms.

We'll cover:

  • Why bridging is essential in Compose Multiplatform.
  • Pros and cons of this approach.
  • Detailed code snippets for platform-specific implementations.
  • Key takeaways for developers.
COMPOSE MULTIPLATFORM BRIDGING


Why Bridging in Compose Multiplatform? 🤔

Imagine a world where you can create beautiful UIs for Android, iOS, Desktop, and Web without switching frameworks. Compose Multiplatform makes this dream a reality by enabling code-sharing while maintaining platform-specific experiences.

However, some features demand special handling. That’s where Compose Multiplatform Bridging comes in! Bridging helps create platform-specific implementations while staying within the Compose ecosystem, ensuring apps look and feel native while leveraging shared business logic.

Before You Begin 🧑‍💻

To make the most out of this guide, we recommend understanding the expect and actual keywords in Kotlin. Check out this detailed blog: Exploring expect and actual in Kotlin .

Pros and Cons of Compose Multiplatform Bridging 🌟

Pros

  • Code Reusability: Share UI logic and reduce duplication.
  • Native Experience: Implement platform-specific features without breaking abstraction.
  • Faster Development: Focus on core functionality while Compose handles the UI.
  • Reduced Maintenance: Unified UI logic across platforms.

Cons

  • Learning Curve: Requires understanding both Compose and platform-specific APIs.
  • Debugging Complexity: Bugs may arise at the intersection of shared and platform-specific code.
  • Limited Resources: Compose Multiplatform is still evolving, so documentation can be sparse.


Bridging Sample Code: Toast Notifications Across Platforms 🛠️

compose multiplatform toast message

Let’s see how to display toast notifications on Android, iOS, and Desktop using bridging.

Common Platform.kt File

This file defines the Platform interface and the expected ShowToast function:


import androidx.compose.runtime.Composable

interface Platform {
    val name: String
}

expect fun getPlatform(): Platform

//our new EXPECT function
@Composable
expect fun ShowToast(message: String)

            

The Platform.kt file defines the Platform interface and two key functions:

  • getPlatform(): This is present by default while creating a project and it is an expect function, meaning its implementation will vary by platform.
  • ShowToast(): A composable function for displaying toast messages. It is also declared with expect, requiring platform-specific implementations.

This structure ensures that the interface and method declarations remain consistent across platforms.


Platform-Specific Implementations 🌐

Android (Platform.android.kt)

Displays a simple Android Toast using LocalContext.


import android.content.Context
import android.os.Build
import android.widget.Toast
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext

class AndroidPlatform : Platform {
    override val name: String = "Android ${Build.VERSION.SDK_INT}"
}

actual fun getPlatform(): Platform = AndroidPlatform()

@Composable
actual fun ShowToast(message: String) {
    val context: Context = LocalContext.current
    Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}

            

In the Android-specific file:

  • Platform Name: The class AndroidPlatform provides the platform's name using Build.VERSION.SDK_INT, indicating the Android version.
  • ShowToast: The function uses Android’s native Toast.makeText to display a simple toast notification. The LocalContext provides the required Context dynamically.

This implementation ensures native-like toast notifications on Android devices.

iOS (Platform.ios.kt)

Creates a toast-like view and animates its fade-out.


import androidx.compose.runtime.Composable
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.interop.LocalUIViewController
import androidx.compose.ui.platform.LocalWindowInfo
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.useContents
import platform.CoreGraphics.CGRectMake
import platform.UIKit.*

class IOSPlatform : Platform {
    override val name: String =
        UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}

actual fun getPlatform(): Platform = IOSPlatform()

@OptIn(ExperimentalForeignApi::class, ExperimentalComposeUiApi::class)
@Composable
actual fun ShowToast(message: String) {
    val toastLabel = UILabel().apply {
        text = message
        textColor = UIColor.whiteColor
        backgroundColor = UIColor.blackColor.colorWithAlphaComponent(0.6)
        textAlignment = NSTextAlignmentCenter
        font = UIFont.systemFontOfSize(14.0)
        numberOfLines = 0
        layer.cornerRadius = 10.0
        clipsToBounds = true
    }
    val maxWidth = LocalWindowInfo.current.containerSize.width.toDouble()
    val maxHeight = LocalWindowInfo.current.containerSize.height.toDouble()
    toastLabel.setFrame(CGRectMake(maxWidth / 4, maxHeight / 2, maxWidth / 2, 50.0))
    LocalUIViewController.current.view.addSubview(toastLabel)

    UIView.animateWithDuration(
        1.5,
        delay = 2.0,
        options = UIViewAnimationOptionCurveEaseInOut,
        animations = { toastLabel.alpha = 0.0 },
        completion = { toastLabel.removeFromSuperview() }
    )
}

The iOS-specific file leverages UIKit for toast creation:

  • Platform Name: The class IOSPlatform fetches the iOS system name and version using UIDevice.
  • ShowToast:
    • A UILabel is dynamically created to act as the toast message. Its properties, such as text color, background color, and font, are configured.
    • The label size is calculated using the screen’s dimensions and scaled appropriately to fit the message.
    • The label is added as a subview to the main view, and an animation is applied to fade it out after a delay.

This approach replicates a toast-like behavior natively on iOS, ensuring smooth UI integration.

Desktop (Platform.desktop.kt)

Uses Java’s SystemTray or a JOptionPane fallback for notifications.


import androidx.compose.runtime.Composable
import java.awt.SystemTray
import java.awt.Toolkit
import java.awt.TrayIcon
import javax.swing.JOptionPane

@Composable
actual fun ShowToast(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("Info", message, TrayIcon.MessageType.INFO)
    } else {
        JOptionPane.showMessageDialog(null, message, "Desktop Notification", JOptionPane.INFORMATION_MESSAGE)
    }
}

            

For Desktop, the implementation utilizes Java’s AWT library:

  • SystemTray Support: If the system supports SystemTray, a tray notification is displayed using a TrayIcon.
  • Fallback: On systems without tray support, a JOptionPane is used to display a modal dialog with the message.

This dual approach ensures compatibility across a wide range of desktop environments.

Using the Toast in App.kt 🍞

This Compose code lets users display a toast message with the click of a button:


@Composable
@Preview
fun App() {
    MaterialTheme {
        var toastContent by remember { mutableStateOf("") }
        Box(
            modifier = Modifier.fillMaxSize().background(color = Color.Red)
        ) {
            if (toastContent.isNotBlank()) {
                ShowToast(toastContent)
                toastContent = ""
            }

            Column(
                modifier = Modifier.fillMaxWidth(),
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Button(onClick = { toastContent = "This is a Toast Message!" }) {
                    Text("Show Toast")
                }
            }
        }
    }
}

            

The App.kt file integrates the platform-specific implementations within a common Compose UI:

  • Toast Logic: The toastContent state variable triggers the ShowToast function when updated.
  • UI Components: A Box acts as the container, while a Column centers the button horizontally. The button updates the toastContent, showing the toast message when clicked.

This composable function demonstrates seamless integration of platform-specific functionality within a unified Compose Multiplatform UI.

Key Takeaways for Developers 🎯

  • Compose Multiplatform bridges the gap between shared and native UIs, empowering developers to write once and customize as needed.
  • Platform-specific implementations like ShowToast allow apps to retain their native feel.
  • Developers must leverage bridging to handle device-specific nuances, ensuring their apps shine on every platform!

🔥 Ready to build cross-platform UIs effortlessly? Dive into Compose Multiplatform and bridge the gap between shared code and native experiences!

Post a Comment

Link copied to clipboard!