Reputation: 319
I'm very new to Kotlin and Compose Multiplatform but I wonder if there is a way to create Platform specific Views
between iOS and Android.
I've managed to make a Platform specific Button for Android but the one for iOS is not visible. The application does run on both platforms without errors.
Usage in App.kt
in commonMain
:
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun App() {
var counter by remember { mutableStateOf(0) }
MaterialTheme {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize()
) {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
Text("Counter: $counter")
PlatformButton.createButton("Click to increase Counter") {
counter++
}
}
}
}
}
My expect class in commonMain
:
import androidx.compose.runtime.Composable
expect class PlatformButton {
companion object {
@Composable
fun createButton(label: String, onClick: () -> Unit): Any
}
}
My actual class in androidMain
:
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
actual class PlatformButton {
actual companion object {
@Composable
actual fun createButton(label: String, onClick: () -> Unit): Any {
return Button(onClick) {
Text(label)
}
}
}
}
My actual class in iosMain
:
import androidx.compose.runtime.Composable
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.ObjCAction
import platform.UIKit.UIButton
import platform.UIKit.UIButtonTypeSystem
import platform.UIKit.UIControlEventTouchUpInside
import platform.darwin.NSObject
import platform.darwin.sel_registerName
actual class PlatformButton {
actual companion object {
@OptIn(ExperimentalForeignApi::class)
@Composable
actual fun createButton(label: String, onClick: () -> Unit): Any {
val uiButton = UIButton.buttonWithType(UIButtonTypeSystem)
uiButton.setTitle(label, forState = 0u)
val block = object : NSObject() {
@ObjCAction
fun buttonClicked() {
onClick()
}
}
uiButton.addTarget(block, action = sel_registerName("buttonClicked"), forControlEvents = UIControlEventTouchUpInside)
return uiButton
}
}
}
I've searched for documentations but couldn't find any. I also don't really like the use of UIKit
in Kotlin
. Isn't there maybe another way of using SwiftUI
-Code and somehow create a Composable
View
out of it with a bridge? What I'm also wondering: Is there a specific return type for such a use case instead of Any
?
Upvotes: 3
Views: 1546
Reputation: 88082
On iOS, you create UIButton
and expect compose to draw it, but it doesn't know how to render this view.
To bridge UIKit
view into Compose, you need UIKitView
:
UIKitView(factory = {
val uiButton = ...
uiButton
})
Also, you don't need a class to define expect compose function, you can declare it directly - it'll make your compose code look much more natural:
@Composable
expect PlatformButton(...)
@Composable
actual PlatformButton(...) {
UIKitView(factory = {
...
})
}
Upvotes: 3