Kotlin Multiplatform (KMP) の使い方やお勧めのライブラリの紹介

kyamada,KotlinCompose

概要

この記事では Kotlin Multiplatform (KMP) の使い方やお勧めのライブラリを紹介します。

Kotlin Multiplatform プロジェクト作成

以下のウィザードでプロジェクトを作成します。

https://kmp.jetbrains.com/ (opens in a new tab)

チュートリアル

公式の以下のチュートリアルが参考になります。

ナビゲーション

ナビゲーションライブラリはVoyager (opens in a new tab)を利用するのがお勧めです。タブナビゲーションなども実装出来ます。

また、Compose Multiplatform 1.6.10-beta01 から実験的にナビゲーションライブラリが追加されたので、そちらの方法でやることもできます。

参考: Navigation and routing (opens in a new tab)

アイコン

androidx.compose.material.icons (opens in a new tab)に用意されているアイコンであれば、以下のように使用できます。

Icon(Icons.Default.PlayArrow, contentDescription = "play")

画像が用意されていない場合は、commonMain/composeResources/drawable ディレクトリ以下に、vector形式のxmlを配置すれば使用できます。

vector形式のxmlは以下の方法で作成できます。

  1. AndroidStudioで androidMain/res/drawable ディレクトリを右クリック > New > Vector Asset
  2. Asset Typeを Clip Artにして、適当なClip Artを選択して作成する。
  3. xmlファイルが作成されるので、commonMain/composeResources/drawable 以下に移動する。

そして、以下のように使用します。

Icon(painterResource(Res.drawable.text_to_speech), contentDescription = "text_to_speech")

ファイル操作

Okio (opens in a new tab)を使うとsharedからファイルの操作が可能です。 zipの解凍も可能ですが、zip圧縮はできません。

ネットワーク

ネットワークライブラリは、チュートリアル (opens in a new tab)でも紹介されている Ktor (opens in a new tab)がおすすめです。

データベース

Google製のRoom (Kotlin Multiplatform) (opens in a new tab)がおすすめです。

SharedPreferences, NSUserDefaults

私は russhwolf/multiplatform-settings (opens in a new tab) というライブラリで共通化しています。

Compose Multiplatform と SwiftUI の相互利用

SwiftUI から Compose Multiplatform を使用したり、逆にCompose Multiplatform から SwiftUI を使用することができます。

Integration with the SwiftUI framework (opens in a new tab)

ただし、現状では SwiftUI から Compose Multiplatform を呼び出す時に具体的な高さを指定しないと表示されない問題があります。 この問題は、以下のページのテクニックで解決できます。

Using Compose for a UI component in a SwiftUI screen (opens in a new tab)

struct SessionDetailsView: View {
    var session: SessionDetails
 
    @State var measuredHeights: [SessionDetails.Speaker: CGFloat] = [:]
 
    var body: some View {
        ScrollView {
            ...
 
            ForEach(session.speakers, id: \.self) { speaker in
                SessionSpeakerInfoViewShared(speaker: speaker.speakerDetails, measuredHeight: binding(for: speaker))
                    .frame(height: binding(for: speaker).wrappedValue)
            }
        }
    }
 
    private func binding(for key: SessionDetails.Speaker) -> Binding<CGFloat> {
        return .init(
            get: { self.measuredHeights[key, default: 1000] },
            set: { self.measuredHeights[key] = $0 })
    }
}
© 品川アプリ.RSS