← Back to garden

item

Jetpack Compose


Installation

Compose - similarly to other Android Libraries - can be a bit hard to setup and understand.

Compose Compiler

Starting from Kotlin 2.0 the compose compiler is in match with the Kotlin version.

Before Kotlin 2.0

Check Compose Compatibility with Compose Koltin Compatibility Map here and set the compatible version on your app’s build.gradle

android {
    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.1"
    }
}
Afer Kotlin 2.0

Once you know and have access to your kotlin version (i.e Version catalog) you can add the compose plugin to your dependencies and your build.gradle(s)

// libs.versions.toml
[plugins]
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }

// Root build.gradle.kts
plugins {
	alias(libs.plugins.compose.compiler) apply false
}

// Android / CMP build.gradle.jts
plugins {
  	alias(libs.plugins.compose.compiler)
}

Enable Android Studio features for compose

If you’re using Android studio and want to have full access to tooling like previews, preview animations, live edit, etc you need to enable the build flag on your apps build.gradle.kts

android {
    buildFeatures {
        compose = true
    }
}
dependencies {
	implementation("androidx.compose.ui:ui-tooling-preview")
    debugImplementation("androidx.compose.ui:ui-tooling")
}
Manage Versions

Compose dependencies have different version numbers so having a “single” compose version that manages its dependencies won’t work. For this we can use a BOM (Bill of Materials) which will manage the version of the dependencies that we decide to include. You can see which version will be including looking at the BOM Library version mapping and if you want to see the Alpha or Beta releases you can see that on the MVN repository.

Usage of BOM (with version catalog/ libs.versions.toml )
// libs.versions.toml
composeBom = "2024.04.01"

androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" }


// Build.gradle.kts - App
dependencies {
	androidTestImplementation(platform(libs.androidx.compose.bom)) //Android only
	implementation(libs.androidx.activity.compose)

    implementation(libs.androidx.ui)


}
Overwrite Version of a Library within BOM
// libs.version.toml
// From
androidx-ui = { group = "androidx.compose.ui", name = "ui" } 
// TO
androidx-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "yourVersion" } 

// build.gradle.kts
dependencies {
    implementation(libs.androidx.ui)
}
Compose Libraries

For Compose Multiplatform, Google mostly develops Android-specific libraries and Jetbrains creates wrappers for them (currently there’s no BOM for Compose Multiplatform ) and many android-specific libraries are available under the org.jetbrains.androidx prefix. (You can see all libraries in MVN)
As briefly mentioned before in Compose BOM, compose is very modular and so it doesn’t include many functionalities out of the box. We will talk about different libraries once we are looking into specific topics and areas, but it’s good to know what the official libraries are and what are they used for.

Tooling
// Android Studio Preview support
    implementation("androidx.compose.ui:ui-tooling-preview")
    debugImplementation("androidx.compose.ui:ui-tooling")
User Interface
    // Material Design 3
    implementation("androidx.compose.material3:material3") // <--- Use this by default
    // or Material Design 2
    implementation("androidx.compose.material:material")
    // or skip Material Design and build directly on top of foundational components
    implementation("androidx.compose.foundation:foundation")
    // or only import the main APIs for the underlying toolkit systems,
    // such as input and measurement/layout
    implementation("androidx.compose.ui:ui")

    // Optional - Included automatically by material, only add when you need
    // the icons but not the material library (e.g. when using Material3 or a
    // custom design system based on Foundation)
    implementation("androidx.compose.material:material-icons-core")
    // Optional - Add full set of material icons
    implementation("androidx.compose.material:material-icons-extended")
    // Optional - Add window size utils
    implementation("androidx.compose.material3.adaptive:adaptive")
Android Integration
// Optional - Integration with activities
    implementation("androidx.activity:activity-compose:1.9.2")
    // Optional - Integration with ViewModels
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.5")
    // Optional - Integration with LiveData
    implementation("androidx.compose.runtime:runtime-livedata")

Use cases

Similar to React, compose is divided in two; The compose compiler and compose UI which makes compose really versatile as the runtime and compiler can be embedded on other platforms similar to ReactNative or React Three Fiber.

History

