Android Compose Tutorial - Label Selector Bar
Introduction
Hello! Welcome to my latest Android Compose tutorial! Today, we are crafting a user interface that provides a selection of labels. This versatile UI component allows users to choose a single option from a group, perfect for selecting categories, types, or any classification you need.
Looking to dive straight into the code? The repository is ready here.
On Today’s Agenda
- Implementing
LabelUi()
: We will start by buildingLabelUi()
, a composable representing an individual label within our selection group. - Crafting
LabelSelectorBar()
: Following that, we will assembleLabelSelectorBar()
, which serves as a container for our collection of labels, enabling a clear and concise selection process.
Environment
- Android Studio Hedgehog | 2023.1.1
- Compose version:
androidx.compose:compose-bom:2023.08.00
- Pixel 5 API 32 Emulator
Step 1: LabelUI Implementation
Let’s dive in by setting up the composable/LabelUi.kt
file with the following code:
// Your package...
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
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.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable
fun LabelUi(
text: String = "",
selected: Boolean = false,
// 1. Layout Customization Parameters
labelTextStyle: TextStyle = TextStyle(
fontWeight = FontWeight.SemiBold,
fontSize = 16.sp
),
backgroundColor: Color = Color.White,
selectedBackgroundColor: Color = Color.Black,
textColor: Color = Color.Black,
selectedTextColor: Color = Color.White,
roundedCornerShapeSize: Dp = 8.dp,
horizontalPadding: Dp = 16.dp,
verticalPadding: Dp = 8.dp,
onClick: () -> Unit = {},
) {
// 2. Interaction Feedback Removal
val interactionSource = remember { MutableInteractionSource() }
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.clickable(
interactionSource = interactionSource,
indication = null,
onClick = onClick
)
.background(
// 3. Label Color Depending the selected Value
if (selected) selectedBackgroundColor else backgroundColor,
RoundedCornerShape(roundedCornerShapeSize)
)
.padding(horizontal = horizontalPadding, vertical = verticalPadding)
) {
Text(
text = text,
// 4. Text Color Depending the selected Value
color = if (selected) selectedTextColor else textColor,
style = labelTextStyle
)
}
}
@Preview
@Composable
fun LabelUiPreview() {
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.White),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
LabelUi(text = "Label1", selected = true)
LabelUi(text = "Label2", selected = false, textColor = Color.Blue)
}
}
- Layout Customization Parameters: These parameters include
labelTextStyle
, which determines the style of the label's text, such as its font weight and size;backgroundColor
andselectedBackgroundColor
, which define the label's background color in its default and selected states, respectively;textColor
andselectedTextColor
, which set the color of the text content for both states; androundedCornerShapeSize
,horizontalPadding
, andverticalPadding
, which control the shape and padding of the label. By providing these properties as parameters with default values, we can makeLabelUi()
highly reusable and adaptable to different UI contexts. - Interaction Feedback Removal: The
interactionSource
is a Compose utility that tracks the interaction state of the clickable component. By settingindication = null
in theclickable
modifier, you are effectively removing the visual feedback that typically occurs when the user interacts with the clickable area (such as a ripple effect on Android). This could create a cleaner UI design when you don’t want to show any state changes when the user clicks the label. - Dynamic Background Color: The background color of the
LabelUi()
is determined by whether theselected
parameter is true or false. Ifselected
is true, theselectedBackgroundColor
(Black by default) is used; otherwise, thebackgroundColor
(White by default) is applied. This allows the label to reflect its selection state to the user visually. - Dynamic Text Color: Similar to the previous point, the color of the text within the
LabelUi
is determined by theselected
state. If the label is selected, theselectedTextColor
(White by default) is used; if it is not selected, thetextColor
(Black by default) is used. This change in text color helps to enhance the visibility of the selected label against the background color change.
When the preview is executed, you should observe two vertically stacked labels showcasing their selected and default states.
Step 2: Label Selector Bar Implementation
Next, create the composable/LabelSelectorBar.kt
file:
// Your package...
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable
fun LabelSelectorBar(
labelItems: List<String> = listOf(),
// 1. LabelSelectorBar Customization Parameters
barHeight: Dp = 56.dp,
horizontalPadding: Dp = 8.dp,
distanceBetweenItems: Dp = 0.dp,
// 2. LabelUi Customization Parameters
labelTextStyle: TextStyle = TextStyle(fontWeight = FontWeight.SemiBold, fontSize = 16.sp),
backgroundColor: Color = Color.White,
selectedBackgroundColor: Color = Color.Black,
textColor: Color = Color.Black,
selectedTextColor: Color = Color.White,
roundedCornerShapeSize: Dp = 8.dp,
labelHorizontalPadding: Dp = 16.dp,
labelVerticalPadding: Dp = 8.dp,
) {
// 3. Stateful Selection Management:
val selectedLabel = rememberSaveable { mutableStateOf(labelItems.firstOrNull() ?: "") }
LazyRow(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.height(barHeight)
) {
item { Spacer(modifier = Modifier.width(horizontalPadding)) }
// 4. Interactive Label Identification
items(labelItems) { label ->
LabelUi(
text = label,
selected = label == selectedLabel.value,
labelTextStyle = labelTextStyle,
backgroundColor = backgroundColor,
selectedBackgroundColor = selectedBackgroundColor,
textColor = textColor,
selectedTextColor = selectedTextColor,
roundedCornerShapeSize = roundedCornerShapeSize,
horizontalPadding = labelHorizontalPadding,
verticalPadding = labelVerticalPadding
) {
selectedLabel.value = label
}
Spacer(modifier = Modifier.width(distanceBetweenItems))
}
item { Spacer(modifier = Modifier.width(horizontalPadding)) }
}
}
@Preview
@Composable
fun LabelSelectorBarPreview() {
LabelSelectorBar(labelItems = listOf("All", "Pop", "Rock", "Jazz", "Hip Hop", "Classical"))
}
LabelSelectorBar()
Customization Parameters: ThebarHeight
parameter sets the height of the bar,horizontalPadding
defines the padding at the start and end of theLazyRow()
, anddistanceBetweenItems
specifies the space between each label item in the row. These parameters allow for flexible layout design, making theLabelSelectorBar
adaptable to different screen sizes and design requirements.LabelUi()
Customization Parameters: These parameters are used to benefit theLabelUi()
function from a range of parameters that offer extensive customization options.- Stateful Selection Management: A
selectedLabel
state is created and remembered across recompositions, which holds the currently selected label. If no label is initially selected, it defaults to an empty string. This state is crucial for identifying active labels and handling user interactions within the label bar. - Interactive Label Identification: Labels in the
LabelSelectorBar()
are identified by their text, streamlining the selection process. Clicking a label updates theselectedLabel
state, reflecting the choice in the UI. This approach works well for simple cases. However, for complex setups, uniqueIDs
for each label are recommended for better scalability and to avoid issues with duplicate or dynamic label texts.
By running the preview function, you should have a container with some items as Labels and only 1 item selected at a time.
Step 3: LabelSelectorBar Demonstration
To demonstrate the LabelSelectorBar()
in action, let’s integrate it into the MainActivity.kt
file. The code should be updated as follows:
// Your package...
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.kenruizinoue.labelselectorbarcomposable.composable.LabelSelectorBar
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
LabelSelectorBar(
labelItems = listOf(
"All", "Pop", "Rock", "Jazz", "Hip Hop", "Classical"
),
barHeight = 80.dp,
horizontalPadding = 12.dp,
distanceBetweenItems = 8.dp,
backgroundColor = Color(0xFFB2F0AD),
selectedBackgroundColor = Color(0xFF294D16),
textColor = Color(0xFF333333),
selectedTextColor = Color(0xFFD8D1D1),
roundedCornerShapeSize = 24.dp,
labelVerticalPadding = 14.dp,
labelHorizontalPadding = 18.dp,
)
}
}
}
Run the app to showcase its customizability!
Next Steps
As we wrap up this tutorial on the LabelSelectorBar()
, it’s important to recognize its role within the larger context of the MusicStreamingDemoApp
. This composable is a key piece in an app that, when assembled, forms an elegant and functional music streaming application.
The beauty of this project lies in its modular design. You can create the four main components in any order you prefer, giving you the flexibility to approach the development process in a way that suits your style or needs best.
The Four Key UI Components
TopBar: This is where users select music from a list. (this tutorial)
TrackList: A dynamic display showcasing various tracks. It’s an interactive segment where users can browse and choose their next listen. (coming soon!)
PlaybackBar: The control center of the music experience. It allows users to play, pause, and navigate through the music.
BottomBar: Navigation hub of the app. (coming soon!)
After creating the individual components, the last step involves integrating them into the main screen of the music app. This is where you see the harmony of the TopBar
, TrackList
, PlaybackBar
, and BottomBar
in action, culminating in a user-friendly and visually appealing music streaming interface. (coming soon!)
For those eager to dive into the most current iteration of the project, the MusicStreamingDemoApp
repository awaits your exploration here.
Discover More
Are these tutorials sparking your curiosity? Let’s dive even deeper into the fascinating world of Android development!
Wrapping Up
I hope you found this tutorial both informative and inspiring. Feel free to take your time to digest the code, experiment with it, and make it your own! Remember, understanding comes with practice and exploration.
If any questions or doubts arise, don’t hesitate to reach out. I’m here to assist you in your journey to becoming a more proficient Android developer.
If you enjoyed this content and are looking forward to more insightful and educational experiences, please consider supporting my work. A clap, a follow, or sharing this tutorial can make a big difference!
See you in the next tutorial, where we will continue to unravel the exciting world of Android Compose!
Deuk Services: Your Gateway to Leading Android Innovation
Are you looking to boost your business with top-tier Android solutions?Partner with Deuk services and take your projects to unparalleled heights.