Skip to content

使用 Kotlin Multiplatform 構建全端應用程式

使用 Kotlin Multiplatform 構建全端應用程式

程式碼範例 full-stack-task-manager

使用的外掛程式

Routing
Routing is a core plugin for handling incoming requests in a server application.
kotlinx.serialization
Content Negotiation
The ContentNegotiation plugin serves two primary purposes: negotiating media types between the client and server and serializing/deserializing the content in a specific format.
Compose MultiplatformKotlin Multiplatform

在本文中,您將學習如何使用 Kotlin 開發一個能在 Android、iOS、Web 和桌面平台上執行的全端應用程式,同時利用 Ktor 實現無縫資料處理。

在本教學結束時,您將瞭解如何執行以下操作:

  • 使用 Kotlin Multiplatform 建立全端應用程式。
  • 瞭解使用 IntelliJ IDEA 產生的專案。
  • 建立呼叫 Ktor 服務的 Compose Multiplatform 用戶端。
  • 在設計的不同層級中重複使用共用型別。
  • 正確包含並配置多平台連結庫。

在之前的教學中,我們使用任務管理員(Task Manager)範例來

處理請求
Learn the basics of routing, handling requests, and parameters in Kotlin with Ktor by building a task manager application.
建立 RESTful API
Learn how to build a backend service using Kotlin and Ktor, featuring an example of a RESTful API that generates JSON files.
以及
使用 Exposed 整合資料庫
Learn the process of connecting Ktor services to database repositories with the Exposed SQL Library.
。 用戶端應用程式保持最簡化,以便您可以專注於學習 Ktor 的基礎知識。

您將建立一個針對 Android、iOS、Web 和桌面平台的用戶端,並使用 Ktor 服務來獲取要顯示的資料。在可能的情況下,您將在用戶端和伺服器之間共享資料型別,從而加快開發速度並減少潛在錯誤。

先決條件

與之前的文章一樣,您將使用 IntelliJ IDEA 作為 IDE。要安裝和配置您的環境,請參閱 Kotlin Multiplatform 快速入門指南

如果這是您第一次使用 Compose Multiplatform,我們建議您在開始本教學之前先完成 Compose Multiplatform 入門 教學。為了降低任務的複雜性,您可以專注於單一用戶端平台。例如,如果您從未使用過 iOS,那麼專注於桌面或 Android 開發可能是明智的。

建立新專案

不使用 Ktor 專案產生器,而是使用 IntelliJ IDEA 中的 Kotlin Multiplatform 專案精靈。它將建立一個基礎的多平台專案,您可以透過用戶端和服務對其進行擴展。用戶端可以使用原生 UI 連結庫(例如 SwiftUI),但在本教學中,您將使用 Compose Multiplatform 為所有平台建立共用 UI。

  1. 啟動 IntelliJ IDEA。
  2. 在 IntelliJ IDEA 中,選擇 File | New | Project
  3. 在左側面板中,選擇 Kotlin Multiplatform
  4. New Project 視窗中指定以下欄位:
    • Name : full-stack-task-manager
    • Project ID : com.example.ktor
  5. 選擇 AndroidDesktopWebServer 作為目標平台。

  6. 如果您使用的是 Mac,也請選擇 iOS 。確保勾選了 Share UI 選項。 Kotlin Multiplatform wizard settings

  7. 點擊 Create 按鈕,等待 IDE 產生並匯入專案。

執行服務

  1. 在 IntelliJ IDEA 中,選擇 ApplicationKt 執行配置。 Run & Debug window
  2. 點擊 Run 按鈕 (IntelliJ IDEA run icon) 以執行該配置。

    Run 工具視窗中將開啟一個新標籤。

  3. 導航至 http://0.0.0.0:8080/ 以開啟應用程式。您應該會在瀏覽器中看到來自 Ktor 的訊息。 A Ktor server browser response

檢查專案

server 資料夾是專案中的三個 Kotlin 模組之一。另外兩個是 coreapp

server 模組的結構與 Ktor 專案產生器 產生的結構非常相似。您有一個專用的組建檔案來宣告外掛程式和相依性,以及一個包含用於構建和啟動 Ktor 服務的程式碼的原始碼集:

Contents of the server folder in a Kotlin Multiplatform project

如果您查看 Application.kt 檔案中的路由指令,您會看到對 sayHello() 函式的呼叫:

kotlin

sayHello() 函式定義在 core 模組中。這是您放置要在伺服器和所有不同用戶端平台之間共享的通用程式碼的地方。

開啟 app/shared/src/commonMain 模組中的 Greeting.kt 檔案,可以看到該處也使用了 sayHello() 函式:

kotlin

app模組包含以下子模組:

  • androidAppdesktopAppiosAppwebApp 子模組分別包含 Android、桌面、iOS 和 Web 用戶端應用程式的平台特定程式碼。目前這些用戶端應用程式都沒有連結到 Ktor 服務。
  • shared 子模組包含您希望提供用戶端的每個平台的原始碼集。這是因為在 commonMain 中宣告的型別需要隨目標平台而異的功能。

    例如,在 Greeting 型別中,目前平台的名稱是透過平台特定的 API 獲取的,這是透過 expect 和 actual 宣告 實現的。

    shared 子模組的 commonMain 原始碼集中,getPlatform() 函式使用 expect 關鍵字宣告:

    kotlin

    然後,每個目標平台提供 getPlatform() 函式的 actual 宣告,如下所示:

    kotlin
    kotlin
    kotlin
    kotlin

