Designing Custom Buttons, Shapes, and UI Components in Android
Course: Android App Development
Topic: Designing Custom Buttons, Shapes, and UI Components
Course Link: Android App Development Course
Demo Link: Register for a Course Demo
Website: JustAcademy
1. Introduction
Designing custom buttons, shapes, and UI components is an important part of Android application development. A well-designed interface helps users understand available actions, navigate screens, and interact with application features easily.
In Jetpack Compose, UI components are created using composable functions. Developers can customize their appearance using modifiers, shapes, colors, borders, elevation, typography, icons, and interaction states. Material 3 provides ready-made components that can be customized or wrapped in reusable composables to establish a consistent design system.
For example, an application may require a rounded blue login button, a card with selected rounded corners, a circular profile image, or a reusable product component. Rather than implementing every element independently, developers can create reusable composables and share them across screens.
Why Custom UI Components Matter
- They create a consistent visual identity for the application.
- They improve usability by making actions recognizable.
- They reduce duplicated UI code through reusable composables.
- They support branding through custom colors, shapes, and typography.
- They make applications easier to maintain and extend.
- They allow visual feedback for pressed, disabled, selected, and focused states.
- They help build interfaces that adapt to different screen sizes and accessibility settings.
2. Learning Objectives
After completing this topic, learners should be able to:
- Understand custom UI component design in Jetpack Compose.
- Create reusable buttons with custom colors, shapes, and text.
- Choose between Material buttons and fully custom clickable components.
- Apply rounded, circular, cut-corner, and custom shapes.
- Use modifiers to control size, spacing, backgrounds, clipping, and borders.
- Build reusable cards, badges, profile elements, and action components.
- Manage click events and enabled or disabled states.
- Use Compose state to display selected and unselected UI states.
- Apply accessibility and responsive design practices.
- Troubleshoot common component styling and interaction issues.
3. Understanding UI Components in Jetpack Compose
A UI component is an individual building block of an application's interface. It can be a simple text label or a complex reusable element containing images, buttons, and other composables.
| Component Type |
Examples |
Purpose |
| Action components |
Button, IconButton, FloatingActionButton |
Allow users to perform actions. |
| Container components |
Card, Surface, Box |
Group and organize content. |
| Input components |
TextField, Checkbox, Switch |
Collect input or preferences. |
| Display components |
Text, Image, Icon |
Present information visually. |
| Feedback components |
Snackbar, AlertDialog, ProgressIndicator |
Communicate status and results. |
| Navigation components |
NavigationBar, NavigationDrawer |
Help users move between destinations. |
| Custom components |
ProductCard, PrimaryActionButton, ProfileBadge |
Implement application-specific design patterns. |
4. Creating a Basic Custom Button
Jetpack Compose provides the Material 3 Button composable. It supports click events, enabled states, custom shapes, colors, borders, and content padding.
Example: Basic Button
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@Composable
fun BasicCustomButton(
onClick: () -> Unit
) {
Button(
onClick = onClick
) {
Text(text = "Continue")
}
}
The onClick parameter receives a function that runs when the button is activated. The content block defines what appears inside the button.
Important Button Parameters
| Parameter |
Description |
onClick |
Function executed when the user activates the button. |
modifier |
Controls layout, size, positioning, and other modifier-based behavior. |
enabled |
Controls whether the button can be activated. |
shape |
Defines the button's outline and corner geometry. |
colors |
Defines container and content colors for different states. |
border |
Adds an optional outline around the button. |
elevation |
Controls the button's elevation behavior when supported. |
contentPadding |
Controls spacing around the button's content. |
5. Designing Buttons with Custom Colors
Custom colors allow buttons to match an application's brand. In Material 3, use ButtonDefaults.buttonColors() to configure the container and content colors.
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
@Composable
fun BrandButton(
onClick: () -> Unit
) {
Button(
onClick = onClick,
colors = ButtonDefaults.buttonColors(
containerColor = Color(0xFF2457C5),
contentColor = Color.White,
disabledContainerColor = Color(0xFFBDBDBD),
disabledContentColor = Color(0xFF666666)
)
) {
Text("Get Started")
}
}
Hexadecimal color values use the format 0xAARRGGBB, where AA represents alpha and RR, GG, and BB represent red, green, and blue. For a fully opaque color, the alpha component is typically FF.
Best Practices for Button Colors
- Use a prominent color for the primary action.
- Choose secondary colors for less important actions.
- Ensure text remains readable against the button background.
- Provide clear disabled-state styling.
- Prefer theme color roles for applications that support light and dark themes.
6. Creating Rounded and Pill-Shaped Buttons
Button shapes communicate style and help establish a consistent interface. Rounded buttons are common in modern Android applications, while pill-shaped buttons are useful for prominent actions and filter controls.
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
@Composable
fun RoundedActionButton(
onClick: () -> Unit
) {
Button(
onClick = onClick,
shape = RoundedCornerShape(12.dp)
) {
Text("Save Changes")
}
}
@Composable
fun PillActionButton(
onClick: () -> Unit
) {
Button(
onClick = onClick,
shape = RoundedCornerShape(50)
) {
Text("Explore Courses")
}
}
RoundedCornerShape(12.dp) defines rounded corners with a fixed radius. RoundedCornerShape(50) uses a percentage-based corner size, producing strongly rounded corners relative to the component's dimensions.
7. Using Different Shape Types
Jetpack Compose provides shape classes that can be applied to Material components or used with modifiers such as clip, background, and border.
| Shape |
Description |
Typical Use |
RoundedCornerShape |
Creates rounded corners. |
Buttons, cards, input containers. |
CircleShape |
Creates a circular shape when the element has equal dimensions. |
Profile images, circular action buttons. |
RectangleShape |
Creates a rectangular shape without rounded corners. |
Full-width surfaces and edge-to-edge containers. |
CutCornerShape |
Creates corners with angled cuts. |
Distinctive cards and decorative components. |
GenericShape |
Allows a shape to be defined using a custom path. |
Specialized badges and decorative layouts. |
Example: Cut-Corner Card
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun CutCornerCard() {
Card(
modifier = Modifier.padding(12.dp),
shape = CutCornerShape(16.dp)
) {
Text(
text = "Special Offer",
modifier = Modifier.padding(20.dp)
)
}
}
Choose shapes according to the component's purpose. A decorative shape should not make the control difficult to recognize or use.
8. Customizing Buttons with Borders
Outlined buttons are useful when an action should be visible without using a solid background. Material 3 provides OutlinedButton, which follows Material interaction and accessibility conventions.
import androidx.compose.foundation.BorderStroke
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
@Composable
fun CustomOutlinedButton(
onClick: () -> Unit
) {
OutlinedButton(
onClick = onClick,
border = BorderStroke(
width = 1.dp,
color = Color(0xFF2457C5)
)
) {
Text("View Details")
}
}
Use a border to establish the component's boundary without making it visually heavier than the primary action. If you need a completely custom border or gradient, consider whether wrapping a Material component or creating a custom component is more suitable.
9. Creating Icon Buttons
An icon button displays an icon that represents an action. It is useful for compact controls such as search, edit, favorite, close, and settings.
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
@Composable
fun FavoriteIconButton(
onClick: () -> Unit
) {
IconButton(onClick = onClick) {
Icon(
imageVector = Icons.Filled.Favorite,
contentDescription = "Add to favorites"
)
}
}
The example requires an appropriate Compose Material icons dependency if the selected icon is not already available in the project. Always provide a meaningful content description for icon-only actions. If an icon is purely decorative and its meaning is already conveyed by adjacent text, use suitable semantics to avoid redundant announcements.
10. Designing Buttons with Icons and Text
A button can combine an icon and text to communicate an action clearly. This pattern is useful for downloads, uploads, payments, and navigation.
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Download
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.unit.dp
@Composable
fun DownloadButton(
onClick: () -> Unit
) {
Button(onClick = onClick) {
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Filled.Download,
contentDescription = null
)
Text("Download")
}
}
}
The icon uses a null content description because the adjacent button text already communicates the action. Ensure the selected icon is available in the configured icon dependency.
11. Understanding Modifiers for Custom Components
Modifiers are fundamental to Compose UI design. They control how components are measured, positioned, drawn, clipped, and interacted with.
| Modifier |
Purpose |
Example |
padding() |
Adds space around or inside content, depending on placement. |
Modifier.padding(16.dp) |
fillMaxWidth() |
Requests the available width from the parent. |
Modifier.fillMaxWidth() |
size() |
Sets preferred width and height. |
Modifier.size(48.dp) |
background() |
Draws a background behind content. |
Modifier.background(Color.Blue) |
clip() |
Clips content and drawing to a shape. |
Modifier.clip(CircleShape) |
border() |
Draws a border around the component. |
Modifier.border(1.dp, Color.Gray) |
clickable() |
Makes a component respond to click interactions. |
Modifier.clickable { } |
alpha() |
Adjusts the transparency of the composable's drawing. |
Modifier.alpha(0.5f) |
Example: Custom Surface with a Shape
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun InformationBadge() {
Box(
modifier = Modifier
.background(
color = MaterialTheme.colorScheme.secondaryContainer,
shape = RoundedCornerShape(10.dp)
)
.padding(
horizontal = 12.dp,
vertical = 8.dp
)
) {
Text(
text = "New",
color = MaterialTheme.colorScheme.onSecondaryContainer
)
}
}
Modifier order matters. In this example, the background is drawn before the padding is applied, so the background includes the padded area. Changing the order can change the visible size and placement of the background.
12. Creating a Reusable Custom Button
Instead of repeating button configuration throughout the application, create a reusable composable that applies the brand's standard appearance. This reduces duplication and makes future design updates easier.
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun PrimaryActionButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true
) {
Button(
onClick = onClick,
modifier = modifier,
enabled = enabled,
shape = RoundedCornerShape(12.dp),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary
)
) {
Text(
text = text,
style = MaterialTheme.typography.labelLarge
)
}
}
Using the Reusable Button
@Composable
fun RegistrationActions(
onRegister: () -> Unit,
onCancel: () -> Unit
) {
PrimaryActionButton(
text = "Register",
onClick = onRegister
)
PrimaryActionButton(
text = "Cancel",
onClick = onCancel,
enabled = false
)
}
This example demonstrates component reuse and the enabled state. In a real screen, a cancel action would usually be implemented as a separate secondary button rather than reusing the primary style unchanged.
13. Building a Custom Card Component
A custom card can combine text, images, badges, and actions into one reusable UI element. For example, a course card may display a course title, short description, and a button to view details.
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun CourseInformationCard(
title: String,
description: String,
modifier: Modifier = Modifier
) {
Card(
modifier = modifier.fillMaxWidth(),
shape = MaterialTheme.shapes.medium
) {
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = title,
style = MaterialTheme.typography.titleMedium
)
Text(
text = description,
style = MaterialTheme.typography.bodyMedium
)
}
}
}
Because the card accepts its title and description as parameters, it can display different information without duplicating its layout code.
14. Creating Custom Shapes with GenericShape
For specialized interfaces, Compose supports custom shapes through GenericShape. A custom shape can be defined with a drawing path and reused for clipping or backgrounds.
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.GenericShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
val TicketShape = GenericShape { size, _ ->
moveTo(0f, 0f)
lineTo(size.width, 0f)
lineTo(size.width, size.height * 0.8f)
lineTo(size.width * 0.5f, size.height)
lineTo(0f, size.height * 0.8f)
close()
}
@Composable
fun TicketBadge() {
Box(
modifier = Modifier
.size(width = 140.dp, height = 90.dp)
.background(
color = Color(0xFF2457C5),
shape = TicketShape
)
)
}
The path uses the component's measured width and height to define a ticket-like polygon. Custom paths should be tested at different sizes and aspect ratios. For ordinary buttons and cards, built-in shapes are usually simpler and more maintainable.
15. Making UI Components Interactive
Interactive components should provide clear feedback when a user taps, selects, or disables them. Compose state allows the UI to update when the underlying state changes.
Example: Selected and Unselected Button
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
@Composable
fun ToggleSelectionButton() {
var selected by remember { mutableStateOf(false) }
Button(
onClick = { selected = !selected },
colors = ButtonDefaults.buttonColors(
containerColor = if (selected) {
Color(0xFF1B5E20)
} else {
Color(0xFF2457C5)
}
)
) {
Text(
text = if (selected) "Selected" else "Select"
)
}
}
The button changes its state when clicked. For a real application, use a component that semantically represents selection, such as a selectable chip or toggle control, when that better matches the interaction. State that must survive configuration changes may require rememberSaveable or state management at a higher level.
16. Creating a Circular Profile Component
Circular images and profile badges are common in messaging applications, dashboards, and user account screens. The CircleShape shape can clip content into a circular form.
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
@Composable
fun ProfileInitials(
initials: String
) {
Box(
modifier = Modifier
.size(56.dp)
.clip(CircleShape)
.background(MaterialTheme.colorScheme.primaryContainer),
contentAlignment = Alignment.Center
) {
Text(
text = initials,
color = MaterialTheme.colorScheme.onPrimaryContainer,
style = MaterialTheme.typography.titleMedium
)
}
}
This example displays initials rather than an image. For a real profile picture, use an image-loading solution and apply the same clipping shape. Provide appropriate alternative text if the image conveys meaningful information.
17. Using Surface for Custom Containers
Surface is a Material 3 building block for content containers. It supports color, shape, tonal elevation, shadow elevation, and optional borders.
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun NoticeSurface() {
Surface(
shape = MaterialTheme.shapes.large,
color = MaterialTheme.colorScheme.surfaceVariant,
contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
tonalElevation = 2.dp,
shadowElevation = 1.dp
) {
Text(
text = "Your changes have been saved.",
modifier = Modifier.padding(16.dp)
)
}
}
Tonal elevation uses the Material color system to distinguish surfaces, while shadow elevation controls the visual shadow. Choose elevation values intentionally rather than adding strong shadows to every container.
18. Designing a Gradient Button
Gradient backgrounds are useful for applications with a distinctive visual identity. Standard Material buttons primarily use solid container colors, so a gradient design may require a custom component.
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.unit.dp
@Composable
fun GradientActionButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
val shape = RoundedCornerShape(14.dp)
val interactionSource = remember {
MutableInteractionSource()
}
Box(
modifier = modifier
.fillMaxWidth()
.clip(shape)
.background(
brush = Brush.horizontalGradient(
colors = listOf(
Color(0xFF2457C5),
Color(0xFF7B3FE4)
)
)
)
.clickable(
interactionSource = interactionSource,
indication = null,
onClick = onClick
)
.semantics {
onClick(
label = text,
action = {
onClick()
true
}
)
}
.padding(vertical = 16.dp),
contentAlignment = Alignment.Center
) {
Text(
text = text,
color = Color.White
)
}
}
This is a simplified visual example. A production custom button must also handle enabled and disabled states, keyboard and focus interactions, touch feedback, minimum touch targets, and accessibility semantics. For most actions, prefer a Material 3 Button because it already provides standard interaction behavior. If you use a custom clickable surface, avoid duplicating click semantics or handling the same action twice; use a carefully designed component and test it with accessibility services.
19. Modifier Order and Shape Behavior
Modifier order affects the appearance and interaction area of a component. For example, clipping before applying a background creates a background that follows the clipped outline.
Modifier
.clip(RoundedCornerShape(16.dp))
.background(Color(0xFF2457C5))
.padding(16.dp)
Compare that with the following sequence:
Modifier
.background(Color(0xFF2457C5))
.clip(RoundedCornerShape(16.dp))
.padding(16.dp)
Modifier behavior depends on the drawing and layout operations involved. Prefer a clear, deliberate modifier order, and test the actual result rather than assuming that reordering modifiers is interchangeable.
20. Accessibility Guidelines for Custom Components
Custom visual elements should remain understandable and operable for users with different abilities. A component that looks attractive but cannot be understood by a screen reader or activated with alternative input methods is incomplete.
- Meaningful labels: Use clear button text or content descriptions for icon-only actions.
- Touch targets: Make interactive controls large enough to activate comfortably. Follow Android accessibility guidance for recommended minimum target sizes.
- Color contrast: Ensure text and icons remain visible against their backgrounds.
- State communication: Make selected, disabled, and error states clear through more than color alone where possible.
- Text scaling: Test the interface with larger system font settings.
- Keyboard support: Verify that interactive components can receive focus and be activated using supported input methods.
- Semantic roles: Use appropriate component semantics so assistive technologies can identify controls correctly.
- Focus indicators: Preserve visible focus feedback for keyboard and other non-touch interactions.
- Predictable behavior: Ensure that a control's appearance matches the action it performs.
21. Common Mistakes and Troubleshooting
| Problem |
Possible Cause |
Solution |
| Button color does not change |
The wrong color parameter is being configured or the theme overrides the intended appearance. |
Check the component's color API and verify the theme values. |
| Rounded corners are not visible |
The shape is applied to a different container or another background is drawn outside the clipped area. |
Apply the shape to the actual component or clip the correct container. |
| Button content is clipped |
Fixed dimensions or excessive padding prevent content from fitting. |
Review size constraints, padding, and text scaling. |
| Click action runs twice |
The same action is wired through multiple click handlers or semantic actions. |
Use one interaction pathway and avoid duplicate handlers. |
| Custom button lacks feedback |
A custom clickable element omits interaction indication or state feedback. |
Prefer a Material button or implement appropriate interaction feedback. |
| UI looks inconsistent across screens |
Each component defines its own unrelated colors, shapes, and typography. |
Create reusable composables and use shared theme values. |
| Icon button is unclear to screen readers |
The icon has no suitable content description. |
Add meaningful accessibility semantics or a content description. |
| Shape looks distorted |
The component's dimensions or aspect ratio do not match the intended design. |
Test the shape at different sizes and use relative geometry where appropriate. |
| Material imports are unresolved |
The dependency is missing or the import belongs to another Material version. |
Check Gradle dependencies and use the correct package. |
| Disabled control still appears active |
Visual styling does not reflect the enabled state. |
Configure disabled colors and pass the correct enabled value. |
22. Practical Project: Custom UI Component Library
Create a small reusable component library for a learning application. The purpose is to standardize buttons, cards, profile badges, and status labels.
Project Requirements
- Create a reusable primary action button with customizable text, click callback, modifier, and enabled state.
- Create a secondary outlined button.
- Create a course card that accepts a title and description.
- Create a circular profile-initials component.
- Create a status badge with a custom background and shape.
- Use shared Material theme colors and typography where possible.
- Ensure that buttons provide clear interaction feedback.
- Test the components in different layouts and font sizes.
- Verify accessibility labels and touch targets.
Expected Result
The final component library should reduce duplicated code and provide a consistent appearance across multiple screens. Developers should be able to reuse the same components by supplying different content and callbacks.
23. Interview Questions and Answers
Q1. What is a custom UI component in Jetpack Compose?
Answer: A custom UI component is a reusable composable function designed to implement a particular visual element or interaction pattern.
Q2. How do you customize a Material 3 button?
Answer: Customize its parameters, such as colors, shape, border, enabled, modifier, and contentPadding.
Q3. What is the purpose of RoundedCornerShape?
Answer: RoundedCornerShape defines rounded corners for buttons, cards, containers, and other composables that support a shape.
Q4. What is the difference between clip and background?
Answer: clip restricts drawing to a specified shape, while background draws a color or brush behind content. Their order can affect the final appearance.
Q5. Why should developers create reusable buttons?
Answer: Reusable buttons reduce code duplication, centralize styling, and make it easier to maintain a consistent design across screens.
Q6. What is the difference between Button and OutlinedButton?
Answer: A standard Material 3 Button typically uses a filled container for a prominent action. An OutlinedButton uses a border and generally gives the action less visual emphasis.
Q7. When should GenericShape be used?
Answer: GenericShape is useful when a built-in shape cannot represent the required custom geometry. Standard shapes should be preferred when they meet the design requirement.
Q8. How can a button be disabled?
Answer: Pass enabled = false to a Material button. The component will not respond to normal activation, and its disabled appearance can be configured.
Q9. What is the role of Modifier in Compose?
Answer: Modifier controls layout, spacing, drawing, clipping, interaction, and other UI behavior. Modifier order can influence the result.
Q10. How do you improve the accessibility of custom buttons?
Answer: Use clear labels, suitable touch targets, adequate contrast, visible focus feedback, correct semantics, and predictable enabled and selected states.
Q11. Why use MaterialTheme in reusable components?
Answer: MaterialTheme provides shared color, typography, and shape values, helping components stay consistent when the application theme changes.
Q12. When should a custom component replace a Material component?
Answer: Create a fully custom component when the design or behavior cannot be achieved appropriately by customizing a standard component. Custom implementations require additional attention to accessibility and interaction behavior.
24. Quick Revision Checklist
- Understand composables and reusable UI components.
- Customize Material 3 button colors and shapes.
- Use rounded, circular, rectangular, and cut-corner shapes appropriately.
- Apply modifiers for layout, backgrounds, borders, and clipping.
- Build reusable buttons and cards with parameters.
- Use MaterialTheme for shared design values.
- Handle enabled, disabled, and selected states correctly.
- Use icons with appropriate accessibility descriptions.
- Test touch targets, text scaling, and color contrast.
- Use custom paths only when built-in shapes are insufficient.
- Avoid duplicate click handling and unnecessary styling duplication.
25. Official Learning Resources
Explore the official Android Developers documentation for Custom Design Systems in Jetpack Compose to learn how to build reusable custom components.
Also review Material Design 3 in Jetpack Compose for information about Material components, theme customization, shapes, and colors.
Conclusion
Designing custom buttons, shapes, and UI components is a key skill in modern Android development. Jetpack Compose makes it possible to build reusable UI elements using composable functions, modifiers, shapes, and Material 3 components. By applying consistent styling, handling interaction states correctly, and following accessibility practices, developers can create interfaces that are attractive, maintainable, and easy to use.
Continue learning with the JustAcademy Android App Development Course, register for a course demo, or visit the JustAcademy website.