koffee
0.2.1indexedLightweight, animated toast system for Jetpack Compose. Enables hot, cold, or custom toasts with animated entrance/exit, customizable layouts, positions, and durations. Zero dependencies ensure compact implementation.
Lightweight, animated toast system for Jetpack Compose. Enables hot, cold, or custom toasts with animated entrance/exit, customizable layouts, positions, and durations. Zero dependencies ensure compact implementation.
A lightweight, animated toast system for Jetpack Compose. Serve your toasts hot, cold, or custom brewed.
🙌 Want to contribute? Check out our Contribution Guidelines.
💬 Have feedback? Share it on Featurebase.
⚠️ Koffee is currently unavailable for iOS. Please feel free to submit a PR for this
This is a demo of how Koffee works. The toasts are swipeable.
📦 Now available on Maven Central
Koffee is now published to Maven Central. The last JitPack release will remain available but will no longer be updated.
// settings.gradle.kts
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral() // ✅ Primary distribution
}
}
// module build.gradle.kts
dependencies {
implementation("io.github.donald-okara:koffee:<latest version>") // Replace with the latest version
}
// settings.gradle.kts
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
maven(url = "https://jitpack.io") // ⚠️ Legacy – no longer updated
}
}
dependencies {
implementation()
}
Koffee has 3 layers:
You can plug in your own toast layouts, define duration policies, and limit how many are shown at once.
As of Koffee v0.1.0, we have a new way to initialize Koffee.
Wrap your root (or target) layout to configure Koffee in one place — stable across recompositions.
KoffeeBar(
modifier = Modifier.fillMaxSize(),
config = myConfig,
) {
NavHost()
}
@OptIn(ExperimentalKoffeeApi::class)
val myConfig = remember {
KoffeeDefaults.config.copy(
layout = { GlowingToast(it) },
dismissible = true,
maxVisibleToasts = 3,
position = ToastPosition.BottomCenter,
animationStyle = ToastAnimation.SlideUp,
durationResolver = ::customDurationResolver,
)
}
fun : ? = (duration) {
ToastDuration. ->
ToastDuration.Medium ->
ToastDuration. ->
ToastDuration.Indefinite ->
}
KoffeeBar(
modifier = Modifier.fillMaxSize(),
config = myConfig,
) {
NavHost()
}
The old method is not deprecated yet. See it below.
You can call Koffee.show from anywhere as long as a host is attached (via KoffeeBar or Koffee.Setup).
Wrap your screen content with KoffeeBar using defaults:
KoffeeBar {
MyScreenContent()
}
Show a simple toast anywhere within a screen attached to a ToastHostState:
Button(
onClick = {
// Koffee.show can be called from ViewModels, Repositories, anywhere your screen has a ToastHostState
Koffee.show(
title = "Success toast",
description = "This is a green notification"
)
}
) {
Text("Hello fam")
}
Customize layout, animation, position, duration, and other properties:
val mySimpleConfig = remember {
KoffeeDefaults.config.copy(
layout = { GlowingToast(it) }, // Pass your preferred toast composable here: @Composable (ToastData) -> Unit
dismissible = true,
maxVisibleToasts = 3,
position = ToastPosition.BottomCenter,
animationStyle = ToastAnimation.SlideUp,
durationResolver = ::customDurationResolver,
)
}
KoffeeBar {
MyScreenContent()
}
Use a toast with actions:
Koffee.show(
title = "Success toast",
description = "With actions",
type = ToastType.Success,
primaryAction = ToastAction(
label = "Share",
onClick = { println("Viewing info details") },
dismissAfter = true
),
secondaryAction = ToastAction(
label = "Copy",
onClick = { println("Copied!") },
dismissAfter = true
)
)
You can pass your own Composable to style the toast as long as its signature matches
@Composable (ToastData) -> Unit
Built with love for Jetpack Compose devs who want more control over their UI.
Made by @donald-okara
Looking for feature X? Open an issue
Koffee is licensed under the Apache License 2.0. You’re free to use, modify, and distribute it under the conditions specified.
override fun onCreate() {
super.onCreate()
Koffee.init {
layout { DefaultToast(it) } // or custom. DefaultToast() is our in built default toast composable
dismissible(true)
durationResolver { customDurationResolver(it) }
}
}
private fun customDurationResolver(duration: ToastDuration): Long? = when (duration) {
ToastDuration.Short -> 5000L
ToastDuration.Medium -> 7000L
ToastDuration.Long -> 10000L
ToastDuration.Indefinite -> null
}
Hint: You can use Koffee.init{} with an empty lambda as there are default values for everything
This goes in your root composable
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.padding(innerPadding),
) {
// 1. Your app UI
TestToasts()
// 2. Render Koffee
Koffee.Setup(
maxVisibleToasts = 2,
)
}
}
Surfaced from shared tags and platforms — no rankings paid for.