Jetpack Compose

Jetpack Compose: Overview

Alex Zhukovich 6 min read
Jetpack Compose: Overview
Table of Contents

Jetpack Compose is a modern toolkit for building native UI for Android applications. It was announced at Google I/O 2019. It is a new approach based on DSL and allows developers to create UI. Ideally, it should help us to write less code and have simpler and more maintainable UI.

Android Studio 4.0 is one of the requirements for using Jetpack Compose. Android Studio supports Preview mechanism for composable functions. It means that we can see changes in UI without recompiling the whole application.

Jetpack Compose artifacts

Jetpack Compose distributed with multiple dependencies and all artifacts can be split into two groups: core and ui.

Let’s start with the cdependencies:

How to set up the project

First of all, we should add all dependencies for Jetpack Compose:

dependencies {
    ...
    implementation 'androidx.compose:compose-compiler:0.1.0-dev04'
    implementation 'androidx.compose:compose-runtime:0.1.0-dev04'
    implementation 'androidx.ui:ui-layout:0.1.0-dev04'
    implementation 'androidx.ui:ui-framework:0.1.0-dev04'
    implementation 'androidx.ui:ui-layout:0.1.0-dev04'
    implementation 'androidx.ui:ui-foundation:0.1.0-dev04'
    implementation 'androidx.ui:ui-animation:0.1.0-dev04'
    implementation 'androidx.ui:ui-tooling:0.1.0-dev04'
    implementation 'androidx.ui:ui-material:0.1.0-dev04'
    ...
}

Note:

  • All dependencies have the same version. Right now it's a 0.1.0-dev04 version. I recommend creating a const with a version of use for all Jetpack Compose dependencies;
  • Jetpack Compose doesn’t use kapt (Kotlin Annotation Processing).

The next step is adding buildFeatures and composeOptions sections with the same version that all the dependencies have.

android { 
    ...
    buildFeatures {
        compose true
    }
    composeOptions { 
        kotlinCompilerExtensionVersion "0.1.0-dev04" 
    } 
}

The final recommendation is to add the following code

android {
   ...
   compileOptions {
      sourceCompatibility JavaVersion.VERSION_1_8
      targetCompatibility JavaVersion.VERSION_1_8
   }
   kotlinOptions {
      jvmTarget = "1.8"
   }
}

Demo

Before starting to work with Jetpack Compose we need to understand @Composable and @Preview annotations:

  • The @Composable annotation allows us to create UI components with Jetpack Compose.
  • The @Preview annotation can be applied to composable functions to show them in the Android Studio preview.

Let’s create a new Activity and use Jetpack Compose for creating a simple UI.

class MainActivity : AppCompatActivity() {

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContent {
           Text(text = "Hello, World")
       }
   }
}

As a result, we will have an application with "Hello, World" text.

Let's try to create a simple application with a static list of coffee drinks.

Application demo

First of all, we can create a class to store data about coffee drinks.

data class CoffeeDrink(
    val name: String,
    @DrawableRes val imageUrl: Int,
    val description: String,
    val ingredients: String
)

The next step is to create a constant with a list of coffee drinks.

fun getCoffeeDrinks(): List<CoffeeDrink> {
    return listOf(
        CoffeeDrink(
            name = "Americano",
            imageUrl = R.drawable.americano_small,
            ingredients = "Espresso, Water"
        ),
        CoffeeDrink(
            name = "Cappuccino",
            imageUrl = R.drawable.cappuccino_small,
            ingredients = "Espresso, Steamed milk foam"
        ),
        CoffeeDrink(
            name = "Espresso",
            imageUrl = R.drawable.espresso_small,
            ingredients = "Ground coffee, Water"
        )
        ...
    )
}

Let's create a composable function for rendering a coffee drinks item and a function with @Preview annotation for checking the result inside IDE without recompiling the application:

@Composable
fun CoffeeDrinkCard(
    coffeeDrink: CoffeeDrinkModel
) {
    Row {
        Container(modifier = LayoutSize(80.dp), alignment = Alignment.Center) {
            DrawImage(image = imageResource(coffeeDrink.imageUrl))
        }
        Container(
            alignment = Alignment.TopLeft,
            modifier = LayoutFlexible(1f)
        ) {
            Column {
                CoffeeDrinkTitle(title = coffeeDrink.name)
                CoffeeDrinkIngredient(ingredients = coffeeDrink.ingredients)
            }
        }
    }
}