執行用戶端應用程式

您可以透過執行目標的執行配置來執行用戶端應用程式。要在 iOS 模擬器上執行應用程式,請按照以下步驟操作:

  1. 在 IntelliJ IDEA 中,選擇 iosApp 執行配置和一個模擬裝置。 Run & Debug window
  2. 點擊 Run 按鈕 (IntelliJ IDEA run icon) 以執行該配置。
  3. 當您執行 iOS 應用程式時,它會在後台使用 Xcode 進行構建並在 iOS 模擬器中啟動。該應用程式顯示一個按鈕,點擊時會切換圖片。 Running the app in the iOS Simulator

    第一次按下按鈕時,目前平台的詳細資訊會新增到按鈕文字中。實現此功能的程式碼位於 app/shared/src/commonMain/kotlin/com/example/ktor/App.kt

    kotlin

    這是一個可組合(composable)函式,您稍後將在本文中對其進行修改。目前,唯一重要的是它顯示了一個 UI 並使用了共享的 Greeting 型別,而該型別又使用了實作通用 Platform 介面的平台特定類別。

既然您已經瞭解了產生專案的結構,就可以逐步新增任務管理員功能。

新增模型型別

首先,新增模型型別並確保用戶端和伺服器都可以訪問它們。

  1. 導航至 gradle/libs.versions.toml 並定義以下 kotlinx.serialization 相依性:
    toml
  2. 導航至 core/build.gradle.kts 並新增序列化外掛程式:

    kotlin
  3. 在同一個檔案中,為 commonMain 原始碼集新增一個新相依性:

    kotlin
  4. 在 IntelliJ IDEA 中,選擇 Build | Sync Project with Gradle Files 以套用更新。Gradle 匯入完成後,您應該會發現 Task.kt 檔案可以編譯成功。
  5. 導航至 core/src/commonMain/kotlin/com/example/ktor 並建立一個名為 model 的新封裝。
  6. 在新封裝中,建立一個名為 Task.kt 的新檔案。
  7. 新增一個列舉來表示優先級(priorities),以及一個類別來表示任務。 Task 類別使用了來自 kotlinx.serialization 連結庫的 Serializable 註解:

    kotlin

建立伺服器

下一階段是為任務管理員建立伺服器端實作。

  1. 導航至 server/src/main/kotlin/com/example/ktor 資料夾並建立一個名為 model 的子封裝。
  2. 在此封裝中,建立一個新的 TaskRepository.kt 檔案,並為儲存庫新增以下介面:

    kotlin
  3. 在同一個封裝中,建立一個名為 InMemoryTaskRepository.kt 的新檔案,包含以下類別:

    kotlin
  4. 導航至 server/src/main/kotlin/.../Application.kt 並將現有程式碼替換為以下實作:

    kotlin

    此實作與之前教學中的實作非常相似,不同之處在於現在為了簡化,我們將所有路由程式碼都放在 Application.module() 函式中。

    輸入此程式碼並新增匯入後,您會發現多個編譯器錯誤,因為程式碼使用了多個需要作為相依性包含的 Ktor 外掛程式,包括用於與 Web 用戶端互動的

    CORS
    Required dependencies: io.ktor:%artifact_name%
    外掛程式。

  5. 開啟 gradle/libs.versions.toml 檔案並定義以下連結庫:
    toml
  6. 開啟伺服器模組組建檔案( server/build.gradle.kts )並新增以下相依性:

    kotlin
  7. 再次在主功能表中執行 Build | Sync Project with Gradle Files。匯入完成後,您應該會發現 ContentNegotiation 型別和 json() 函式的匯入工作正常。
  8. 重新執行伺服器。您應該會發現路由可以從瀏覽器訪問。
  9. 導航至 以查看 JSON 格式的任務伺服器回應。 Server response in browser

建立用戶端

