Slider for Slint
KitSlider · Slint Kit 0.3.0
Continuous slider projecting a domain value onto a normalized position.

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.
Click the track, then press Right: Rust increases the accepted volume in five-percentage-point steps.
ui/app.slint
export { KitFormValidation, HtmlInputKind, NumberInputFormat, KitInputConstraints, KitValidationResult, KitValidityState } from "@kit/kit.slint";
// SPDX-License-Identifier: GPL-3.0-or-later
import { KitSlider, RangeThumb, NavigationStep, KitTheme, KitThemes } from "@kit/kit.slint";
export component App inherits Window {
title: "KitSlider example";
preferred-width: 520px;
preferred-height: 220px;
max-width: 100000px;
max-height: 100000px;
background: root.theme.canvas;
in-out property <KitTheme> theme: KitThemes.light;
in property <int> lower;
in property <int> upper;
callback position(RangeThumb, float);
callback navigate(RangeThumb, NavigationStep);
callback begin();
callback commit();
callback cancel();
VerticalLayout {
padding: 24px;
spacing: 16px;
alignment: start;
control := KitSlider { theme: root.theme; label: "Volume"; position: root.upper * 0.01; value-label: root.upper + "%";
position-requested(value) => { root.position(RangeThumb.upper, value); }
navigate(step) => { root.navigate(RangeThumb.upper, step); }
gesture-started => { root.begin(); } committed => { root.commit(); } cancelled => { root.cancel(); }
}
result := Text { text: "Volume: " + root.upper + "%"; color: root.theme.text-primary; }
}
}
src/main.rs
// SPDX-License-Identifier: GPL-3.0-or-later
use slint::ComponentHandle;
use slint_kit::{RangeController, RangeThumb, DecimalValue, DecimalStep, NumericBounds, OrderedRange, StepDirection};
use std::{cell::RefCell, rc::Rc};
mod ui { slint::include_modules!(); }
fn value(n: i64) -> DecimalValue { DecimalValue::from_integer(n) }
fn thumb(t: ui::RangeThumb) -> RangeThumb { match t { ui::RangeThumb::Lower => RangeThumb::Start, ui::RangeThumb::Upper => RangeThumb::End } }
fn publish(app: &ui::App, range: &RangeController) {
app.set_lower(range.value().start().to_string().parse().expect("integral percent"));
app.set_upper(range.value().end().to_string().parse().expect("integral percent"));
}
fn main() -> Result<(), slint::PlatformError> {
let app = ui::App::new()?;
slint_kit::init_form_validation!(ui, app);
let range = Rc::new(RefCell::new(RangeController::new(NumericBounds::new(value(0), value(100)).unwrap(), OrderedRange::new(value(0), value(65)).unwrap(), DecimalStep::new(value(5)).unwrap()).unwrap()));
publish(&app, &range.borrow());
let state = range.clone(); let weak = app.as_weak();
app.on_position(move |t, position| { if !position.is_finite() { return; }
let quantized = value((position.clamp(0.0, 1.0) * 100.0).round() as i64);
let _ = state.borrow_mut().set(thumb(t), quantized);
if let Some(app) = weak.upgrade() { publish(&app, &state.borrow()); }
});
let state = range.clone(); let weak = app.as_weak();
app.on_navigate(move |t, step| {
let t = thumb(t);
{ let mut range = state.borrow_mut();
let _ = match step {
ui::NavigationStep::Next | ui::NavigationStep::PageNext => range.step(t, StepDirection::Increment),
ui::NavigationStep::Previous | ui::NavigationStep::PagePrevious => range.step(t, StepDirection::Decrement),
ui::NavigationStep::First => { let v = if t == RangeThumb::Start { value(0) } else { range.value().start() }; range.set(t, v) },
ui::NavigationStep::Last => { let v = if t == RangeThumb::End { value(100) } else { range.value().end() }; range.set(t, v) },
};
}
if let Some(app) = weak.upgrade() { publish(&app, &state.borrow()); }
});
let state = range.clone(); app.on_begin(move || state.borrow_mut().begin_drag());
let state = range.clone(); app.on_commit(move || state.borrow_mut().commit_drag());
let weak = app.as_weak(); app.on_cancel(move || { range.borrow_mut().cancel_drag(); if let Some(app) = weak.upgrade() { publish(&app, &range.borrow()); } });
app.run()
}
Cargo.toml and build.rs
Cargo.toml
[package]
name = "slint-kit-slider-example"
version = "0.1.0"
edition = "2024"
rust-version = "1.92"
license = "GPL-3.0-or-later"
publish = false
[dependencies]
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 position from 0 to 1 and a readable value-label. Convert position-requested(float) and navigate(NavigationStep) into requests on your domain type.
gesture-started, committed and cancelled delimit a reversible interaction. The view bounds rendering, while steps, rounding and accepted values belong to the controller.
Choose between built-in options and custom composition
Shared options
label— built-in property; custom composition example
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

// SPDX-License-Identifier: GPL-3.0-or-later
import { KitSlider, KitTheme, KitThemes } from "@kit/kit.slint";
export component Example inherits Window {
title: "KitSlider example";
preferred-width: 420px;
preferred-height: 180px;
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: "Slider"; color: root.theme.text-primary; font-size: 20px * root.theme.text-scale; font-weight: 600; }
Text { text: "Volume · 65%"; color: root.theme.text-primary; font-size: 14px * root.theme.text-scale; wrap: word-wrap; } KitSlider { theme: root.theme; label: "Volume"; position: 0.65; value-label: "65%"; position-requested(value) => { root.status = "Position requested"; } }
if root.status != "": Text { text: root.status; color: root.theme.text-secondary; font-size: 12px * root.theme.text-scale; wrap: word-wrap; }
}
}
Public API
Inherits Rectangle.
Properties
| Name | Type | Direction |
|---|---|---|
theme |
KitTheme |
in |
access |
FieldAccess |
in |
position |
float |
in |
label |
string |
in |
value-label |
string |
in |
Types and variants
| Type | Declaration |
|---|---|
enum FieldAccess |
editable, read-only, disabled |
enum NavigationStep |
next, previous, first, last, page-next, page-previous |
Callbacks
position-requested(float)navigate(NavigationStep)gesture-started()committed()cancelled()
Functions
take-focus()