Exploring Jetpack Compose: Container

Within Android Studio 4.0 Canary 1 we can start exploring Jetpack compose, a new way to build the UI for your android applications in a declarative manner. To get started with jetpack compose, there is a great tutorial on the official developer site. In this series of articles I want to dive into each of the components that are available, exploring how we can utilise each of them within our applications.


In this article we’re going to look with at the Container component. When it comes to containing a single view within a parent container for use within our layout we have a Composable Container component, located in the androidx.ui.layout package. This parent container can be used to add specific constraints to how our child component is laid out, as well as applying any styling that we may not want to add directly to the child component itself.

@Composable
fun Container(
    modifier: Modifier = Modifier.None,
    padding: EdgeInsets = EdgeInsets(0.dp),
    alignment: Alignment = Alignment.Center,
    expanded: Boolean = false,
    constraints: DpConstraints = DpConstraints(),
    width: Dp? = null,
    height: Dp? = null,
    children: @Composable() () -> Unit
) 

As we can see above, the Container holds a collection of properties which can be used to control the display of both the container and the child that is contained inside of it. When it comes to using the container, we need to wrap our child view like so:

Container {
    Text(text = "Hello there")
}

As you can see, this isn’t currently adding any extra value to our layout. Let’s take a look at each of the properties within our Container that we can utilise:

padding

This property can be used to add padding to the container. This makes the container grow in size – which can be useful for when you need a container to be larger in some cases, for example when a component needs to be larger in size for visual purposes or when a touch state on a component needs to be larger.

When assigning a value for the padding property we need to pass in an androidx.ui.layout.EdgeInsets reference. This EdgeInsets class takes a value for each of the left, top, right and bottom insets in the form of a Dp reference. We can then use this EdgeInsets value to add some padding to our container, as seen in the above preview image.

Container(padding = EdgeInsets(Dp(16f))) {
    Text(text = "Hello there")
}

width & height

If we need to set a specific width and/or height for our container, then we can do so by using the width and height properties. When these properties are provided with values, the dimensions of our container are set accordingly to the fixed size. Note, these values can be set independently – if not set, then each one will wrap the content of the child components.

When assigning a value for either the width or height property we need to pass in a Dp reference, which declares the size to be given to each dimension.

Container(
    width = Dp(100f),
    height = Dp(100f)
) {
    Text(text = "Hello there")
}

alignment

If the child content of our container does not fill the entire space of our container, then we may want to align the content in some way – if the container width / height has been set as above, then this is a scenario where this may apply. Using the alignment property we can declare how child components should be aligned within the container.

Container(
    width = Dp(100f),
    height = Dp(60f),
    alignment = Alignment.BottomRight
) {
    Text(text = "Hello there")
}

When assigning a value to the alignment property we can use one of the following values to declare how the child should be positioned:

  • TopLeft
  • TopCenter
  • TopRight
  • CenterLeft
  • Center
  • CenterRight
  • BottomLeft
  • BottomCenter
  • BottomRight

expanded

In some cases we may want our container to fill all of the space that is available to it on screen. When this is the case, we can make use of the expanded property on our container. When expanding a container, it’s also important to think about the child component contained within it – if the child component does not already fill the space of the container then the alignment property will be used to position it inside.

Container(
    alignment = Alignment.BottomRight,
    expanded = true
) {
    Text(text = "Hello there")
}

constraints

There may be times where we wish to apply certain constraints to our container. This may be useful in cases where minimum / maximum values are required for the height and width properties of our container. In this case we can make use of the constraints property to apply these kind of rules for our container component.

data class DpConstraints(
    val minWidth: Dp = 0.dp,
    val maxWidth: Dp = Dp.Infinity,
    val minHeight: Dp = 0.dp,
    val maxHeight: Dp = Dp.Infinity
)

When providing some constraints for our container, we can pass a reference to the DpConstraints class with the constraints that we wish to enforce. For example, the following would allow me to apply a minimum width and height to my container, whilst relying on the default values for the maximum dimensions in the DpConstraints class.

DpConstraints(minWidth = Dp(200f), minHeight = Dp(200f))

Alongside this, there are also some helper functions that can be used to instantiate new instances of the DpConstraints class. To begin with we have the tightConstraints function.

DpConstraints.tightConstraints(width = Dp(200f), height = Dp(200f))

When using this we need to pass a value for both the width and height – each of these will then be used for the corresponding minimum and maximum values of the constraints, as shown in the source code for the function:

fun tightConstraints(width: Dp, height: Dp) = 
    DpConstraints(width, width, height, height)

Alternatively there is a tightConstraintsForWidth function which can be used to enforce matching constraints for only the width properties of the container. This means that the minWidth and maxWidth properties will both be assigned with the given Dp value.

fun tightConstraintsForWidth(width: Dp) = DpConstraints(
    minWidth = width,
    maxWidth = width,
    minHeight = 0.dp,
    maxHeight = Dp.Infinity
)

Finally we have the tightConstraintsForHeight function which behaves in a similar way as the tightConstraintsForWidth function, except applying the given value to to minHeight and maxHeight properties.

fun tightConstraintsForHeight(height: Dp) = DpConstraints(
    minWidth = 0.dp,
    maxWidth = Dp.Infinity,
    minHeight = height,
    maxHeight = height
)

modifier

Using the androidx.ui.core.Modifier interface allows you to perform modifications on child components of the container. These modifications can be performed as the chain of children is iterated through, meaning that whatever is being modified can be accumulated and then applied for the next child in the iteration process. It’s likely for now that you may not need to utilise this, but we’ll take a look at it in a following article as it applies to a lot of components in jetpack compose.


Throughout this article we’ve dived into all the different parts of the Container component and how they can be used to control its look and feel. Whilst we’ve only looked at some introductory usage of these properties, now that we know what is available here and how it can be used, this gives us to knowledge we need for what Container has to offer.

Whilst Jetpack Compose is still in preview, some of these details are likely to change. But in the meantime if you have any thoughts or questions, please feel free to reach out!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s