Jetpack Compose: Button Customizations

Jetpack Compose is Android’s modern UI toolkit designed to simplify and accelerate UI development. Among its many features, the Button composable stands out as a fundamental element for user interaction. However, the default Button might not always align with your app’s design aesthetics. Fortunately, Jetpack Compose offers extensive customization options, allowing you to tailor the appearance and behavior of your buttons to create a unique user experience. In this blog post, we will delve into the various ways you can customize buttons in Jetpack Compose.

Why Customize Buttons in Jetpack Compose?

Customizing buttons is essential for:

  • Branding Consistency: Ensures that buttons match your app’s overall design and brand identity.
  • Enhanced User Experience: Creates visually appealing and intuitive interfaces.
  • Improved Accessibility: Tailors button styles to meet accessibility standards, ensuring usability for all users.

Basic Button Customization

Let’s start with the fundamental customizations you can apply directly to the Button composable.

1. Text and Content

The simplest customization is changing the button’s text. You can also add leading and trailing icons to provide visual context.


import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.material.Icon
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite

@Composable
fun MyCustomButton() {
    Button(onClick = { /* Handle button click */ }) {
        Icon(imageVector = Icons.Filled.Favorite, contentDescription = "Favorite Icon")
        Text("Add to Favorites")
    }
}

@Preview
@Composable
fun PreviewMyCustomButton() {
    MyCustomButton()
}

2. Colors

You can modify the background and text colors of the button using the colors parameter.


import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.material.ButtonDefaults
import androidx.compose.ui.graphics.Color

@Composable
fun MyColoredButton() {
    Button(
        onClick = { /* Handle button click */ },
        colors = ButtonDefaults.buttonColors(
            backgroundColor = Color.Green,
            contentColor = Color.White
        )
    ) {
        Text("Save Changes")
    }
}

@Preview
@Composable
fun PreviewMyColoredButton() {
    MyColoredButton()
}

3. Shape and Elevation

The button’s shape can be altered using the shape parameter, and its elevation (shadow) can be adjusted for different states (e.g., pressed, enabled, disabled) using the elevation parameter.


import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.unit.dp
import androidx.compose.material.ButtonDefaults
import androidx.compose.ui.graphics.Color

@Composable
fun MyRoundedButton() {
    Button(
        onClick = { /* Handle button click */ },
        shape = RoundedCornerShape(16.dp),
        elevation = ButtonDefaults.elevation(
            defaultElevation = 6.dp,
            pressedElevation = 12.dp
        ),
        colors = ButtonDefaults.buttonColors(
            backgroundColor = Color.Blue,
            contentColor = Color.White
        )
    ) {
        Text("Continue")
    }
}

@Preview
@Composable
fun PreviewMyRoundedButton() {
    MyRoundedButton()
}

Advanced Button Customization

For more advanced customizations, you can create custom button styles using CompositionLocalProvider or by building a completely custom button composable.

1. Using CompositionLocalProvider

CompositionLocalProvider allows you to provide custom values for pre-defined composable attributes, affecting all child composables. This is useful for applying a consistent style across your app.


import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.material.LocalTextStyle
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.graphics.Color
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.sp
import androidx.compose.ui.text.font.FontWeight

@Composable
fun MyAppTheme(content: @Composable () -> Unit) {
    CompositionLocalProvider(
        LocalTextStyle provides TextStyle(
            fontSize = 18.sp,
            fontWeight = FontWeight.Bold,
        )
    ) {
        content()
    }
}

@Composable
fun MyThemedButton() {
    Button(onClick = { /* Handle button click */ }) {
        Text("Get Started")
    }
}

@Preview
@Composable
fun PreviewMyThemedButton() {
    MyAppTheme {
        MyThemedButton()
    }
}

2. Creating a Custom Button Composable

For maximum control, create a custom button composable. This approach allows you to define the layout, styling, and behavior of the button from scratch.


import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

@Composable
fun CustomButton(text: String, onClick: () -> Unit) {
    Box(
        modifier = Modifier
            .clickable(onClick = onClick)
            .background(Color.Magenta)
            .padding(16.dp),
        contentAlignment = Alignment.Center
    ) {
        Text(text = text, color = Color.White, fontSize = 20.sp)
    }
}

@Preview
@Composable
fun PreviewCustomButton() {
    CustomButton(text = "Click Me") {
        // Handle button click
    }
}

3. Styling Enabled/Disabled States

Handling enabled and disabled states effectively improves the usability of your app. You can apply different styles based on the enabled state of the button.


import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.material.ButtonDefaults
import androidx.compose.ui.graphics.Color

@Composable
fun MyStyledButton(enabled: Boolean = true) {
    Button(
        onClick = { /* Handle button click */ },
        enabled = enabled,
        colors = ButtonDefaults.buttonColors(
            backgroundColor = if (enabled) Color.Blue else Color.Gray,
            contentColor = Color.White
        )
    ) {
        Text("Submit")
    }
}

@Preview
@Composable
fun PreviewMyStyledButtonEnabled() {
    MyStyledButton(enabled = true)
}

@Preview
@Composable
fun PreviewMyStyledButtonDisabled() {
    MyStyledButton(enabled = false)
}

Conclusion

Customizing buttons in Jetpack Compose offers endless possibilities for creating visually appealing and functional UI elements. Whether you’re tweaking basic attributes like colors and shapes, applying consistent styles through CompositionLocalProvider, or crafting fully custom button composables, the power is in your hands. Experiment with these techniques to build buttons that elevate the user experience of your Android apps.