Blog para desarrollo de aplicaciones en Android, aprende paso a paso como crear aplicaciones.

Usamos cookies propias y de terceros que entre otras cosas recogen datos sobre sus hábitos de navegación para mostrarle publicidad personalizada y realizar análisis de uso de nuestro sitio.
Si continúa navegando consideramos que acepta su uso. OK Más información | Y más

Jetpack Compose Desktop y Multiplatform

Jetpack Compose Desktop y Multiplatform

Hola amigos 👋 Bienvenidos a un nuevo tutorial de Universo Android. Hoy aprenderemos a crear aplicaciones multiplataforma con Jetpack Compose, permitiéndote escribir código una sola vez y ejecutarlo en Android, Desktop (Windows, macOS, Linux) y Web.

Al finalizar este tutorial tendrás:

  • Proyecto Compose Multiplatform
  • Aplicación para Android y Desktop
  • Código compartido entre plataformas
  • UI adaptable
  • Recursos específicos por plataforma
  • Configuración completa

🟩 1. ¿Qué es Compose Multiplatform?

Compose Multiplatform es la evolución de Jetpack Compose que permite crear aplicaciones para múltiples plataformas (Android, iOS, Desktop, Web) compartiendo código UI y lógica de negocio.

🟩 2. Plataformas Soportadas

  • Android: Apps móviles nativas
  • Desktop: Windows, macOS, Linux
  • iOS: Aplicaciones iOS (experimental)
  • Web: Aplicaciones web (experimental)

🟩 3. Crear Proyecto Multiplatform

Opción 1: Usar Wizard de IntelliJ IDEA

  1. Descarga IntelliJ IDEA Community (gratis)
  2. New Project → Kotlin Multiplatform
  3. Selecciona: Android + Desktop
  4. Finaliza creación

Opción 2: Crear Manualmente

🟩 4. Estructura del Proyecto

MyComposeApp/
├── androidApp/           # Código Android
│   └── src/
├── desktopApp/          # Código Desktop
│   └── src/
├── common/              # Código compartido
│   └── src/
│       ├── commonMain/  # Código común
│       ├── androidMain/ # Específico Android
│       └── desktopMain/ # Específico Desktop
└── build.gradle.kts

🟩 5. Configuración Gradle (Proyecto)

📄 build.gradle.kts (root)

plugins {
    kotlin("multiplatform") version "1.9.20" apply false
    id("com.android.application") version "8.1.4" apply false
    id("org.jetbrains.compose") version "1.5.10" apply false
}

🟩 6. Configuración Gradle (Common)

📄 common/build.gradle.kts

plugins {
    kotlin("multiplatform")
    id("org.jetbrains.compose")
    id("com.android.library")
}

kotlin {
    android()
    jvm("desktop")

    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation(compose.runtime)
                implementation(compose.foundation)
                implementation(compose.material3)
                implementation(compose.ui)
            }
        }

        val androidMain by getting {
            dependencies {
                implementation("androidx.activity:activity-compose:1.8.2")
            }
        }

        val desktopMain by getting {
            dependencies {
                implementation(compose.desktop.currentOs)
            }
        }
    }
}

android {
    namespace = "com.example.myapp.common"
    compileSdk = 34

    defaultConfig {
        minSdk = 24
    }
}

🟩 7. Código Compartido

📄 common/src/commonMain/App.kt

package com.example.myapp

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

