{"id":13609361,"url":"https://github.com/dokar3/compose-sonner","last_synced_at":"2025-10-05T16:43:54.214Z","repository":{"id":216620088,"uuid":"741798053","full_name":"dokar3/compose-sonner","owner":"dokar3","description":"An opinionated toast component for Compose Multiplatform.","archived":false,"fork":false,"pushed_at":"2024-10-16T18:43:59.000Z","size":680,"stargazers_count":188,"open_issues_count":11,"forks_count":11,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-10-18T17:30:06.752Z","etag":null,"topics":["android","compose","compose-multiplatform","desktop","jetpack-compose","kotlin","toast","web"],"latest_commit_sha":null,"homepage":"https://dokar3.github.io/compose-sonner/","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dokar3.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-11T06:08:15.000Z","updated_at":"2024-10-18T08:02:10.000Z","dependencies_parsed_at":"2024-03-07T06:24:36.562Z","dependency_job_id":"4c2705e0-f098-4737-947a-47f71f7f44d2","html_url":"https://github.com/dokar3/compose-sonner","commit_stats":null,"previous_names":["dokar3/compose-sonner"],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dokar3%2Fcompose-sonner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dokar3%2Fcompose-sonner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dokar3%2Fcompose-sonner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dokar3%2Fcompose-sonner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dokar3","download_url":"https://codeload.github.com/dokar3/compose-sonner/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247190251,"owners_count":20898702,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","compose","compose-multiplatform","desktop","jetpack-compose","kotlin","toast","web"],"created_at":"2024-08-01T19:01:34.363Z","updated_at":"2025-10-05T16:43:49.172Z","avatar_url":"https://github.com/dokar3.png","language":"Kotlin","readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"./images/banner.png\"  alt=\"Compose Sonner\"/\u003e\n  \u003ch1\u003eCompose Sonner\u003c/h1\u003e\n  \u003cimg alt=\"Maven Central\" src=\"https://img.shields.io/maven-central/v/io.github.dokar3/sonner?style=flat-square\u0026color=%23335AE7\"\u003e\n  \u003cimg alt=\"GitHub Workflow Status (with event)\" src=\"https://img.shields.io/github/actions/workflow/status/dokar3/compose-sonner/build.yaml?style=flat-square\"\u003e\n\u003c/div\u003e\n\n\u003e An opinionated toast component for Compose Multiplatform.\n\nThis is a Compose implementation of [sonner](https://github.com/emilkowalski/sonner) - an excellent toast library by [emilkowalski](https://github.com/emilkowalski).\n\n### Demo\n\nWasmJs Demo\n\n[https://dokar3.github.io/compose-sonner/](https://dokar3.github.io/compose-sonner/)\n\nVideo\n\nhttps://github.com/dokar3/compose-sonner/assets/68095777/ff97c6cc-012e-4152-8c40-0d2ba382c757\n\n### Features\n\n- Almost the same as emilkowalski's React implementation: UI, Animations, and even the API.\n  These include:\n    - Stacked toasts and expanded toasts\n    - Different types and rich colors\n    - Transitions\n    - Positions (Alignments)\n    - Swipe down to dismiss\n    - Close button and action button\n    - Custom icons\n    - Durations\n- Lazy and performant, you can display a large amount of toasts at once\n- Fully customizable, you can render your own Composable on it\n- Supports both Material 2 and Material 3 because it does not depend on them\n\n### Roadmaps\n\n- [x] iOS target. Done in [#8](https://github.com/dokar3/compose-sonner/pull/8), thanks to [@shubhamsinghshubham777](https://github.com/shubhamsinghshubham777)!\n- [x] Online wasmJs sample\n\n# Usages\n\n### Installation\n\nbuild.gradle(.kts)\n\n```kotlin\nimplementation(\"io.github.dokar3:sonner:\u003cVERSION\u003e\")\n```\n\nlibs.versions.toml\n\n```toml\nsonner = { module = \"io.github.dokar3:sonner\", version = \"\u003cVERSION\u003e\" }\n```\n\n### Basic\n\n```kotlin\nval toaster = rememberToasterState()\n\nButton(onClick = { toaster.show(\"Hello world!\") }) {\n    Text(\"Show a toast\")\n}\n\nToaster(state = toaster)\n```\n\n### Types\n\n```kotlin\ntoaster.show(\n    message = \"Message\",\n    type = ToastType.[Normal | Success | Info | Warning | Error],\n)\n```\n\n### Dark theme\n\n```kotlin\ntoaster.show(\n    message = \"Message\",\n    darkTheme = true,\n)\n```\n\n### Positions\n\n```kotlin\nToaster(\n    state = toaster,\n    alignment = Alignment.[TopStart | TopCenter | TopEnd | BottomStart | BottomCener | BottomEnd],\n)\n```\n\n### Durations\n\n```kotlin\ntoaster.show(\n    message = \"Message\",\n    duration = [ToasterDefaults.DurationLong | 5000.milliseconds | Duration.INFINITE],\n)\n```\n\n### Updates\n\n```kotlin\nconst val TOAST_ID_LOADING = ANYTHING\n\ntoaster.show(message = \"Loading\", id = TOAST_ID_LOADING, duration = Duration.INFINITE)\n\ntoaster.show(message = \"Success\", id = TOAST_ID_LOADING)\n```\n\n### Dismiss\n\n```kotlin\ntoaster.dismiss(id)\ntoaster.dismiss(toast)\ntoaster.dismissAll()\n```\n\n### Buttons\n\n```kotlin\n// Enable close buttons\nToaster(\n    state = toaster,\n    showCloseButton = true,\n)\n\n// Set an action button\ntoaster.show(\n    message = \"Message\",\n    action = TextToastAction(\n        text = \"Dismiss\",\n        onClick = { toaster.dismiss(it) },\n    )\n)\n```\n\n### Icons\n\n```kotlin\nToaster(\n    state = toaster,\n    iconSlot = { toast -\u003e\n        // ICON_LOADING can be anything, it's just a mark\n        if (toast.icon == ICON_LAODING) {\n            LoadingIcon()\n        } else {\n            // Fallback to the default icon slot\n            ToasterDefaults.iconSlot(toast)\n        }\n    },\n)\n```\n\n### Dismiss pause\n\n```kotlin\nToaster(\n    state = toaster,\n    dismissPause = ToastDismissPause.[Never | OnNotFront | OnInvisible],\n)\n```\n\n### Other\n\n```kotlin\n@Composable\nfun Toaster(\n    contentColor: @Composable (toast: Toast) -\u003e Color,\n    border: @Composable (toast: Toast) -\u003e BorderStroke,\n    background: @Composable (toast: Toast) -\u003e Brush,\n    shape: @Composable (toast: Toast) -\u003e Shape,\n    elevation: Dp,\n    shadowAmbientColor: Color,\n    shadowSpotColor: Color,\n    contentPadding: PaddingValues,\n    containerPadding: @Composable (toast: Toast) -\u003e PaddingValues,\n    widthPolicy: @Composable (toast: Toast) -\u003e ToastWidthPolicy,\n    offset: IntOffet,\n)\n```\n\n### Interact with ViewModel UiState\n\nDefine the mapping function\n\n```kotlin\nfun UiMessage.toToast(): Toast = when (this) {\n    is UiMessage.Error -\u003e Toast(id = id, message = message, type = ToastType.Error)\n    is UiMessage.Success -\u003e Toast(id = id, message = message, type = ToastType.Success)\n}\n```\n\nThen\n\n```kotlin\nval toaster = rememberToasterState(\n    onDismissed = { viewModel.removeUiMessageById(it.id as Long) },\n)\n\nval uiState by viewModel.uiState.collectAsState()\n\nLaunchedEffect(viewModel, toaster) {\n    // Listen to messages changes and map to toasts\n    val toastsFlow = viewModel.uiState.map { it.uiMessages.map(UiMessage::toToast) }\n    toaster.listenMany(toastsFlow)\n}\n\nToaster(state = toaster)\n```\n\nOr use an adapter function\n\n```kotlin\n@Composable\nfun UiMessageToaster(\n    messages: List\u003cUiMessage\u003e,\n    onRemoveMessage: (id: Long) -\u003e Unit,\n    modifier: Modifier = Modifier,\n) {\n    val toaster = rememberToasterState(\n        onToastDismissed = { onRemoveMessage(it.id as Long) },\n    )\n\n    val currentMessages by rememberUpdatedState(messages)\n\n    LaunchedEffect(toaster) {\n        // Listen to State\u003cList\u003cUiMessage\u003e\u003e changes and map to toasts\n        toaster.listenMany { currentMessages.map(UiMessage::toToast) }\n    }\n\n    Toaster(state = toaster, modifier = modifier)\n}\n\n@Composable\nfun YourScreen(...) {\n    val uiState by viewModel.uiState.collectAsState()\n\n    ...\n\n    UiMessageToaster(\n        messages = uiState.uiMessages,\n        onRemoveMessage = { viewModel.removeUiMessageById(it) },\n    )\n}\n```\n\n# License\n\n```\nCopyright 2024 dokar3\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","funding_links":[],"categories":["Kotlin","Uncategorized"],"sub_categories":["Uncategorized"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdokar3%2Fcompose-sonner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdokar3%2Fcompose-sonner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdokar3%2Fcompose-sonner/lists"}