Recently, we wrote about the demonstrative move to declarative UI. With Jetpack Compose, Android is joining the declarative trends.
Jetpack Compose is a new declarative UI toolkit by Google made for building native Android apps that is rapidly gaining traction. In contrast to the traditional XML Views, Jetpack Compose allows you to build UIs using composable functions that describe how the UI should look and behave.
The main advantage of using Jetpack Compose is that it allows you to write UI code that is more concise and easy to understand. This leads to improved maintainability and reduced development time.
The main disadvantage of using Jetpack Compose is that it’s relatively new, so its ecosystem is limited compared and the number of available libraries, tools, and resources is lower than the traditional ecosystem.
Despite that, Jetpack Compose’s ability to be integrated into existing Android apps and be gradually adopted has made its way into countless apps. Some of the bigger and more popular apps that started adopting Jetpack Compose include Lyft, Twitter, Airbnb, Square, Reddit, and Firefox.
The recommended IDE for working with Jetpack Compose is Android Studio. After downloading and installing Android Studio, you’ll get the option to create a new project. To create a new Jetpack Compose application, you need to select either the Empty Compose Activity
(which uses Material v2 which is more stable), or Empty Compose Activity (Material3)
(which uses the Material v3 which is still in Preview). You can see both options in the top right of this screenshot:
This is the easiest way to get started with Jetpack Compose. If you’d like to enable Jetpack Compose into an existing Android application, here’s what you need to do:
Add the following build configurations in your app’s build.gradle
file:
android {
buildFeatures {
// this flag enables Jetpack Compose
compose true
}
composeOptions {
// the compiler version should match
// your project's Kotlin version
kotlinCompilerExtensionVersion = "1.3.2"
}
}
Add the Compose BOM (Bill of Materials) and the subset of Compose dependencies to your dependencies:
dependencies {
def composeBom = platform('androidx.compose:compose-bom:2022.12.00')
implementation composeBom
androidTestImplementation composeBom
// Choose one of the following:
// Material Design 3
implementation 'androidx.compose.material3:material3'
// 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'
// Android Studio Preview support
implementation 'androidx.compose.ui:ui-tooling-preview'
debugImplementation 'androidx.compose.ui:ui-tooling'
// UI Tests
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
debugImplementation 'androidx.compose.ui:ui-test-manifest'
// 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:material3-window-size-class'
// Optional - Integration with activities
implementation 'androidx.activity:activity-compose:1.5.1'
// Optional - Integration with ViewModels
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1'
// Optional - Integration with LiveData
implementation 'androidx.compose.runtime:runtime-livedata'
// Optional - Integration with RxJava
implementation 'androidx.compose.runtime:runtime-rxjava2'
}
Jetpack Compose uses Composables to define the view hierarchy, and modifier to apply visual appearance and behavior changes to the composables they’re added to.
Composable functions (or just Composables) are ordinary Kotlin functions that are annotated with @Composable
, can be nested within another composable functions, and return a hierarchy of other composables in order to define their UI. Let’s see a simple composable that defines a contact row UI that contains a user photo, and a name and phone number:
@Composable
fun ContactRow(user: User) {
Row {
Image (
painter = painterResource(id = R.drawable.user),
contentDescription = "A photo of a user"
)
Column {
Text(user.name)
Text(user.phone)
}
}
}
The Row
composable is a layout composable that renders its children one next to another. The Image
composable is the first child which is going to render the user
drawable. Then we have the Column
composable which, similar to the Row
, is a layout composable, but it renders its children one below another. The children of the Column
composable are two Text
composables that render the user’s name and phone number.