Slide 1

Slide 1 text

Compose and KMP Elevating Cross-Platform Excellence: Compose Unleashed with Kotlin Multiplatform Adit Lal

Slide 2

Slide 2 text

Android GDE - @aditlal
 🛠 Architect | Entrepreneur 🔊 Speaker
 🌎 Globe Trotter
 🍻 Beer enthusiast Adit GDE

Slide 3

Slide 3 text

Coroutines Null-Safety Extension-Functions Smart-Casts Delegated-Properties Versatility on the Server Side streamlined approach Open Source Modern Language Features Uni fi ed Codebase

Slide 4

Slide 4 text

Kotlin multiplatform View - Compose

Slide 5

Slide 5 text

Kotlin multiplatform

Slide 6

Slide 6 text

Kotlin multiplatform

Slide 7

Slide 7 text

Kotlin multiplatform

Slide 8

Slide 8 text

Common
 Kotlin Kotlin/JS JS code Kotlin/JVM JVM code Kotlin/Native Native code Kotlin multiplatform

Slide 9

Slide 9 text

Kotlin multiplatform Source - https://shorturl.at/xyAPX

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

Kotlin multiplatform View - Compose

Slide 12

Slide 12 text

Kotlin multiplatform View - Compose Compose

Slide 13

Slide 13 text

Android Kotlin / JVM iOS Swift / LLVM Web JS Desktop Kotlin / JVM

Slide 14

Slide 14 text

Different devices Different platform Different challenges

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

Platform Sh a red

Slide 17

Slide 17 text

Components ( Compose) Idea of UI

Slide 18

Slide 18 text

UI Components ( Compose) UI Components ( Swift UI) Approach #1 Platform Individual UI Components

Slide 19

Slide 19 text

UI Components ( Compose) UI Components ( Swift UI) Approach #2 Platform - Some shared UI Components Shared UI( Compose)

Slide 20

Slide 20 text

Approach #3 Platform - all shared UI Components Shared UI( Compose) UI Components ( Compose) UI Components ( Swift UI)

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

