RestApi

Dáta sem, dáta tam.

Nástroje používateľa

Nástoje správy stránok


kotlin-compose-multiplatform

Compose Multiplatform

Compose Multiplatform (CM) je deklaratívný framework. Slúži na tvorbu UI pre viacero platforiem, teda pre Android, iOS, desktop (Linux, Windows), web. Vývoj sa uskutočňuje v jazyku Kotlin. Compose Multiplatform je produkt firmy JetBrains a rozširuje sadu nástrojov Jetpack Compose od spoločnosti Google pre Android a zároveň zdieľa kompilátor a runtime Compose s Jetpack Compose a používa rovnaké API. Nové projekty je vhodné písať už pod knižnicami CM, čo zabezpečí bezproblémové zdieľanie kódu pre MP a ich dokonalejšiu vzájomnú podobnosť.

Deklarácia sa realizuje funkciam označenými @Composable. Mená funkcií začínajú veľkým písmenom (ako triedy). Užívateľské Rozhranie sa kombinuje z týchto funkcií. a ďalších komponentov pre rozloženie. Základné komponenty sú napríklad: Row, Column, Text, Button a mnoho ďalších. Prispôsobenie komponentov je možné pomocou modifikátorov. Spolu všetky komponnety zabezpečia ich rozloženie, vstup od používateľa, animácie a zmeny stavov.
V IntelliJ IDEA aj Android Studio sa vizualizuje rozloženie kompozovateľnéhych prvkov označených ako @Preview.

Kotlin : Material3
Android Developers: Jetpack Compose
Klibs.io : KMP knižnice
GitHub : Material Theme Builder
Material.io : Material Design 3 (Android, Web, Flutter)
Ak ste pracovali s JavaFX, potom Compose je podobne štrukturovaný. Pridanú hodnotu v Compose tvorí Modifier, ktorý ovplyvňuje vzhľad.

Štruktúra projektu

Každá platforma potrebuje určitý vstupný bod, teda funkciu či notifikáciu:

  • Android → Activity
  • iOS → @main
  • JVM → main()
  • Kotlin/JS, Kotlin/Wasm → main()

commonMain je zdrojová zložka s kódom pre Kotlin Multiplatform. Pri kompilácii sa odtiaľ vytvára štruktúra binárnych súborov pre distribúciu. Tu sa použijú len multiplatformové knižnice. Tu definované štruktúry budú dostupné pre všetky platformy projektu.

Ceielené platformy sa definujú zväčša pri vytvorení projektu, ale je to možné i neskôr (composeApp.build.gradle.kts).

Zdrojové sady pre cielené platformy sú vedľa zložky commonMain a sú vo vlastných zložkách (androidMain, iosMain, jvmMain, …). Každý zdroj obsahuje definíciu cieľa kompilácie, aj dostupné závislosti.

Ovládanie UI

  • Softvérové ​​klávesnice - pre iOS napodobňuje podľa Android čo najverenjšie
  • Dotyk a Myš - emulujú sa gestá na pohyb myši, ale nepodpořujú sa viacdotykové gestá
  • Kopírovanie, Preklad a podobne - zabezpečuje platforma
  • Rolovanie - pre Android a iOS je v platforme, pre ostatné je to koliesko myši
  • Interakcia - sa riadí podľa manuálov SwiftUI a UIKit a pre počítače podľa Swing
  • Späť - pre Android je to v platforme, pre iOS sa emuluje Android, pre PC klávesou ESC
  • Text - podľa platformy

Kompozície komponentov

Kompozície svojim rozložením komponetov vytvárajú používateľské rozhrania (UI). Takéto funkcie sú označené náveštím @Composable. Zväčša môžu byť aj v IDE vizualizované označením @Preview, teda ak neprijímajú v parametre stavy.

Základné štrukturovanie kompozície je prvkami:

  • Column, FlowColumn - prvky pod sebou
  • Row, FlowRow - prvky vedľa seba
  • Box - prvky v oblasti

Modifikátory

umožňujú nastaviť rozmery, zarovnanie, odsadenie, interakcie a iné…

