Jetpack Compose for Webを使ってみた。

使い方や感想を書いていきます。
↓はサンプルです。

https://github.com/UramnOIL/JetpackComposePractice

## Jetpack Compose for Webとは

https://compose-web.ui.pages.jetbrains.team

> Reactive web UIs for Kotlin, based on Google's modern toolkit and brought to you by JetBrains
> Compose for Web simplifies and accelerates UI development for web applications, and aims to enable UI code sharing between web, desktop, and Android applications in the future. Currently in technology preview.

GoogleのJetpack ComposeをベースとしたWeb用の宣言的UIフレームワークです。他にもJetpack Compose for Desktopもあります。 
Jetpack ComposeのコードはAndroidとDesktop、Webで共有できます。
DOMの操作もサポートしており、Reactのような書き心地でUIを作成することができます。

## ソースコード

### Gradle

```web//build.gradle.kts
import org.jetbrains.compose.compose

repositories {
    mavenCentral()
    maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
}

// Add compose gradle plugin
plugins {
    kotlin("multiplatform") version "1.5.31"
    id("org.jetbrains.compose") version "1.0.0-alpha4-build362"
}

// Enable JS(IR) target and add dependencies
kotlin {
    js(IR) {
        browser()
        binaries.executable()
    }
    sourceSets {
        val jsMain by getting {
            dependencies {
                implementation(compose.web.core)
                implementation(compose.web.widget)
                implementation(compose.runtime)
            }
        }
    }
}

compose.web {
}
```

`kotlin("js")`を指定したら`run`タスクが`org.jetbrains.kotlin.js`と`org.jetbrains.compose`で競合していて適用できなかったので仕方なく`multiplatform`を使っています。

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

`REPLACE_WITH_YOUR_MODULE_NAME`にはモジュール名を入れます。サンプルでは`web`モジュール内で作成しているのでweb.jsとなります。

```src/main/kotlin/Main.kt
import androidx.compose.runtime.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import org.jetbrains.compose.web.css.*
import org.jetbrains.compose.web.css.keywords.auto
import org.jetbrains.compose.web.dom.*
import org.jetbrains.compose.web.renderComposable

class ViewModel {
    private val mutableStateFlow = MutableStateFlow(0)

    val stateFlow: StateFlow<Int>
        get() = mutableStateFlow

    fun onIncrementState() {
        mutableStateFlow.value++
    }
}

val viewModel = ViewModel()

@Composable
fun App() {
    val count by viewModel.stateFlow.collectAsState(viewModel.stateFlow.value, Dispatchers.Main)

    P {
        Text(count.toString())
    }
    Button(
        attrs = {
            onClick { viewModel.onIncrementState() }
        }
    ) {
        Text("Click")
    }
}

fun main() {
    renderComposable(rootElementId = "root") { // ベースとなる要素にレンダリング
        App() // NOTE 他の関数に切り出さないと何故か怒られる
    }
}
```

従来のJetpack Composeとほぼ変わりありません。
新しくHTMLの要素が使えるようになったくらいです。

現時点でJetpack Compose for Webでは`compose.foundation`や`compose.material`は利用できないので注意が必要です。

## 結果

![jetpack_compose.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271071/2132e611-ca66-efef-7f94-1fbb98d5bb3a.gif)

## まとめ

僕自身まだ浅めの知識しかないので、とりあえず基本的な雰囲気だけ紹介しました。

Jetpack Compose for Webは現時点でテクニカルプレビューなのでお遊び程度のことしかできませんが、今後Flutterの対抗馬となり得るポテンシャルがあります。(もしかしたら将来for iOSも出るかも…？)