New Android UI Kit: Jetpack Compose

Fundamentals

Enes Zor
Trendyol Tech
7 min readNov 28, 2022

--

Jetpack compose is a new UI kit in Android Development with less code. You can forget XML and be very close to Kotlin. With simple means, developers don’t have to switch between XML to Kotlin files anymore. Moreover, listing, animations, and components can be figured out with just a few lines of Kotlin codes. Let’s dive deep!

phonearena.com by Preslav Kateliev

Jetpack Compose is used by some big companies such as Twitter, Square, Monzo, and Cuvva. According to these companies' experiences, it is magnificent. Let’s have a look at the points :

  • Android theme management is really improved with Compose.
  • When the application state is changed, the related UI is recomposed.
  • There is only one source of truth. You do not have to think of View State.
  • It is compatible with your existing Views. ( fragment, activity, and views)
  • Easy Maintainable.
  • It is interoperable with ViewModel, Coroutines, and Navigation.
  • Live preview reduces the build time.
  • It is declarative.

Now, we have some ideas about Jetpack Compose. Let’s share new details. Jetpack compose allows us to render our app without imperatively mutating frontend views. We mentioned that declarative and imperative terms before. You can ask what they are. Let’s explain to them together.

The declarative and imperative programming paradigm

Paradigm can be explained as solving method of any problem. Programming paradigms are ways of expressing differently to complete the functionality of any task with any programming language.

Everyone who remembers to start coding his first task in an imperative way. The imperative programming paradigm focuses on how to solve problems. Every detail of implementation can be seen in these types of programming. In other words, each required step of functionality exists in the programming set. Let’s give an example.

This code is calculated to the total value of given integers. As you see we can see each step of this method. Actually, we are saying how to calculate it with the implementation of each step. Remember your first task in degree. If your instructor wants code summation of integers, you probably think step by step and ask yourself how. And, you did like that especially if you code C++ or C at the final.

Let’s mention declarative programming. Declarative programming is based on what is unlike imperative programming. You know Kotlin if you are developing Android applications. And probably use functional programming and DSL. These terms are coming under declarative programming. Declarative code describes itself with specific commands and methods without showing any detail. Let’s convert the total integers calculator in a declarative way.

Yes. We did just one line of code with what we needed. It is declarative programming. Absolutely, we can use filter(), map(), and flatMap() according to our requirements. But please think of when we need a flatMap() operation in declarative and how to do it in an imperative way. Absolutely, it can be completed imperative way but we need to code more lines. This means it is open to bugs and side effects as well. Furthermore, more lines decrease readability.

The negative way of declarative programming is hard to find bugs and also memory allocation in terms of performance. Many of us work with developers together in our company. Readability and understandability are the most important things for us. Moreover, better control is important. Declarative programming provides all of them to us.

Composition

Composition is the process of showing related components in your UI. It is consist of @Composable functions. Every compose function needs to be annotated with @Composable to inform the compiler about adding a UI view tree. Compose was built on top of functions. We are already very familiar with the functions. It is always used to write clean code and complete single responsibility as well. No class, no inheritance in view.

Let’s have a first look together! This composable function will give simple Text to use when it is composed. Its equivalent in Android View is TextView. You can set your text, alignment, and style just with three lines. As you see, it is fully declarative. We already mentioned that in previous sections. We are focusing on what is here. The only need is what we want to see on the screen when the screen is in composition or recomposition. Lastly, I would like to mention that compose draw screen from scratch with only the necessary, parts. It can be seen as a disadvantage. There is a term recomposition to improve and optimize this drawing process. I will mention that in further parts.

  • Compose functions can work in parallel.
  • Compose functions needs to be called inside another compose function.
  • Compose has compatibility with Android Views.

Compose comparing XML

I’d like to create a factorial calculation screen. To use this screen functionality, the user needs to tab text and see the dropdown. And, select any number from there. Finally, he will see the factorial calculation of this number. Check the full compose code below.

If I tried to do it in the fragment (XML), I would need to think of two different places. These are fragments and XML. Firstly, I need to add Spinner ( I am even not sure this DropdownMenu can be drawn from any native Android component but Spinner just came to my mind. A custom view can be written as well with a recycler view. At the final, we need to add one or more components to achieve DropdownMenu functionality.) Going that way is breaking the Single Source of Truth rule. Our data can be updated at any place at the same time. Furthermore, we need to use core Android functions such as setText() , setVisibility(), etc. These functions increase the complexity of code. This imperative way focuses more on detail and drawing screen how. Because we declare our XML before the code build process and update it later. After the build process, we need to relate the item with findViewById() if we need to update views according to our data.

