使用 Kotlin Multiplatform 构建全栈应用程序
代码示例: full-stack-task-manager
使用的插件:
在本文中,你将学习如何使用 Kotlin 开发一个全栈应用程序,该应用程序可在 Android、iOS 和桌面平台上运行,并同时利用 Ktor 实现无缝数据处理。
完成本教程后,你将了解如何执行以下操作:
- 使用 Kotlin Multiplatform 创建全栈应用程序。
- 了解使用 IntelliJ IDEA 生成的项目。
- 创建调用 Ktor 服务的 Compose Multiplatform 客户端。
- 在设计的不同层之间复用共享类型。
- 正确包含和配置多平台库。
在之前的教程中,我们使用任务管理器示例来
你将创建一个面向 Android、iOS 和桌面平台的客户端,并使用 Ktor 服务来获取要显示的数据。在可能的情况下,你将在客户端和服务器之间共享数据类型,从而加快开发速度并减少潜在错误。
先决条件
与之前的文章一样,你将使用 IntelliJ IDEA 作为 IDE。要安装和配置你的环境,请参阅 Kotlin Multiplatform 快速入门 指南。
如果这是你首次使用 Compose Multiplatform,我们建议你在开始本教程之前,先完成 Compose Multiplatform 入门 教程。为了降低任务的复杂性,你可以专注于一个客户端平台。例如,如果你从未使用过 iOS,那么专注于桌面或 Android 开发可能更明智。
创建新项目
请使用 IntelliJ IDEA 中的 Kotlin Multiplatform 项目向导,而不是 Ktor 项目生成器。 它将创建一个基本的多平台项目,你可以通过客户端和服务对其进行扩展。客户端可以使用原生 UI 库,例如 SwiftUI,但在本教程中,你将使用 Compose Multiplatform 为所有平台创建共享 UI。
- 启动 IntelliJ IDEA。
- 在 IntelliJ IDEA 中,选择 File | New | Project 。
- 在左侧面板中,选择 Kotlin Multiplatform 。
- 在 New Project 窗口中指定以下字段:
- Name : full-stack-task-manager
- Group : com.example.ktor
选择 Android 、 Desktop 和 Server 作为目标平台。
如果你使用的是 Mac,请同时选择 iOS 。确保选中 Share UI 选项。
单击 Create 按钮,等待 IDE 生成并导入项目。
运行服务
- 在 Project 视图中,导航到 server/src/main/kotlin/com/example/ktor/full_stack_task_manager 并打开 Application.kt 文件。
- 单击 Run 按钮 (
) ,启动
main()
函数旁边的应用程序。Run 工具窗口中将打开一个新选项卡,其日志以消息“Responding at http://0.0.0.0:8080”结尾。
导航到 http://0.0.0.0:8080/ 以打开应用程序。 你将看到浏览器中显示来自 Ktor 的消息。
检查项目
项目的 server 文件夹是三个 Kotlin 模块之一。另外两个是 shared 和 composeApp 。
server 模块的结构与 Ktor 项目生成器生成的结构非常相似。 你有一个专门的构建文件来声明插件和依赖项,以及一个包含构建和启动 Ktor 服务代码的源代码集:

