Slint Kit Documentation

Choices and values

Range Slider for Slint

KitRangeSlider · Slint Kit 0.3.0

Two-thumb slider for a controlled range.

Two-thumb slider for a controlled range.
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.

Move either thumb or use arrow keys. Rust keeps ordered whole-number endpoints and applies five-point keyboard steps.

ui/app.slint

export { KitFormValidation, HtmlInputKind, NumberInputFormat, KitInputConstraints, KitValidationResult, KitValidityState } from "@kit/kit.slint";
// SPDX-License-Identifier: GPL-3.0-or-later
import { KitRangeSlider, RangeThumb, NavigationStep, KitTheme, KitThemes } from "@kit/kit.slint";
export component App inherits Window {
    title: "KitRangeSlider 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 := KitRangeSlider { theme: root.theme; label: "Allowed range"; lower-position: root.lower * 0.01; upper-position: root.upper * 0.01; lower-label: root.lower + "%"; upper-label: root.upper + "%";
            position-requested(thumb, value) => { root.position(thumb, value); }
            navigate(thumb, step) => { root.navigate(thumb, step); }
            gesture-started(thumb) => { root.begin(); } committed(thumb) => { root.commit(); } cancelled(thumb) => { root.cancel(); }
        }
        result := Text { text: "Range: " + root.lower + " to " + 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(20), 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-range-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

Project lower-position, upper-position and their labels from a validated range. Handle callbacks with RangeThumb.lower or .upper to preserve thumb identity.

Each thumb has keyboard focus and emits typed navigation steps. The controller enforces endpoint order and restores the range on cancellation.

Choose between built-in options and custom composition

Shared options

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
Two-thumb slider for a controlled range.
Native screenshot of the same example, rendered with Skia.
// SPDX-License-Identifier: GPL-3.0-or-later
import { KitRangeSlider, KitTheme, KitThemes } from "@kit/kit.slint";

export component Example inherits Window {
    title: "KitRangeSlider 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: "Range Slider"; color: root.theme.text-primary; font-size: 20px * root.theme.text-scale; font-weight: 600; }
        Text { text: "Price range · $20–$75"; color: root.theme.text-primary; font-size: 14px * root.theme.text-scale; wrap: word-wrap; } KitRangeSlider { theme: root.theme; label: "Price range"; lower-position: 0.2; upper-position: 0.75; lower-label: "$20"; upper-label: "$75"; }
        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
access FieldAccess in
lower-position float in
upper-position float in
label string in
lower-label string in
upper-label string in

Types and variants

Type Declaration
enum FieldAccess editable, read-only, disabled
enum NavigationStep next, previous, first, last, page-next, page-previous
enum RangeThumb lower, upper

Callbacks

Functions

See also