Hello. Before leaving for the weekend, we are in a hurry to share with you another translation prepared specifically for students of the course
“Android Developer. Advanced course .

We are trying a new UI-framework for Android applications
Over the past few years, participating in many mobile projects, I have had to use various technologies, such as Android, ReactNative and Flutter. Switching from ReactNative back to classic Android made me have mixed feelings. The return to Kotlin went well, but I really missed the React UI framework. The small reusable components that create the user interface are great and provide more flexibility and speed of development.
')
Returning to classic Android, I had to worry about keeping the View hierarchy as uniform as possible. Because of this, it is difficult to truly devote yourself to the component approach. This makes copy-paste more tempting, which leads to more complex and less supported code. Ultimately, we keep ourselves from experimenting with the user interface that could improve the UX.
Android reveals Jetpack Compose. Illustration: Emanuel BagillaJetpack Compose to the rescue
So after watching
What's new in Android from the Google I / O 2019 conference, I immediately started to figure out Compose and tried to learn more about it. Compose is a reactive user interface toolkit, fully developed by Kotlin. Compose looks very similar to existing user interface frameworks such as React, Litho, or Flutter.
The current structure of the Android UI framework has been around since 2008, and over time it became more complex and rather hard to maintain. Jetpack Compose seeks to start all over again with the philosophy of modern components. The framework is written with the following main objectives in mind:
- Unrelated to platform releases: This allows you to quickly fix bugs, since Compose does not depend on new Android releases.
- Smaller technology stack: The framework does not force you to use a View or Fragment when creating a user interface. All elements are components and can be freely put together.
- Transparent state management and event handling: One of the most important and complex things that need to be addressed in large applications is the handling of data flow and state in your user interface. Compose clarifies who is responsible for the state and how events should be handled, just as React handles it.
- Writing less code: Writing the user interface in Android usually requires a lot of code, especially when creating more complex layouts, for example, using RecyclerView. Compose is designed to greatly simplify the way you create a user interface.
This makes it easy to create isolated and reusable components, making it easy to create a new screen with existing elements. Helping you, as a developer, focus on creating a user-friendly user interface, instead of trying to control the View hierarchy and tame View and Fragment.
Simple application with Compose: Hello World
Let's look at the code for a simple “Hello World” application with Jetpack Compose.
class ComposeActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { CraneWrapper { MyApp() } } } @Composable fun MyApp() { MaterialTheme { Text(text = "Hello world!", style = +themeTextStyle { h3 }) } } }
In the
onCreate
method
onCreate
we set the contents of our application by calling
setContent
. This is a method that initializes a composite tree of widgets and wraps it in
FrameLayout
.
To make it work, we need to wrap our application in
CraneWrapper
and
MaterialTheme
.
CraneWrapper
is responsible for configuring providers for
Context
,
FocusManager
and
TextInputService
.
MaterialTheme
required to provide the colors, styles, and fonts of your widgets. With this in mind, we can add a
Text
component that will display our text on the screen in a specific style.
State introduction
Managing data flow and states can be a daunting task. To illustrate how easy it is with Compose, let's create a simple counter application.
To work with states, Jetpack Compose uses ideas from other modern UI frameworks, such as Flutter and React. There is a unidirectional and reactive data stream that causes your widget to be updated or "reassembled".
@Composable fun MyApp() { MaterialTheme { Counter() } } @Composable fun Counter() { val amount = +state { 0 } Column { Text(text = "Counter demo") Button(text = "Add", onClick = { amount.value++ }) Button(text = "Subtract", onClick = { amount.value-- }) Text(text = "Clicks: ${amount.value}") } }
In the example above, we add the “Add” and “Subtract” buttons along with a label that displays the current number of clicks. As you can see in the example below, by updating the “amount” state, widgets are intelligently recomposed when the state changes.
Launch demo applicationThe
amount
state is initialized using
+state { 0 }
. Trying to figure out what kind of witchcraft, I climbed into the source code. This is my opinion, although I am still not sure that I understand everything.
state {...}
creates an
Effect<
State <
T <
code>>
. The
Effect
class is a fuzzy class that contains a block of executable code that is executed positionally in the context of the composition. The
State
class contains one value with the
Model
type, essentially making this value observable. The + operator is a temporary operator that resolves the
State
from
Effect
.
Custom state models
Instead of using
+state {}
to create a single value model, we can also create a custom model using the @Model annotation. We can improve our application counter by dividing it into smaller widgets and passing the model to other widgets that update and display the status of this model.
@Model class CounterModel { var counter: Int = 0 var header = "Counter demo" fun add() { counter++ } fun subtract() { counter-- } }
Using the
@Model
annotation, the Compose Compiler plugin makes all the variables in your model observable so that they can be used to rebuild widgets. Let's update our widget to use
CounterModel
:
@Composable fun Counter(counterModel: CounterModel) { Column { CounterHeader(counterModel) AddSubtractButtons(counterModel) CounterLabel(counterModel) } } @Composable fun CounterHeader(counterModel: CounterModel) { Text(text = counterModel.header) } @Composable fun AddSubtractButtons(counterModel: CounterModel) { Button( text = "Add", onClick = { counterModel.add() }) Button( text = "Subtract", onClick = { counterModel.subtract() }) } @Composable fun CounterLabel(counterModel: CounterModel) { Text(text = "Clicks: ${counterModel.counter}") }
The only widget that makes up the counter application is now divided into several smaller composable widgets.
CounterModel
is passed to various widgets, either to display the state of the model, or to change the state of the model using the
add()
or
subtract()
functions.
No more view
It is important to understand that Jetpack Compose widgets do not use a view or fragment under the hood, these are just functions that are painted on the canvas. The Compose Compiler plugin handles all functions annotated with
@Composable
and automatically updates the UI hierarchy.
For example, the
Divider
widget consists of a
Padding
widget that contains a
DrawFillRect
widget. Looking at the source code of
DrawFillRect
, it becomes clear that he draws lines right on the canvas. All other widgets are implemented in the same way.
@Composable private fun DrawFillRect(brush: Brush) { Draw { canvas, parentSize -> val paint = Paint() brush.applyBrush(paint) canvas.drawRect(parentSize.toRect(), paint) } }
DrawFillRect source code that is used inside the Divider widget
If we look at the Layout Inspector by running one of the sample applications from Google, we’ll clearly see that there is no
View
or
ViewGroups
when launching the Android application from Compose. We see a
FrameLayout
containing the
CraneWrapper
that we created in the code, from there the Compose UI hierarchy is displayed on the screen.
Layout Inspector inspects the Jetpack Compose application.The absence of views also means that Jetpack Compose cannot use the currently available view, such as
android.widget.Button
, and must create all widgets from scratch. If you look, for example, at Flutter, which uses the same approach, you can see that this is hard work. This is one of the reasons why the Jetpack Compose will take time before it is ready for use in production.
All elements are widgets.
Just like Flutter, in Compose, all elements are widgets. More complex widgets were broken down into elementary widgets with clear responsibilities. Therefore, even padding, spacers, and so on are widgets. For example, if you want to add an indent around a button, simply wrap it in the padding widget:
Padding(padding = 16.dp) { Button(text = "Say hello", onClick = { ... }) }
Linking code to user interface
Connecting Kotlin code with UI widgets is very easy. For example, if you want to show a user interface that repeats or depends on some conditions. So, you can easily display a list of names, as shown below.
Column { listOf("John", "Julia", "Alice", "Mark").forEach { Text(text = it) } }
This is a really powerful feature, but you must be careful not to program too much logic at the user interface level.
Compatible with your Android applications
Compose is designed in such a way that you can add it to an existing application and gradually transfer some parts of your UI to a new framework. The examples above add a Jetpack Compose UI to a single activity. You can also embed Compose widgets into an existing XML layout using the
GenerateView
annotation:
@Composable @GenerateView fun Greeting(name: String) { }
Conclusion
I am delighted with Compose, because it reduces the growing suffering that I experience when developing for Android. It helps to be more flexible, focus on creating a user-friendly user interface, and clear responsibility also helps to avoid mistakes.
Compose has a long way to go, in my opinion, it can be used in production not earlier than in a year or two. However, I think that now is the right moment to take a look at the Jetpack Compose. The creators are actively looking for feedback, at this stage you can still make changes. All feedback will help improve this new framework.
Read my article
“Try Jetpack Compose today” to learn how to connect Compose pre-alpha. Also, I think it will be very interesting for you to watch the
video on the templates of the declarative interface with Google I / O.
I look forward to when I can use Compose in real Android applications!
That's all. We are looking forward to your comments and a great weekend!