如果你查看 Application.kt 文件中的路由指令,你将看到对 greet()
函数的调用:
这将创建 Greeting
类型的一个实例,并调用其 greet()
方法。 Greeting
类在 shared 模块中定义:
shared 模块包含将在不同目标平台之间使用的代码。
shared 模块集中的 commonMain 源代码集包含将在所有平台上使用的类型。 如你所见,Greeting
类型就是在此处定义的。 这也是你放置要在服务器和所有不同客户端平台之间共享的公共代码的地方。
shared 模块还包含你希望提供客户端的每个平台的源代码集。这是因为 commonMain 中声明的类型可能需要因目标平台而异的功能。对于 Greeting
类型,你希望使用平台特有的 API 获取当前平台的名称。 这通过 预期 和实际声明 实现。
在 shared 模块的 commonMain 源代码集中,你使用 expect
关键字声明一个 getPlatform()
函数:
然后,每个目标平台 都必须提供 getPlatform()
函数的 actual
声明,如下所示:
项目中还有一个额外的模块,即 composeApp 模块。 它包含 Android、iOS、桌面和 Web 客户端应用的代码。 这些应用目前未链接到 Ktor 服务,但它们确实使用了共享的 Greeting
类。
运行客户端应用程序
你可以通过执行目标平台的运行配置来运行客户端应用程序。要在 iOS 模拟器上运行应用程序,请按照以下步骤操作:
- 在 IntelliJ IDEA 中,选择 iosApp 运行配置和模拟设备。
- 单击 Run 按钮 (
) 以运行配置。
当你运行 iOS 应用时,它在后台使用 Xcode 构建并在 iOS 模拟器中启动。 该应用显示一个按钮,点击时切换图像。
首次按下按钮时,当前平台的详细信息将添加到其文本中。实现此功能的代码位于 composeApp/src/commonMain/kotlin/com/example/ktor/full_stack_task_manager/App.kt 中:
kotlin这是一个可组合函数,你将在本文后面进行修改。目前重要的是,它显示了一个 UI 并使用了共享的
Greeting
类型,而Greeting
类型又使用了实现通用Platform
接口的平台特有类。
现在你了解了生成的项目结构,可以逐步添加任务管理器功能了。
添加模型类型
首先,添加模型类型,并确保客户端和服务器都可以访问它们。
- 导航到 shared/src/commonMain/kotlin/com/example/ktor/full_stack_task_manager 并创建一个名为 model 的新包。
- 在新包中,创建一个名为 Task.kt 的新文件。
添加一个
enum
来表示优先级,并添加一个class
来表示任务。Task
类使用kotlinx.serialization
库中的Serializable
类型进行注解:kotlin你会注意到导入和注解都无法编译。这是因为项目尚未依赖
kotlinx.serialization
库。导航到 shared/build.gradle.kts 并添加序列化插件:
kotlin在同一个文件中,向 commonMain 源代码集添加一个新的依赖项:
kotlin- 导航到 gradle/libs.versions.toml 并定义以下内容: toml
- 在 IntelliJ IDEA 中,选择 Build | Sync Project with Gradle Files 以应用更新。Gradle 导入完成后,你将发现你的 Task.kt 文件成功编译。
请注意,即使不包含序列化插件,代码也能够编译,但是,在网络上序列化 Task
对象所需的类型将不会生成。这将导致在尝试调用服务时出现运行时错误。
将序列化插件放在另一个模块(例如 server 或 composeApp )不会在构建时导致错误。但同样,序列化所需的额外类型也不会生成,从而导致运行时错误。
创建服务器
下一阶段是为我们的任务管理器创建服务器实现。
- 导航到 server/src/main/kotlin/com/example/ktor/full_stack_task_manager 文件夹,并创建一个名为 model 的子包。
在此包中,创建一个新的 TaskRepository.kt 文件,并为我们的版本库添加以下接口:
kotlin在同一个包中,创建一个名为 InMemoryTaskRepository.kt 的新文件,其中包含以下类:
kotlin导航到 server/src/main/kotlin/.../Application.kt 并用以下实现替换现有代码:
kotlin此实现与之前教程中的实现非常相似,不同之处在于现在为了简化起见,你已将所有路由代码放置在
Application.module()
函数中。一旦你输入此代码并添加了导入,你将发现多个编译错误,因为代码使用了多个 Ktor 插件,这些插件需要作为依赖项包含在内, 其中包括用于与 Web 客户端交互的
CORS插件。Required dependencies: io.ktor:%artifact_name% Code example: full-stack-task-manager Native server support: ✅- 打开 gradle/libs.versions.toml 文件并定义以下库: toml
打开服务器模块构建文件( server/build.gradle.kts )并添加以下依赖项:
kotlin- 再次,从主菜单中选择 Build | Sync Project with Gradle Files。 导入完成后,你将发现
ContentNegotiation
类型和json()
函数的导入工作正常。 - 重新运行服务器。你将发现可以从浏览器访问路由。
创建客户端
为了使你的客户端能够访问服务器,你需要包含 Ktor Client。这涉及三种类型的依赖项:
- Ktor Client 的核心功能。
- 平台特有的引擎来处理网络连接。
- 对内容协商和序列化的支持。
- 在 gradle/libs.versions.toml 文件中,添加以下库: toml
- 导航到 composeApp/build.gradle.kts 并添加以下依赖项: kotlin
完成后,你可以为客户端添加一个
TaskApi
类型,作为 Ktor Client 的轻量级包装器。 - 从主菜单中选择 Build | Sync Project with Gradle Files 以导入构建文件中的更改。
- 导航到 composeApp/src/commonMain/kotlin/com/example/ktor/full_stack_task_manager 并创建一个名为 network 的新包。
在新包中,为客户端配置创建一个新的 HttpClientManager.kt 文件:
kotlin请注意,你应该将
1.2.3.4
替换为当前机器的 IP 地址。你无法从 Android 虚拟设备或 iOS 模拟器上运行的代码调用0.0.0.0
或localhost
。在同一个 composeApp/.../full_stack_task_manager/network 包中,创建一个新的 TaskApi.kt 文件,其中包含以下实现:
kotlin导航到 commonMain/.../App.kt 并用以下实现替换
App
可组合项。 这将使用TaskApi
类型从服务器检索任务 list,然后 在列中显示每个任务的名称:kotlin在服务器运行时,通过运行 iosApp 运行配置来测试 iOS 应用程序。
单击 Fetch Tasks 按钮以显示任务 list:
NOTE
在此演示中,为了清晰起见,我们简化了流程。在实际应用程序中,避免通过网络发送未加密的数据至关重要。在 Android 平台上,你需要显式地授予应用程序网络权限,并允许它以明文形式发送和接收数据。要启用这些权限,请打开 composeApp/src/androidMain/AndroidManifest.xml 并添加以下设置:
xml使用 composeApp 运行配置运行 Android 应用程序。 你现在将发现你的 Android 客户端也将运行:
对于桌面客户端,你应该为包含窗口分配尺寸和标题。 打开文件 composeApp/src/desktopMain/.../main.kt 并通过更改
title
和设置state
属性来修改代码:kotlin使用 composeApp [desktop] 运行配置运行桌面应用程序:
使用 composeApp [wasmJs] 运行配置运行 Web 客户端:
改进 UI
客户端现在正在与服务器通信,但这远非一个吸引人的 UI。
打开位于 composeApp/src/commonMain/.../full_stack_task_manager 中的 App.kt 文件,并用下面的
App
和TaskCard
可组合项替换现有的App
:kotlin通过此实现,你的客户端现在具有一些基本功能。
通过使用
LaunchedEffect
类型,所有任务都在启动时加载,而LazyColumn
可组合项允许用户滚动浏览任务。最后,创建了一个单独的
TaskCard
可组合项,它又使用一个Card
来显示每个Task
的详细信息。已添加按钮来删除和更新任务。重新运行客户端应用程序——例如,Android 应用。 你现在可以滚动浏览任务、查看其详细信息并删除它们:
添加更新功能
要完成客户端,请集成允许更新任务详细信息的功能。
- 导航到 composeApp/src/commonMain/.../full_stack_task_manager 中的 App.kt 文件。
添加
UpdateTaskDialog
可组合项和必要的导入,如下所示:kotlin这是一个可组合项,通过对话框显示
Task
的详细信息。description
和priority
放置在TextField
可组合项中,以便可以更新它们。当用户按下更新按钮时,它会触发onConfirm()
回调。更新同一个文件中的
App
可组合项:kotlin你正在存储一个额外的状态片段,即当前选定的任务。如果此值不为 null,则我们调用
UpdateTaskDialog
可组合项,并将onConfirm()
回调设置为使用TaskApi
向服务器发送 POST 请求。最后,当你创建
TaskCard
可组合项时,你使用onUpdate()
回调来设置currentTask
状态变量。- 重新运行客户端应用程序。你现在应该能够使用按钮更新每个任务的详细信息。
后续步骤
在本文中,你已在 Kotlin Multiplatform 应用程序的上下文中使用 Ktor。你现在可以创建一个包含多个服务和客户端的项目,面向各种不同的平台。
如你所见,可以构建特性而无需任何代码重复或冗余。项目所有层中所需的类型都可以放置在 shared 多平台模块中。服务所需的功能放置在 server 模块中,而客户端所需的功能则放置在 composeApp 中。
这种开发不可避免地需要客户端和服务器技术知识。但是你可以使用 Kotlin Multiplatform 库和 Compose Multiplatform 来最大限度地减少你需要学习的新内容。即使你的重点最初只在一个平台上,随着你的应用程序需求增长,你也可以轻松添加其他平台。