- [Ktor](https://ktor.io/) - [Koin](https://insert-koin.io/) - [Kotlinx Serialization](https://kotlinlang.org/docs/serialization.html) - [Kotlinx Coroutines](https://kotlinlang.org/docs/coroutines-overview.html) - [Compose ImageLoader](https://github.com/qdsfdhvh/compose-imageloader) - [KMM-ViewModel](https://github.com/rickclephas/KMM-ViewModel) - [Multiplatform Settings](https://github.com/russhwolf/multiplatform-settings)

Slide 25

Slide 25 text

ZStack { LazyVG r id( . . . ) { Fo r Each(id: .id) { item in Image(item.u r l) . r ende r ingMode(.o r iginal) . r esizable() .scaledToFill() } }.task { await r eposito r y.getImages() } }

Slide 26

Slide 26 text

class MainActivity : AppCompatActivity() { ove r r ide fun onC r eate(savedInstanceState: Bundle?) { supe r .onC r eate(savedInstanceState) setContent { MainView() } } }

Slide 27

Slide 27 text

impo r t SwiftUI @main st r uct iOSApp: App { va r body: some Scene { WindowG r oup { ContentView() } } } st r uct ContentView: View { va r body: some View { Text("Hello, wo r ld!") .padding() } }

Slide 28

Slide 28 text

st r uct ComposeView: UIViewCont r olle r Rep r esentable { func makeUIViewCont r olle r (context: Context) - > UIViewCont r olle r { r etu r n Main_iosKt.MainViewCont r olle r () } func updateUIViewCont r olle r (_ uiViewCont r olle r : UIViewCont r olle r , context: Context) {} } st r uct ContentView: View { va r body: some View { ComposeView() .igno r esSafeA r ea(.all) } }

Slide 29

Slide 29 text

st r uct ComposeView: UIViewCont r olle r Rep r esentable { func makeUIViewCont r olle r (context: Context) - > UIViewCont r olle r { r etu r n Main_iosKt.MainViewCont r olle r () } func updateUIViewCont r olle r (_ uiViewCont r olle r : UIViewCont r olle r , context: Context) {} } st r uct ContentView: View { va r body: some View { ComposeView() .igno r esSafeA r ea(.all) } }

Slide 30

Slide 30 text

NavigationView { ZStack { ComposeView() } }.toolba r { / / .... } Shared UI component

Slide 31

Slide 31 text

p r ivate val cache: MutableMap = mutableMapOf() @OptIn(Expe r imentalResou r ceApi : : class) @Composable actual fun font(name: St r ing, r es: St r ing, weight: FontWeight, style: FontStyle) : Font { r etu r n cache.getO r Put( r es) { val byteA r r ay = r unBlocking { r esou r ce("font/$ r es.ttf"). r eadBytes() } and r oidx.compose.ui.text.platfo r m.Font( r es, byteA r r ay, weight, style) } }

Slide 32

Slide 32 text

p r ivate val cache: MutableMap = mutableMapOf() @OptIn(Expe r imentalResou r ceApi : : class) @Composable actual fun font(name: St r ing, r es: St r ing, weight: FontWeight, style: FontStyle) : Font { r etu r n cache.getO r Put( r es) { val byteA r r ay = r unBlocking { r esou r ce("font/$ r es.ttf"). r eadBytes() } and r oidx.compose.ui.text.platfo r m.Font( r es, byteA r r ay, weight, style) } }

Slide 33

Slide 33 text

src androidApp iOSApp shared Structure

Slide 34

Slide 34 text

shared Structure src commonMain androidMain iOSMain build.gradle.kts

Slide 35

Slide 35 text

plugins { kotlin("multiplatform") } val commonMain by getting { dependencies { implementation(compose.ui) implementation(compose.foundation) implementation(compose.material) implementation(compose.runtime) } } shared

Slide 36

Slide 36 text

Kotlin code 👇 Kotlin/Native compiler 👇 Native code for iOS 👇 UIKit framework 👇 Compose UI

Slide 37

Slide 37 text

General Tips to ensure smooth UI

Slide 38

Slide 38 text

@Composable fun CustomTheme( windowSize: WindowSize, da r kTheme: Boolean = isSystemInDa r kTheme(), content: @Composable () - > Unit, ) { CompositionLocalP r ovide r ( LocalCustomColo r s p r ovides if (da r kTheme) da r kColo r s else lightColo r s, LocalWindowSize p r ovides windowSize ) { content() } }

Slide 39

Slide 39 text

@Composable fun CustomTheme( windowSize: WindowSize, da r kTheme: Boolean = isSystemInDa r kTheme(), content: @Composable () - > Unit, ) { CompositionLocalP r ovide r ( LocalCustomColo r s p r ovides if (da r kTheme) da r kColo r s else lightColo r s, LocalWindowSize p r ovides windowSize ) { content() } }

Slide 40

Slide 40 text

@Composable fun AppContent() { if (LocalWindowSize.cu r r ent = = WindowSize.COMPACT) { Column { / * content * / } } else { Row { / * content * / } } }

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

molecule

Slide 44

Slide 44 text

sealed class UiState { object Loading: UiState() data class Success(val images: List) : UiState() data class E r r o r (val e r r o r Message: St r ing) : UiState() } Moko -MVVM

Slide 45

Slide 45 text

sealed class UiState { object Loading: UiState() data class Success(val images: List) : UiState() data class E r r o r (val e r r o r Message: St r ing) : UiState() } Architecture

Slide 46

Slide 46 text

abst r act class MoleculeViewModel : ViewModel() { } Architecture

Slide 47

Slide 47 text

@Composable ove r r ide fun models(events: F l ow) : UiState { val uiState = r emembe r { mutableStateOf(UIState.Loading) } LaunchedEffect(Unit) { val imagesList = imagesReposito r y.getImages() uiState.value = UIState.Success(imagesList) } r etu r n uiState } Architecture

Slide 48

Slide 48 text

UI - Image loading and cache

Slide 49

Slide 49 text

Animation & Interactivity - Exploring the Potential (with Caveats) Explore the expressive power of Compose a nim a tion APIs, but be mindful of pl a tform-speci fi c consider a tions for complex a nim a tions a nd custom libr a ries. • a nim a te*AsSt a te •upd a teTr a nsition •Anim a t a ble with a nim a teTo or sn a pTo

Slide 50

Slide 50 text

Animation & Interactivity - Exploring the Potential (with Caveats) Explore the expressive power of Compose a @Composable fun AnimatedButton(text: String, onClick: () -> Unit) { val isClicked = remember { false } Button( modi fi er = Modi fi er . fi llMaxWidth() .animateAsState( targetValue = if (isClicked) Color.Green else Color.Blue, animationSpec = tween(durationMillis = 200) ), onClick = { isClicked = !isClicked onClick() } ) { Text(text) } }

Slide 51

Slide 51 text

Animation & Interactivity - Exploring the Potential (with Caveats) Explore the expressive power of Compose a @Composable fun AnimatedButton(text: String, onClick: () -> Unit) { val isClicked = remember { false } Button( modi fi er = Modi fi er . fi llMaxWidth() .animateAsState( targetValue = if (isClicked) Color.Green else Color.Blue, animationSpec = tween(durationMillis = 200) ), onClick = { isClicked = !isClicked onClick() } ) { Text(text) } }

Slide 52

Slide 52 text

Explore the expressive power of Compose a Unle a sh the power of d a t a with Ktor's pl a tform- a gnostic networking a nd Room Multipl a tform's sh a red persistence a cross pl a tforms This dyn a mic duo simpli fi es d a t a a ccess a nd empowers you to build d a t a -driven experiences th a t work se a mlessly everywhere. Data Access & Networking - Ktor & Room Multiplatform Powerhouse

Slide 53

Slide 53 text

Data Access & Networking - Ktor & Room Multiplatform Powerhouse Explore the expressive power of Compose a Unle a This dyn a / / Kto r netwo r k call suspend fun getImages() : List { val r esponse = kto r Client.get("https: / / api.example.com/models") val use r = r esponse.body() r etu r n use r } / / Room Multiplatfo r m data access fun saveImages(model: ImageModels) { r unBlocking { database.imageDao().inse r t(model) } }

Slide 54

Slide 54 text

Explore the expressive power of Compose a / / Kto r netwo r k call suspend fun getImages() : List { val r esponse = kto r Client.get("https: / / api.example.com/models") val use r = r esponse.body() r etu r n use r } / / Room Multiplatfo r m data access fun saveImages(model: ImageModels) { r unBlocking { database.imageDao().inse r t(model) } } Data Access & Networking - Ktor & Room Multiplatform Powerhouse

Slide 55

Slide 55 text

Advanced UI Building Blocks - Pixel-Perfect Canvas in Action Explore the expressive power of Compose a Be subjective to a d a ptive l a youts - remember its di ff erent pl a tform a t pl a y, things c a n bre a k. Witness a nd experiment with the power of modi fi ers, pl a cement, a nd dr a wing a ttention to cr a ft unique a nd a d a pt a ble UI experiences.

Slide 56

Slide 56 text

Explore the expressive power of Compose a Unle a This dyn a Box(modif i e r = Modif i e r .f i llMaxSize() .offset { ca r dPositions.value[ca r ds.f i r st().id] ?: Offset.Ze r o } / / Implement d r agging gestu r e fo r ca r d r e - o r de r ing .d r aggable(enabled = t r ue) { d r agDelta, _ - > ca r dPositions.value = ca r dPositions.value.toMutableMap().apply { / / Update ca r d positions based on d r ag delta val newPositions = ca r ds.mapIndexed { index, ca r d - > ca r d.id to Offset(d r agDelta.x * index, d r agDelta.y * index) }.toMap() putAll(newPositions) } } ) { Image(ca r d.image, modif i e r = Modif i e r .f i llMaxSize()) } Advanced UI Building Blocks - Pixel-Perfect Canvas in Action

Slide 57

Slide 57 text

Explore the expressive power of Compose a Unle a This dyn a Box(modif i e r = Modif i e r .f i llMaxSize() .offset { ca r dPositions.value[ca r ds.f i r st().id] ?: Offset.Ze r o } / / Implement d r agging gestu r e fo r ca r d r e - o r de r ing .d r aggable(enabled = t r ue) { d r agDelta, _ - > ca r dPositions.value = ca r dPositions.value.toMutableMap().apply { / / Update ca r d positions based on d r ag delta val newPositions = ca r ds.mapIndexed { index, ca r d - > ca r d.id to Offset(d r agDelta.x * index, d r agDelta.y * index) }.toMap() putAll(newPositions) } } ) { Image(ca r d.image, modif i e r = Modif i e r .f i llMaxSize()) } Advanced UI Building Blocks - Pixel-Perfect Canvas in Action

Slide 58

Slide 58 text

Advanced UI Building Blocks - Pixel-Perfect Canvas in Action Explore the expressive power of Compose a •Complexity management: Break down complex layouts into smaller composables for maintainability and testing purposes. •Performance optimization: Pay attention to drawing calls and animations to avoid performance bottlenecks. •Accessibility: Ensure your custom layout is accessible by considering keyboard navigation and screen reader compatibility.

Slide 59

Slide 59 text

Small actions - how to cross the platform lines Explore the expressive power of Compose a inte r face Sto r e { fun send(action: Action) val events: Sha r edF l ow }

Slide 60

Slide 60 text

Small actions - how to cross the platform lines Explore the expressive power of Compose a fun Co r outineScope.c r eateSto r e() : Sto r e { val events = MutableSha r edF l ow() r etu r n object : Sto r e { ove r r ide fun send(action: Action) { launch { events.emit(action) } } ove r r ide val events: Sha r edF l ow = events.asSha r edF l ow() } }

Slide 61

Slide 61 text

Small actions - shared across platforms Explore the expressive power of Compose a @Composable actual fun BackHandle r (isEnabled: Boolean, onBack: () - > Unit) { LaunchedEffect(isEnabled) { sto r e.events.collect { if(isEnabled) { onBack() } } } }

Slide 62

Slide 62 text

Small actions - shared across platforms Explore the expressive power of Compose a @Composable expect fun BackHandle r (isEnabled: Boolean, onBack: () - > Unit) / / And r oid implementation @Composable actual fun BackHandle r (isEnabled: Boolean, onBack: () - > Unit) { BackHandle r (isEnabled, onBack) }

Slide 63

Slide 63 text

Gotchas - iOS Explore the expressive power of Compose a

Slide 64

Slide 64 text

Gotchas - good on Android Explore the expressive power of Compose a .focusP r ope r ties { canFocus = false }

Slide 65

Slide 65 text

• Start Small, Scale Smart: ‣ Don't dive into rewriting your entire app at once. ‣ Begin with a small feature, like a login screen, and gradually expand your Compose footprint. This minimizes risk and allows you to iron out any kinks before committing fully. • Leverage Common Code: ‣ Compose Multiplatform's core strength lies in code sharing across platforms. ‣ Focus on building reusable components and logic in commonMain to maximize ef fi ciency and maintainability. Tips

Slide 66

Slide 66 text

• Embrace the Latest: ‣ JetBrains actively improves Compose Multiplatform. Stay updated with the latest libraries and tools (e.g., Compose 1.6.x) to bene fi t from bug fi xes, performance enhancements, and new features. • Test Rigorously: ‣ Testing is crucial for any app, even more so for multiplatform ones. Utilize comprehensive testing strategies like unit, integration, and UI testing to ensure your Compose code functions fl awlessly across platforms. Tips

Slide 67

Slide 67 text

• Community is Key: ‣ The Compose community thrives on collaboration. Join forums, Slack channels, and social media groups to learn best practices, troubleshoot issues, and stay informed about the latest developments. Tips

Slide 68

Slide 68 text

Kotlin multiplatform - 🛠 Tooling
 📦 Storage
 🏗 Architecture 🔑 Crypto
 🗃 Serializer - code and tools - 🍎 Compose UI 🧮 Arithmetic - 📋 Log
 📱 Device
 🔍 Analytics 📁 File
 ⏰ Date-Time - 🎨 Graphics 🛢 Resources - 🌎 Network
 💉 Dependency Injection 🩺 Test
 🚀 Language extensions ➿ Asynchronous
 🧩 Service SDK
 🔧 Utils

Slide 69

Slide 69 text

Kotlin Slack Kotlin Of fi cial Kotlin Training https://kotlinlang.org/docs/multiplatform-publish-lib.html Kotlin by: - https://jakewharton.com/presentations/ - http://antonioleiva.com/kotlin Resources

Slide 70

Slide 70 text

Twine https://github.com/ms a sik a nth/twine

Slide 71

Slide 71 text

Kotlin multiplatform https://github.com/joreilly/PeopleInSpace https://github.com/joreilly/ClimateTraceKMP https://github.com/halcyonmobile/MultiplatformPlayground https://github.com/msasikanth/twine (Compose Shared UI) https://slackhq.github.io/circuit/ https://github.com/chrisbanes/haze https://github.com/dhis2/dhis2-mobile-ui Kotlin Multiplatform project with SwiftUI, Jetpack Compose, Wear Compose, Compose for Desktop, Compose for Web and Kotlin/JS + React clients along with Ktor backend. Resources

Slide 72

Slide 72 text

https://chrisb a nes.me/posts/swiftui-for-jetp a ck-compose-devs-st a te/ https://bumble-tech.github.io/ a ppyx/ https://tl a ster.github.io/PreCompose/ https://github.com/terr a kok/kmp- a wesome https://github.com/AAkir a /Kotlin-Multipl a tform-Libr a ries https://github.com/exyte/ComposeMultipl a tformDribbbleAudio Resources

Slide 73

Slide 73 text

Kotlin Multiplatform: Share Logic Across Mobile, Web, and Beyond - This video explores sharing logic across platforms using Kotlin Multiplatform. Compose Multiplatform: Building UIs for Android, iOS, and Beyond - Covers the basics of Compose Multiplatform and building UIs for various platforms. Advanced Kotlin Multiplatform: Tips and Tricks - Focuses on advanced usage of Kotlin Multiplatform with tips and strategies. Real-world Compose Multiplatform Applications - Discusses practical examples and bene fi ts of applications built with Compose Multiplatform. KotlinConf 2024

Slide 74

Slide 74 text

KMP + Compose Wizard https://terrakok.github.io/Compose-Multiplatform-Wizard/

Slide 75

Slide 75 text

https://speakerdeck.com/rivuchk/creating-sdks-for-multiple-platforms-with-kmp KMP at JioCinema

Slide 76

Slide 76 text

Thats all folks! https://cal.com/adit/30min 🔗aditlal.dev Slides - scan here travelwithadit