JetBrains KMP : Práca s modifikátormi
Android Developers : Rozloženie komponetov, Modifikátory

Reťazenie modifikátorov

parametre modifikátora je možné zreťaziť: Modifier.padding(24.dp).fillMaxWidth(). Význam má postupnosť volaní, pretože následujúci parameter je viazaný na predchádzajúci.

Vstavané modifikátory

niektoré parametre (napríklad: size, padding a offset) môžu mať viac parametrov oddelených čiarkami.
requiredSize() prepisuje obmädzenie
paddingFromBaseline() odsadzuje voči zákldnej čiare offset() relatívne posúva prvok

Modifikátory s obmedzeným rozsahom

matchParentSize() oznamuje dieťaťu parametee rodiča weight() v rámci Row alebo Column sa vzťahuje k svojim súrodencom

definícia modifikátorov

opätovné použitie inštancií modifikátorov zlepšuje čitateľnosť kódu a môže zvýšiť výkon

Vlastné modifikátory

je môžené si tiež vytvoriť vlastné modifikátory nad rámec vstavaných

Android Developers : Chain existing modifiers

Adaptívné rozloženia

Pre konzistentný vzhľad na všetkých platformách je vhodné dodržať pokyny:

  • Preferovť kanonické vzory rozloženia (detaily zoznamu, informačný kanál a podporný panel)
  • Zachovať konzistenciu opätovným použitím zdieľaných štýlov pre odsadenie, typografiu a ďalšie dizajnové prvky.
  • Rozdeiť zložité rozloženia na opakovane použiteľné.
  • Rozmiestňovať podľa hustoty a orientácie obrazovky.
Android Developers : Kanonické vzory rozloženia

Používanie tried veľkostí okien

Triedy veľkosti okien, teda body zlomu kategorizujú dizajny rozloženia na kompaktné, stredné a rozšírené. Preto je vhodné využiť WindowSizeClass pridaním závosilosti:

// libs.version.toml:
[versions]
adaptive = "1.2.0-alpha06"
[libraries]
adaptive = { module = "org.jetbrains.compose.material3.adaptive:adaptive", version.ref = "adaptive" }
 
// build.gradle.kts:composeApp
kotlin { sourceSets { commonMain.dependencies {
  implementation(libs.adaptive) }}}

Multiplatformové zdroje

CM poskytuje knižnicu compose-multiplatform-resources (Gradle) pre prístup k zdrojom v kóde pre podporované platformy. Zdroje sú statický obsah: obrázky, písma a reťazce, použitie vo vlastnej aplikácii. Zdroje sa čítajú synchrónne vo vlákne volajúceho, okrem asynchrónneho čítania pre nespracované súbory a webové zdroje.

JetBrains KMP : Prehľad zdrojov

Pôvodná zložka zdrojov pre Android res.* so zložkami drawable, mipmap, values.strings.xml je použiteľná len v composeApp.kotlin.<prj>.MainActvity.kt, teda v MP je treba použiť vlastné zložky a zdroje. Absolutna cesta: /<prj>/composeApp/src/androidMain/res/values/strings.xml.

V Android Studio nie je viditeľná (25sept) zložka zdrojov, avšak je možné ju editovať (Finder). Absolútna cesta: /<prj>/composeApp/src/commonMain/composeResources/values/strings.xml, kde values/strings.xml je manuálne vytvorené.

Tento XML je treba editovať v externom editore a po spustení sa jeho obsahom generuje Class

Od CM 1.6.10 a Kotlin 2.0.0 už vygenerovaný projekt zahŕňa potrebné závislosti a zdroje a importy, takže po vytvorení …/strings.xml sa pri spustení projektu vygeneruje composeApp.commonMain.kotlin.<prj>.composeapp.generated.resources.String0.commonMain.kt, ktoré umožňí import v Composable funkciách importovať napríklad vlastné stringy : import <prj>.composeapp.generated.resources.my_string. Absolutna cesta: /<prj>/composeApp/build/generated/compose/resourceGenerator/kotlin/commonMainResourceAccessors/<prj>/composeapp/generated/resources/.
Samotný kód potom môže vyzerať takto: Text(stringResource(Res.string.my_string)), kedy tag my_string je možné používať až po builde projektu: Build → Assemble project.
Každý string bude mať vlatný import: import <prj>.composeapp.generated.resources.my_string