為了讓您的用戶端能夠訪問伺服器,您需要包含 Ktor 用戶端。這涉及三種類型的相依性:

  • Ktor 用戶端的核心功能。
  • 處理網路的平台特定引擎。
  • 對內容協商(content negotiation)和序列化的支援。
  1. gradle/libs.versions.toml 檔案中,新增以下連結庫:
    toml
  2. 導航至 app/shared/build.gradle.kts 並新增以下相依性:
    kotlin

    完成此操作後,您可以新增一個 TaskApi 型別,作為您的用戶端對 Ktor 用戶端的薄包裝函式。

  3. 在主功能表中選擇 Build | Sync Project with Gradle Files 以匯入組建檔案中的變更。
  4. 導航至 app/shared/src/commonMain/kotlin/com/example/ktor 並建立一個名為 network 的新封裝。
  5. 在新封裝中,建立一個新的 HttpClientManager.kt 檔案用於用戶端配置:

    kotlin

    1.2.3.4 替換為您目前電腦的 IP 地址。您將無法從在 Android 虛擬裝置或 iOS 模擬器上執行的程式碼中呼叫 0.0.0.0localhost

    TIP

    尋找您的 IP 地址:

    由於行動模擬器無法訪問 localhost,您需要電腦的實際 IP 地址。要尋找您的 IP 地址,請執行以下命令之一:

    • macOS: ifconfig | grep "inet " | grep -v 127.0.0.1
    • Linux: hostname -I | awk '{print $1}'
    • Windows: ipconfig 並尋找 "IPv4 Address"
  6. 在同一個 app/shared/.../network 封裝中,建立一個具有以下實作的新 TaskApi.kt 檔案:

    kotlin
  7. 導航至 app/shared/.../App.kt 並將程式碼替換為以下實作。這將使用 TaskApi 型別從伺服器獲取任務列表,然後在列中顯示每個任務的名稱:

    kotlin
  8. 在伺服器執行的同時,透過執行 iosApp 執行配置來測試 iOS 應用程式。

  9. 點擊 Fetch Tasks 按鈕以顯示任務列表: App running on iOS

    NOTE

    在本次演示中,為了清晰起見,我們簡化了流程。在現實世界的應用程式中,避免透過網路發送未加密的資料至關重要。
  10. 在 Android 平台上,您需要明確地授予應用程式網路權限,並允許其以明文形式發送和接收資料。要啟用這些權限,請開啟 app/androidApp/src/main/AndroidManifest.xml 並新增以下設定:

    xml
  11. 使用 app.androidApp 執行配置來執行 Android 應用程式。您現在應該會發現您的 Android 用戶端也可以正常執行: App running on Android

  12. 對於桌面用戶端,您將為容器視窗分配尺寸和標題。開啟檔案 app/desktopApp/src/.../main.kt 並透過變更 title 並設定 state 屬性來修改程式碼:

    kotlin
  13. 使用 app [hot] 🔥 執行配置執行桌面應用程式: App running on desktop

  14. 使用以下執行配置之一執行 Web 用戶端:

    • app [js]: 執行您的 Kotlin/JS 應用程式。
    • app [wasmJs]: 執行您的 Kotlin/Wasm 應用程式。
    App running on web

改進 UI

用戶端現在正在與伺服器通信,但這顯然稱不上是一個美觀的 UI。

  1. 開啟位於 app/shared/src/commonMain/.../ktorApp.kt 檔案,並將現有的 App 替換為下面的 AppTaskCard 可組合項:

    kotlin

    透過此實作,您的用戶端現在具備了一些基本功能。

    透過使用 LaunchedEffect 型別,所有任務都會在啟動時載入,而 LazyColumn 可組合項允許使用者捲動任務列表。

    最後,建立了一個單獨的 TaskCard 可組合項,它轉而使用 Card 來顯示每個 Task 的詳細資訊。還新增了用於刪除和更新任務的按鈕。

  2. 重新執行用戶端應用程式 — 例如 Android 應用程式。您現在可以捲動任務、查看其詳細資訊並將其刪除: App running on Android with improved UI

新增更新功能

為了完成用戶端,請加入允許更新任務詳細資訊的功能。

  1. 導航至 app/shared/src/commonMain/.../ktor 中的 App.kt 檔案。
  2. 新增 UpdateTaskDialog 可組合項和必要的匯入,如下所示:

    kotlin

    這是一個使用對話方塊顯示 Task 詳細資訊的可組合項。descriptionpriority 被放置在 TextField 可組合項中,以便它們可以被更新。當使用者按下更新按鈕時,它會觸發 onConfirm() 回呼。

  3. 更新同一個檔案中的 App 可組合項:

    kotlin

    您正在儲存一個額外的狀態,即當前選取的任務。如果此值不為 null,那麼我們將調用我們的 UpdateTaskDialog 可組合項,並將 onConfirm() 回呼設定為使用 TaskApi 向伺服器發送 POST 請求。

    最後,當您建立 TaskCard 可組合項時,您使用 onUpdate() 回呼來設定 currentTask 狀態變數。

  4. 重新執行用戶端應用程式。您現在應該能夠透過使用按鈕來更新每個任務的詳細資訊。 Deleting tasks on Android

後續步驟

在本文中,您已在 Kotlin Multiplatform 應用程式的內容中使用了 Ktor。您現在可以建立一個包含多個服務和用戶端,並針對一系列不同平台的專案。

正如您所看到的,構建功能時無需任何程式碼重複或冗餘。專案所有層級所需的型別都可以放置在 core 多平台模組中。僅服務需要的功能放在 server 模組中,而僅用戶端需要的功能則放在 app 模組中。

這種開發必然需要用戶端和伺服器技術的知識。但您可以使用 Kotlin Multiplatform 連結庫和 Compose Multiplatform 來最大限度地減少您需要學習的新內容。即使您最初只專注於單一平台,隨著對應用程式需求的成長,您也可以輕鬆新增其他平台。