@Preview
@Composable
fun previewCard() {
    CoffeeDrinkCard(
        coffeeDrink = getCoffeeDrinks().first()
    )
}
Card preview

The Row and Column functions allow us to position in rows and columns. It allows the creation of a complex layout by combining them. A similar option was available previously with LinearLayout.

The final step is to create a composable function for rendering a list of coffee drinks, preview functions and call a composable function inside onCreate#setContent function.

class MainActivity : AppCompatActivity() {

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContent {
           CoffeeDrinksList(coffeeDrinks)
       }
   }

    @Composable
    fun CoffeeDrinkList(
        coffeeDrinks: List&lt;CoffeeDrink>
    ) {
        VerticalScroller {
            Column {
                for (coffee in coffeeDrinks) {
                    Ripple(bounded = true) {
                        Container {
                            Column {
                                CoffeeDrinkCard(
                                    coffeeDrink = coffee
                                )
                                Opacity(0.08f) {
                                    Divider(modifier = LayoutPadding(left = 88.dp), color = Color.Black)
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    @Preview
    @Composable
    fun previewCoffeeDrinkList(coffeeDrinks: ModelList<CoffeeDrinkModel>) {
        CoffeeDrinkList(
            coffeeDrinks = getCoffeeDrinks()
        )
    }
}

After the small demonstration of Jetpack Compose, you may have many questions, and I'll try to answer a few of them:

  • How to work with string resources? We can work with string resources as we worked previously using the getString(R.string.any_string) function;
  • How to support different screen sizes? We have access to the device configuration, and we can use the if-else statement inside composable functions;
  • How to change the state of components? We can create a model class with @Model annotation, and after changes in these objects, components will be re-drawn

Summary

Jetpack Compose allows us to create composable functions for building UI with DSL. The current version is 0.1.0-dev04, and the API may change a lot. After the next developer release, some of these functions may be deprecated or removed completely as it was between 0.1.0-dev03 and 0.1.0-dev04 versions.

However, even right now, we can see a new vision of building Android UI. This approach has the following benefits compared with the classic approach (XML files):

  • "UI Preview" feature of Android Studio allows us to save a lot of time and helps to avoid re-compile application for checking UI changes;
  • Splitting huge components into small composable functions allows us to debug and change them easily in isolation;
  • Naming is a significant part of Jetpack Compose because of implementing complex UI, we will create many small functions;
  • We have a developer release right now, and API may change from version to version.

Let’s try to scan UI with Layout Inspector.

Layout Inspector

As you can see, we have one composable view. This means that we will have additional API for testing UI. Right now you can find available API in the "ui-test" library.

This is the first article from a series about Jetpack Compose. During this series, we will build and test applications. Next time we will talk about the state for composable function and possible ways of re-drawing the components. Stay tuned.

GitHub project (source code can change): JetpackComposeWorkshop.


Mobile development with Alex

A blog about Android development & testing, Best Practices, Tips and Tricks

Share
More from Mobile development with Alex
Jetpack Compose: Divider
Jetpack Compose

Jetpack Compose: Divider

This article covers using and customizing the “Dividers” components from the "Material 2" and "Material 3" libraries in the Jetpack Compose. In addition to that, we will explore the difference between implementation of the Divider, HorizontalDivider and VerticalDivider.
Alex Zhukovich 4 min read
Jetpack Compose: Switch
Jetpack Compose

Jetpack Compose: Switch

This article covers creating and customizing the "Switch" component in Jetpack Compose for enabling/disabling features. It explores differences between "Material" and "Material 3" libraries, and how to interact with and verify the Switch component's state in UI tests.
Alex Zhukovich 8 min read

Great! You’ve successfully signed up.

Welcome back! You've successfully signed in.

You've successfully subscribed to Mobile development with Alex.

Success! Check your email for magic link to sign-in.

Success! Your billing info has been updated.

Your billing was not updated.