Jetpack Compose UI (part 3of3)

We now get into the details of how to build a UI using Jetpack Compose.

Deprecated: You can now just download Android Studio 4.0+ to build with Compose

Read this only for a historical reference. Otherwise you are just waisting your time :-)

In the first two articles we got to the point where everyone can add code to MyComposeActivity.kt and see the effects in the emulator.

Please review those articles and post question if you have any issues. We need everyone to be on the same page. I assume you know Kotlin and higher order functional programming. I personally have not coded in Java for over three years … and love Protocol Programming in Swift and Functional Programming in Kotlin.

Before we start building our UIs lets establish a little background. Why do we need Jetpack Compose? The Android UI Toolkit Team reviews some of the regrets and bad decisions they made in the last 10 years and how Jetpack Compose is their hope of fixing them. They aim to providing developers a new wonderful system for building UIs. The major point they are trying to solve is the management of UI state.

Ideas behind Jetpack Compose:

  • Concise and Idiomatic Kotlin — Built with the benefits that Kotlin brings
  • Declarative — Fully declarative for defining UI components
  • Compatible — Compatible with existing views
  • Enable Beautiful Apps — Designed with Material Design
  • Accelerate Development — writing less code and using tools
Jetpack Compose

They draw inspiration from other declarative UI toolkits. http://intelligiblebabble.com/content-on-declarative-ui/

Jetpack Compose First Principles (background info): http://intelligiblebabble.com/compose-from-first-principles/

  • UIs are Trees — Programming model and rendering function to describe how that tree will change over time.
  • UI as a Transform Function — Take our model (data structure) and write an extension function with a receiver to render the UI. Building a framework where “decomposition [or factoring of common bits of UI logic into functions] is a critically important feature.”
  • Positional Memoization — Cache and reference each node in the same exact order every time the function is executed and update using Groups.
  • State — The state is only the concern of the UI and will be held in an instance of “State”.
  • The @Composable Annotation — tells the compiler to generate all the boilerplate code for us.

When “composing” a UI in our MyComposeActivity.kt. We need to start thinking in a “Composable” mindset. As someone who has coded in Flutter these concepts are familiar. https://flutter.dev/docs/development/ui/widgets-intro

NOTE: This new way of building Composable UI elements is completely separate / disjoint from the current way. These Composable elements do not rely at all on View as they draw directly to the Canvas using drawRec(). So a @Compose Button() and a View Button() have no relation! → Because Jetpack Compose does not use View in any way all View components can be completely removed once everyone has made the transition. Till that point we can mix these two by calling @Composable from an Activity like we are in this example or we can embed it into an existing XML layout using the ```GenerateView``` annotation.

We no longer use XML to build the UI but we use Composable functions which are the building blocks (smallest UI units) to build larger Composable functions in Jetpack Compose. We can think of this as a Tree. Let’s look at our current “MyComposeActivity.kt” file:

On line 18 we are “building” the UI. CraneWrapper is a Composable that hosts the compose tree, adds UX functionality and also allows us to set the MaterialTheme (line 23) which is needed to provide colors, styles and fonts to our widgets.

We see our code logic and style are mixed. This is a very “Component” way of thinking. This is nice because we don’t have to go running around looking through a ton of files just to make a small change and can set the state of the UI depending on the state of a count .

We all have the demo running … so try playing around with this. Update MyComposeActivity.kt

val style = arrayOf(themeTextStyle {h1} , themeTextStyle {h2}, themeTextStyle {h3})MaterialTheme {
for(i in 0..2) {
Text(text = "Hello world!", style = +style[i])
}
}

This will render the text three time with increasing header size one on top of each other.

Layout our scaffolding with Jetpack Compose

Just as a note: There is no real documentation on how the Jetpack Compose layout components work but we can look at the Flutter components and reason between the two. https://flutter.dev/docs/development/ui/layout

The Jetpack Compose Layout components source code is here and we can think about which components to use: https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/ui/layout/src/main/java/androidx/ui/layout

We have some nice layout components: Align.kt , AspectRatio.kt, ConstrainedBox.kt, Container.kt, DpConstraints.kt, Flex.kt, Padding.kt, Scroller.kt, Spacer.kt, Stack.kt, Wrap.kt.

Start by pulling everything out of MyApp() function. Remember that our demo is inside the Material Design menu so all our components must be wrapped MaterialTheme {}. Don’t feel overwhelmed by all these layout components. we have the source at the bottom and it will all make sense when we describe it.

Here is a list of some layout components with a link to the source **which has examples of usage**:

Rows:

Column:

Alignment, Spacer & Padding:

We will build a layout that places three items in a row across the top, three columns of three items in the middle and a row of three items along the bottom. The code would look like this

… remembering Padding goes all the way around and Spacer is just a filler.

Components for our UI with Jetpack Compose — As you read this section remember the KeyPoint: Composable Functions are defined in terms of other Composable Functions.

To build a UI just call @Composable component functions and wrap that in a Layout Composable function.

List of Material @Composable Components https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/ui/material/src/main/java/androidx/ui/material/

Here are the source with usage. These components are self explanatory by their names.

  1. AppBar.kt, Button.kt, Checkbox.kt, Divider.kt. FloatingActionButton.kt, MaterialTheme.kt, ProgressIndicator.kt, RadioButton.kt, Switch.kt, Text.kt
  2. borders/, clip/, ripple/, surface/

We are going to take our components and place them in our layout

// Example components ...
@Composable
fun myText() {
Text(text = "Hello world!", style = +themeTextStyle { h1 })
}
@Composable
fun myButtonWithState() {
val amount = +state { 0 }
Button(text = "ButCount ${amount.value}", onClick = { amount.value++ })
}
@Composable
fun PurpleSquare() {
Container(width = BigSize, height = BigSize) {
DrawRectangle(color = Color(0xFF6200EE.toInt()))
}
}

@Composable
fun CyanSquare() {
Container(width = SmallSize, height = SmallSize) {
DrawRectangle(color = Color(0xFF03DAC6.toInt()))
}
}
private val SmallSize = 24.dp
private val BigSize = 48.dp

All we do is interleave our components with our layout. So we get this.

https://gitlab.com/snippets/1863017

We just take this file and replace it with our MyComposeActivity.kt and run the demo.

Layout with Jetpack Compose

If we start thinking this is an easy way to build a UI then we should try rotating the phone. It’s not quite smoke and mirrors, it’s just not really usable. We look forward to the day we can use ConstraintLayout and MotionLayout with Jetpack Compose.

UI with Layout and Components

Apologies, still trying to get the Checkbox and Switch working. Will update the post when they are active. KT

State management for Jetpack Compose

Now that we have our layout and components (buttons etc …) what about state. We earlier said the most important aspect of Jetpack Compose is the way it handles state.

One important point is that data is always passed down from the parent and events are always passed up from the widgets.

This makes it very easy to reason about what is happening.

As we can see myButton component contains its own state and we can add a little code that a click of the button increase the count also changing the color.

val amount = +state { 0 }
Composable Button with State

We can also use the @model annotation to build a model class to hold state. Let’s add this model to our FAB and keep track of FAB clicks.

@Model
class FABCounterModel {
var counterFAB: Int = 0
fun add() { counterFAB++ }
fun subtract() { counterFAB-- }
}

Here is the final version of MyComposeActivity.kt with the model added.

Replace your current version with this version, run the demo and play with the code. It should be fairly easy to follow.

Understanding Jetpack Compose.

The hope is we now have a good understanding of how things work. Jetpack Compose has a long way to go but we will keep learning it as is evolves.

To get a different perspective on declarative programming you might want to check out this comparison of Jetpack Compose and SwiftUI.

Post questions and I will try to answer them in a timely fashion. But I am writing a new post on Swift for TensorFlow and it is a bear of a project.

Get the Medium app