KMP构建Compose Multiplatform共享UI教程

Viewed 0

使用 KMP 构建 Compose Multiplatform 共享 UI

最近,Android 开发已经相当现代化,大量的 XML 布局已经被取代,这正是 Jetpack Compose 的时代。Jetpack Compose 是 Android 的现代化、完全声明式 UI 工具包,凭借其强大而直观的基于 Kotlin 的语法,简化了 UI 开发,同时也为未来更简洁、更反应灵敏、更动态的移动应用打开了大门。

Jetpack Compose 得到了开发人员的广泛采用和认可。与此同时,跨平台解决方案的需求也在不断增长。因此,JetBrains 与谷歌携手将 Compose 体验扩展到更跨平台,Compose Multiplatform 应运而生。

有了这个 UI 框架,你就可以将 Kotlin Multiplatform 的代码共享功能扩展到应用逻辑之外,一次性实现 UI,然后将其用于所有目标平台,包括 iOS、Android 和桌面。

在本教程中,我将创建一个带有自定义数据的懒列表示例应用,可在 Android、iOS 和桌面(如 Mac)上运行。为创建 UI,你将使用 Compose Multiplatform 框架并学习其基础知识:Composable、主题、布局、事件和 Modifier。支持的平台包括 Android、iOS 和桌面。

现在的问题是,Kotlin Multiplatform 和 Compose Multiplatform 之间有什么区别?Kotlin Multiplatform 是用于在平台间共享业务逻辑的工具,但它有一个很大的缺点——开发人员仍需使用 Jetpack Compose(Android)和 SwiftUI(iOS)等本地工具来实现 UI。

在此,Compose Multiplatform 登场,填补了 KMP 的空白,完善了 Kotlin 体验。通过将 Compose 和 KMP 结合起来,你可以根据项目的复杂程度,使代码库中 80%-95% 的内容由 Kotlin 构成。这对于 Android 开发人员来说,相当令人兴奋。

简而言之,两者的区别在于:

  • Kotlin Multiplatform:仅共享业务逻辑,需要单独的 Android 和 iOS UI。
  • Compose Multiplatform:共享业务逻辑,并且 Android 和 iOS 共享 UI。

那么哪个更好呢?很明显,Compose 跨平台更优。

深度实现

首先,你需要一台用于跨平台开发的工作机。你需要安装 Android Studio 并配置 KMP 插件、Xcode 和 CocoaPods 依赖管理器。

好消息是,你甚至不需要花时间进行适当的 KMP + Compose 项目设置,JetBrains 提供了一个便捷模板,其中包含对该模板中发生的事情的逐步解释。你可以在该模板中选择平台并下载,它会为你创建一个示例项目设置。

让我解释一下项目结构:

  • composeApp:包含平台特定文件夹,其中包含入口点、共享 UI 和业务逻辑。
  • commonMain:在这个模块中,我们将保留所有共享的 Compose UI 和业务逻辑。
  • androidMain:Kotlin 模块,Android 应用的入口点,包含平台特定的类,如 Application 和 Activity,以及所需的其他依赖项。
  • desktopMain:Kotlin 模块,桌面应用的入口点,包含平台特定的类,如 Window 类。
  • iosMain:包含特定平台配置和类的 Xcode 项目。在构建过程中,commonMain 模块会作为 CocoaPod 依赖项捆绑到 iOS 项目中。

让我们看看如何定义每个平台的依赖关系和平台间的共同依赖关系。在 Gradle 文件夹中,我们有版本目录文件:

[versions]
agp = "8.1.4"
android-compileSdk = "34"
android-minSdk = "24"
android-targetSdk = "34"
androidx-activityCompose = "1.8.2"
androidx-appcompat = "1.6.1"
androidx-constraintlayout = "2.1.4"
androidx-core-ktx = "1.12.0"
androidx-espresso-core = "3.5.1"
androidx-material = "1.10.0"
androidx-test-junit = "1.1.5"
compose = "1.5.4"
compose-compiler = "1.5.6"
compose-plugin = "1.5.11"
junit = "4.13.2"
kotlin = "1.9.21"
ktor = "2.3.7"
coroutines = "1.7.3"

