Say Goodbye to Plain Toasts! Design Custom Jetpack Compose Toasts

Say Goodbye to Plain Toasts! Design Custom Jetpack Compose Toasts

A Toast in Android is a small, temporary pop-up message that appears on the screen to provide quick feedback to the user. It does not block user interaction and disappears automatically after a short duration.


Default toast in Android

For example, when clicking a mic button, a toast can show:

? "Listening..." when recording starts

? "Stopped listening" when recording stops

However, the default Android Toast feels plain and may not align with your app’s theme. What if you could create a stylish, customizable toast that matches your app’s branding?

Let’s explore a clean and reusable way to implement a custom toast in Jetpack Compose.


1?? Defining the Custom Toast Composable ??

Below is a reusable ToastMessage Composable that listens to state changes from ToastManager and automatically disappears after 2 seconds:

@Composable
fun ToastMessage(
    modifier: Modifier = Modifier.padding(12.dp),
    shape: RoundedCornerShape = CircleShape
) {
    val toastMessage by ToastManager.toastMessage.collectAsState()
    val showMessage by ToastManager.showMessage.collectAsState()
    val isSuccess by ToastManager.isSuccess.collectAsState()

    if (showMessage) {
        val backgroundColor = if (isSuccess) {
            Color(0xFFB2F1C0)
        } else {
            Color(0xFFFFDAD6)
        }

        val iconColor = if (isSuccess) {
            Color(0xFF2F6A43)
        } else {
              Color(0xFFFF2514)
        }

        val icon = if (isSuccess) Icons.Default.CheckCircle else Icons.Default.Close
        val contentDescription = if (isSuccess) "Success icon" else "Error icon"

        LaunchedEffect(showMessage) {
            if (showMessage) {
                delay(2000) // Hide after 2 seconds
                ToastManager.hideToast()
            }
        }

        Row(
            modifier = modifier
                .background(backgroundColor, shape)
                .padding(12.dp)
        ) {
            Icon(
                imageVector = icon,
                contentDescription = contentDescription,
                tint = iconColor,
                modifier = Modifier.padding(end = 8.dp)
            )
            Text(text = toastMessage)
        }
    }
}        

Why Use ToastManager?

Instead of managing toast visibility inside each screen, we use a centralized ToastManager to handle state updates. This makes our toast easy to trigger from anywhere in the app.

Now, let’s define the ToastManager in the next step.

2??Defining the ToastManager Singleton ??

A singleton ToastManager helps us manage toast messages centrally, ensuring a consistent UI experience across the app. This way, we don’t need to declare a new instance in every ViewModel—we can just call ToastManager.showToast(...) whenever needed.

object ToastManager {
    private val _toastMessage = MutableStateFlow("")
    val toastMessage: StateFlow<String> = _toastMessage

    private val _showMessage = MutableStateFlow(false)
    val showMessage: StateFlow<Boolean> = _showMessage

    private val _isSuccess = MutableStateFlow(true)
    val isSuccess: StateFlow<Boolean> = _isSuccess

    fun showToast(message: String, isSuccess: Boolean) {
        _toastMessage.update { message }
        _isSuccess.update { isSuccess }
        _showMessage.update { true }
    }

    fun hideToast() {
        _showMessage.update { false }
        _toastMessage.update { "" } 
    }
}
        

Why Singleton?

Since ToastManager is a singleton, we don’t need to initialize it in every ViewModel. We can directly call ToastManager.showToast(...) anywhere in the app.

3?? Integrating the Toast in Screen and ViewModel ??

Step 1: Add ToastMessage() to Your Screen

Place ToastMessage() inside the main screen composable. Typically, it appears at the bottom center of the screen.

@Composable
fun MyScreen(viewModel: MyViewModel = hiltViewModel()) {
    Scaffold(
        topBar = { /* Top bar content here */ },
        content = { paddingValues ->
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(paddingValues)
            ) {
                // Other screen components
               Button(onClick = { viewModel.insertData("New Data") }) {
                  Text("Add Data")
               }
                Box(
                    modifier = Modifier
                        .align(Alignment.BottomCenter)
                        .padding(bottom = 124.dp)
                ) {
                    ToastMessage()
                }
            }
        }
    )
}        

Step 2: Trigger Toast in ViewModel

When a user inserts data by clicking a button, the ViewModel triggers a toast message based on success or failure.

@HiltViewModel
class MyViewModel @Inject constructor(private val repository: Repository) : ViewModel() {

 fun insertData(data: String) {
 viewModelScope.launch {
  try {
              repository.insertData(data)
      ToastManager.showToast(message = "Data added successfully", isSuccess = true)
       } catch (e: Exception) {
      ToastManager.showToast(message = "Error while entering data", isSuccess = false)
        }
      }
    }
}
        

?? Final Output

Your custom toast message is now:

Reusable across all screens

Styled to match your app’s branding

Triggerable from anywhere in your app

Toast message demo
Custom Toast

???Conclusion

With this setup, you can seamlessly integrate a custom toast into your Jetpack Compose app.

Why it’s better than the default Toast:

? Matches app theme – Customizable colors, shapes, and animations

? More control – Centralized ToastManager handles messages efficiently

? Reusable – Works across all screens without ViewModel-specific toast logic

Try it out in your project and enhance your app’s UX!??

要查看或添加评论,请登录

Henil Chhipani的更多文章

  • Extension functions in Kotlin

    Extension functions in Kotlin

    Kotlin is an amazing programming language that brings a plethora of powerful features to developers. One standout…

  • WHat is MVVM Architecture and How is it Useful in Android App Development?

    WHat is MVVM Architecture and How is it Useful in Android App Development?

    MVVM Stands for Model-View-ViewModel, a design pattern that helps developers separate concerns in their applications…

  • Camera Access in your Android App | Camera2 Api | CameraX Api

    Camera Access in your Android App | Camera2 Api | CameraX Api

    In Android development, accessing the camera is crucial for apps that need photography, video recording, or even…

  • Async-storage in React Native CLI

    Async-storage in React Native CLI

    Recently while exploring React Native CLI, I explored a concept known as Async Storage. This article will discuss what…

  • Coroutine in kotlin

    Coroutine in kotlin

    Coroutines are akin to threads in other languages. They facilitate asynchronous code execution separately from the main…

  • Caching mechanism in Glide library | Android

    Caching mechanism in Glide library | Android

    Glide is one the best library tool to load network image on your app. Recently i am researching on how glide is working.

    1 条评论

其他会员也浏览了