@Composable
fun App() {
    var showContent by remember { mutableStateOf(false) }
    var counter by remember { mutableStateOf(0) }

    MaterialTheme {
        Surface(
            modifier = Modifier.fillMaxSize(),
            color = MaterialTheme.colorScheme.background
        ) {
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .verticalScroll(rememberScrollState())
                    .padding(16.dp),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.spacedBy(16.dp)
            ) {
                // Título
                Text(
                    text = "Compose Multiplatform",
                    style = MaterialTheme.typography.headlineLarge,
                    color = Color(0xFF2196F3)
                )

                Text(
                    text = "Una app, múltiples plataformas",
                    style = MaterialTheme.typography.bodyLarge,
                    color = Color.Gray
                )

                Spacer(modifier = Modifier.height(16.dp))

                // Información de plataforma
                PlatformInfoCard()

                // Contador
                CounterCard(counter) { counter++ }

                // Botón toggle
                Button(
                    onClick = { showContent = !showContent },
                    modifier = Modifier.fillMaxWidth()
                ) {
                    Text(if (showContent) "Ocultar" else "Mostrar Contenido")
                }

                // Contenido animado
                AnimatedVisibility(showContent) {
                    Card(
                        modifier = Modifier.fillMaxWidth(),
                        colors = CardDefaults.cardColors(
                            containerColor = Color(0xFF4CAF50)
                        ),
                        shape = RoundedCornerShape(16.dp)
                    ) {
                        Column(
                            modifier = Modifier.padding(24.dp)
                        ) {
                            Text(
                                "✓ Código compartido",
                                color = Color.White
                            )
                            Text(
                                "✓ UI declarativa",
                                color = Color.White
                            )
                            Text(
                                "✓ Nativo en todas las plataformas",
                                color = Color.White
                            )
                        }
                    }
                }

                // Lista de características
                FeaturesSection()
            }
        }
    }
}

@Composable
fun PlatformInfoCard() {
    Card(
        modifier = Modifier.fillMaxWidth(),
        colors = CardDefaults.cardColors(
            containerColor = Color(0xFF2196F3)
        ),
        shape = RoundedCornerShape(16.dp)
    ) {
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .padding(24.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(
                "Ejecutando en:",
                style = MaterialTheme.typography.titleMedium,
                color = Color.White
            )
            Text(
                getPlatformName(),
                style = MaterialTheme.typography.headlineMedium,
                color = Color.White
            )
        }
    }
}

@Composable
fun CounterCard(counter: Int, onIncrement: () -> Unit) {
    Card(
        modifier = Modifier.fillMaxWidth(),
        shape = RoundedCornerShape(16.dp)
    ) {
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .padding(24.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(
                "Contador",
                style = MaterialTheme.typography.titleLarge
            )
            Text(
                "$counter",
                style = MaterialTheme.typography.displayLarge,
                color = Color(0xFFFF5722)
            )
            Spacer(modifier = Modifier.height(16.dp))
            Button(onClick = onIncrement) {
                Text("Incrementar")
            }
        }
    }
}

@Composable
fun FeaturesSection() {
    Card(
        modifier = Modifier.fillMaxWidth(),
        shape = RoundedCornerShape(16.dp)
    ) {
        Column(
            modifier = Modifier.padding(24.dp),
            verticalArrangement = Arrangement.spacedBy(12.dp)
        ) {
            Text(
                "Características",
                style = MaterialTheme.typography.titleLarge,
                color = Color(0xFF2196F3)
            )
            
            FeatureItem("📱", "Android nativo")
            FeatureItem("💻", "Windows, macOS, Linux")
            FeatureItem("🎨", "UI moderna con Material 3")
            FeatureItem("⚡", "Rendimiento nativo")
            FeatureItem("🔄", "Hot Reload")
        }
    }
}

@Composable
fun FeatureItem(icon: String, text: String) {
    Row(
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(12.dp)
    ) {
        Text(icon, style = MaterialTheme.typography.headlineSmall)
        Text(text, style = MaterialTheme.typography.bodyLarge)
    }
}

// Función expect (declaración)
expect fun getPlatformName(): String

🟩 8. Implementación Android

📄 common/src/androidMain/Platform.kt

package com.example.myapp

actual fun getPlatformName(): String {
    return "Android ${android.os.Build.VERSION.SDK_INT}"
}

🟩 9. Implementación Desktop

📄 common/src/desktopMain/Platform.kt

package com.example.myapp

actual fun getPlatformName(): String {
    val os = System.getProperty("os.name")
    val arch = System.getProperty("os.arch")
    return "$os ($arch)"
}

🟩 10. App Android

📄 androidApp/build.gradle.kts

plugins {
    id("com.android.application")
    kotlin("android")
    id("org.jetbrains.compose")
}

android {
    namespace = "com.example.myapp.android"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.example.myapp.android"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"
    }
}