[libraries]
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" }
compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" }
compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" }
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" }
ktor-client-cio= {module ="io.ktor:ktor-client-cio", version.ref = "ktor"}
kotlin-serialization = {module = "io.ktor:ktor-serialization-kotlinx-json", version.ref="ktor"}
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }

[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
androidLibrary = { id = "com.android.library", version.ref = "agp" }
jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
  • [versions]:带编号的版本名称。
  • [libraries]:带有模块和版本号的库名。
  • [plugins]:带有 ID 和版本号的插件名称。

现在我们可以像这样在 build.gradle 中使用库。项目级 build.gradle 的插件配置类似。现在让我们快速回顾一下如何定义依赖关系:

sourceSets {
    val desktopMain by getting

    androidMain.dependencies {
        implementation(libs.compose.ui.tooling.preview)
        implementation(libs.androidx.activity.compose)
        implementation(libs.ktor.client.okhttp)
        implementation(libs.kotlinx.coroutines.android)
    }
    commonMain.dependencies {
        implementation(compose.runtime)
        implementation(compose.foundation)
        implementation(compose.material)
        implementation(compose.ui)
        @OptIn(ExperimentalComposeLibrary::class)
        implementation(compose.components.resources)
        implementation(libs.ktor.client.core)
        implementation(libs.kotlin.serialization)
        implementation(libs.kotlinx.coroutines.core)
    }
    desktopMain.dependencies {
        implementation(compose.desktop.currentOs)
        implementation(libs.ktor.client.cio)
    }
    iosMain.dependencies {
        implementation(libs.ktor.client.darwin)
    }
}
  • androidMain.dependencies:用于 Android 特定的依赖关系。
  • iosMain.dependencies:用于 iOS 特定的依赖关系。
  • desktopMain.dependencies:用于桌面特定的依赖关系。
  • commonMain.dependencies:用于平台间的通用依赖关系。

所有平台都有一个入口 App.kt

@Composable
fun App() {
    MaterialTheme {
        Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
            ListScreen(Repository.getDataList())
        }
    }
}

@Composable
fun ListScreen(list: List<Products>) {
    LazyColumn(modifier = Modifier.fillMaxWidth(), contentPadding = PaddingValues(16.dp)) {
        items(list) { data ->
            ColumnItem(data.title, data.description, data.price.toString(), data.image)
        }
    }
}

@OptIn(ExperimentalResourceApi::class)
@Composable
fun ColumnItem(name: String, description: String, price: String, image: String) {
    Card(
        modifier = Modifier.padding(8.dp)
            .fillMaxWidth()
            .wrapContentHeight(),
        shape = MaterialTheme.shapes.medium,
        elevation = 5.dp,
        backgroundColor = MaterialTheme.colors.surface
    ) {
        Row(
            verticalAlignment = Alignment.CenterVertically,
        ) {
            Image(
                painter = painterResource(res = "compose-multiplatform.xml"),
                contentDescription = null,
                modifier = Modifier.size(100.dp)
                    .padding(8.dp),
                contentScale = ContentScale.Fit,
            )
            Column(Modifier.padding(4.dp)) {
                Text(
                    modifier = Modifier.padding(2.dp),
                    text = name,
                    maxLines = 3,
                    style = MaterialTheme.typography.h6,
                    color = MaterialTheme.colors.onSurface,
                )
                Text(
                    modifier = Modifier.padding(2.dp),
                    text = description,
                    maxLines = 3,
                    style = MaterialTheme.typography.body2,
                )
                Text(
                    modifier = Modifier.padding(2.dp),
                    text = "Price USD $price",
                    style = MaterialTheme.typography.button,
                )
            }
        }
    }
}

让我们从顶部选择平台并运行应用,即可在 Android、iOS 和桌面上看到相同的 UI。

总结一下,我们学习了跨平台环境设置、项目结构、如何声明通用依赖和特定平台依赖,以及如何在平台间共享 UI。在接下来的博客中,我将介绍 Ktor 如何处理跨平台网络请求。希望本教程对你有所帮助,Happy Coding!

0 Answers