Upgrade to Pro — share decks privately, control downloads, hide ads and more …

It Just Works

cmota
November 12, 2021

It Just Works

These last years had been really exciting for app development. First, Android developers moved to Kotlin, then we had Kotlin Multiplatform where we managed to have our app’s business logic shared across iOS and desktop. Now with Compose we can go even further and reuse the UI. In this talk, I’m going to show you can create a library and the UI for your Android application and share it across a Desktop app and the web - it just works*.

cmota

November 12, 2021
Tweet

More Decks by cmota

Other Decks in Technology

Transcript

  1. Photo by FarawayPictures on deviantART @cafonsomota IT JUST WORKS* (*RUNNING

    COMPOSE ON ANDROID, DESKTOP, AND THE WEB ALONG WITH KMP)
  2. 👨💻 Android GDE 🗣 Founder @GDGCoimbra and co-founder @Kotlin_Knights ✍

    Author @rwenderlich 🗺 Loves travel, photography and running 🍻 Looking to taste a Moretti with all of you @cafonsomota
  3. 2008 2012 2018 2013 2011 2017 2006 2010 2009 2015

    * Kotlin Multiplatform * @cafonsomota Mobile
  4. 2012 2018 2014 2017 2006 2010 2009 2015 * *

    Kotlin Multiplatform Web @cafonsomota
  5. Great!! Are you going to do it natively? I’ve got

    a new app idea! droiders 🤖 📱 @cafonsomota
  6. Great!! Are you going to do it natively? I’ve got

    a new app idea! droiders 🤖 📱 Natively? You can use Xamarin and instead of android only, you get android and iOS @cafonsomota
  7. Great!! Are you going to do it natively? I’ve got

    a new app idea! droiders 🤖 📱 Natively? You can use Xamarin and instead of android only, you get android and iOS For that, you should use Flutter! It has better performance! @cafonsomota
  8. Great!! Are you going to do it natively? I’ve got

    a new app idea! droiders 🤖 📱 Natively? You can use Xamarin and instead of android only, you get android and iOS For that, you should use Flutter! It has better performance! But it uses Dart! You’re already familiar with Kotlin, you should try Kotlin Multiplatform! @cafonsomota
  9. Great!! Are you going to do it natively? I’ve got

    a new app idea! droiders 🤖 📱 Natively? You can use Xamarin and instead of android only, you get android and iOS For that, you should use Flutter! It has better performance! But it uses Dart! You’re already familiar with Kotlin, you should try Kotlin Multiplatform! I’ve been using react native and it’s great! @cafonsomota
  10. Great!! Are you going to do it natively? I’ve got

    a new app idea! droiders 🤖 📱 Natively? You can use Xamarin and instead of android only, you get android and iOS For that, you should use Flutter! It has better performance! But it uses Dart! You’re already familiar with Kotlin, you should try Kotlin Multiplatform! I’ve been using react native and it’s great! 12 people are typing @cafonsomota
  11. How to decide? Team background and size Which language is

    your team familiar with? Do they want to learn new things? Can you have developers focused on each platform you’re targeting? Project type Are you developing an MVP? 1yr long project? 5yr? Never ending project? Constraints Time: It hasn’t started yet, and you’re already behind schedule? Budget: Can you hire/allocate more people? @cafonsomota
  12. Framework dependent OS updates require the fw to be updated

    and launch a new app Missing features OS/device features depend on the fw support, you might need to write them Slower performance Commitment to one framework Common disadvantages @cafonsomota
  13. It’s Kotlin ❤ Take advantage of all the language features

    (that you’re already familiar with) Low risk Decide what’s worth to share across platforms Consistency across platforms One tech-stack Strong community support KOtlin Multiplatform @cafonsomota
  14. presentation presentation presentation presentation model model model model parser parser

    parser parser view network view network view network view network desktop web iOS android @cafonsomota
  15. business logic business logic business logic model parser network presentation

    model parser network presentation model parser network presentation model parser network presentation business logic view view view view desktop web iOS android @cafonsomota
  16. android iOS desktop model parser network presentation common view view

    view view java/kotlin objective-c/ swift (kotlin) JS supported in jvm web @cafonsomota
  17. src/commonMain/sample/Platform.kt expect object Platform { val name: String } You

    need to de fi ne actual per target actual for Android actual for Desktop actual for JS Kotlin Multiplatform: Platform Specific Code :shared @cafonsomota
  18. src/commonMain/sample/Platform.kt expect object Platform { val name: String } src/androidMain/sample/Platform.kt

    actual object Platform { actual val name: String = "android" } Kotlin Multiplatform: Platform Specific Code :shared @cafonsomota
  19. src/commonMain/sample/Platform.kt expect object Platform { val name: String } src/androidMain/sample/Platform.kt

    actual object Platform { actual val name: String = “android” } Kotlin Multiplatform: Platform Specific Code :shared @cafonsomota
  20. src/commonMain/sample/Platform.kt :shared expect object Platform { val name: String }

    src/androidMain/sample/Platform.kt src/desktopMain/sample/Platform.kt src/webMain/sample/Platform.kt actual object Platform { actual val name: String = “android” } actual object Platform { actual val name: String = “desktop” } actual object Platform { actual val name: String = “web” } Kotlin Multiplatform: Platform Specific Code @cafonsomota
  21. *.kt common expect JVM actual *.kt, *.java, *.jar Native *.kt,

    *C, Swift, Framework JS *.kt, *.js, NPM actual actual
  22. Jetpack Compose compose.animation compose.material compose.foundation compose.ui compose.runtime compose.compiler Compose Compiler

    Compose Runtime Compose UI Toolkit (Android) Compose Animation Compose UI Compose Foundation Compose Material
  23. Jetpack Compose compose.animation compose.material compose.foundation compose.ui compose.runtime compose.compiler Compose Compiler

    Compose Runtime Compose UI Toolkit (Android) Compose Animation Compose UI Compose Foundation Compose Material
  24. Compose Compiler Compose Runtime Compose UI Toolkit (Android) Compose Animation

    Compose UI Compose Foundation Compose Material Compose compiler android.googlesource.com/platform/frameworks/support/+/HEAD/compose/compiler Written in Kotlin Transforms @Composable into UI Doesn’t use the Annotation Processor The plugin works at system/code generation level Doesn’t impact build times Open source Available in the AOSP
  25. Compose Runtime Platform agnostic Doesn’t know what Android or UI

    are Tree management solution Compose Compiler Compose Runtime Compose UI Toolkit (Android) Compose Animation Compose UI Compose Foundation Compose Material
  26. Compose UI Toolkit Compose Compiler Compose Runtime Compose UI Toolkit

    (Android) Compose Animation Compose UI Compose Foundation Compose Material compose.ui Handles input management, Drawing, Layouts, etc. compose.foundation Contains Basic building: Column, Text, Image, etc. compose.material Material design system to use on components compose.animation Animations to use easily and out side of the box
  27. Compose UI Raspberry pi Compose Compiler Compose Runtime Compose UI

    Android Compose UI Desktop Compose UI Web Compose UI Console Compose
  28. Now that we’ve got the android app ready, How quickly

    can we have desktop and web apps? - all the dreamers out there
  29. SELECT YOUR LEVEL I just want to see the story.

    I want a lighter experience. Give me the full challenge! @cafonsomota
  30. SELECT YOUR LEVEL I just want to see the story.

    I want a lighter experience. Give me the full challenge! 👉 @cafonsomota
  31. SELECT YOUR LEVEL I just want to see the story.

    I want a lighter experience. Give me the full challenge! 👉 @cafonsomota
  32. SELECT YOUR LEVEL I just want to see the story.

    I want a lighter experience. Give me the full challenge! 👉 @cafonsomota
  33. KOtlin Multiplatform: Targets jvm JS android android 
 NDK iOS

    watchOS tvOS macOS Linux Windows Web Assembly @cafonsomota
  34. KOtlin Multiplatform: Libraries Kotlinx.x Coroutines, serialization, datetime, atomicfu, etc. Network

    Ktor: github.com/ktorio/ktor Storage Database: github.com/cashapp/sqldelight Others Curated list: github.com/AAkira/Kotlin-Multiplatform-Libraries @cafonsomota
  35. KOtlin Multiplatform: Creating a new KMM project 1. Install the

    KMM plugin on Android Studio 2. Create a new KMM Application ⚠ There’s a version that supports Mac M1 :shared :shared @cafonsomota
  36. androidApp/ desktopApp/ webApp/ shared/ KOtlin Multiplatform: Project Structure your common

    code (logic) androidMain/ commonMain/ desktopMain/ jsMain/ :shared @cafonsomota
  37. androidApp/ desktopApp/ webApp/ shared/ KOtlin Multiplatform: Project Structure your platform

    speci fi c code (engine, drivers, etc.) :shared androidMain/ commonMain/ desktopMain/ jsMain/ @cafonsomota
  38. shared/build.gradle.kts KOtlin Multiplatform: Refactoring to KMP kotlin { ... sourceSets

    { val commonMain by getting dependencies implementation(“io.ktor:ktor-client-core:1.6.4") val androidMain by getting dependencies implementation("io.ktor:ktor-client-android:1.6.4") val desktopMain by getting val jsMain by getting dependencies implementation("io.ktor:ktor-client-js:1.6.4") } } Network ✅ Migrating to Ktor :shared
  39. shared/build.gradle.kts ✅ Migrating to SQLDelight KOtlin Multiplatform: Refactoring to KMP

    kotlin { ... sourceSets { val commonMain by getting val androidMain by getting dependencies implementation("com.squareup.sqldelight:android-driver:1.5.1") val desktopMain by getting dependencies implementation("com.squareup.sqldelight:native-driver:1.5.1") val jsMain by getting dependencies implementation("com.squareup.sqldelight:sqljs-driver:1.5.1") } } :shared
  40. kotlin { ... sourceSets { val commonMain by getting dependencies

    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.0") val androidMain by getting val desktopMain by getting val jsMain by getting } } shared/build.gradle.kts ✅ Migrating to kotlinx.serialization KOtlin Multiplatform: Refactoring to KMP :shared
  41. kotlin { ... sourceSets { val commonMain by getting dependencies

    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.0") val androidMain by getting val desktopMain by getting val jsMain by getting } } shared/build.gradle.kts ✅ Migrating to kotlinx.serialization KOtlin Multiplatform: Refactoring to KMP shared module And of course, Also migratE the API Calls
  42. KOtlin Multiplatform You don’t need to commit your entire codebase

    to KMP The goal is to share your business logic Di ff erent apps share di ff erent parts of their code - what makes sense to them! Space *Adapted from: KotlinConf 2019: Opening Keynote by Andrey Breslav Full KMP Cash App Shares: business logic Yandex Maps Shares: business logic, wrappers for C++ libraries PlanGrid Planboard Workspace Shares: business logic, sync logic, mgmnt o ffl ine data Shares: business logic Shares: business logic, networking, o ffl ine caching lyrs
  43. KOtlin Multiplatform: ‘Suggestions’ Start small You’re going to develop a

    new feature? There’s part of your app that needs to be refactored? Want to improve your test coverage? why not do it on the shared module? @cafonsomota
  44. KOtlin Multiplatform: ‘Challenges’ 🟡 Not all libraries are available on

    KMP Those who are might not support all targets They might not be compatible with your Kotlin version @cafonsomota
  45. KOtlin Multiplatform: ‘Challenges’ 🟡 Not all libraries are available for

    KMP Those who are might not support all targets They might not be compatible with your Kotlin version 🟠 Project initial setup requires some time Invalidate cache and restart is needed sometimes to recover code auto-complete There are properties that you need to con fi gure: export (iOS), @JsName (JS), etc. Especially when targeting Web, you should enable the IR compiler @cafonsomota
  46. KOtlin Multiplatform: ‘Challenges’ 🟡 Not all libraries are available for

    KMP Those who are might not support all targets They might not be compatible with your Kotlin version 🟠 Project initial setup requires some time Invalidate cache and restart is needed sometimes to recover code auto-complete There are properties that you need to con fi gure: export (iOS), @JsName (JS), etc. Especially when targeting Web, you should enable the IR compiler 🔴 Generated library might need to lose some weight Especially when targeting Web, they can easily gain several MB’s @cafonsomota
  47. Compose: Getting Ready If you’re starting Compose for the fi

    rst time Start by developing fi rst on Android Once done, you can move it to a shared module If you’re already familiar with it Create a shared-ui module that will de fi ne the UI for the di ff erent platforms @cafonsomota
  48. Compose: Getting Ready 1. Create a new module called shared-ui

    2. You can just copy-paste shared Rename it to shared-ui Remove all of it’s .kt classes @cafonsomota
  49. Compose To share your Compose UI you’ll need to do

    a bit of KMP Di ff erent platforms, mean di ff erent behaviours For instance, you can resize a window on the desktop but not on your phone* You have di ff erent input types, etc. * well, you can have the app in split screen mode, yes 🙂 @cafonsomota
  50. Compose src/shared-ui/build.gradle.kts :shared-ui plugins { kotlin("multiplatform") id("org.jetbrains.compose") version “1.0.0-beta5" id("com.android.library")

    kotlin("plugin.serialization") } kotlin { ... sourceSets { val commonMain by getting { dependencies { api(compose.foundation) api(compose.runtime) api(compose.foundation) api(compose.material) api(compose.materialIconsExtended) api(compose.ui) api(compose.uiTooling) } } } }
  51. Compose You’re using two di ff erent versions of Compose

    androidx.compose org.jetbrains.compose @cafonsomota
  52. Compose Compose is interoperable with XML You don’t need to

    rewrite your entire app with Compose You’ll only be able to share the features that are using Compose @cafonsomota
  53. Compose: ‘Challenges’ 🟠 Initial setup is… challenging You’ll need to

    use JetBrains Compose plugin Adding the wrong con fi guration might increase building times in several minutes @cafonsomota
  54. Compose: ‘Challenges’ 🟠 Initial setup is… challenging You’ll need to

    use JetBrains Compose plugin Adding the wrong con fi guration might increase building times in several minutes 🟠 Libraries depend on the Compose version You’ll need to use a compatible one @cafonsomota
  55. Compose: ‘Challenges’ 🟠 Initial setup is… challenging You’ll need to

    use JetBrains Compose plugin Adding the wrong con fi guration might increase building times in several minutes 🟠 Libraries depend on the Compose version You’ll need to use a compatible one 🟠 Not all features are available for Desktop/ Web There’s no direct support for i18n, fonts and other resources LiveData, ViewModels, etc. are only available through third-party libraries @cafonsomota
  56. Compose: ‘Challenges’ 🔴 Web… is in it’s initial stages Not

    all Compose functions are ported to the Web The ones that are, often require that you use DOM elements to manipulate them Adding this abstraction manually will be really time consuming @cafonsomota
  57. Compose: ‘Challenges’ Accompanist A set of libraries that provide Compose

    ready features Examples: Insets, Paging, Permissions, Swipe to refresh, etc. 📐 Insets 🍫 System UI Controller 🎨 AppCompat Theme Adapter 🧭✨Navigation-Animation 📫 Permissions ⏳ Placeholder 🌊 Flow Layouts 🧭🎨 Navigation-Material 🖌 Drawable Painter ⬇ Swipe to Refresh 📖 Pager @cafonsomota
  58. Accompanist A set of libraries that provide Compose ready features

    Examples: Insets, Paging, Permissions, Swipe to refresh, etc. Compose: ‘Challenges’
  59. Accompanist A set of libraries that provide Compose ready features

    Examples: Insets, Paging, Permissions, Swipe to refresh, etc. Not all Compose libraries are supported on Desktop and Web… for now! Compose: ‘Challenges’
  60. Compose: ‘Challenges’ Accompanist A set of libraries that provide Compose

    ready features Examples: Insets, Paging, Permissions, Swipe to refresh, etc. Not all Compose libraries are supported on Desktop and Web… for now! Community to the rescue! 🚀
  61. Accompanist (by Syer10) A subset of accompanist libraries ported by

    Syer10 to Desktop Available at: syer10.github.io/accompanist Compose: ‘Challenges’
  62. Accompanist (by Syer10) A subset of accompanist libraries ported by

    Syer10 to Desktop Available at: syer10.github.io/accompanist ⚠ only works on Desktop Compose: ‘Challenges’
  63. Compose: ‘Challenges’ Accompanist (by Syer10) A subset of accompanist libraries

    ported by Syer10 to Desktop Available at: syer10.github.io/accompanist ⚠ only works on Desktop Why? 😭
  64. plugins { kotlin("multiplatform") id("org.jetbrains.compose") version "1.0.0-alpha3" } kotlin { api(compose.material)

    api(compose.ui) } Syer10 Accompanist Syer10/accompanist/build.gradle.kts Compose: ‘Challenges’
  65. plugins { kotlin("multiplatform") id("org.jetbrains.compose") version "1.0.0-alpha3" } kotlin { api(compose.material)

    api(compose.ui) } Syer10 Accompanist Syer10/accompanist/build.gradle.kts Compose: ‘Challenges’ ⚠ Not exported to Android
  66. plugins {1 kotlin("multiplatform") id("org.jetbrains.compose") version “1.0.0-alpha3" id("com.android.library") }2 kotlin {a

    api(compose.material) api(compose.ui) }b Syer10 Accompanist Syer10/accompanist/build.gradle.kts Compose: ‘Challenges’
  67. plugins {1 kotlin("multiplatform") id("org.jetbrains.compose") version “1.0.0-alpha3" id("com.android.library") }2 kotlin {a

    android {b publishLibraryVariants("release", “debug") }c jvm(“desktop") }d Syer10 Accompanist Syer10/accompanist/build.gradle.kts Compose: ‘Challenges’
  68. plugins {1 kotlin("multiplatform") id("org.jetbrains.compose") version “1.0.0-alpha3" id("com.android.library") }2 kotlin {a

    android {b publishLibraryVariants("release", “debug") }c jvm(“desktop”)e sourceSets {f val commonMain by getting {g dependencies {h api(compose.material) api(compose.ui) }i }j val androidMain by getting val desktopMain by getting }d Syer10 Accompanist Syer10/accompanist/build.gradle.kts Compose: ‘Challenges’
  69. plugins {1 kotlin("multiplatform") id("org.jetbrains.compose") version “1.0.0-alpha3" id("com.android.library") }2 kotlin {a

    android {b publishLibraryVariants("release", “debug") }c jvm(“desktop”)e sourceSets {f val commonMain by getting {g dependencies {h api(compose.material) api(compose.ui) }i }j val androidMain by getting val desktopMain by getting }d android { compileSdk = 31 sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") defaultConfig { minSdk = 21 targetSdk = 31 } compileOptions { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } } Syer10 Accompanist Syer10/accompanist/build.gradle.kts Compose: ‘Challenges’
  70. plugins { kotlin("multiplatform") id("org.jetbrains.compose") version “1.0.0-alpha3" id("com.android.library") } kotlin {

    android { publishLibraryVariants("release", “debug") } jvm(“desktop") sourceSets { val commonMain by getting { dependencies { api(compose.material) api(compose.ui) } } val androidMain by getting val desktopMain by getting } android { compileSdk = 31 sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") defaultConfig { minSdk = 21 targetSdk = 31 } compileOptions { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } } Syer10 Accompanist Syer10/accompanist/build.gradle.kts Compose: ‘Challenges’ ✅ Supports Android and Desktop
  71. Compose: Image Loading Coil-Compose Image loading library implemented in Kotlin

    (and using Coroutines) Not all Compose libraries are supported on Desktop and Web… for now?
  72. Not all Compose libraries are supported on Desktop and Web…

    for now? Compose: Image Loading Coil-Compose Image loading library implemented in Kotlin (and using Coroutines) Community to the rescue! 🚀
  73. Compose: Image Loading Kamel Asynchronous media loading library for Android

    and Desktop Uses Ktor in the background Get it from: github.com/alialbaali/Kamel @cafonsomota
  74. Compose: Image Loading val request = ImageRequest.Builder(LocalContext.current) .data(url) .crossfade(true) .build()

    val painter = rememberImagePainter(request) when (val resource = lazyPainterResource(url)) { is ImagePainter.Loading -> { AddImagePreviewLoading(modifier) } is ImagePainter.Success -> { Image( painter = painter, contentScale = ContentScale.Crop, contentDescription = "Image preview", modifier = modifier) } is ImagePainter.Failure -> { AddImagePreviewFailure(modifier) } Coil
  75. Compose: Image Loading when (val resource = lazyPainterResource(url)) { is

    Resource.Loading -> { AddImagePreviewLoading(modifier) } is Resource.Success -> { KamelImage( resource = resource, contentScale = ContentScale.Crop, contentDescription = "Image preview", modifier = modifier, crossfade = true ) } is Resource.Failure -> { AddImagePreviewFailure(modifier) } Kamel
  76. Compose: Resources Resources Imagine that you want to show an

    image Hello world! Image( painter = painterResource(id = R.drawable.ic_launcher), contentDescription = stringResource(id = R.string.app_name) )
  77. Compose: Resources Resources Imagine that you want to show an

    image Implementation shared-ui/commonMain contains the expect implementation shared-ui/androidMain contains the xml fi les (vectors) and actual implementation shared-ui/desktopMain contains the images (png’s) and actual implementation @cafonsomota
  78. @Composable public expect fun icAbout(): Painter @Composable public expect fun

    icHome(): Painter Resources: Images shared-ui/commonMain/Icons.kt Compose: Resources
  79. @Composable public actual fun icAbout() = painterResource(R.drawable.ic_about) @Composable public actual

    fun icHome() = painterResource(R.drawable.ic_home) Resources: Images shared-ui/androidApp/Icons.kt Compose: Resources
  80. @Composable public actual fun icAbout() = painterResource("images/ic_about.png") @Composable public actual

    fun icHome() = painterResource("images/ic_home.png") Resources: Images shared-ui/desktopApp/Icons.kt Compose: Resources
  81. Compose: Resources Resources Imagine that you want to set a

    custom font Hello world! Hello world!
  82. Compose: Resources Resources Imagine that you want to set a

    custom font Hello world! Text( text = “Hello World!”, style = typography.h4 ) Hello world!
  83. Compose: Resources Resources Imagine that you want to set a

    custom font Hello world! private val HollywoodHillsFontFamily = FontFamily( Font(R.font.sf_hollywood_hills) ) h4 = TextStyle( color = colorAccent, fontFamily = HollywoodHillsFontFamily, fontSize = fontSizeLarge ) Hello world!
  84. Compose: Resources Resources Imagine that you want to set a

    custom font Implementation shared-ui/commonMain/resources contains the font fi les shared-ui/commonMain/platform de fi nes the expect function shared-ui/androidMain/platform de fi nes the actual function shared-ui/desktopMain/platform de fi nes the actual function @cafonsomota
  85. @Composable expect fun Font( name: String, res: String ): Font

    Resources: Font shared-ui/commonMain/platform/PlatformFont.kt Compose: Resources
  86. @Composable actual fun Font( name: String, res: String, ): Font

    { val context = LocalContext.current val id = context.resources.getIdentifier(res, "font", context.packageName) return Font(id) } Resources: Font shared-ui/androidMain/platform/PlatformFont.kt Compose: Resources
  87. @Composable actual fun Font( name: String, res: String ): Font

    { androidx.compose.ui.text.platform.Font("font/$res.ttf") } shared-ui/desktopMain/platform/PlatformFont.kt Resources: Font Compose: Resources
  88. Compose: Lifecycle, ViewModel, LiveData and Navigation PreCompose (by Tlaster) Lifecycle,

    ViewModel, LiveData and Navigation are Android components They now have been ported to Compose desktop Get it from: github.com/Tlaster/PreCompose
  89. Compose: WebApp What about Web? Is in an initial stage

    and the paradigm is di ff erent It’s focused on DOM rendering Only a set of components are available: Box, Button, Column, Row, Slider, Text @cafonsomota
  90. Compose: WebApp @Composable fun Column(content: @Composable () -> Unit) {

    Div({ style { display(DisplayStyle.Flex) alignItems(AlignItems.Center) flexGrow(1.0) } }) { content() } } :webApp webApp/components/Column.kt
  91. Compose: WebApp Sharing logic Works without any issue The generated

    JS fi le might be up to several MBs Sharing UI It’s going to be time consuming to share everything You’re going to need to create several components Most of the libraries don’t support it (yet) Nevertheless… it’s fun to see how long we’ll be able to go in a couple of months! @cafonsomota
  92. Status 🟢 Kotlin Multiplatform Mobile is stable-ish Can be used

    in production although part of it is in alpha/beta 🟢 Compose (Android) is stable Go on! It’s time to dive-in into declarative UI 🟡 Compose (Desktop) is alpha-ish 🔴 Compose (Web) is alpha 🟠 Documentation and samples 🟢 Community and support @cafonsomota
  93. Jetpack Compose samples (Google) github.com/android/compose-samples Jetpack Compose samples (JetBrains) https://github.com/JetBrains/compose-jb/

    Jetpack/JetBrains Compose Playground foso.github.io/Jetpack-Compose-Playground/ Jetpack Compose Internals Kotlin Slack Resources @cafonsomota
  94. SELECT YOUR LEVEL I just want to see the story.

    I want a lighter experience. Give me the full challenge! 👉 @cafonsomota
  95. SELECT YOUR LEVEL I just want to see the story.

    I want a lighter experience. Give me the full challenge! 👉 @cafonsomota
  96. Kotlin Multiplatform and Compose template Alicerce In Portuguese alicerce means

    foundation A Kotlin Multiplatform with Compose template Currently supports: Android and Desktop Contains an initial project con fi guration along with the libraries mentioned here Open to feedback and contributions! Alicerce
  97. Photo by FarawayPictures on deviantART @cafonsomota IT JUST WORKS* (*RUNNING

    COMPOSE ON ANDROID, DESKTOP, AND THE WEB ALONG WITH KMP)