Every compose component occurs stateless on their screen, unlike android views. It only can be updated with data changes. It is a Single Source of Truth and a declarative approach. Everything depends on data here. So, how do we make them stateful? I will explain all of them in the next section.

Recomposition

We already describe composition before. Composition is never a thing that only occurs once. When the composable state changes, recomposition happens. It depends on the state of change. To do that you need to make your composable stateful. Let’s begin with remember.

  • remember

remember is the inline function that has a function parameter to compute value only once in composition, and return the same value during recomposition. You can say that what we need that? Can we hold the same value as a primitive? Let’s have a look at a real-time example.

As you see we have two Date() variables. One of them is wrapped remember another one is normal value initialization. If we have a value that is not required to be reinitialized again, we can use remember. It will be calculated only once, and the same object will be used in the next recomposition. It is a really big advantage in terms of performance. The memory won’t be heavy. If we need to reset our variables and have updated Data, we can initialize our object normally. Every object will be instantiated in each recomposition.

  • remember { mutableStateOf }

mutableStateOf is a single value holder. We can write and read value with this structure. It makes that composables are stateful. If we write a new value to mutableStateOf, recomposition will happen. Furthermore, we will get an updated value from the same mutableStateOf object via remember.

In our factorial example, we wrapped our text and expanded values out with remember { mutableStateOf }. In this example, we want to keep our dropdown state ( expanded or not ) and our factorial calculation result as text. Please attention, these parameters can be updated according to user actions. According to these actions, the screen needs to be updated. It is a recomposition. When these parameters are updated, recomposition will be started and only functions which use these parameters will be recomposed. If you want to learn them in detail, let’s have a look at compose scopes via possible user actions and Logs in the factorial example.

  • All components belonging to the initial composition are below. You can say that why RSCheckPointThree is not rendered because we did not select any number yet from the dropdown. In the initial composition (first run), we see just text and a button. These component composition scopes can be seen in logs.
E/RSCheckPointOne: androidx.compose.runtime.RecomposeScopeImpl@c4eaaf8
E/RSCheckPointTwo: androidx.compose.runtime.RecomposeScopeImpl@c4eaaf8
E/RSCheckPointFour: androidx.compose.runtime.RecomposeScopeImpl@1c08a3b
  • When we touch the screen, you will see dropdown items (numbers) on the screen. This event will trigger recomposition because the dropdown menu expanded status is changed here. Remember that every compose component burns stateless in jetpack compose. So, we are keeping it as mutable. When changed expanded state of a dropdown menu, the functions which already depend on expanded parameters, are recomposed. As you see below.
E/RSCheckPointOne: androidx.compose.runtime.RecomposeScopeImpl@c4eaaf8
E/RSCheckPointTwo: androidx.compose.runtime.RecomposeScopeImpl@c4eaaf8
E/RSCheckPointThree: androidx.compose.runtime.RecomposeScopeImpl@2fd74b1
  • When any item is selected from the dropdown list, the result is calculated and shown on the screen. Here, our text is updated. So, the functions which are read text will be recomposed.
E/RSCheckPointOne: androidx.compose.runtime.RecomposeScopeImpl@c4eaaf8
E/RSCheckPointTwo: androidx.compose.runtime.RecomposeScopeImpl@c4eaaf8
E/RSCheckPointThree: androidx.compose.runtime.RecomposeScopeImpl@2fd74b1
  • When the reset button is pressed, the result is recalculated and the functions which are read the text are composed.
E/RSCheckPointOne: androidx.compose.runtime.RecomposeScopeImpl@c4eaaf8
E/RSCheckPointTwo: androidx.compose.runtime.RecomposeScopeImpl@c4eaaf8
  • Finally, I would like to mention that our reset button just composed only one time as you see. In the previous part of this article, we already mention that for each recomposition compose draw screen scratch, but it updates with only the necessary parts. In the factorial example, we observed recomposition three times but the button only recomposed one time. The reason behind that compose did not find it necessary. It calculates that it is stateless so it was not updated.

In this article, I would like to mention about fundamentals of jetpack compose. It is a really large topic. Furthermore, it is not possible to complete all things with just one article. I hope that you can introduce yourself to jetpack compose with this article. Thank you for your reading 🙌

References:
https://developer.android.com/jetpack/compose
https://www.amazon.com/Android-Development-Jetpack-Compose-declarative/dp/1801812160

--

--