! Zložky vlastných zdrojov musia mať názvy: drawable, files, font, values

Trieda Res.kt je umiestnená: /<prj>/composeApp.commonMain.kotlin.<prj>.composeapp.generated.resources.

Lokalizácia

jazyková lokalizácia je prípona za pomlčkou podľa normy ISO 639-1 alebo ISO 639-2, prípadne regionálna prípona podľa normy ISO 3166-1-alpha-2 má za ďalšou pomlčkou r, následujúce XX označenie regiónu (values-spa-rMX)

  • vytvoriť zložku values-sk v /<prj>/composeApp/src/commonMain/composeResources/
  • skopírovať do nej values.xml a preložiť
  • po Build → Assemble project bude v assets.composeResources vytvorená zložka s lokalizáciou

Ďaší postup je v odstavci Lokalizácia reťazcov.

Životný cyklus

Životný cyklus komponentov je prevzatý z konceptu životného cyklu Jetpack Compose. Zároveň poskytuje spoločnú LifecycleOwner implementáciu, ktorá rozširuje pôvodnú funkcionalitu Jetpack Compose na iné platformy a pomáha sledovať stavy životného cyklu v spoločnom kóde.
Závislosť bude v novom projekte už vygenerovaná: implementation("org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.4")
Pro použití korutín je Lifecycle.coroutineScope hodnota viazaná na Dispatchers.Main.immediate

       (initial state) (dead state)
       ┏━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━┓ ┏━━━━━━━━━┓ ┏━━━━━━━━━┓ ┏━━━━━━━━━┓
STAVY  ┃ INITIALISED ┃ ┃ DESTROYED ┃ ┃ CREATED ┃ ┃ STARTED ┃ ┃ RESUMED ┃
       ┗━━┳━━━━━━━━━━┛ ┗━━┳━━━━━━━━┛ ┗━━┳━━━━━━┛ ┗━━━━┳━━━━┛ ┗━━━━━━┳━━┛
          ┃ ON_CREATE     ┃             ┃             ┃             ┃
          ┣━━━━━━━━━━━━━━━╋━━━━━━━━━━━━▶┃ ON_START    ┃             ┃
          ┃               ┃             ┃━ ━ ━ ━ ━ ━ ▶┃ ON_RESUME   ┃
          ┃               ┃             ┃             ┃━ ━ ━ ━ ━ ━ ▶┃
          ┃               ┃             ┃             ┃             ┃
UDALOSTI  ┃               ┃             ┃             ┃ ON_PAUSE    ┃
          ┃               ┃             ┃ ON_STOP     ┃◄ ━ ━ ━ ━ ━ ▶┃
          ┃               ┃ ON_DESTROY  ┃◄ ━ ━ ━ ━ ━ ━┃             ┃
          ┃               ┃◄ ━ ━ ━ ━ ━ ━┃             ┃             ┃          
       ┏━━┻━━━━━━━━━━┓ ┏━━┻━━━━━━━━┓ ┏━━┻━━━━━━┓ ┏━━━━┻━━━━┓ ┏━━━━━━┻━━┓
STAVY  ┃ INITIALISED ┃ ┃ DESTROYED ┃ ┃ CREATED ┃ ┃ STARTED ┃ ┃ RESUMED ┃
       ┗━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━┛ ┗━━━━━━━━━┛ ┗━━━━━━━━━┛ ┗━━━━━━━━━┛
JetBrains KMP : Životný cyklus

ViewModel

Bežný ViewModel z Android je aj pre KMP.
Závislosť bude v novom projekte už vygenerovaná: implementation("org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4")
Po deklarácii triedy reprezentujúcej ViewModel je možné triedu použiť v Compose funkcii.
Pri použití korutín je ViewModel.viewModelScope hodnota viazaná na Dispatchers.Main.immediate

