diff --git a/.gitignore b/.gitignore index 4cfbd6e..d316997 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,7 @@ /buildSrc/build/* .DS_Store /build +*/build/ /captures .externalNativeBuild +.kotlin diff --git a/build.gradle.kts b/build.gradle.kts index b6ffcdc..b11da72 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,6 +6,8 @@ plugins { alias(libs.plugins.android.library) apply false alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.kotlin.compose) apply false + alias(libs.plugins.kotlin.multiplatform) apply false + alias(libs.plugins.compose.multiplatform) apply false alias(libs.plugins.maven.publish) apply false alias(libs.plugins.detekt) signing diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a5f5405..cd52a5c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,6 +19,9 @@ material = "1.12.0" # Compose androidxComposeBom = "2025.08.00" +# Compose Multiplatform +composeMultiplatform = "1.6.10" + [libraries] # Gradle plugins @@ -46,6 +49,13 @@ androidx-compose-animation = { group = "androidx.compose.animation", name = "ani androidx-compose-layout = { group = "androidx.compose.foundation", name = "foundation-layout" } androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime" } +# Compose Multiplatform +compose-mpp-runtime = { group = "org.jetbrains.compose.runtime", name = "runtime", version.ref = "composeMultiplatform" } +compose-mpp-ui = { group = "org.jetbrains.compose.ui", name = "ui", version.ref = "composeMultiplatform" } +compose-mpp-foundation = { group = "org.jetbrains.compose.foundation", name = "foundation", version.ref = "composeMultiplatform" } +compose-mpp-animation = { group = "org.jetbrains.compose.animation", name = "animation", version.ref = "composeMultiplatform" } +compose-mpp-material3 = { group = "org.jetbrains.compose.material3", name = "material3", version.ref = "composeMultiplatform" } + [plugins] # Build plugins android-application = { id = "com.android.application", version.ref = "agp" } @@ -53,6 +63,10 @@ android-library = { id = "com.android.library", version.ref = "agp" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +# Compose Multiplatform +kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } +compose-multiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" } + # Quality plugins detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detektGradlePlugin" } @@ -72,4 +86,12 @@ compose = [ "androidx-compose-material2", "androidx-compose-animation", "androidx-compose-layout" -] \ No newline at end of file +] + +compose-mpp = [ + "compose-mpp-runtime", + "compose-mpp-ui", + "compose-mpp-foundation", + "compose-mpp-animation", + "compose-mpp-material3", +] diff --git a/library-compose-multiplatform/build.gradle.kts b/library-compose-multiplatform/build.gradle.kts new file mode 100644 index 0000000..ea78887 --- /dev/null +++ b/library-compose-multiplatform/build.gradle.kts @@ -0,0 +1,66 @@ +plugins { + alias(libs.plugins.kotlin.multiplatform) + alias(libs.plugins.android.library) + alias(libs.plugins.compose.multiplatform) + alias(libs.plugins.kotlin.compose) + + id(libs.plugins.conventions.lint.get().pluginId) +} + +kotlin { + jvmToolchain(ProjectSettings.Kotlin.JvmToolchainVersion) + + // Android target + androidTarget { + compilations.all { + compileTaskProvider.configure { + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) + } + } + } + } + + // iOS target + listOf( + iosX64(), + iosArm64(), + iosSimulatorArm64() + ).forEach { + it.binaries.framework { + baseName = "DonutCompose" + isStatic = true + } + } + + sourceSets { + val commonMain by getting { + dependencies { + implementation(libs.bundles.compose.mpp) + } + } + + val commonTest by getting { + dependencies { + implementation(kotlin("test")) + } + } + } +} + +android { + namespace = "app.futured.donut.cmp" + compileSdk = ProjectSettings.targetSdk + + defaultConfig { + minSdk = ProjectSettings.minSdkLibraryCompose + } + + testOptions { + targetSdk = ProjectSettings.targetSdk + } + + lint { + targetSdk = ProjectSettings.targetSdk + } +} diff --git a/library-compose-multiplatform/gradle.properties b/library-compose-multiplatform/gradle.properties new file mode 100644 index 0000000..dabc816 --- /dev/null +++ b/library-compose-multiplatform/gradle.properties @@ -0,0 +1,11 @@ +# Gradle +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 + +# Kotlin +kotlin.code.style=official +kotlin.mpp.androidSourceSetLayoutV2AndroidStyleDirs.nowarn=true +kotlin.mpp.applyDefaultHierarchyTemplate=false + +# Android +android.useAndroidX=true +android.nonTransitiveRClass=true diff --git a/library-compose-multiplatform/src/commonMain/kotlin/app/futured/donut/cmp/HelloWorld.kt b/library-compose-multiplatform/src/commonMain/kotlin/app/futured/donut/cmp/HelloWorld.kt new file mode 100644 index 0000000..00c048b --- /dev/null +++ b/library-compose-multiplatform/src/commonMain/kotlin/app/futured/donut/cmp/HelloWorld.kt @@ -0,0 +1,22 @@ +package app.futured.donut.cmp + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier + +/** + * Simple Hello World composable for testing Compose Multiplatform setup. + * This will work on both Android and iOS. + */ +@Composable +fun HelloWorld() { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Text(text = "Hello World from Compose Multiplatform! 🍩") + } +} diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index a9ee359..7b713bc 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -29,6 +29,7 @@ android { dependencies { implementation(project(":library")) implementation(project(":library-compose")) + implementation(project(":library-compose-multiplatform")) implementation(platform(libs.androidx.compose.bom)) implementation(libs.bundles.compose) diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index d691742..f274f1f 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -30,6 +30,10 @@ + + diff --git a/sample/src/main/kotlin/app/futured/donutsample/ui/helloworld/HelloWorldActivity.kt b/sample/src/main/kotlin/app/futured/donutsample/ui/helloworld/HelloWorldActivity.kt new file mode 100644 index 0000000..041ca90 --- /dev/null +++ b/sample/src/main/kotlin/app/futured/donutsample/ui/helloworld/HelloWorldActivity.kt @@ -0,0 +1,30 @@ +package app.futured.donutsample.ui.helloworld + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.ComposeView +import app.futured.donut.cmp.HelloWorld + +class HelloWorldActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val composeView = ComposeView(this) + setContentView(composeView) + + composeView.setContent { + MaterialTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colors.background + ) { + HelloWorld() + } + } + } + } +} diff --git a/sample/src/main/kotlin/app/futured/donutsample/ui/main/MainActivity.kt b/sample/src/main/kotlin/app/futured/donutsample/ui/main/MainActivity.kt index a5cdb66..59fc0e9 100644 --- a/sample/src/main/kotlin/app/futured/donutsample/ui/main/MainActivity.kt +++ b/sample/src/main/kotlin/app/futured/donutsample/ui/main/MainActivity.kt @@ -7,6 +7,7 @@ import androidx.appcompat.app.AppCompatActivity import app.futured.donutsample.R import app.futured.donutsample.ui.playground.PlaygroundActivity import app.futured.donutsample.ui.playground.compose.PlaygroundComposeActivity +import app.futured.donutsample.ui.helloworld.HelloWorldActivity class MainActivity : AppCompatActivity() { @@ -21,5 +22,9 @@ class MainActivity : AppCompatActivity() { findViewById(R.id.compose_sample_button).setOnClickListener { startActivity(Intent(this, PlaygroundComposeActivity::class.java)) } + + findViewById(R.id.helloworld_sample_button).setOnClickListener { + startActivity(Intent(this, HelloWorldActivity::class.java)) + } } } diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml index 39769f1..6a4bfaf 100644 --- a/sample/src/main/res/layout/activity_main.xml +++ b/sample/src/main/res/layout/activity_main.xml @@ -28,10 +28,21 @@ android:id="@+id/compose_sample_button" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginBottom="16dp" android:text="@string/navigate_to_compose_view" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintBottom_toTopOf="@+id/helloworld_sample_button" app:layout_constraintTop_toBottomOf="@+id/android_view_sample_button" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> +