HomeAndroidConstraints and modifier order. Learn to use a psychological mannequin to… |...

Constraints and modifier order. Learn to use a psychological mannequin to… | by Jolanda Verhoef | Android Builders | Feb, 2023


Episode 3 of MAD Abilities: Compose Layouts and Modifiers

Within the earlier MAD Abilities article, you realized in regards to the three phases of Compose that rework knowledge into UI. We created a psychological mannequin to assist us purpose about our app’s design implementation. On this episode, we’ll use that psychological mannequin to be taught to purpose about modifier chaining and the way it influences the sizes of our composables.

You may also watch this text as a MAD Abilities video:

Keep in mind from final episode that we now have three phases of reworking knowledge into UI:

  1. Composition: What to point out
  2. Format: The place to position it
  3. Drawing: Tips on how to render it

Modifiers can have an effect on totally different phases. For instance, the measurement and padding modifier affect the scale and spacing of a composable through the structure section, and the clip modifier influences the form of a composable through the draw section:

Flow diagram of Data to Composition to Layout to Drawing to UI. Layout is annotated with size and padding, Drawing is annotated with clip.

We additionally know that we are able to add multiple modifier to a composable, creating a sequence. Nevertheless, it isn’t at all times obvious how the totally different modifiers in these chains have an effect on one another.

Let’s begin with some workouts. For every of the next code snippets, attempt to determine which possibility could be the outcome of executing that snippet, possibility A or B:

Downside 1

/* Copyright 2023 Google LLC. 
SPDX-License-Identifier: Apache-2.0 */

Picture(
painterResource(R.drawable.frag),
contentDescription = null,
Modifier
.fillMaxSize()
.measurement(50.dp)
)

Option A shows an image filling the height of the box, centered horizontally. Option B shows a small image, centered in the top left of the box.

Downside 2

/* Copyright 2023 Google LLC. 
SPDX-License-Identifier: Apache-2.0 */

Picture(
painterResource(R.drawable.frag),
contentDescription = null,
Modifier
.fillMaxSize()
.wrapContentSize()
.measurement(50.dp)
)

Option A shows an image filling the height of the box, centered horizontally. Option B shows a small image, centered in the box.

Downside 3

/* Copyright 2023 Google LLC. 
SPDX-License-Identifier: Apache-2.0 */

Picture(
painterResource(R.drawable.frag),
contentDescription = null,
Modifier
.clip(CircleShape)
.padding(10.dp)
.measurement(100.dp)
)

Option A shows a round image centered in the box. Option B shows a clipped off round image, centered in the box.

Not precisely positive in regards to the solutions? You’ve come to the precise place then! Proceed studying to know extra. (psst, the solutions are on the finish of the weblog publish)

To learn to purpose about modifier order, we must be taught in regards to the position of Constraints through the structure section.

Do not forget that within the final article we mentioned how the structure section follows a three-step algorithm to seek out every structure node’s width, top and x, y coordinate:

  1. Measure youngsters: A node measures its youngsters, if any.
  2. Determine personal measurement: Based mostly on these measurements, a node decides by itself measurement.
  3. Place youngsters: Every little one node is positioned relative to a node’s personal place.

Constraints assist discovering the precise sizes for our nodes through the first two steps of this algorithm. They’re minimal and most bounds for a node’s width and top. When the node decides on its measurement, its measured measurement ought to fall on this given measurement vary.

Constraints are handed down from mother or father to little one within the UI tree, throughout step one of the algorithm. When a mother or father node measures its youngsters, it gives these constraints to every little one to allow them to know the way huge or small they’re allowed to be. Then, when it decides its personal measurement, it additionally adheres to the constraints that have been handed in by its personal dad and mom.

Forms of constraints

Constraints might be bounded, indicating a minimal and a most width and top:

Container with arrows from 100 to 300 horizontally, and 100 to 200 vertically. The area that falls in those bounds is colored.
Bounded constraints

Constraints will also be unbounded, wherein case the node will not be constrained to any measurement. The utmost width and top bounds are then set to infinity:

Container that moves off the screen, with arrows from 0 to infinity both horizontally and vertically. The whole area is colored.
Unbounded constraints

Or constraints might be precise, asking the node to observe an actual measurement requirement. The minimal and most bounds are set to the identical worth:

Container with dots at 300 horizontally, and 200 vertically. The whole area is colored in a light color.
Actual constraints

In fact, mixtures of those are additionally legitimate, for instance bounding the width, whereas permitting for an unbounded most top, or setting an actual width however offering a bounded top:

Combos of bounded, unbounded, and precise widths and heights

To grasp how constraints are handed from mother or father to little one, and the way sizes are then resolved primarily based on these constraints, it’s finest to stroll by means of an instance. Nevertheless, that is a lot simpler to current in a video format, so I’d recommend you watch the chapter “an instance” of the MAD Abilities video:

By watching the video you must have an excellent understanding of how constraints have an effect on the scale of composables, and the way modifiers have an effect on these constraints. On this part we’ll take a better have a look at some particular modifiers and the way they impression constraints.

measurement modifier

Let’s have a look at the next UI tree, that must be rendered in a container of 300dp by 200dp. The constraints are bounded, permitting widths between 100dp and 300dp, and heights between 100dp and 200dp.

UI tree showing incoming constraints of 100 to 300 width, 100 to 200 height. Shows wrapper modifier node called size without specifying the size. The node wraps a generic layout node.

The measurement modifier adapts the incoming constraints to match the worth handed to it, for instance150dp:

