In modern Android development with Jetpack Compose, providing feedback to the user is crucial for a good user experience. A Snackbar is a lightweight, non-modal message displayed at the bottom of the screen to provide brief information about app processes. It’s commonly used to confirm user actions, display errors, or provide simple notifications. In this comprehensive guide, we’ll explore how to implement Snackbar messaging in Jetpack Compose, covering everything from basic implementation to advanced customization.
What is a Snackbar?
A Snackbar is a UI element that displays brief, important messages to the user. Unlike AlertDialogs, Snackbars do not interrupt the user’s workflow and disappear automatically after a short period or upon user interaction. They are an integral part of providing real-time feedback in a non-intrusive way.
Why Use Snackbars?
- Non-Intrusive: Does not interrupt the user’s current task.
- Informative: Provides immediate feedback on actions performed.
- User-Friendly: Conveys simple and quick messages, enhancing the overall experience.
How to Implement Snackbar Messaging in Jetpack Compose
To implement Snackbar messaging, follow these steps:
Step 1: Set Up Dependencies
Make sure you have the necessary Material Design dependencies in your build.gradle file:
dependencies {
implementation("androidx.compose.material3:material3:1.1.1")
implementation("androidx.compose.material:material:1.5.4") // For legacy compatibility
implementation("androidx.compose.ui:ui:1.6.0")
implementation("androidx.compose.ui:ui-tooling-preview:1.6.0")
implementation("androidx.compose.runtime:runtime:1.6.0")
implementation("androidx.compose.material:material-icons-core:1.5.4") // for icons
}
Step 2: Basic Snackbar Implementation
Here’s a basic example of showing a Snackbar in Jetpack Compose using SnackbarHost and SnackbarHostState:
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import kotlinx.coroutines.launch
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.Button
import androidx.compose.foundation.layout.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.runtime.rememberCoroutineScope
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SimpleSnackbarExample() {
val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
Scaffold(
snackbarHost = {
SnackbarHost(hostState = snackbarHostState)
},
content = { padding ->
Column(
modifier = Modifier.fillMaxSize().padding(padding),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = {
scope.launch {
snackbarHostState.showSnackbar(
message = "Hello Snackbar!",
duration = SnackbarDuration.Short
)
}
}) {
Text("Show Snackbar")
}
}
}
)
}
@Preview(showBackground = true)
@Composable
fun PreviewSimpleSnackbarExample() {
SimpleSnackbarExample()
}
In this example:
SnackbarHostStateis used to control the Snackbar.- The
Scaffoldcomposable provides a structure to display aSnackbarHost. - A
CoroutineScopeis used to launch the Snackbar viasnackbarHostState.showSnackbar. SnackbarDurationspecifies how long the Snackbar will be displayed.
Step 3: Adding Action to Snackbar
You can add an action button to the Snackbar, such as an “Undo” or “Retry” option:
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.tooling.preview.Preview
import kotlinx.coroutines.launch
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.Button
import androidx.compose.foundation.layout.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.graphics.Color
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SnackbarWithActionEvent() {
val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
Scaffold(
snackbarHost = {
SnackbarHost(hostState = snackbarHostState)
},
content = { padding ->
Column(
modifier = Modifier.fillMaxSize().padding(padding),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = {
scope.launch {
val result = snackbarHostState.showSnackbar(
message = "File deleted",
actionLabel = "Undo",
duration = SnackbarDuration.Short
)
when (result) {
SnackbarResult.ActionPerformed -> {
// Handle the undo action
println("Undo action performed")
}
SnackbarResult.Dismissed -> {
// Handle the dismissal
println("Snackbar dismissed")
}
}
}
}) {
Text("Delete File")
}
}
}
)
}
@Preview(showBackground = true)
@Composable
fun PreviewSnackbarWithActionEvent() {
SnackbarWithActionEvent()
}
Key points in this example:
- The
actionLabelparameter adds a button to the Snackbar. - The result of
showSnackbarindicates whether the action was performed or the Snackbar was dismissed.
Step 4: Customizing the Snackbar
You can customize the colors and content of the Snackbar using a custom Snackbar composable:
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.tooling.preview.Preview
import kotlinx.coroutines.launch
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.Button
import androidx.compose.foundation.layout.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.graphics.Color
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CustomSnackbarExample() {
val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
Scaffold(
snackbarHost = {
SnackbarHost(hostState = snackbarHostState) { data ->
Card(
modifier = Modifier.padding(8.dp),
containerColor = Color.DarkGray, // custom color
contentColor = Color.White // custom text color
) {
Row(
modifier = Modifier.padding(16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(text = data.visuals.message)
data.visuals.actionLabel?.let { actionLabel ->
Button(onClick = { scope.launch { data.performAction() } }) {
Text(text = actionLabel, color = Color.White)
}
}
}
}
}
},
content = { padding ->
Column(
modifier = Modifier.fillMaxSize().padding(padding),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = {
scope.launch {
snackbarHostState.showSnackbar(
message = "Customized Snackbar!",
actionLabel = "Dismiss",
duration = SnackbarDuration.Short
)
}
}) {
Text("Show Custom Snackbar")
}
}
}
)
}
@Preview(showBackground = true)
@Composable
fun PreviewCustomSnackbarExample() {
CustomSnackbarExample()
}
Key aspects of customization:
- Wrap the Snackbar content in a
Cardcomposable. - Customize the
containerColorandcontentColorfor custom styling. - Use the
dataparameter fromSnackbarHostto access the Snackbar’s properties.
Step 5: Displaying Snackbars with Different Durations
You can specify the duration a Snackbar is displayed:
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.tooling.preview.Preview
import kotlinx.coroutines.launch
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.Button
import androidx.compose.foundation.layout.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.runtime.rememberCoroutineScope
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SnackbarDurationsExample() {
val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
Scaffold(
snackbarHost = {
SnackbarHost(hostState = snackbarHostState)
},
content = { padding ->
Column(
modifier = Modifier.fillMaxSize().padding(padding),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = {
scope.launch {
snackbarHostState.showSnackbar(
message = "Short Snackbar",
duration = SnackbarDuration.Short
)
}
}) {
Text("Show Short Snackbar")
}
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = {
scope.launch {
snackbarHostState.showSnackbar(
message = "Long Snackbar",
duration = SnackbarDuration.Long
)
}
}) {
Text("Show Long Snackbar")
}
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = {
scope.launch {
snackbarHostState.showSnackbar(
message = "Indefinite Snackbar. Tap to dismiss.",
duration = SnackbarDuration.Indefinite
)
}
}) {
Text("Show Indefinite Snackbar")
}
}
}
)
}
@Preview(showBackground = true)
@Composable
fun PreviewSnackbarDurationsExample() {
SnackbarDurationsExample()
}
SnackbarDuration.Short: Displayed for a brief period.SnackbarDuration.Long: Displayed for a longer duration.SnackbarDuration.Indefinite: Remains visible until dismissed by the user or programmatically.
Conclusion
Snackbar messaging is a crucial aspect of providing real-time, non-intrusive feedback in Jetpack Compose applications. By using SnackbarHost, SnackbarHostState, and CoroutineScope, you can easily display Snackbars for user confirmations, errors, or simple notifications. Customizing the appearance and adding actions further enhances the user experience, making your app more interactive and user-friendly. Experiment with the various configurations to find what works best for your specific needs!