Exploring Jetpack Compose: Button

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 start with the Button component, something that we’re likely to use in most of our applications. Let’s image that we’re creating a button for the authentication screen of our app – here we’re going to create a new Button component. The Button is a composable component – there are two functions which can be used to build this. The first takes the text (used to declare the text displayed on the button), onClick (used to handle click events on the button) and style (used to handle the style of the button) attributes.

@Composable
fun Button(
    text: String,
    onClick: (() -> Unit)? = null,
    style: ButtonStyle = ContainedButtonStyle()
)

We can see here that at a minimum we need to provide a text value for our Button, with the other attributes having default values assigned to them. let’s go ahead and create a simple button with some text and an onClick handler:

Button(text = "Authenticate", onClick = {
    // handle button click
})

In the preview section of Android Studio we can see our button displayed, as above. In some cases we may want to customise the actual content of the button a bit more, in these cases we may want to make use of the other Button function which allows us to pass in a collection of composable children in place of the text attribute:

@Composable
fun AuthenticationButton() {
    Button(onClick = {
        // handle button click
    }, children = {
        Text(text = "Authenticate")
    })
}

This would be useful in cases where you wish to display additional content inside of a button. Whilst the above example only displays a Text component – this would allow you to customise the Text component to suit the look and feel of your application, or even display an icon to the side of the text inside of the button.

Button styles

At the heart of each button style that we wish to use is the ButtonStyle class. Each style is essentially a function which is used to return an instance of the ButtonStyle class, with each class providing the required attributes for that style type and at the same time, enforcing others.

The base ButtonStyle class can be assigned to the style of the Button you are created, but if doing so then you’ll need to provide a value for at least the color and shape attributes at a minimum. The ButtonStyle is a great place for when you need complete control over the look and feel of the button component.

@Immutable
data class ButtonStyle(
    val color: Color,
    val shape: Shape,
    val border: Border? = null,
    val elevation: Dp = 0.dp,
    val paddings: EdgeInsets = ButtonPaddings,
    val textStyle: TextStyle? = null,
    val rippleColor: Color? = null
)

The Button component also provides us with a collection of helper functions which can be used to provide specific type of ButtonStyle instances. In most cases these will suffice in your applications, so let’s take a look at how each one of these can be used.


Text Button Style

A text button is essentially a button without a background shape or border, it only consists of the text component within the Button component.

When we want to compose a Text Button we use the same Button component, but instead using the TextButtonStyle for the style attribute. For the TextButtonStyle we can set the contentColor (which is the color of the text) or the shape (the shape of the button).

@Composable
fun AuthenticationButton() {
    Button(text = "Authenticate", onClick = {
        // handle button click
    }, style = TextButtonStyle(contentColor = Color.Cyan))
}

If we dive into the source code for the TextButtonStyle we can see that we can provide this shape and contentColor attribute. However, for the ButtonStyle that is being created there are a collection of defaults which are used for the style. For example the color (background color of the button), we cannot change these the TextButtonStyle enforces those stated values for the attributes – this makes sense for the color as we creating a Text Button, which should not have a background color assigned to it.

fun TextButtonStyle(
    shape: Shape = +themeShape { button },
    contentColor: Color? = +themeColor { primary }
) = ButtonStyle(
    color = Color.Transparent,
    shape = shape,
    paddings = TextButtonPaddings,
    textStyle = TextStyle(color = contentColor),
    rippleColor = contentColor
)

Contained Button Style

The contained button is the standard style of buttons that we often see in applications, it’s also the default style that is applied to the Button component if you not provide a style for use.

However, we can also provide an instance of the ContainedButtonStyle with our own attribute values for more specific styling. For the ContainedButtonStyle we can set the color (which is the color of the button itself), the elevation (the shape of the button), the shape (the shape of the button) and the rippleColor (the color of the ripple effect when pressed).

@Composable
fun AuthenticationButton() {
    Button(
        text = "Authenticate", onClick = {
            // handle button click
        }, style = ContainedButtonStyle(
            color = Color.White, 
            rippleColor = Color.DarkGray,
            elevation = Dp(4f)
        )
    )
}

If we dive into the source code for the ContainedButtonStyle then we can see how this style is composed. Each of the attributes we mentioned above have default values, using colors which are obtained from our application theme. Therefore, because the ContainedButtonStyle is the default button style, by default our button will be presented using this style whilst using our application theme in the process.

fun ContainedButtonStyle(
    color: Color = +themeColor { primary },
    shape: Shape = +themeShape { button },
    elevation: Dp = 2.dp,
    rippleColor: Color? = null
) = ButtonStyle(
    color = color,
    shape = shape,
    elevation = elevation,
    rippleColor = rippleColor
)

Outlined Button Style

The outline button style can be used to show a button similar to that of the Contained button, with the difference of a stroke being used to outline the body of the button.

When we want to compose an Outlined Button component we use the same Button component, instead using the OutlinedButtonStyle for the style attribute. For the OutlinedButtonStyle we can set the contentColor (which is the color of the text), the color (the color of the button), the border (the color used for the stroke border) and the elevation (the elevation for the button).

@Composable
fun AuthenticationButton() {
    Button(
        text = "Authenticate", onClick = {
            // handle button click
        }, style = OutlinedButtonStyle(
            color = Color.White, 
            contentColor = Color.DarkGray,
            border = Border(Color.DarkGray, Dp(4f)),
            elevation = Dp(4f)
        )
    )
}

If we dive into the source for the OutlinedButtonStyle then we can see how this style is composed. Each of the attributes we mentioned above have default values, using colors which are obtained from our application theme. Therefore we can pass in an instance of the OutlinedButtonStyle without any of these attribute values and our theme will be used to customise the style accordingly.

fun OutlinedButtonStyle(
    border: Border = Border(+themeColor { onSurface.copy(alpha = OutlinedStrokeOpacity) }, 1.dp),
    color: Color = +themeColor { surface },
    shape: Shape = +themeShape { button },
    elevation: Dp = 0.dp,
    contentColor: Color? = +themeColor { primary }
) = ButtonStyle(
    color = color,
    shape = shape,
    border = border,
    elevation = elevation,
    textStyle = TextStyle(color = contentColor),
    rippleColor = contentColor
)

From this post we’ve learnt how we can present Button components within our jetpack compose UI, along with how we can customise their look and feel with the use of the available ButtonStyle implementations.

If there are any questions on how to make use of the Button component then please do reach out. Otherwise, I’ll catch you in the next Jetpack compose post!

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