dependencies {
    implementation(project(":common"))
    implementation("androidx.activity:activity-compose:1.8.2")
}

📄 androidApp/src/main/MainActivity.kt

package com.example.myapp.android

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import com.example.myapp.App

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            App()
        }
    }
}

🟩 11. App Desktop

📄 desktopApp/build.gradle.kts

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

kotlin {
    jvm {
        withJava()
    }
    
    sourceSets {
        val jvmMain by getting {
            dependencies {
                implementation(project(":common"))
                implementation(compose.desktop.currentOs)
            }
        }
    }
}

compose.desktop {
    application {
        mainClass = "MainKt"
        
        nativeDistributions {
            targetFormats(
                org.jetbrains.compose.desktop.application.dsl.TargetFormat.Dmg,
                org.jetbrains.compose.desktop.application.dsl.TargetFormat.Msi,
                org.jetbrains.compose.desktop.application.dsl.TargetFormat.Deb
            )
            packageName = "MyComposeApp"
            packageVersion = "1.0.0"
        }
    }
}

📄 desktopApp/src/jvmMain/Main.kt

import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState
import androidx.compose.ui.unit.dp
import com.example.myapp.App

fun main() = application {
    Window(
        onCloseRequest = ::exitApplication,
        title = "Compose Multiplatform App",
        state = rememberWindowState(width = 800.dp, height = 600.dp)
    ) {
        App()
    }
}

🟩 12. Settings Gradle

📄 settings.gradle.kts

pluginManagement {
    repositories {
        google()
        gradlePluginPortal()
        mavenCentral()
    }
}

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.name = "MyComposeApp"
include(":androidApp")
include(":desktopApp")
include(":common")

🟩 13. Ejecutar las Aplicaciones

Android

./gradlew :androidApp:installDebug

Desktop

./gradlew :desktopApp:run

Crear Ejecutable Desktop

./gradlew :desktopApp:createDistributable

🟩 14. Recursos por Plataforma

// En commonMain
expect fun loadImage(): ImageBitmap

// En androidMain
actual fun loadImage(): ImageBitmap {
    // Cargar desde recursos Android
}

// En desktopMain
actual fun loadImage(): ImageBitmap {
    // Cargar desde recursos Desktop
}

🟩 15. Ventajas y Desventajas

Ventajas

✓ Código compartido (50-90%)
✓ UI consistente
✓ Desarrollo más rápido
✓ Mantenimiento simplificado
✓ Nativo en todas las plataformas

Desventajas

✗ Curva de aprendizaje
✗ iOS aún experimental
✗ Menos recursos que Flutter
✗ Requiere conocimiento de Kotlin

🟩 16. Comparación con Flutter

CaracterísticaCompose MPFlutter
LenguajeKotlinDart
MadurezCreciendoMaduro
RendimientoNativoCerca de nativo
AndroidExcelenteMuy bueno
iOSExperimentalExcelente
DesktopEstableEstable
ComunidadMediaGrande

▶️ Cómo Ejecutar

  1. Descarga IntelliJ IDEA
  2. Crea proyecto Kotlin Multiplatform
  3. Agrega dependencias Compose
  4. Copia el código
  5. Ejecuta para Android o Desktop

🧪 Resultado Final

Aplicación funcionando en Android y Desktop con código compartido al 80-90%.

📥 Descargar Proyecto

👉 

🙌 Gracias por Visitar mi Blog

✔️ Compártelo
✔️ Déjame un comentario
✔️ Sígueme para más contenido

❓ Preguntas Frecuentes

1. ¿Qué es Compose Multiplatform?
Framework que permite crear apps para múltiples plataformas compartiendo código UI y lógica.

2. ¿Es mejor que Flutter?
Depende. Compose es mejor para devs Android/Kotlin. Flutter tiene más madurez y comunidad.

3. ¿Funciona en iOS?
Sí, pero aún es experimental. Se recomienda para producción solo Android y Desktop.

4. ¿Cuánto código puedo compartir?
Entre 70-90% del código puede ser compartido entre plataformas.

No hay comentarios:

Publicar un comentario