Kotlin 多平台 Compose + 桌面 + Web + 移动设备

问题描述 投票:0回答:4

目前是否可以有一个使用 compose 的 kotlin 多平台项目来同时共享桌面、Web 和移动设备的 ui 代码?我发现的所有示例仅涵盖带有 JS Front + Jvm Backend 或 JVM Android + Desktop + Common Module 的多平台,并且我在同时使用所有这些设置项目时遇到了麻烦。

我尝试这样做:

plugins {
    kotlin("multiplatform")
    id("org.jetbrains.compose") version "1.0.1-rc2"
    id("com.android.library")
}
kotlin {
    android()
    jvm("desktop") {
       ...
    }
    js{
       ...
    }
    sourceSets {
        val commonMain by getting {
            dependencies {
               ...
            }
        }
        val commonTest by getting {
            dependencies {
               ...
            }
        }
        val androidMain by getting {
            dependencies {
                ...
            }
        }
        val androidTest by getting {
            dependencies {
                ...
            }
        }
        val desktopMain by getting {
            dependencies {
                ...
            }
        }
        val desktopTest by getting
        val jsMain by getting{
            dependencies{
                ...
            }
        }
        val jsTest by getting {
            dependencies {
                ...
            }
        }
    }
}

但它会产生错误:

:common:jsMain: Could not resolve org.jetbrains.compose.runtime:runtime:1.0.1-rc2.
Required by:
    project :common

如果我评论 JS 相关部分它可以工作,或者如果我评论所有非 js 相关内容它也可以工作

评论所有与撰写相关的内容也有效

问题仅出现在组合所有内容时

android-jetpack-compose kotlin-multiplatform
4个回答
8
投票

简短的回答是:,目前这是不可能的。

JB 团队正在致力于此类支持,可以在这些示例中进行测试,但目前它还处于实验阶段,不能保证很快就会发布。 Compose JB 版本现已与 Android Compose 同步,因此我预计 1.2.0 将由两者大约在同一时间发布,即使 Web 支持尚未完成。


我无法重现您的错误,但我假设您尚未从公共依赖项中删除

compose.foundation
compose.material

目前,公共模块只有

compose.runtime
可用,这使得此时几乎不可能进行任何布局:甚至
Button
Text
都不可用。

正如你在JS应用示例中看到的,

Text
不是从
androidx.compose.material.Text
导入的,而是从
org.jetbrains.compose.web.dom.Text
导入的,这是一个完全不同的元素,所以它不能在公共模块中使用。

在这一点上,我想说 Compose JS 是另一个允许你以 Compose 风格编写 UI 的框架。



1
投票

**

这个答案并不是这个问题的准确答案。正确的 这个问题的答案是@PhilipDukhov 的答案。然而,这可能 帮助登陆此页面的其他人找到其他问题的解决方案 问题我只是把它留在这里。 欲了解更多信息,请阅读评论 我和@PhilipDukhov 之间的这个答案。

**

是的,可以在一个项目中包含所有三个模块。太长了

我做了一些解决方法,并设法将所有东西都放在一个项目中,即

  1. 为桌面撰写
  2. 适用于 Android 的 Compose
  3. 为网络撰写

进一步之前的建议

尽管下面的代码片段有效,通过阅读您将会 能够真正实现你想要的。我个人推荐你 只需访问 Github 存储库这里。我认为这会节省时间 如果您正在创建一个新项目。但是,如果您要添加网络 当前 KMM 项目的模块继续阅读。

可以通过保持正确的依赖关系来解决问题

所以项目结构应该是这样的

enter image description here

注意:如果您正在创建一个新的 KMM 项目,您将只有通用、桌面和 Android 模块。(如您所知)

第1步:您需要将一个文件夹添加到根目录(android、common、desktop文件夹所在的位置),将其命名为web(应该没关系)

第2步:在这个新创建的目录web中添加几个文件夹 你的网络目录应该是这样的

enter image description here

注意:不要添加构建(其自动生成)仅添加

src/jsMain
   -kotlin
   -resources
src/jsTest 
    -kotlin
   -resources

并创建一个名为 build.gradle.kts

的文件

第3步:将这个新添加的Gradle文件的内容更改为类似这样的内容

import org.jetbrains.compose.compose
import org.jetbrains.compose.desktop.application.dsl.TargetFormat

plugins {
    kotlin("multiplatform")
    id("org.jetbrains.compose") version "1.0.0"
}

group = "com.example"
version = "1.0"

