Introduction to Jetpack Compose
User interfaces are essential for every Android app. They serve as the medium through which your users interact with your app. When built right, users are delighted as they use your app. If not, it will lead to bad user experiences.
And we know developing interfaces on Android is more challenging than our users might think. Let me introduce you to Jetpack Compose to simplify and accelerate this process.
What is Jetpack Compose?
Jetpack Compose is the new toolkit to build native UIs for Android. The name is derived from the combination of “Jetpack” and “Compose.”
Jetpack are libraries to help Android developers build apps consistent across Android devices and versions using best practices and consistent codebases.
Compose is a component of Jetpack that simplifies the process of creating user interfaces on Android devices. The name comes from the Object Oriented Principle of Composition.
In other words;
Jetpack Compose is a modern library or framework for building native UI on Android using Composition.
Why Jetpack Compose?
1. Declarative over Imperative thinking.
The hardest part about building a UI is how it changes or updates over time. For such changes to happen, the following must occur:
- Views that make up the UI should capture users’ specific actions, gestures, or events.
- Update the UI state in response to user-generated events.
- Update the UI to represent the new state.
Traditionally, you can use imperative thinking in Android to perform such changes. Imperative programming uses functions to describe how the UI should look when data or state changes.
By being imperative, you tell the UI to do what you want or show the views you need.
Below is a profile screen in two different states:
Using imperative thinking, the code to render the user profile might be:
You must handle 12 “if statements” to display the user profile. If you forget one if statement, the user profile will render an incorrect UI.
With Jetpack Compose, you use the declarative approach.
By being declarative, you describe what the UI should be, rather than how the UI should look.
When displaying the user profile to the left, declarative programming would show a red follow button rather than make the follow button have a red background.
The user profile would be declared in Jetpack Compose as:
2. Composition over Inheritance.
Inheritance is a programming pattern where a child class inherits the properties or behaviors of a parent class. The original Android UI toolkit is built on the principle of Inheritance.
Take a look at the image below.
ImageButton is a child widget of ImageView, and so is ImageView to View and View to Object. This means methods of ImageView are methods of ImageButton.
It would take a lot of work to build and maintain a custom ImageButton due to the many parent methods you must re-implement.
Jetpack Compose is built on Composition.
Composition is a principle in object-oriented programming that says that classes should achieve different behaviors and code reuse by containing instances of other classes to implement the desired functionality.
In Jetpack Compose, Composition will mean that you can build your UI by using multiple classes rather than inheriting a single class.
An ImageButton can be built in Jetpack Compose as such:
ImageButton contains Icon() and Box() components, both pre-built widgets offered by Jetpack Compose.
This makes building complex views easy because there are compositions of other views. Also, there is code reusability. Just as Icon() and Box() are used to build ImageButton composable, ImageButton() can likewise be used to implement another widget.
3. Encapsulation over Callbacks.
There are two things to think about when building UIs on Android:
- Which view you’d use to display any given data?
- Who is responsible for data changes?
The Android UI toolkit has widgets that assume both roles. While it’s evident that views should display states, it is not to have them manage states. Views managing data changes (states) implement callbacks or listeners in the form of interfaces.
For instance, TextView, used to display text, has the addTextChangedListener callback that tells you whenever the text has been changed. But the changes to the text can be from the user or another source.
Jetpack Compose decouples components responsible for displaying state from parts responsible for updating state by embracing Encapsulation.
Encapsulation is a concept in object-oriented programming(OOP) that hides class implementation from other parts of a computer program.
Using this concept means your UI will represent the state and isn’t responsible for managing the state.
Imagine building a simple contact list:
Contact is a state, defined in Kotlin as:
Using Jetpack Compose, you create a Contact composable like this:
Here, your Contact Composable or widget displays each Contact and has nothing to do with updating the Contact.
4. Separation of Concerns: Cohesion over Coupling.
Building a simple Android UI requires defining a layout in XML and inflating it into a Kotlin or Java Object. Making the UI responsive requires that you connect it with your business logic.
While this allows you to see your UI in action, the lengthy process goes against a concept known as Separation of Concern.
Separation of Concern is a design principle that suggests that you should separate your program into distinct sections with each section addressing a distinct concern.
A concern is a set of information that affects the code of a computer program.
It could be as general as the hardware on which you run your app, or as specific as the name of which class to instantiate.
If you have always heard that you should separate your business logic from your UI logic, that is a separation of concern.
Separation of Concern has two main concepts: Cohesion and Coupling. To better understand these concepts, imagine an app to be a group of modules, and each module contains many units.
Dependencies between modules would represent Coupling.
On the other hand, interactions between units in a module would describe Cohesion.
The goal is to group related code as much as possible(reduce coupling and increase cohesion). This way, your code is maintainable over time and scales as your app grows.
By design, implementing a UI in Android encourage implicit dependencies between the layout written in XML and the business logic written in Kotlin.
Implicit dependency is not by your own doing but by how the Android UI framework is designed—such dependency results from the language difference between the modules, hence, a forced line of separation.
This forced line of separation disappears in Jetpack Compose because everything is written in Kotlin.
Jetpack Compose is built with the idea that a framework should not separate your concerns for you. You should be able to decide where to draw the line of separation, depending on what makes sense for your situation.
This means that the implicit dependencies you had between your layout and business logic now become explicit.
Another benefit of having your UI and business logic written in Kotlin is that you can refactor those explicit dependencies to reduce Coupling and increase Cohesion.
Components of Jetpack Compose.
There are several components that makeup Jetpack Compose. These components can be placed into two distinct parts we shall call hosts.
1. Development host
The first part contains all the tools that would help you write composables. They are:
- Kotlin Compiler: Jetpack Compose is written in Kotlin and uses many features, like trailing lambdas and commas, so it is easy to build your UI.
- Compose Compiler Plugin: If you have noticed, every compose code is a function that begins with the @Composable annotation. This plugin translates those annotations to UI-specific code for Android to render on the screen.
- Android Studio: As the preferred IDE for Android development, it offers specific tools that make building with Compose easy.
2. Device host.
The second part contains the components necessary to run compose code on your device. Those components are:
- Compose Runtime: Compose logic doesn’t know anything about Android or UIs. It only knows how to work with tree structures to emit specific items. The runtime provides an entry point for the compose compiler to understand the tree structure and emit UIs or other UI-related things like animations.
- Compose UI Core: It handles interaction with the device, such as layout, drawing, measurement, and input management.
- Compose UI Foundation: It contains essential building blocks like Text, Button, Row, Column, and other default interactions.
- Compose UI Material: An implementation of the Material Design System. It provides Material components, making using Material Design in your app easy.
- Compose UI Material 3: Implementing the Material Design 3 system, the new Material Design on Android 12 and higher. It provides Material 3 components, making using them in your app easy.
- Compose UI Animation: It includes components for adding animations to your app.
Resources
Now that you know what Jetpack Compose is, its components, and how different it is from the Old Android UI toolkit, these resources should help you learn how to build interfaces using the framework.
Setup: To use Jetpack Compose in a new or existing Android app, check out:
Playgrounds: To delve right into experimenting with the different device components, check out:
Codelabs: If you enjoy a step-by-step approach, here are two codelabs that cover the basics of the framework:
Samples: There are sample projects that you should follow through if you fancy how to build full apps with the framework.
Courses: If you prefer a mix of different content materials(text, video, and codelabs) while earning badges and tracking your progress, here are courses provided by the Jetpack team that covers everything from having a material-designed app to building for different devices.