JetBrains KMP : Common ViewModel

Koncept navigácie vyjadruje Navigačný graf s destináciami a prepojeniami medzi nimi. Za cieľ sa považuje uzol, ktorý reprezentuje iný uzol, vnorený graf, či dialóg. Prejdenie do cieľa vykoná jeho zobrazenie. Trasa určuje cieľ a cestu k nemu.
Aplikácia reprezentuje cestu a zásobník a spätný zásobník cieľov. Po kroku do cieľa sa tento presunie na začiatok zásobníka, aby bol možný krok späť. Cieľ je možné priradiť URI (priamy odkaz).

Pri potrebe využiť navigačnú knižnicu je treba pridať závislosť: implementation("org.jetbrains.androidx.navigation:navigation-compose:2.9.0") :

// libs.version.toml:
[versions]
navigationCompose = "2.9.0"
[libraries]
navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
 
// build.gradle.kts:composeApp
kotlin { sourceSets { commonMain.dependencies {
  implementation(libs.navigation.compose) }}}

Umožňuje presmerovať na konkrétnu destináciu v príslušnej aplikácii. Môžu byť užitočné aj na získanie externého vstupu do aplikácie (napríklad OAuth). Implementujú sa tak, že najprv sa zaregistrujú, potom definujú cesty k cieľom a nakoniec ich aplikácia spravuje.

JetBrains KMP : Navigácia v aplikácii Compose
JetBrains KMP : Navigácia a smerovanie
JetBrains KMP : Priame odkazy - Deep links
JetBrains KMP : Operácie drag-and-drop

Testovanie KMP

Rozhranie KMP UI pre test volá API runComposeUiTestfunkciu a testovacie fExterný odkaz]]unkcie sa volajú na ComposeUiTest prijímači. Pre desktop API je využité JUnit.

Pre testovanie sa použije postup:

  • pridajť zdrojový kód pre testy a potrebné závislosti
  • napísať a spustite vzorový test
  • prispôsobenie testu

Bežné testy sa tvoria a spúšťajú v composeApp.commonMain.commonTest.kotlin a príkazom v terminály :

  • Android - ./gradlew :composeApp:connectedAndroidTest
  • iOS - ./gradlew :composeApp:iosSimulatorArm64Test
  • desktop - ./gradlew :composeApp:desktopTest
  • Wasm - ./gradlew :composeApp:wasmJsTest
JetBrains KMP : Testovanie KMP
Android Developers : Test your Compose layou

Lokalizácia reťazcov

KMP ponúka spoločnú knižnicu na správu zdrojov a generovanie kódu pre jednoduchý prístup k prekladom. Pripraviť zdroje treba tak, ako to je vysvetlnie v tomto článku je aj v odstavci Multiplatformové zdroje → Lokalizácia.

Po zostavení KMP spracuje strings.xml súbory zdrojov composeResources a vytvorí statické vlastnosti prístupu pre každý reťazcový zdroj: Res.strings, kedy pri použití @Composable bude j vizualizovaný v IDE.

V strings.xml je možné použiť šablóny pre String a Double: <string name="welcome_message">Welcome, %s!</string> a v kóde následne volať vložennú premennú ako parameter: Text(stringResource(Res.strings.welcome_message, "User"))

JetBrains KMP : Lokalizácia reťazcov

Regionálne formáty

Formátovanie je momentálne implementované pomocou kotlinx-datetime knižnice. Reprezentujú textové zobrazenia veličín či ich výber (dátum, čas, mena).

JetBrains KMP : Regionálne formáty

Projekt

Po vytvorení kompletného projektu budú v module composeApp nasledujúce zdrojové sady:

  • androidMain - (Kotlin/JVM)
  • commonMain - zdrojové kódy (Kotlin)
  • iosMain - (Kotlin/Native)
  • jsMain - pre web (Kotlin/JS)
  • jvmMain - pre desktop (Kotlin/JVM)
  • wasmJsMain - pre web (Kotlin/Wasm)
  • webMain - zdroje pre jsMain a wasmJsMain
  • commonTest - ak sú zahrnuté aj testy