Size is set to 150 and exact constraints of 150 by 150 are shown in a container.

However what if the requested measurement is just too small or too huge? That’s, what if the width and top are smaller than the smallest constraint sure, or bigger than the biggest constraint sure?

Size of 50 and size of 400 are set in the wrapper modifier node. The containers show how these sized do not fall in the constraints bounds.

On this case, the modifier will attempt to match the handed constraints as intently as it could possibly, whereas nonetheless adhering to the constraints handed in:

The containers show how the small size resolves to fixed constraints of 100 by 100, and the large size resolves to fixed constraints of 300 by 200.

This additionally explains why chaining a number of measurement modifiers doesn’t work. The primary measurement modifier will set each the minimal and most constraints to a hard and fast worth, and although the second measurement modifier requests a smaller or bigger measurement, it nonetheless wants to stick to the precise bounds handed in, so it won’t override these values:

size modifier with size 100 wrapping size modifier with size 50, showing how this still resolves in a size of 100, as the second modifier needs to adhere to the minimum bounds set by the first modfier.

requiredSize modifier

For those who do want your node to override the incoming constraints, you’ll be able to exchange the measurement modifier with one other modifier known as requiredSize. It should exchange the incoming constraints and go the scale you specify as an alternative, as precise bounds. Then, when the scale is handed again up the tree, the kid node can be centered within the accessible house:

Size modifier of 100 wraps requiredSize modifier of 50. Constraints are updated to exact constraints of 100, then set to exact constraints of 50. The layout reports a size of 50, but the requiredSize modifier reports the original size of 100. It centers the content in the available space.

width and top modifiers

In earlier examples we used a measurement modifier, that adapts each width and top of the constraints. Nevertheless, we are able to additionally exchange these with the width modifier, that units a fastened width however leaves the peak undecided. Or we are able to use the top modifier, that units a fastened top however leaves the width undecided:

sizeIn modifier

For those who want fine-grained management over the constraints, and wish to adapt them to your precise wants, you should utilize the sizeIn modifier:

Now that we realized about constraints and the way they affect measurements, let’s return to our authentic use instances and discover the precise options.

Downside 1

/* Copyright 2023 Google LLC. 
SPDX-License-Identifier: Apache-2.0 */

Picture(
painterResource(R.drawable.frag),
contentDescription = null,
Modifier
.fillMaxSize()
.measurement(50.dp)
)

Right here’s the answer:

Option A was right!
  • The fillMaxSize modifier adjustments the constraints to set each the minimal width and top to the utmost worth — 300dp in width, and 200dp in top.
  • So although the measurement modifier desires to make use of a measurement of 50dp, it nonetheless wants to stick to the incoming minimal constraints. And thus the scale modifier can even output the precise constraint bounds of 300 by 200, successfully ignoring the worth offered within the measurement modifier.
  • The Picture follows these bounds and reviews a measurement of 300 by 200, which is handed all the best way up.

Downside 2

/* Copyright 2023 Google LLC. 
SPDX-License-Identifier: Apache-2.0 */

Picture(
painterResource(R.drawable.frag),
contentDescription = null,
Modifier
.fillMaxSize()
.wrapContentSize()
.measurement(50.dp)
)

Right here’s the answer:

Option B was right!
  • The fillMaxSize modifier will nonetheless behave the identical, and adapt the constraints to set each the minimal width and top to the utmost worth — 300dp in width, and 200dp in top.
  • The wrapContentSize modifier resets the minimal constraints. So whereas fillMaxSize resulted in fastened constraints, wrapContentSize resets it again to bounded constraints. The next node can now take up the entire house once more, or be smaller than all the house.
  • The measurement modifier units the constraints to minimal and most bounds of 50.
  • The Picture resolves to a measurement of 50 by 50, and the measurement modifier forwards that.
  • The wrapContentSize modifier has a particular property. It takes its little one, and places it within the middle of the accessible minimal bounds that have been handed to it. The scale it communicates to its dad and mom is thus equal to the minimal bounds that have been handed into it.

By combining simply three modifiers, we have been in a position to outline a measurement for our composable and middle it in its mother or father!

Downside 3

/* Copyright 2023 Google LLC. 
SPDX-License-Identifier: Apache-2.0 */

Picture(
painterResource(R.drawable.frag),
contentDescription = null,
Modifier
.clip(CircleShape)
.padding(10.dp)
.measurement(100.dp)
)

Right here’s the answer:

Option B was right!
  • The clip modifier doesn’t change the constraints.
  • The padding modifier lowers the utmost constraints.
  • The measurement modifier units all constraints to 100dp.
  • The Picture adheres to these constraints and reviews a measurement of 100 by 100dp.
  • The padding modifier provides 10dp on all sizes, so it will increase the reported width and top by 20dp.
  • Now within the drawing section, the clip modifier acts on a canvas of 120 by 120dp. Thus it creates a circle masks of that measurement.
  • The padding modifier then insets its content material by 10dp on all sizes, so it lowers the canvas measurement to 100 by 100dp.
  • The Picture is then drawn in that canvas. You possibly can see that the picture is clipped primarily based on the unique circle of 120dp, and thus we see a non-round outcome.

Wow, that was so much! You realized about constraints, and used them to purpose about modifiers, learn how to organize them, and measurements.

Within the subsequent publish we’ll present you the way you should utilize this info to start out implementing your individual customized structure.

Bought any questions? Depart a remark beneath or use the #MADCompose hashtag on Twitter and we’ll tackle your questions in our upcoming reside Q&A for the collection on March ninth. Keep tuned!

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments