Jetpack Compose : State & Constraint Layout

This article is all about how we can create declarative native user interface in Android. It covers an important concept of Compose:State and explains how to render UI with the help of constraint layout using compose.

Introduction

Jetpack Compose is basically a modern UI toolkit that simplifies and accelerates UI development on Android and is based on Google’s Material Design UI. This toolkit actually eliminates the need to design the layout in a separate XML file. It has several benefits that plays a major role in rolling up the development. Android Developers

Compose is an absolute shift in Android UI development, but it also majorly simplifies many of the challenges that the legacy UI system already has. Many applications nowadays uses SDUI to render the UI as it provides more control over the displayed content in the applications.Compose provides a native UI platform in order to build apps through SDUI.

Benefits

  1. Concise in nature
  2. Accelerates Development
  3. Compatibility with existing views & widgets
  4. Unidirectional Data Flow
  5. Helps in implementing Server Driven UI (SDUI)

Prerequisite

  • Android Development
  • Constraint Layout

Composable Functions

In order to create our UI ,we’ve to write function with composable annotation as below:

There are number of widgets with a lot of parameters to customize UI as required.The properties of a parameter (such as modifier, textStyle, width, height,border etc) on a widget applies sequentially in a builder pattern.

Compose: State

So state is any value that is supposed to have change in it. For instance, LiveData is nothing but a state that the UI observes for any change.

Those values of state includes value of LiveData instance,property of a class,room database entity state, jSON update from API call etc. And as soon as the state changes, we need to update the UI to reflect those changes to user.

Get Started

The above function has a mutable property counter ,whose state is supposed to be changed over time.Initially, by default the property holds a value i.e 1 an integer. As the user clicks on the Text field, the value of the counter gets incremented.And the function gets re-invoked to update the state,as the function gets executed again,it doesn’t reset the value back to 1 because of the remember {}expression.

The remember{ } lambda expression is again a composable function that remember the value produced by calculation. Calculation will only be evaluated during the composition. Recomposition will always return the value produced by composition. In a nutshell, it maintains the last updated value of the property.

Recomposition is the process of updating the UI as a result of a State or Data Change that a Composable is using to display. During recomposition, Compose understands which data each Composable uses and only updates the UI components that have changed. Whereas,the rest of the Composables are skipped.

In the above function,every time the user clicks on the text,the function gets re-invoked in order to update the UI state.

As we call the function inside the setContent of onCreate() function in our activity. We can see the state of the text is getting updated by incrementing the value.

Here we are now able to maintain and update the state of a mutable property.

On a broader picture,we can consider an API call which returns some response data that gets updated.We are storing the response in a mutable list that is enclosed within remember function.We just need to update the value of the mutable list, as we get the updated response from the server,it automatically re-invoke our composable function and further update the UI state.

Constraint Layout

In this example, we will be creating a very basic login page UI. There are few basic steps involved in creating constraint layouts, which are as follows:

Step 1) Create a constraint set that includes references of all the UI widgets that are being rendered.

createRefFor(“YOUR_VIEW_ID”) is used to create a reference to denote the particular widget. createRefs() can also be used to create references in a single line of code.

Step 2) Give constraints to all the widgets with respect to each other/parent layout/guideline etc. By passing the widget reference to the constrain(“YOUR_VIEW_ID”) function and setting the properties as required, we can set the constraints.

Step 3) Finally we can add the constraints to the constraint layout, where we can specify the UI widgets, set there properties,modifiers, provide them there respective layout id’s.

Step 4) We can create guidelines to constrain the views as below:

Then use the guideLine1 object to constrain any of the view by specifying link to the guideline within the constrain scope.

Step 5) In order to arrange the views by chaining them together vertically or horizontally, we can use chains as below:

We can specify multiple views that we want to chain them together in the respective layout, along with the chain style.

@Composable
fun RenderLayout() {
val constraints = ConstraintSet {
//References of widgets in the layout
val title = createRefFor("tvTitle")
val logo = createRefFor("ivLogo")
val input1 = createRefFor("etInput1")
val input2 = createRefFor("etInput2")
val button = createRefFor("btnSubmit")

//guideline
val guideLine1 = createGuidelineFromTop(0.5f)

//set constraints to the widgets
constrain(title)
{
top.linkTo(logo.bottom)
start.linkTo(parent.start)
end.linkTo(parent.end)
width = Dimension.fillToConstraints
height = Dimension.wrapContent
}

constrain(logo)
{
top.linkTo(parent.top)
start.linkTo(parent.start)
end.linkTo(parent.end)
bottom.linkTo(title.top)
width = Dimension.value(150.dp)
height = Dimension.value(150.dp)
}

constrain(input1)
{
bottom.linkTo(guideLine1)
start.linkTo(parent.start)
end.linkTo(parent.end)
width = Dimension.fillToConstraints
height = Dimension.wrapContent
}

constrain(input2)
{
top.linkTo(guideLine1)
start.linkTo(parent.start)
end.linkTo(parent.end)
width = Dimension.fillToConstraints
height = Dimension.wrapContent
}

constrain(button)
{
start.linkTo(parent.start)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
width = Dimension.fillToConstraints
height = Dimension.wrapContent
}

createVerticalChain(title,logo,chainStyle = ChainStyle.Spread)

}
//Adding constraints in the constraint layout
ConstraintLayout
(
constraints, modifier = Modifier
.fillMaxSize()
) {
Text(
text = "CONSTRAINTS",
textAlign = TextAlign.Center,
fontWeight = FontWeight.Bold,
style = TextStyle(
fontSize = 24.sp,
color = Color.DarkGray,
fontFamily = FontFamily(Font(R.font.cabin_semicondensed_bold))
),
modifier = Modifier
.padding(100.dp)
.layoutId("tvTitle")
)

Image(
painter = painterResource(id = R.drawable.ic_baseline_content_paste_24),
contentDescription = "Content",
modifier = Modifier
.padding(20.dp)
.layoutId("ivLogo")
)
TextField(
value = "",
onValueChange = {},
label = {
Text("Username")
},
modifier = Modifier
.layoutId("etInput1")
.padding(start = 10.dp, end = 10.dp)
)
TextField(
value = "",
onValueChange = {},
label = { Text("Password") },
modifier = Modifier
.layoutId("etInput2")
.padding(start = 10.dp, end = 10.dp)

)
Button(
onClick = { Toast.makeText(this, "Submit", Toast.LENGTH_SHORT).show() },
modifier = Modifier
.layoutId("btnSubmit")
)
{
Text("SUBMIT")
}
}
}

Final Output:

In this post we’ve understood the concept of state in compose as well as how to create constraint layout and give constraints to the view within our jetpack compose UI. Along with, how we can customize their look and feel with the use of the modifiers and other properties of compose library.

If there are any queries, questions please do reach out. I am still learning and exploring, so if you have any suggestions or opinions, please let me know so I can improve my work.

Thanks *

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store