Odporúča sa kód špecifický pre platformu písať priamo do jeho zdrojovej sady (nie duplikovať v zdroji).

Zdrojový kód začína v: composeApp/src/commonMain/kotlin/App.kt, vo funkcii App() { … }

JetBrains KMP : Spustenie aplikácie na Android, iOS, desktop(jvm),

Generovanie artefaktov pre web distribúciu : View | Tool Windows | Gradle → kotlin browser → jsBrowserDistribution & wasmJsBrowserDistribution ⇒ composeApp/build/dist/wasmJs[js]/productionExecutable :

JetBrains Kotlin : Generate artifacts pre Js a Wasm
hotový príklad Js: https://regalems.restapi.sk/js
hotový príklad Wasm: https://regalems.restapi.sk/wasmJs (wasmJs nie je funkčné…)

Generovanie desktop distribúcie : View | Tool Windows | Gradle → compose desktop → createReleaseDistributable ⇒ ~/IdeaProjects/<projectName>/composeApp/build/compose/binaries/main-release/app pre macOS je možné skopírovať priamo do Finder | Aplikácie,

Generovanie iných projektov

Projekt mobilnej platformy so zdieľanou logikou a natívným UI sa vytvorí definíciou pri vytváraní projektu, napríklad v IJI:

  • Generators → Kotlin Multiplatform
  • [+] Android
  • [+] iOS [Do not share UI]

Po tom je v zložkách následujúce obsadeie:

  • shared - spoločná logika pre Android aj iOS
    • commonMain - Interface + Class: shared.src.commonMain.kotlin.<prjName>.<Class>.kt
    • androidMain - Android Class: shared.src.androidMain.kotlin.<prjName>.<Class>.android.kt
    • iosMain - iOS Class: shared.src.iosMain.kotlin.<prjName>.<Class>.ios.kt
  • composeApp - projekt Android: composeApp.src.androidMain.kotlin.<prjName>.App.kt, ale aj Android projekt
  • iosApp - projekt Xcode: iosApp.iosApp.Preview Content.iOSApp.swift + ContentView.swift

Pridanie závislostí

Závislosti môžu byť Multiplatformové, alebo Natívne. Pre oba typy závislostí je možné použiť lokálne aj externé repozitáre.

Pre ostré projekty je vhodné zjednodušené cesty v implementáciách rozvinúť do <projectName>.gradle.libs.versions.toml

kotlinx-datetime

Zabezpečuje operácie s dátumom a časom, pre typmy ako Instant, LocalDateTime a TimeZone. Po implementácii do composeApp/build.gradle.kts je nutné synchronizovať Gradle a v terminály IDE spustiť:

./gradlew kotlinUpgradeYarnLock kotlinWasmUpgradeYarnLock
JetBrains KMP: pridanie závislosti s DT
Klibs.io : kotlinx-datetime

KMP závislosti

kotlinx-datetime

kotlinx.corutines

kotlinx.serialization

Ktor

sqlDelight

  • možné a odporúčané je inštalovať plugin SQLDelight na prácu so .sq súbormi v IDE

Pridanie závislosti do .shared.build.gradle.kts (IJIdea) :

plugins {
    // json serialization
    kotlin("plugin.serialization") version "2.2.21"
}
 
kotlin {
    // ktor actual version
    val ktorVersion = "3.3.1"
 
    sourceSets {
        all { languageSettings.optIn("kotlin.time.ExperimentalTime") }
 
        commonMain.dependencies {
            // date-time
            implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.7.1")
            // coroutines
            implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
            // ktor
            implementation("io.ktor:ktor-client-core:${ktorVersion}")
            implementation("io.ktor:ktor-client-content-negotiation:${ktorVersion}")  // serialization and deserialization
            implementation("io.ktor:ktor-serialization-kotlinx-json:${ktorVersion}")  // set JSON for serialization/deserialization
        }
        commonTest.dependencies {
            implementation(libs.kotlin.test)
        }
        androidMain.dependencies {
            // ktor
            implementation("io.ktor:ktor-client-android:$ktorVersion")
        }
        iosMain.dependencies {
            // ktor
            implementation("io.ktor:ktor-client-darwin:$ktorVersion")
        }
    }
}

