Slint Kit Documentation

Feedback and progress

Toast for Slint

KitToast · Slint Kit 0.3.1

Identified transient notification with optional action and dismissal.

Identified transient notification with optional action and dismissal.
Native app built from the example below.

Try the component

Download the complete project, extract it and run cargo run --locked in its folder. Rust 1.92+; Slint Kit 0.3.0 from crates.io. You can also copy the four files below.

Show an archive notice, Undo its action, or dismiss it. This example keeps notices until an explicit action.

ui/app.slint

export { KitFormValidation, HtmlInputKind, NumberInputFormat, KitInputConstraints, KitValidationResult, KitValidityState } from "@kit/kit.slint";
// SPDX-License-Identifier: GPL-3.0-or-later
import { KitToast, KitButton, ToastEntry, ToastInteraction, AlertSeverity, KitTheme, KitThemes } from "@kit/kit.slint";
export component App inherits Window {
    title: "KitToast example";
    preferred-width: 520px;
    preferred-height: 420px;
    max-width: 100000px;
    max-height: 100000px;
    background: root.theme.canvas;
    in-out property <KitTheme> theme: KitThemes.light;
    in property <[ToastEntry]> entries;
    in property <string> result: "Project archived";
    callback undo(int);
    callback dismiss-toast(int);
    callback interaction(int, ToastInteraction, bool);
    callback add();
    VerticalLayout {
        padding: 24px;
        spacing: 16px;
        alignment: start;
        KitButton { theme: root.theme; text: "Show archive notice"; activated => { root.add(); } }
        if root.entries.length > 0: control := KitToast { theme: root.theme; entry: root.entries[0]; action(token) => { root.undo(token); } dismiss(token) => { root.dismiss-toast(token); } interaction(token, source, engaged) => { root.interaction(token, source, engaged); } }
        result := Text { text: root.result; color: root.theme.text-primary; }
    }

}

src/main.rs

// SPDX-License-Identifier: GPL-3.0-or-later
use web_time::Instant;
use slint::{ComponentHandle, ModelRc, VecModel};
use slint_kit::{ToastQueue, ToastLifetime, ToastPause};
use std::{cell::RefCell, rc::Rc, num::NonZeroUsize};
mod ui { slint::include_modules!(); }
fn publish(app: &ui::App, queue: &ToastQueue<()>) {
    let entries = queue.entries().map(|entry| ui::ToastEntry { token: i32::try_from(entry.id.get()).expect("view token capacity"), title: "Project archived".into(), message: "Your project can be restored.".into(), action_label: "Undo".into(), severity: ui::AlertSeverity::Success, dismissible: true }).collect::<Vec<_>>();
    app.set_entries(ModelRc::new(VecModel::from(entries)));
}
fn main() -> Result<(), slint::PlatformError> {
    let app = ui::App::new()?;
    slint_kit::init_form_validation!(ui, app);
    let epoch = Instant::now();
    let queue = Rc::new(RefCell::new(ToastQueue::new(NonZeroUsize::new(1).unwrap())));
    queue.borrow_mut().push((), ToastLifetime::Persistent, epoch.elapsed()).expect("toast identity");
    publish(&app, &queue.borrow());
    let state = queue.clone(); let weak = app.as_weak();
    app.on_add(move || { state.borrow_mut().push((), ToastLifetime::Persistent, epoch.elapsed()).expect("toast identity"); if let Some(app) = weak.upgrade() { publish(&app, &state.borrow()); app.set_result("Project archived".into()); } });
    let state = queue.clone(); let weak = app.as_weak();
    app.on_undo(move |token| {
        let id = state.borrow().entries().find(|entry| i32::try_from(entry.id.get()).ok() == Some(token)).map(|entry| entry.id);
        if let Some(id) = id { state.borrow_mut().remove(id); if let Some(app) = weak.upgrade() { publish(&app, &state.borrow()); app.set_result("Archive undone".into()); } }
    });
    let state = queue.clone(); let weak = app.as_weak();
    app.on_dismiss_toast(move |token| {
        let id = state.borrow().entries().find(|entry| i32::try_from(entry.id.get()).ok() == Some(token)).map(|entry| entry.id);
        if let Some(id) = id { state.borrow_mut().remove(id); if let Some(app) = weak.upgrade() { publish(&app, &state.borrow()); app.set_result("Toast dismissed".into()); } }
    });
    app.on_interaction(move |token, source, engaged| {
        let id = queue.borrow().entries().find(|entry| i32::try_from(entry.id.get()).ok() == Some(token)).map(|entry| entry.id);
        if let Some(id) = id { queue.borrow_mut().set_paused(id, match source { ui::ToastInteraction::Pointer => ToastPause::Pointer, ui::ToastInteraction::Focus => ToastPause::Focus }, engaged, epoch.elapsed()); }
    });
    app.run()
}

Cargo.toml and build.rs

Cargo.toml

[package]
name = "slint-kit-toast-example"
version = "0.1.0"
edition = "2024"
rust-version = "1.92"
license = "GPL-3.0-or-later"
publish = false

[dependencies]
web-time = "=1.1.0"
slint-kit = "=0.3.0"
slint = { version = "=1.18.1", default-features = false, features = ["std", "compat-1-18", "backend-winit", "renderer-skia", "accessibility"] }

[build-dependencies]
slint-kit-build = "=0.3.0"

[workspace]

build.rs

fn main() { slint_kit_build::compile("ui/app.slint").expect("compile component example"); }

Files: Cargo.toml · build.rs · ui/app.slint · src/main.rs

Supply entry: ToastEntry and handle action(token) and dismiss(token) in the owning queue. Connect interaction(token, ToastInteraction, bool) to Rust lifetime pauses.

ToastInteraction.pointer and .focus are separate pause sources. The view adds no timer; the controller preserves remaining lifetime and stable identities.

Visual composition

The file below produces the screenshot. For controller-driven components, it shows the displayed state without supplying the Rust wiring. Create a project.

Show the screenshot source
Identified transient notification with optional action and dismissal.
Native screenshot of the same example, rendered with Skia.
// SPDX-License-Identifier: GPL-3.0-or-later
import { AlertSeverity, KitTheme, KitThemes, KitToast } from "@kit/kit.slint";

export component Example inherits Window {
    title: "KitToast example";
    preferred-width: 480px;
    preferred-height: 260px;
    max-width: 100000px;
    max-height: 100000px;
    background: root.theme.canvas;
    in-out property <KitTheme> theme: KitThemes.light;
    in-out property <string> status;
    VerticalLayout {
        padding: 24px; spacing: 16px; alignment: start;
        Text { text: "Toast"; color: root.theme.text-primary; font-size: 20px * root.theme.text-scale; font-weight: 600; }
        KitToast { theme: root.theme; entry: { token: 11, title: "Project archived", message: "Your notes are safely stored.", action-label: "Undo", severity: AlertSeverity.success, dismissible: true }; action(token) => { root.status = "Undo requested"; } }
        if root.status != "": Text { text: root.status; color: root.theme.text-secondary; font-size: 12px * root.theme.text-scale; wrap: word-wrap; }
    }
}

Download this example

Public API

Inherits Rectangle.

Properties

Name Type Direction
theme KitTheme in
entry ToastEntry in

Types and variants

Type Declaration
enum AlertSeverity information, success, warning, error
struct ToastEntry token: int, title: string, message: string, action-label: string, severity: AlertSeverity, dismissible: bool
enum ToastInteraction pointer, focus

Callbacks

See also