Jetpack compose started off as a way to write Declarative user interfaces by using the Android XML Views system within Kotlin code, kind of like React did with JSX in JS (which is not part of the JS programming language so it has to go trough a build tool first) But Jetbrains (the creators of Kotlin preferred to use the Kotlin’s capabilities to create DSLs instead of adding XML support.
image


Overview

Jetpack Compose is Android’s way to build declarative UIs with Kotlin, similar to SwiftUI on iOS Development and React on Web development. Achieving very similar results as React, in the sense that in compose each function can be a self-contained component with inputs to operate and outputs UI or state (like React Hooks).

Compose is not just UI

Basic Concepts

All of these topics are part of the compose-core library

State

State in compose is a wrapper on a value that allows compose to track its changes and re-execute `composable` functions, most of the time state needs to be marked as mutable, this can be done with `mutableStateOf` and it will wrap our value with the `State` and we will be able to access the current value of the state by using the `.value` property. This minor inconvenience can be fixed by using Kotlin delegates so it would look something like:

var name by mutableStateOf("")

Since compose are functions the state is only saved when rendering, instead of a component t lifecycle

Side-Effects

An effect is a composable function that doesn’t emit UI and causes side effects to run when a composition completes.

LauchedEffect: run suspend functions in the compostable’s scope
rememberCoroutineScope: Obtain a composition-aware scope to launch a coroutine outside the composable

As LaunchedEffect is a composable function, it can only be used inside other composable functions. In order to launch a coroutine outside of a composable, but scoped so that it will be automatically canceled once it leaves the composition

rememberUpdatedState
DisposableEffect : An Effect that requires cleanup

Similar to React’s Cleanup function in useEffect you can launch an side-effect at the initial composition of the composable and a cleanup callback when the composable leaves the composition. This is specially useful for observers that you need to remove.

val currentOnStart by rememberUpdatedState(onStart)
    val currentOnStop by rememberUpdatedState(onStop)

    // If `lifecycleOwner` changes, dispose and reset the effect
    DisposableEffect(lifecycleOwner) {
        // Create an observer that triggers our remembered callbacks
        // for sending analytics events
        val observer = LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_START) {
                currentOnStart()
            } else if (event == Lifecycle.Event.ON_STOP) {
                currentOnStop()
            }
        }

        // Add the observer to the lifecycle
        lifecycleOwner.lifecycle.addObserver(observer)

        // When the effect leaves the Composition, remove the observer
        onDispose {
            lifecycleOwner.lifecycle.removeObserver(observer)
        }
    }
Composition

The composition is a Tree-structure of your composables that describe your UI. This keeps track of all the composables available on your UI.

Recomposition
When the state of the app changes, a recomposition will be scheduled and will re-execute the composables that might have changed, then the composition is updated to reflect the changes.
Similar to React’s Virtual DOM.

The composition can only be created by initial composition and updated by recomposition.

Lifecycle

A composable lifecycle is defined by the following events:

  • Initial composition
  • Recompose 0 or more times
  • Leaving the composition

A composable lifecycle is quite simple compared to Views or Activities. If a compostable needs to interact with those more complex lifecycles you should use Side-Effects
lifecycle-composition

Phases

Styling

All of these topics are part of the compose-ui library

Basic Layouts

In compose composables are used to create portions of the UI and layouts allow you precisely arrange them. Many of the composables are in fact, layouts.

  • Column / LazyColumn / FlowColumn
  • Row / LazyRow / FlowRow
  • Box / BoxWithConstrains
  • Grid
  • Spacer

Custom Layouts

Custom Layouts (Position Absolute)
Adaptive Layout

Modifiers

Resources
Animations
Material Theming
Compose Navigation
  • Supports:
    • composable
    • dialog
    • activity
    • fragments
    • nested navgraphs
Nav3
  • Voyager
  • Decompose
  • Circuit
  • Adaptive Navigation
  • Layout Transitions

Gestures

Animations

Accessibility

  • Touch & Input
  • Merge Decendants
  • Roles
  • Order
  • Custom actions

Ecosystem

Mobile

Navigation

Official
Navigation3
Voyager

Voyager is a Programatic navigation library, similar to the iOS navigation style, where you can just call navigator.push(PostDetailsScreen(post.id)) and it will navigate to the new screen (no graph needed)

Drawbacks
  • No official deeplinks
  • No adaptive navigation
Voyant

An extension for voyager and the official navigation to support iOS back navigation natively

iOS

Calf
Compose-cupertino

**UI **

Charts
Vico ⭐
  • Multiplatform

  • Variable X/Y

  • Infinite scroll

  • Paginated scroll

  • Stacked bars

  • Highlight bars~

  • Legends

  • Donut

Compose Charts

A multiplatform library that provides the most common charts

YCharts

An amazing and customisable charts libray, but currently only supported on Android

Markdown renderer

A Multiplatform library to render markdown content, with support for images, links, custom styles and components.

  • No Bold Support
  • Issues with Image rendering
3D

Unfortunatelly Android still doesn’t have a official 3d library yet and the best option for now is to use Spline

Web

Kilua

Framework for Building full-stack applications with compose

  • Focus on frontend with integartion with Tailwind and other technologies
  • SSR support
  • Integration with Kotlin backend libraries like Ktor and Spring
  • Export to Static Site (?)
Compose HTML
Compose Web
Kobweb

Opinionated
Build Websites using Compose-html, with support for backend and frontend, as well as other utilities, it is like the Next.js of Compose

In my experience this is a great project, but for static site rendering it uses a whole browser to render the site…

Extra

CLI

Architecture

Inter-op with Fragments & Views

  • AndroidFragment<MyFragment>()
  • AndroidView(ViewBinding::inflate) {}

Documentation

Compose theme interop with XML Colors
Displaying and Updating GeoJSON on a MapBox in Jetpack Compose
Expanding Compose Snackbars