kotlin {
    js(IR) {
        browser {
            testTask {
                testLogging.showStandardStreams = true
                useKarma {
                    useChromeHeadless()
                    useFirefox()
                }
            }
        }
        binaries.executable()
    }
    sourceSets {
        val jsMain by getting {
            dependencies {
                implementation(compose.web.core)
                implementation(compose.runtime)
            }
        }
        val jsTest by getting {
            dependencies {
                implementation(kotlin("test-js"))
            }
        }
    }
}

第4步:并将Main.kt添加到

jsMain/kotlin
,内容如下

 renderComposable(rootElementId = "root") {
    Div(
        attrs = {
            // specify attributes here
            style {
                // specify inline style here
            }
        }
    ) {
        Text("A text in <div>")
    }
}

第4步:将index.html添加到jsMain/resources中

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>MultiplatformTest Sample</title>
</head>
<body>
<div id="root"></div>
<script src="web.js"></script>
</body>
</html>

注意:上面代码片段中提到的 web.js 文件是敏感文件,项目 生成时你需要确保它只是 web.js 否则 您在步骤 1 中创建的folder_name.js

第5步:最后将Web模块添加到settings.gradle.kts文件中

pluginManagement {
    repositories {
        google()
        jcenter()
        gradlePluginPortal()
        mavenCentral()
        maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
    }
    
}
rootProject.name = "MultiplatformTest"


include(":android")
include(":desktop")
include(":common")
include(":web")  // Note web module is included here 

我已经创建了一个存储库,请检查此存储库,您可以将其用作您的项目的模板

存储库链接:https://github.com/PSPanishetti/ComposeMultiplatform

如果您有任何疑问,请随时询问他们。


1
投票

是的,现在可以了。对 IOS 的支持已移至测试版,而 Web 则处于测试版。对于新项目,您可以使用 KMP 向导 https://kmp.jetbrains.com/

对于现有项目,您可以按照上述启动项目的结构对当前项目进行更改。但我建议将您的旧代码移至这个新项目。

基本上,您需要在

composeApp
(从 shared 重命名)模块的 build.gradle 文件中包含以下内容。请注意,该项目使用 WASM 进行 Web 构建。您可以同时使用 JS 和 WASM。这使用基于画布的实现来进行撰写,而不是基于 HTML 的实现。

import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig

plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.androidApplication)
    alias(libs.plugins.jetbrainsCompose)
    alias(libs.plugins.compose.compiler)
}

kotlin {
    @OptIn(ExperimentalWasmDsl::class)
    wasmJs {
        moduleName = "composeApp"
        browser {
            commonWebpackConfig {
                outputFileName = "composeApp.js"
                devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply {
                    static = (static ?: mutableListOf()).apply {
                        // Serve sources to debug inside browser
                        add(project.projectDir.path)
                    }
                }
            }
        }
        binaries.executable()
    }
    
    androidTarget {
        @OptIn(ExperimentalKotlinGradlePluginApi::class)
        compilerOptions {
            jvmTarget.set(JvmTarget.JVM_11)
        }
    }
    
    jvm("desktop")
    
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach { iosTarget ->
        iosTarget.binaries.framework {
            baseName = "ComposeApp"
            isStatic = true
        }
    }
    
    sourceSets {
        val desktopMain by getting
        
        androidMain.dependencies {
            implementation(compose.preview)
            implementation(libs.androidx.activity.compose)
        }
        commonMain.dependencies {
            implementation(compose.runtime)
            implementation(compose.foundation)
            implementation(compose.material)
            implementation(compose.ui)
            implementation(compose.components.resources)
            implementation(compose.components.uiToolingPreview)
        }
        desktopMain.dependencies {
            implementation(compose.desktop.currentOs)
        }
    }
}

android {
    namespace = "com.shreyashkore.kmpwizard"
    compileSdk = libs.versions.android.compileSdk.get().toInt()

    sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
    sourceSets["main"].res.srcDirs("src/androidMain/res")
    sourceSets["main"].resources.srcDirs("src/commonMain/resources")

    defaultConfig {
        applicationId = "com.shreyashkore.kmpwizard"
        minSdk = libs.versions.android.minSdk.get().toInt()
        targetSdk = libs.versions.android.targetSdk.get().toInt()
        versionCode = 1
        versionName = "1.0"
    }
    packaging {
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
        }
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = false
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
    buildFeatures {
        compose = true
    }
    dependencies {
        debugImplementation(compose.uiTooling)
    }
}

compose.desktop {
    application {
        mainClass = "MainKt"

        nativeDistributions {
            targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
            packageName = "com.shreyashkore.kmpwizard"
            packageVersion = "1.0.0"
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.