podrobné vloženie závislostí

  • koin
  • sqlDelight
  • lifecycleViewmodelCompose
  • material3
gradle/libs.versions.toml
[versions]
agp = "8.11.2"  #// JetBrains say "8.7.3"
#// ...
coroutinesVersion = "1.10.2"
dateTimeVersion = "0.7.1"
koin = "4.1.0"
ktor = "3.3.1"
sqlDelight = "2.1.0"
#// lifecycleViewmodelCompose = "2.9.1"  # no need (see: androidx-lifecycle = "2.9.5")
material3 = "1.3.2"
 
[libraries]
#// ...
android-driver = { module = "app.cash.sqldelight:android-driver", version.ref = "sqlDelight" }
koin-androidx-compose = { module = "io.insert-koin:koin-androidx-compose", version.ref = "koin" }
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutinesVersion" }
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "dateTimeVersion" }
ktor-client-android = { module = "io.ktor:ktor-client-android", version.ref = "ktor" }
ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" }
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
native-driver = { module = "app.cash.sqldelight:native-driver", version.ref = "sqlDelight" }
runtime = { module = "app.cash.sqldelight:runtime", version.ref = "sqlDelight" }
#// androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" }
androidx-compose-material3 = { module = "androidx.compose.material3:material3", version.ref="material3" }
 
[plugins]
#// ...
kotlinxSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqlDelight" }
shared/build.gradle.kts
plugins {
    // ...
    alias(libs.plugins.kotlinxSerialization)
    alias(libs.plugins.sqldelight)
}
 
kotlin {
    // ...
    sourceSets {
        all {
            languageSettings.optIn("kotlin.time.ExperimentalTime")
        }
        iosMain.dependencies {
            implementation(libs.ktor.client.darwin)
            implementation(libs.native.driver)
        }
        androidMain.dependencies {
            // ...
            implementation(libs.ktor.client.android)
            implementation(libs.android.driver)
        }
        commonMain.dependencies {
            // ...
            implementation(libs.kotlinx.coroutines.core)
            implementation(libs.ktor.client.core)
            implementation(libs.ktor.client.content.negotiation)
            implementation(libs.ktor.serialization.kotlinx.json)
            implementation(libs.runtime)
            implementation(libs.kotlinx.datetime)
            implementation(libs.koin.core)
        }
    }
}
 
// ...
sqldelight {
    databases {
        create("AppDatabase") {
            packageName.set("<projectName>.cache")
        }
    }
}

Iné závislosti

vyhľadávacia služba KMP knižníc od JetBrains : Klibs.io
Kotlin & KMP Dependency Injection Framework : Koin
Kompletný ekosystém pre vývoj moderných API : Apollo , docs Apollo
výkonný HTTP klient (pôvodne OkHTTP) : Okio , Multiplatform

omitted:
* Android => Android(AndroidStudio) + iOS(Xcode), add shared module commonMain & iosMain


SQLDelight

Je vhodné nainštalovať plugin SQLDelight do IDE, ako je vyššie uvedené. Potom budú ponúkané automaticky rôzne časti štruktúry projektu a kódu. V odkaze na JB Konfigurácia SQLDelight je presný postup ako vytvoriť štruktúru zložiek a pridať súbory pre operácie s DB. Taktiež v ľavom rámci IDE je dostupný doplnok SQLDelight,

Po vytvorení súborov sa v terminále spustí: (prípadne použiť panel: Gradle→<prjName>→Tasks→sqldelight→generateCommonMainAppDatabaseInterface)

./gradlew generateCommonMainAppDatabaseInterface

čo vygeneruje štruktúru a kódy do : shared/build/generated/sqldelight

JetBrains KMP : Konfigurácia SQLDelight
JetBrains Marketplace : SQLDelight
JetBrains KMP : Vytvorenie aplikácie pre iOS - sqlite3
kotlin-compose-multiplatform.txt · Posledná úprava: od attilajancik